1.9.21
This commit is contained in:
parent
7b24ce1097
commit
6cf7c3c589
@ -13,6 +13,8 @@ use think\facade\Request;
|
|||||||
use think\facade\Lang;
|
use think\facade\Lang;
|
||||||
use think\facade\View;
|
use think\facade\View;
|
||||||
use think\facade\Route;
|
use think\facade\Route;
|
||||||
|
use taoser\SetArr;
|
||||||
|
use app\common\lib\Uploads;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 控制器基础类
|
* 控制器基础类
|
||||||
@ -250,11 +252,12 @@ abstract class BaseController
|
|||||||
$indexUrl = $this->getIndexUrl();
|
$indexUrl = $this->getIndexUrl();
|
||||||
if(config('taoler.url_rewrite.article_as') == '<ename>/'){
|
if(config('taoler.url_rewrite.article_as') == '<ename>/'){
|
||||||
// 分类可变路由
|
// 分类可变路由
|
||||||
$artUrl = (string) Route::buildUrl('article_detail', ['id' => $aid, 'ename'=> $ename]);
|
$artUrl = (string) url('article_detail', ['id' => (int) $aid, 'ename'=> $ename]);
|
||||||
|
//$artUrl = (string) Route::buildUrl('article_detail', ['id' => $aid, 'ename'=> $ename]);
|
||||||
} else {
|
} else {
|
||||||
$artUrl = (string) url('article_detail', ['id' => $aid]);
|
$artUrl = (string) url('article_detail', ['id' => $aid]);
|
||||||
}
|
}
|
||||||
|
//dump($artUrl);
|
||||||
|
|
||||||
// 判断是否开启绑定
|
// 判断是否开启绑定
|
||||||
//$domain_bind = array_key_exists('domain_bind',config('app'));
|
//$domain_bind = array_key_exists('domain_bind',config('app'));
|
||||||
@ -288,6 +291,191 @@ abstract class BaseController
|
|||||||
return $url;
|
return $url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关键词
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setTags($data)
|
||||||
|
{
|
||||||
|
$tags = [];
|
||||||
|
|
||||||
|
if($data['flag'] == 'on') {
|
||||||
|
// 百度分词自动生成关键词
|
||||||
|
if(!empty(config('taoler.baidu.client_id')) == true) {
|
||||||
|
$url = 'https://aip.baidubce.com/rpc/2.0/nlp/v1/lexer?charset=UTF-8&access_token='.config('taoler.baidu.access_token');
|
||||||
|
|
||||||
|
//headers数组内的格式
|
||||||
|
$headers = array();
|
||||||
|
$headers[] = "Content-Type:application/json";
|
||||||
|
$body = array(
|
||||||
|
"text" => $data['tags']
|
||||||
|
);
|
||||||
|
$postBody = json_encode($body);
|
||||||
|
$curl = curl_init();
|
||||||
|
curl_setopt($curl, CURLOPT_URL, $url);
|
||||||
|
curl_setopt($curl, CURLOPT_POST, true);
|
||||||
|
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);//设置请求头
|
||||||
|
curl_setopt($curl, CURLOPT_POSTFIELDS, $postBody);//设置请求体
|
||||||
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||||
|
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');//使用一个自定义的请求信息来代替"GET"或"HEAD"作为HTTP请求。(这个加不加没啥影响)
|
||||||
|
$datas = curl_exec($curl);
|
||||||
|
if($datas == false) {
|
||||||
|
echo '接口无法链接';
|
||||||
|
} else {
|
||||||
|
$res = stripos($datas,'error_code');
|
||||||
|
// 接收返回的数据
|
||||||
|
$dataItem = json_decode($datas);
|
||||||
|
if($res == false) {
|
||||||
|
// 数据正常
|
||||||
|
$items = $dataItem->items;
|
||||||
|
foreach($items as $item) {
|
||||||
|
if($item->pos == 'n' && !in_array($item->item,$tags)){
|
||||||
|
$tags[] = $item->item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 接口正常但获取数据失败,可能参数错误,重新获取token
|
||||||
|
$url = 'https://aip.baidubce.com/oauth/2.0/token';
|
||||||
|
$post_data['grant_type'] = config('taoler.baidu.grant_type');;
|
||||||
|
$post_data['client_id'] = config('taoler.baidu.client_id');
|
||||||
|
$post_data['client_secret'] = config('taoler.baidu.client_secret');
|
||||||
|
|
||||||
|
$o = "";
|
||||||
|
foreach ( $post_data as $k => $v )
|
||||||
|
{
|
||||||
|
$o.= "$k=" . urlencode( $v ). "&" ;
|
||||||
|
}
|
||||||
|
$post_data = substr($o,0,-1);
|
||||||
|
$res = $this->request_post($url, $post_data);
|
||||||
|
// 写入token
|
||||||
|
SetArr::name('taoler')->edit([
|
||||||
|
'baidu'=> [
|
||||||
|
'access_token' => json_decode($res)->access_token,
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
echo 'api接口数据错误 - ';
|
||||||
|
echo $dataItem->error_msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 手动添加关键词
|
||||||
|
// 中文一个或者多个空格转换为英文空格
|
||||||
|
$str = preg_replace('/\s+/',' ',$data['tags']);
|
||||||
|
$att = explode(' ', $str);
|
||||||
|
foreach($att as $v){
|
||||||
|
if ($v !='') {
|
||||||
|
$tags[] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return json(['code'=>0,'data'=>$tags]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// api_post接口
|
||||||
|
function request_post($url = '', $param = '')
|
||||||
|
{
|
||||||
|
if (empty($url) || empty($param)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$postUrl = $url;
|
||||||
|
$curlPost = $param;
|
||||||
|
$curl = curl_init();//初始化curl
|
||||||
|
curl_setopt($curl, CURLOPT_URL,$postUrl);//抓取指定网页
|
||||||
|
curl_setopt($curl, CURLOPT_HEADER, 0);//设置header
|
||||||
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);//要求结果为字符串且输出到屏幕上
|
||||||
|
curl_setopt($curl, CURLOPT_POST, 1);//post提交方式
|
||||||
|
curl_setopt($curl, CURLOPT_POSTFIELDS, $curlPost);
|
||||||
|
$data = curl_exec($curl);//运行curl
|
||||||
|
curl_close($curl);
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题调用百度关键词词条
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function getBdiduSearchWordList($words)
|
||||||
|
{
|
||||||
|
if(empty($words)) return json(['code'=>-1,'msg'=>'null']);
|
||||||
|
$url = 'https://www.baidu.com/sugrec?prod=pc&from=pc_web&wd='.$words;
|
||||||
|
//$result = Api::urlGet($url);
|
||||||
|
$curl = curl_init();
|
||||||
|
curl_setopt($curl, CURLOPT_URL, $url);
|
||||||
|
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
|
||||||
|
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
|
||||||
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||||
|
$datas = curl_exec($curl);
|
||||||
|
curl_close($curl);
|
||||||
|
$data = json_decode($datas,true);
|
||||||
|
if(isset($data['g'])) {
|
||||||
|
return json(['code'=>0,'msg'=>'success','data'=>$data['g']]);
|
||||||
|
} else {
|
||||||
|
return json(['code'=>-1,'msg'=>'null']);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* baidu push api
|
||||||
|
*
|
||||||
|
* @param string $link
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function baiduPushUrl(string $link)
|
||||||
|
{
|
||||||
|
// baidu 接口
|
||||||
|
$api = config('taoler.baidu.push_api');
|
||||||
|
if(!empty($api)) {
|
||||||
|
$url[] = $link;
|
||||||
|
$ch = curl_init();
|
||||||
|
$options = array(
|
||||||
|
CURLOPT_URL => $api,
|
||||||
|
CURLOPT_POST => true,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_POSTFIELDS => implode("\n", $url),
|
||||||
|
CURLOPT_HTTPHEADER => array('Content-Type: text/plain'),
|
||||||
|
);
|
||||||
|
curl_setopt_array($ch, $options);
|
||||||
|
curl_exec($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传接口
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function uploadFiles($type)
|
||||||
|
{
|
||||||
|
$type = Request::param('type');
|
||||||
|
$uploads = new Uploads();
|
||||||
|
switch ($type){
|
||||||
|
case 'image':
|
||||||
|
$upRes = $uploads->put('file','article_pic',1024,'image');
|
||||||
|
break;
|
||||||
|
case 'zip':
|
||||||
|
$upRes = $uploads->put('file','article_zip',1024,'application|image');
|
||||||
|
break;
|
||||||
|
case 'video':
|
||||||
|
$upRes = $uploads->put('file','article_video',102400,'video|audio');
|
||||||
|
break;
|
||||||
|
case 'audio':
|
||||||
|
$upRes = $uploads->put('file','article_audio',102400,'audio');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$upRes = $uploads->put('file','article_file',1024,'image');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return $upRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ use think\facade\Request;
|
|||||||
use think\facade\Db;
|
use think\facade\Db;
|
||||||
use think\facade\Cache;
|
use think\facade\Cache;
|
||||||
use taoler\com\Files;
|
use taoler\com\Files;
|
||||||
|
use app\common\lib\Msgres;
|
||||||
|
|
||||||
class Forum extends AdminController
|
class Forum extends AdminController
|
||||||
{
|
{
|
||||||
@ -326,4 +327,188 @@ class Forum extends AdminController
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加帖子文章
|
||||||
|
* @return string|\think\Response|\think\response\Json|void
|
||||||
|
*/
|
||||||
|
public function add()
|
||||||
|
{
|
||||||
|
if (Request::isAjax()) {
|
||||||
|
|
||||||
|
$data = Request::only(['cate_id', 'title', 'title_color', 'tiny_content', 'content', 'upzip', 'tags', 'description', 'captcha']);
|
||||||
|
$data['user_id'] = 1; //管理员ID
|
||||||
|
// 调用验证器
|
||||||
|
$validate = new \app\common\validate\Article;
|
||||||
|
$result = $validate->scene('Artadd')->check($data);
|
||||||
|
if (true !== $result) {
|
||||||
|
return Msgres::error($validate->getError());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取内容图片音视频标识
|
||||||
|
$iva= $this->hasIva($data['content']);
|
||||||
|
$data = array_merge($data,$iva);
|
||||||
|
// 获取分类ename
|
||||||
|
$cate_ename = Db::name('cate')->where('id',$data['cate_id'])->value('ename');
|
||||||
|
|
||||||
|
$article = new Article();
|
||||||
|
$result = $article->add($data);
|
||||||
|
if ($result['code'] == 1) {
|
||||||
|
// 获取到的最新ID
|
||||||
|
$aid = Db::name('article')->max('id');
|
||||||
|
// 清除文章tag缓存
|
||||||
|
Cache::tag('tagArtDetail')->clear();
|
||||||
|
|
||||||
|
$link = $this->getRouteUrl((int)$aid, $cate_ename);
|
||||||
|
// 推送给百度收录接口
|
||||||
|
$this->baiduPushUrl($link);
|
||||||
|
|
||||||
|
$url = $result['data']['status'] ? $link : (string)url('index/');
|
||||||
|
$res = Msgres::success($result['msg'], $url);
|
||||||
|
} else {
|
||||||
|
$res = Msgres::error('add_error');
|
||||||
|
}
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
//1.查询分类表获取所有分类
|
||||||
|
$cateList = Db::name('cate')->where(['status'=>1,'delete_time'=>0])->order('sort','asc')->cache('catename',3600)->select();
|
||||||
|
|
||||||
|
//2.将catelist变量赋给模板 公共模板nav.html
|
||||||
|
View::assign('cateList',$cateList);
|
||||||
|
|
||||||
|
return View::fetch('add');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑文章
|
||||||
|
* @param $id
|
||||||
|
* @return string|\think\Response|\think\response\Json|void
|
||||||
|
* @throws \think\db\exception\DataNotFoundException
|
||||||
|
* @throws \think\db\exception\DbException
|
||||||
|
* @throws \think\db\exception\ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function edit($id)
|
||||||
|
{
|
||||||
|
$article = Article::find($id);
|
||||||
|
|
||||||
|
if(Request::isAjax()){
|
||||||
|
$data = Request::only(['id','cate_id','title','title_color','content','upzip','tags','description','captcha']);
|
||||||
|
$data['user_id'] = 1;
|
||||||
|
//调用验证器
|
||||||
|
$validate = new \app\common\validate\Article();
|
||||||
|
$res = $validate->scene('Artadd')->check($data);
|
||||||
|
|
||||||
|
if(true !== $res){
|
||||||
|
return Msgres::error($validate->getError());
|
||||||
|
} else {
|
||||||
|
//获取内容图片音视频标识
|
||||||
|
$iva= $this->hasIva($data['content']);
|
||||||
|
$data = array_merge($data,$iva);
|
||||||
|
|
||||||
|
$result = $article->edit($data);
|
||||||
|
if($result == 1) {
|
||||||
|
//删除原有缓存显示编辑后内容
|
||||||
|
Cache::delete('article_'.$id);
|
||||||
|
$link = $this->getRouteUrl((int) $id, $article->cate->ename);
|
||||||
|
// 推送给百度收录接口
|
||||||
|
$this->baiduPushUrl($link);
|
||||||
|
$editRes = Msgres::success('edit_success',$link);
|
||||||
|
} else {
|
||||||
|
$editRes = Msgres::error($result);
|
||||||
|
}
|
||||||
|
return $editRes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 查询标签
|
||||||
|
$tag = $article->tags;
|
||||||
|
$tags = [];
|
||||||
|
if(!is_null($tag)) {
|
||||||
|
$attr = explode(',',$tag);
|
||||||
|
foreach($attr as $key => $v){
|
||||||
|
if ($v !='') {
|
||||||
|
$tags[] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
View::assign(['article'=>$article,'tags'=>$tags]);
|
||||||
|
//1.查询分类表获取所有分类
|
||||||
|
$cateList = Db::name('cate')->where(['status'=>1,'delete_time'=>0])->order('sort','asc')->cache('catename',3600)->select();
|
||||||
|
|
||||||
|
//2.将catelist变量赋给模板 公共模板nav.html
|
||||||
|
View::assign('cateList',$cateList);
|
||||||
|
|
||||||
|
return View::fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用百度关键词
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function tagWord()
|
||||||
|
{
|
||||||
|
$data = Request::only(['tags','flag']);
|
||||||
|
return $this->setTags($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题调用百度关键词词条
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function getWordList()
|
||||||
|
{
|
||||||
|
$title = input('title');
|
||||||
|
return $this->getBdiduSearchWordList($title);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容中是否有图片视频音频插入
|
||||||
|
*
|
||||||
|
* @param [type] $content
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function hasIva($content)
|
||||||
|
{
|
||||||
|
//判断是否插入图片
|
||||||
|
$isHasImg = strpos($content,'img[');
|
||||||
|
$data['has_img'] = is_int($isHasImg) ? 1 : 0;
|
||||||
|
//判断是否插入视频
|
||||||
|
$isHasVideo = strpos($content,'video(');
|
||||||
|
$data['has_video'] = is_int($isHasVideo) ? 1 : 0;
|
||||||
|
//判断是否插入音频
|
||||||
|
$isHasAudio = strpos($content,'audio[');
|
||||||
|
$data['has_audio'] = is_int($isHasAudio) ? 1 : 0;
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取描述,过滤html
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function getDescription()
|
||||||
|
{
|
||||||
|
$data = Request::only(['content']);
|
||||||
|
$description = getArtContent($data['content']);
|
||||||
|
return json(['code'=>0,'data'=>$description]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传接口
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function uploads()
|
||||||
|
{
|
||||||
|
$type = Request::param('type');
|
||||||
|
return $this->uploadFiles($type);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: TaoLer <alipay_tao@qq.com>
|
* @Author: TaoLer <alipay_tao@qq.com>
|
||||||
* @Date: 2021-12-06 16:04:50
|
* @Date: 2021-12-06 16:04:50
|
||||||
* @LastEditTime: 2022-06-29 15:29:13
|
* @LastEditTime: 2022-07-29 17:17:41
|
||||||
* @LastEditors: TaoLer
|
* @LastEditors: TaoLer
|
||||||
* @Description: admin路由配置
|
* @Description: admin路由配置
|
||||||
* @FilePath: \TaoLer\app\admin\route\route.php
|
* @FilePath: \github\TaoLer\app\admin\route\route.php
|
||||||
* Copyright (c) 2020~2022 https://www.aieok.com All rights reserved.
|
* Copyright (c) 2020~2022 https://www.aieok.com All rights reserved.
|
||||||
*/
|
*/
|
||||||
use think\facade\Route;
|
use think\facade\Route;
|
||||||
|
291
app/admin/view/forum/add.html
Normal file
291
app/admin/view/forum/add.html
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
{extend name="public/base" /}
|
||||||
|
|
||||||
|
{block name="body"}
|
||||||
|
|
||||||
|
<div class="layui-form layui-form-pane" lay-filter="layuiadmin-form-addforum" id="layuiadmin-form-addforum">
|
||||||
|
<div class="layui-tab layui-tab-brief" lay-filter="user">
|
||||||
|
<div class="layui-tab-content" id="LAY_ucm" style="padding: 20px 0">
|
||||||
|
<div class="layui-tab-item layui-show">
|
||||||
|
|
||||||
|
<div class="layui-row layui-col-space15 layui-form-item">
|
||||||
|
<div class="layui-col-md3">
|
||||||
|
<label class="layui-form-label">{:lang('special column')}</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<select lay-verify="required" name="cate_id" lay-filter="column">
|
||||||
|
<option></option>
|
||||||
|
{volist name="cateList" id="cate"}
|
||||||
|
<option value="{$cate.id}" {if($Request.param.cate == $cate.ename)} selected {/if}>{:cookie('think_lang') == 'en-us' ? $cate.ename : $cate.catename}</option>
|
||||||
|
{/volist}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-col-md8">
|
||||||
|
<label for="L_title" class="layui-form-label">{:lang('title')}</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="text" id="L_title" name="title" required lay-verify="required" autocomplete="off" class="layui-input" style="position:relative;" value=""/>
|
||||||
|
<input type="hidden" id="L_title_color" name="title_color" autocomplete="off" class="layui-input" />
|
||||||
|
<div class="layui-input bdsug layui-hide">
|
||||||
|
<ul class="wordlist">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-col-md1">
|
||||||
|
<div id="color"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="layui-form-item layui-form-text">
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<textarea
|
||||||
|
id="L_content"
|
||||||
|
name="content"
|
||||||
|
required
|
||||||
|
lay-verify="required"
|
||||||
|
placeholder="{:lang('please input the content')}"
|
||||||
|
class="layui-textarea fly-editor"
|
||||||
|
style="height: 260px">
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-inline">
|
||||||
|
<label class="layui-form-label">{:lang('enclosure')}</label>
|
||||||
|
<div class="layui-input-inline" style="width: 190px">
|
||||||
|
<input type="text" class="layui-input" name="upzip" value="" placeholder="zip,image文件" title="上传附件" />
|
||||||
|
</div>
|
||||||
|
<button type="button" class="layui-btn" id="zip-button"><i class="layui-icon"></i>{:lang('uploads')}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">{:lang('描述')}</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<textarea name="description" class="layui-textarea" placeholder="SEO描述"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-inline">
|
||||||
|
<label class="layui-form-label">{:lang('add tags')}</label>
|
||||||
|
<div class="layui-input-inline" style="width: 190px">
|
||||||
|
<input type="text" class="layui-input" name="tags" value="" placeholder="多个用空格隔开" title="添加标签" />
|
||||||
|
</div>
|
||||||
|
<button type="button" class="layui-btn" id="article-tags-button">{:lang('add')}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-btn-container"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="layui-form-item layui-hide">
|
||||||
|
<button type="submit" class="layui-btn" lay-filter="article-add" lay-submit id="article-add">{:lang('post now')}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/block}
|
||||||
|
{block name="js"}
|
||||||
|
<script src="/static/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src="/addons/taonyeditor/tinymce/tinymce.min.js"></script>
|
||||||
|
<script src="/addons/taonyeditor/tinymce/tinymce-jquery.min.js"></script>
|
||||||
|
<script>
|
||||||
|
//定义选择器
|
||||||
|
var mytextareaid = 'textarea#L_content';
|
||||||
|
var imagePrependUrl = "{$domain}";
|
||||||
|
//定义文件上传接口接口
|
||||||
|
var taonyUploadUrl = "{:url('forum/uploads')}",
|
||||||
|
taonyUploadImgage = "{:url('forum/uploads')}?type=image",
|
||||||
|
taonyUploadVideo = "{:url('forum/uploads')}?type=video";
|
||||||
|
taonyUploadZip = "{:url('forum/uploads')}?type=zip";
|
||||||
|
taonyUploadAudio = "{:url('forum/uploads')}?type=audio";
|
||||||
|
$(mytextareaid).removeClass();
|
||||||
|
</script>
|
||||||
|
<script src="/addons/taonyeditor/js/taonyeditor.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
layui.config({
|
||||||
|
base: '/static/admin/' //静态资源所在路径
|
||||||
|
}).extend({
|
||||||
|
index: 'lib/index' //主入口模块
|
||||||
|
}).use(["form", "colorpicker", "upload", 'editor'], function () {
|
||||||
|
var $ = layui.jquery,
|
||||||
|
form = layui.form,
|
||||||
|
colorpicker = layui.colorpicker,
|
||||||
|
upload = layui.upload;
|
||||||
|
|
||||||
|
var editor = layui.editor;
|
||||||
|
|
||||||
|
// 从详情页自动调用端口过滤,获取描述信息
|
||||||
|
tinymce.get('L_content').on('mouseleave', function() {
|
||||||
|
var content = tinymce.get('L_content').getContent({format: 'text'});
|
||||||
|
content = content.replace(/[\r\n]/g,"").replace(/\n/g, '').replace(/\s/g, '').replace(/\t/g, '');
|
||||||
|
if(content.length >200) {
|
||||||
|
content = content.substring(0,200);
|
||||||
|
}
|
||||||
|
// var test = tinymce.activeEditor.getContent({format: 'text'});
|
||||||
|
$('[name="description"]').val(content);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// 改变标题颜色
|
||||||
|
colorpicker.render({
|
||||||
|
elem: "#color",
|
||||||
|
color: "#393d49",
|
||||||
|
predefine: true, // 开启预定义颜色
|
||||||
|
done: function (color) {
|
||||||
|
//譬如你可以在回调中把得到的 color 赋值给表单
|
||||||
|
$("#L_title_color").val(color);
|
||||||
|
$("#L_title").css("color", color);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
//上传附件
|
||||||
|
upload.render({
|
||||||
|
elem: "#zip-button",
|
||||||
|
url: "{:url('forum/uploads')}", //改成您自己的上传接口
|
||||||
|
data: { type: "zip" },
|
||||||
|
accept: "file", //普通文件
|
||||||
|
done: function (res) {
|
||||||
|
if (res.status == 0) {
|
||||||
|
$('input[name="upzip"]').val(res.url);
|
||||||
|
layer.msg("上传成功");
|
||||||
|
} else {
|
||||||
|
layer.msg(res.msg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 发布文章
|
||||||
|
// form.on("submit(article-add)", function (data) {
|
||||||
|
// var field = data.field;
|
||||||
|
// var numArr = new Array();
|
||||||
|
// $(".layui-btn-container").children("button").each(function () {
|
||||||
|
// numArr.push($(this).val()); //添加至数组
|
||||||
|
// });
|
||||||
|
// tags = numArr.lenth ? "" : numArr.join(",");
|
||||||
|
// var index = layer.load(1);
|
||||||
|
// $.ajax({
|
||||||
|
// type: "post",
|
||||||
|
// url: "{:url('Forum/add')}",
|
||||||
|
// data: field,
|
||||||
|
// dataType: "json",
|
||||||
|
// success: function (data) {
|
||||||
|
// if (data.code == 0) {
|
||||||
|
// layer.msg(data.msg, { icon: 6, time: 2000 }, function () {
|
||||||
|
// location.href = data.url;
|
||||||
|
// });
|
||||||
|
// } else {
|
||||||
|
// layer.open({ title: "发布失败", content: data.msg, icon: 5, anim: 6 });
|
||||||
|
// }
|
||||||
|
// layer.close(index);
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// return false;
|
||||||
|
// });
|
||||||
|
|
||||||
|
|
||||||
|
// 手动添加tags
|
||||||
|
$("#article-tags-button").on("click", function () {
|
||||||
|
var tags = $("input[name='tags']").val();
|
||||||
|
var flag = "off";
|
||||||
|
getTags(tags, flag);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 通过接口自动获取tag的内容
|
||||||
|
var conf = "{:empty(config('taoler.baidu.client_id'))}";
|
||||||
|
if (conf !== "1") {
|
||||||
|
$("#L_title").on("blur", function () {
|
||||||
|
var title = $(this).val();
|
||||||
|
var flag = "on";
|
||||||
|
// 清空上次生成的tag button
|
||||||
|
$(".layui-btn-container").children("button").each(function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
getTags(title, flag);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 百度词条
|
||||||
|
var baidu_title_switch = "{:config('taoler.config.baidu_title_switch')}";
|
||||||
|
if(baidu_title_switch == 1) {
|
||||||
|
$("#L_title").bind('input propertychange',function () {
|
||||||
|
var title = $(this).val();
|
||||||
|
var str = '';
|
||||||
|
if(title.length > 0 ) {
|
||||||
|
$.post("{:url('forum/getWordList')}",{title:title},function(res){
|
||||||
|
// 动态生成ur>li内容
|
||||||
|
if (res.code == 0) {
|
||||||
|
// 显示动态框
|
||||||
|
$(".bdsug").removeClass('layui-hide');
|
||||||
|
for (var i = 0; i < res.data.length; i++) {
|
||||||
|
//str += '<li data-key=' + res.data[i].q + '><b>' + res.data[i].q.replace(title,'') + '</b></li>';
|
||||||
|
str += '<li data-key=' + res.data[i].q + '><b>' + res.data[i].q + '</b></li>';
|
||||||
|
}
|
||||||
|
// 清空ul并追加li
|
||||||
|
$('.wordlist').empty().append(str);
|
||||||
|
// 点击李获取li值并复制给#L_title input的value
|
||||||
|
$(".bdsug li").on('click',function(){
|
||||||
|
var word = $(this).attr('data-key');
|
||||||
|
var words = title + '(' + word + ')';
|
||||||
|
$("#L_title").val(words);
|
||||||
|
// 关闭动态框
|
||||||
|
$(".bdsug").addClass('layui-hide');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$(".bdsug").addClass('layui-hide');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$(".bdsug").addClass('layui-hide');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加关键词button
|
||||||
|
function getTags(tags, flag) {
|
||||||
|
if (tags == "") {
|
||||||
|
layer.msg("tag不能为空");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//把得到的tags放进数组
|
||||||
|
var numArr = new Array();
|
||||||
|
$(".layui-btn-container")
|
||||||
|
.children("button")
|
||||||
|
.each(function () {
|
||||||
|
numArr.push($(this).val()); //添加至数组
|
||||||
|
});
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "post",
|
||||||
|
url: "{:url('Forum/tagWord')}",
|
||||||
|
data: { tags: tags, flag: flag },
|
||||||
|
daType: "json",
|
||||||
|
success: function (data) {
|
||||||
|
if (data.code == 0) {
|
||||||
|
for (var i = 0; i < data.data.length; i++) {
|
||||||
|
if ($.inArray(data.data[i], numArr) < 0) {
|
||||||
|
$(".layui-btn-container").append(
|
||||||
|
'<button type="button" class="layui-btn layui-btn-sm layui-btn-danger" value=' + data.data[i] + ">" + data.data[i] + " x" + "</button>"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$("input[name='tags']").val("");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除tag
|
||||||
|
$(document).ready(function () {
|
||||||
|
$(".layui-btn-container").on("click", "button", function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{/block}
|
255
app/admin/view/forum/edit.html
Normal file
255
app/admin/view/forum/edit.html
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
{extend name="public/base" /}
|
||||||
|
|
||||||
|
{block name="body"}
|
||||||
|
<div class="layui-form layui-form-pane">
|
||||||
|
<div class="layui-tab layui-tab-brief" lay-filter="user">
|
||||||
|
<div class="layui-form layui-tab-content" id="LAY_ucm" style="padding: 20px 0;">
|
||||||
|
<div class="layui-tab-item layui-show">
|
||||||
|
|
||||||
|
<input type="hidden" name="id" value="{$article.id}">
|
||||||
|
<div class="layui-row layui-col-space15 layui-form-item">
|
||||||
|
<div class="layui-col-md3">
|
||||||
|
<label class="layui-form-label">{:lang('special column')}</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<select lay-verify="required" name="cate_id" lay-filter="column">
|
||||||
|
<option></option>
|
||||||
|
{volist name="cateList" id="cate"}
|
||||||
|
<option value="{$cate.id}" {if $article.cate_id == $cate.id}selected{/if}>{:cookie('think_lang') == 'en-us' ? $cate.ename : $cate.catename}</option>
|
||||||
|
{/volist}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-col-md8">
|
||||||
|
<label for="L_title" class="layui-form-label">{:lang('title')}</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="text" id="L_title" name="title" required lay-verify="required" autocomplete="off" class="layui-input" value="{$article.title}">
|
||||||
|
<input type="hidden" id="L_title_color" name="title_color" autocomplete="off" class="layui-input" value="{$article.title_color ?? '#333'}">
|
||||||
|
<input type="hidden" name="user_id" value="{$article.user_id}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-col-md1">
|
||||||
|
<div id="color"></div>
|
||||||
|
<div id="test9" style="margin-left: 30px;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="layui-form-item layui-form-text">
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<textarea id="L_content" name="content" required lay-verify="required" placeholder="详细内容" class="layui-textarea fly-editor" style="height: 260px;">{$article.content}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-inline">
|
||||||
|
<label class="layui-form-label">{:lang('enclosure')}</label>
|
||||||
|
<div class="layui-input-inline" style="width: 190px;">
|
||||||
|
<input type="text" class="layui-input" name="upzip" value="{$article.upzip}" placeholder="zip,jpg格式" title="上传附件"/>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="layui-btn" id="zip-button"><i class="layui-icon"></i>上传文件</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">{:lang('描述')}</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<textarea name="description" class="layui-textarea" placeholder="SEO描述">{$article.description}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-inline">
|
||||||
|
<label class="layui-form-label">{:lang('add tags')}</label>
|
||||||
|
<div class="layui-input-inline" style="width: 190px;">
|
||||||
|
<input type="text" class="layui-input" name="tags" placeholder="多个用空格隔开" title="添加标签"/>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="layui-btn" id="article-tags-button">{:lang('add')}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-btn-container">
|
||||||
|
{volist name="tags" id="vo" }
|
||||||
|
<button type="button" class="layui-btn layui-btn-sm layui-btn-danger" value="{$vo}">{$vo} x</button>
|
||||||
|
{/volist}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item layui-hide">
|
||||||
|
<button type="submit" class="layui-btn" lay-filter="article-edit" lay-submit id="article-edit">{:lang('post now')}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block name="js"}
|
||||||
|
<script src="/static/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src="/addons/taonyeditor/tinymce/tinymce.min.js"></script>
|
||||||
|
<script src="/addons/taonyeditor/tinymce/tinymce-jquery.min.js"></script>
|
||||||
|
<script>
|
||||||
|
//定义选择器
|
||||||
|
var mytextareaid = 'textarea#L_content';
|
||||||
|
var imagePrependUrl = "{domain}";
|
||||||
|
//定义文件上传接口接口
|
||||||
|
var taonyUploadUrl = "{:url('forum/uploads')}",
|
||||||
|
taonyUploadImgage = "{:url('forum/uploads')}?type=image",
|
||||||
|
taonyUploadVideo = "{:url('forum/uploads')}?type=video";
|
||||||
|
taonyUploadZip = "{:url('forum/uploads')}?type=zip";
|
||||||
|
taonyUploadAudio = "{:url('forum/uploads')}?type=audio";
|
||||||
|
$(mytextareaid).removeClass();
|
||||||
|
</script>
|
||||||
|
<script src="/addons/taonyeditor/js/taonyeditor.js"></script>
|
||||||
|
<script>
|
||||||
|
layui.use(['colorpicker','form','upload', 'editor'], function(){
|
||||||
|
var $ = layui.jquery
|
||||||
|
,colorpicker = layui.colorpicker
|
||||||
|
,form = layui.form
|
||||||
|
,upload = layui.upload;
|
||||||
|
|
||||||
|
var editor = layui.editor;
|
||||||
|
// 从详情页自动调用端口过滤,获取描述信息
|
||||||
|
tinymce.get('L_content').on('mouseleave', function() {
|
||||||
|
var content = tinymce.get('L_content').getContent({format: 'text'});
|
||||||
|
content = content.replace(/[\r\n]/g,"").replace(/\n/g, '').replace(/\s/g, '').replace(/\t/g, '');
|
||||||
|
if(content.length >200) {
|
||||||
|
content = content.substring(0,200);
|
||||||
|
}
|
||||||
|
// var test = tinymce.activeEditor.getContent({format: 'text'});
|
||||||
|
$('[name="description"]').val(content);
|
||||||
|
});
|
||||||
|
|
||||||
|
//预定义颜色项
|
||||||
|
colorpicker.render({
|
||||||
|
elem: '#color'
|
||||||
|
,color: "{$article.title_color ?? '#333'}"
|
||||||
|
,predefine: true // 开启预定义颜色
|
||||||
|
,done: function(color){
|
||||||
|
//譬如你可以在回调中把得到的 color 赋值给表单
|
||||||
|
$('#L_title_color').val(color);
|
||||||
|
//改变标题颜色
|
||||||
|
$('#L_title').css("color", color);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//指定允许上传的文件类型
|
||||||
|
upload.render({
|
||||||
|
elem: '#zip-button'
|
||||||
|
,url: "{:url('forum/uploads')}" //改成您自己的上传接口
|
||||||
|
,data: {type:'zip'}
|
||||||
|
,accept: 'file' //普通文件
|
||||||
|
,done: function(res){
|
||||||
|
if(res.status == 0){
|
||||||
|
$('input[name="upzip"]').val(res.url);
|
||||||
|
layer.msg('上传成功');
|
||||||
|
} else {
|
||||||
|
layer.msg(res.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//编辑文章
|
||||||
|
form.on('submit(article-edit)', function(data){
|
||||||
|
var field = data.field;
|
||||||
|
var numArr = new Array();
|
||||||
|
$('.layui-btn-container').children('button').each(function(){
|
||||||
|
numArr.push($(this).val());//添加至数组
|
||||||
|
});
|
||||||
|
tags = numArr.lenth ? '' : numArr.join(',');
|
||||||
|
$.ajax({
|
||||||
|
type:"post",
|
||||||
|
url:"{:url('Forum/edit')}",
|
||||||
|
data: field,
|
||||||
|
daType:"json",
|
||||||
|
success:function (data){
|
||||||
|
if (data.code == 0) {
|
||||||
|
layer.msg(data.msg,{icon:6,time:2000}, function(){
|
||||||
|
location.href = data.url;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
layer.open({title:'编辑失败',content:data.msg,icon:5,anim:6});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取描述的内容
|
||||||
|
$("#L_content").bind('input propertychange', function(){
|
||||||
|
var content = $(this).val()
|
||||||
|
$.ajax({
|
||||||
|
type:"post",
|
||||||
|
url:"{:url('Forum/getDescription')}",
|
||||||
|
data:{"content":content},
|
||||||
|
daType:"json",
|
||||||
|
success:function (data){
|
||||||
|
if (data.code == 0) {
|
||||||
|
$('[name="description"]').val(data.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
|
||||||
|
// 手动添加tags
|
||||||
|
$('#article-tags-button').on('click',function(){
|
||||||
|
var tags = $("input[name='tags']").val();
|
||||||
|
var flag = 'off';
|
||||||
|
getTags(tags,flag);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取tag的内容
|
||||||
|
var conf = "{:empty(config('taoler.baidu.client_id'))}";
|
||||||
|
if(conf !== '1'){
|
||||||
|
$("#L_title").on('blur', function(){
|
||||||
|
var title = $(this).val();
|
||||||
|
var flag = 'on';
|
||||||
|
// 清空上次生成的tag button
|
||||||
|
$('.layui-btn-container').children('button').each(function(){
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
getTags(title,flag);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 循环添加button
|
||||||
|
function getTags(tags,flag)
|
||||||
|
{
|
||||||
|
if(tags == ''){
|
||||||
|
layer.msg('不能为空');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//把得到的tags放进数组
|
||||||
|
var numArr = new Array();
|
||||||
|
$('.layui-btn-container').children('button').each(function(){
|
||||||
|
numArr.push($(this).val());//添加至数组
|
||||||
|
});
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type:"post",
|
||||||
|
url:"{:url('Forum/tagWord')}",
|
||||||
|
data:{"tags":tags,"flag":flag},
|
||||||
|
daType:"json",
|
||||||
|
success:function (data){
|
||||||
|
if (data.code == 0) {
|
||||||
|
for(var i=0; i<data.data.length; i++){
|
||||||
|
if($.inArray(data.data[i],numArr) < 0){
|
||||||
|
$('.layui-btn-container').append('<button type="button" class="layui-btn layui-btn-sm layui-btn-danger" value='+data.data[i]+'>'+data.data[i]+' x'+'</button>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$("input[name='tags']").val("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//删除tag
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('.layui-btn-container').on('click','button',function(){
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{/block}
|
@ -47,7 +47,8 @@
|
|||||||
|
|
||||||
<div class="layui-card-body">
|
<div class="layui-card-body">
|
||||||
<div style="padding-bottom: 10px;">
|
<div style="padding-bottom: 10px;">
|
||||||
<button class="layui-btn layuiadmin-btn-forum-list" data-type="batchdel">删除</button>
|
<button class="layui-btn layuiadmin-btn-forum-list" data-type="add">添加</button>
|
||||||
|
<button class="layui-btn layuiadmin-btn-forum-list" data-type="batchdel">删除</button>
|
||||||
</div>
|
</div>
|
||||||
<table id="LAY-app-forum-list" lay-filter="LAY-app-forum-list" ></table>
|
<table id="LAY-app-forum-list" lay-filter="LAY-app-forum-list" ></table>
|
||||||
<script type="text/html" id="avatarTpl">
|
<script type="text/html" id="avatarTpl">
|
||||||
@ -66,8 +67,8 @@
|
|||||||
<input type="checkbox" name="status" value="{{d.id}}" lay-skin="switch" lay-filter="artStatus" lay-text="通过|{{ d.check == 0 ? '待审' : '禁止' }}" {{ d.check == 1 ? 'checked' : '' }}>
|
<input type="checkbox" name="status" value="{{d.id}}" lay-skin="switch" lay-filter="artStatus" lay-text="通过|{{ d.check == 0 ? '待审' : '禁止' }}" {{ d.check == 1 ? 'checked' : '' }}>
|
||||||
</script>
|
</script>
|
||||||
<script type="text/html" id="table-forum-list">
|
<script type="text/html" id="table-forum-list">
|
||||||
|
<a class="layui-btn layui-btn-xs" lay-event="edit" ><i class="layui-icon layui-icon-edit"></i></a>
|
||||||
{if condition="checkRuleButton('forum/listdel')"}
|
{if condition="checkRuleButton('forum/listdel')"}
|
||||||
<!--a class="layui-btn layui-btn-disabled layui-btn-xs" lay-event="edit" ><i class="layui-icon layui-icon-edit"></i>编辑</a-->
|
|
||||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del"><i class="layui-icon layui-icon-delete"></i></a>
|
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del"><i class="layui-icon layui-icon-delete"></i></a>
|
||||||
{else /}<a class="layui-btn layui-btn-danger layui-btn-xs layui-btn-disabled"><i class="layui-icon layui-icon-delete"></i></a>{/if}
|
{else /}<a class="layui-btn layui-btn-danger layui-btn-xs layui-btn-disabled"><i class="layui-icon layui-icon-delete"></i></a>{/if}
|
||||||
</script>
|
</script>
|
||||||
@ -78,6 +79,7 @@
|
|||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block name="js"}
|
{block name="js"}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var forumList = "{:url('Forum/list')}",
|
var forumList = "{:url('Forum/list')}",
|
||||||
forumListdel = "{:url('Forum/listdel')}",
|
forumListdel = "{:url('Forum/listdel')}",
|
||||||
@ -88,6 +90,7 @@ var forumList = "{:url('Forum/list')}",
|
|||||||
forumTags = "{:url('Forum/tags')}",
|
forumTags = "{:url('Forum/tags')}",
|
||||||
forumTagsDelete = "{:url('Forum/tagsdelete')}",
|
forumTagsDelete = "{:url('Forum/tagsdelete')}",
|
||||||
forumTagsForm = "{:url('Forum/tagsform')}";
|
forumTagsForm = "{:url('Forum/tagsform')}";
|
||||||
|
forumEdit = "{:url('Forum/edit')}";
|
||||||
layui.config({
|
layui.config({
|
||||||
base: '/static/admin/' //静态资源所在路径
|
base: '/static/admin/' //静态资源所在路径
|
||||||
}).extend({
|
}).extend({
|
||||||
@ -173,12 +176,66 @@ layui.config({
|
|||||||
layer.msg('已删除');
|
layer.msg('已删除');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
,add: function(){
|
||||||
|
layer.open({
|
||||||
|
type: 2
|
||||||
|
,title: '添加'
|
||||||
|
,content: 'add.html'
|
||||||
|
,maxmin: true
|
||||||
|
,area: ['100%', '100%']
|
||||||
|
,btn: ['确定', '取消']
|
||||||
|
,yes: function(index, layero){
|
||||||
|
var iframeWindow = window['layui-layer-iframe'+ index]
|
||||||
|
,submitID = 'article-add'
|
||||||
|
,submit = layero.find('iframe').contents().find('#'+ submitID);
|
||||||
|
|
||||||
|
//监听提交
|
||||||
|
iframeWindow.layui.form.on('submit('+ submitID +')', function(data){
|
||||||
|
var field = data.field; //获取提交的字段
|
||||||
|
|
||||||
|
//提交 Ajax 成功后,静态更新表格中的数据
|
||||||
|
$.ajax({
|
||||||
|
type:"post",
|
||||||
|
url:"{:url('Forum/add')}",
|
||||||
|
data: field,
|
||||||
|
daType:"json",
|
||||||
|
success:function (data){
|
||||||
|
if (data.code == 0) {
|
||||||
|
layer.msg(data.msg,{
|
||||||
|
icon:6,
|
||||||
|
time:2000
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
layer.open({
|
||||||
|
tiele:'添加失败',
|
||||||
|
content:data.msg,
|
||||||
|
icon:5,
|
||||||
|
anim:6
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
table.reload('LAY-app-forum-list'); //数据刷新
|
||||||
|
layer.close(index); //关闭弹层
|
||||||
|
});
|
||||||
|
|
||||||
|
submit.trigger('click');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$('.layui-btn.layuiadmin-btn-forum-list').on('click', function(){
|
$('.layui-btn.layuiadmin-btn-forum-list').on('click', function(){
|
||||||
var type = $(this).data('type');
|
var type = $(this).data('type');
|
||||||
active[type] ? active[type].call(this) : '';
|
active[type] ? active[type].call(this) : '';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// $(document).on('focusin', function(e) {
|
||||||
|
// if ($(e.target).closest(".tox-tinymce, .tox-tinymce-aux, .moxman-window, .tam-assetmanager-root").length) {
|
||||||
|
// e.stopImmediatePropagation();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{/block}
|
{/block}
|
||||||
|
@ -178,26 +178,26 @@
|
|||||||
|
|
||||||
//提交 Ajax 成功后,静态更新表格中的数据
|
//提交 Ajax 成功后,静态更新表格中的数据
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type:"post",
|
type:"post",
|
||||||
url:"{:url('User/userform')}",
|
url:"{:url('User/userform')}",
|
||||||
data:{"name":field.username,"phone":field.phone,"email":field.email,"user_img":field.avatar,"sex":field.sex},
|
data:{"name":field.username,"phone":field.phone,"email":field.email,"user_img":field.avatar,"sex":field.sex},
|
||||||
daType:"json",
|
daType:"json",
|
||||||
success:function (data){
|
success:function (data){
|
||||||
if (data.code == 0) {
|
if (data.code == 0) {
|
||||||
layer.msg(data.msg,{
|
layer.msg(data.msg,{
|
||||||
icon:6,
|
icon:6,
|
||||||
time:2000
|
time:2000
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
layer.open({
|
layer.open({
|
||||||
tiele:'添加失败',
|
tiele:'添加失败',
|
||||||
content:data.msg,
|
content:data.msg,
|
||||||
icon:5,
|
icon:5,
|
||||||
anim:6
|
anim:6
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
table.reload('LAY-user-manage'); //数据刷新
|
table.reload('LAY-user-manage'); //数据刷新
|
||||||
layer.close(index); //关闭弹层
|
layer.close(index); //关闭弹层
|
||||||
});
|
});
|
||||||
|
@ -269,7 +269,7 @@ class Article extends BaseController
|
|||||||
|
|
||||||
$link = $this->getRouteUrl((int)$aid, $cate_ename);
|
$link = $this->getRouteUrl((int)$aid, $cate_ename);
|
||||||
// 推送给百度收录接口
|
// 推送给百度收录接口
|
||||||
$this->baiduPush($link);
|
$this->baiduPushUrl($link);
|
||||||
|
|
||||||
$url = $result['data']['status'] ? $link : (string)url('index/');
|
$url = $result['data']['status'] ? $link : (string)url('index/');
|
||||||
$res = Msgres::success($result['msg'], $url);
|
$res = Msgres::success($result['msg'], $url);
|
||||||
@ -328,7 +328,7 @@ class Article extends BaseController
|
|||||||
Cache::delete('article_'.$id);
|
Cache::delete('article_'.$id);
|
||||||
$link = $this->getRouteUrl((int) $id, $article->cate->ename);
|
$link = $this->getRouteUrl((int) $id, $article->cate->ename);
|
||||||
// 推送给百度收录接口
|
// 推送给百度收录接口
|
||||||
$this->baiduPush($link);
|
$this->baiduPushUrl($link);
|
||||||
$editRes = Msgres::success('edit_success',$link);
|
$editRes = Msgres::success('edit_success',$link);
|
||||||
} else {
|
} else {
|
||||||
$editRes = Msgres::error($result);
|
$editRes = Msgres::error($result);
|
||||||
@ -386,25 +386,7 @@ class Article extends BaseController
|
|||||||
public function uploads()
|
public function uploads()
|
||||||
{
|
{
|
||||||
$type = Request::param('type');
|
$type = Request::param('type');
|
||||||
$uploads = new Uploads();
|
return $this->uploadFiles($type);
|
||||||
switch ($type){
|
|
||||||
case 'image':
|
|
||||||
$upRes = $uploads->put('file','article_pic',1024,'image');
|
|
||||||
break;
|
|
||||||
case 'zip':
|
|
||||||
$upRes = $uploads->put('file','article_zip',1024,'application|image');
|
|
||||||
break;
|
|
||||||
case 'video':
|
|
||||||
$upRes = $uploads->put('file','article_video',102400,'video|audio');
|
|
||||||
break;
|
|
||||||
case 'audio':
|
|
||||||
$upRes = $uploads->put('file','article_audio',102400,'audio');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$upRes = $uploads->put('file','article_file',1024,'image');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return $upRes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -443,23 +425,7 @@ class Article extends BaseController
|
|||||||
public function getWordList()
|
public function getWordList()
|
||||||
{
|
{
|
||||||
$title = input('title');
|
$title = input('title');
|
||||||
if(empty($title)) return json(['code'=>-1,'msg'=>'null']);
|
return $this->getBdiduSearchWordList($title);
|
||||||
$url = 'https://www.baidu.com/sugrec?prod=pc&from=pc_web&wd='.$title;
|
|
||||||
//$result = Api::urlGet($url);
|
|
||||||
$curl = curl_init();
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $url);
|
|
||||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
|
|
||||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
$datas = curl_exec($curl);
|
|
||||||
curl_close($curl);
|
|
||||||
$data = json_decode($datas,true);
|
|
||||||
if(isset($data['g'])) {
|
|
||||||
return json(['code'=>0,'msg'=>'success','data'=>$data['g']]);
|
|
||||||
} else {
|
|
||||||
return json(['code'=>-1,'msg'=>'null']);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -470,102 +436,9 @@ class Article extends BaseController
|
|||||||
public function tags()
|
public function tags()
|
||||||
{
|
{
|
||||||
$data = Request::only(['tags','flag']);
|
$data = Request::only(['tags','flag']);
|
||||||
$tags = [];
|
return $this->setTags($data);
|
||||||
|
|
||||||
if($data['flag'] == 'on') {
|
|
||||||
// 百度分词自动生成关键词
|
|
||||||
if(!empty(config('taoler.baidu.client_id')) == true) {
|
|
||||||
$url = 'https://aip.baidubce.com/rpc/2.0/nlp/v1/lexer?charset=UTF-8&access_token='.config('taoler.baidu.access_token');
|
|
||||||
|
|
||||||
//headers数组内的格式
|
|
||||||
$headers = array();
|
|
||||||
$headers[] = "Content-Type:application/json";
|
|
||||||
$body = array(
|
|
||||||
"text" => $data['tags']
|
|
||||||
);
|
|
||||||
$postBody = json_encode($body);
|
|
||||||
$curl = curl_init();
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $url);
|
|
||||||
curl_setopt($curl, CURLOPT_POST, true);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);//设置请求头
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $postBody);//设置请求体
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');//使用一个自定义的请求信息来代替"GET"或"HEAD"作为HTTP请求。(这个加不加没啥影响)
|
|
||||||
$datas = curl_exec($curl);
|
|
||||||
if($datas == false) {
|
|
||||||
echo '接口无法链接';
|
|
||||||
} else {
|
|
||||||
$res = stripos($datas,'error_code');
|
|
||||||
// 接收返回的数据
|
|
||||||
$dataItem = json_decode($datas);
|
|
||||||
if($res == false) {
|
|
||||||
// 数据正常
|
|
||||||
$items = $dataItem->items;
|
|
||||||
foreach($items as $item) {
|
|
||||||
if($item->pos == 'n' && !in_array($item->item,$tags)){
|
|
||||||
$tags[] = $item->item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 接口正常但获取数据失败,可能参数错误,重新获取token
|
|
||||||
$url = 'https://aip.baidubce.com/oauth/2.0/token';
|
|
||||||
$post_data['grant_type'] = config('taoler.baidu.grant_type');;
|
|
||||||
$post_data['client_id'] = config('taoler.baidu.client_id');
|
|
||||||
$post_data['client_secret'] = config('taoler.baidu.client_secret');
|
|
||||||
|
|
||||||
$o = "";
|
|
||||||
foreach ( $post_data as $k => $v )
|
|
||||||
{
|
|
||||||
$o.= "$k=" . urlencode( $v ). "&" ;
|
|
||||||
}
|
|
||||||
$post_data = substr($o,0,-1);
|
|
||||||
$res = $this->request_post($url, $post_data);
|
|
||||||
// 写入token
|
|
||||||
SetArr::name('taoler')->edit([
|
|
||||||
'baidu'=> [
|
|
||||||
'access_token' => json_decode($res)->access_token,
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
echo 'api接口数据错误 - ';
|
|
||||||
echo $dataItem->error_msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 手动添加关键词
|
|
||||||
// 中文一个或者多个空格转换为英文空格
|
|
||||||
$str = preg_replace('/\s+/',' ',$data['tags']);
|
|
||||||
$att = explode(' ', $str);
|
|
||||||
foreach($att as $v){
|
|
||||||
if ($v !='') {
|
|
||||||
$tags[] = $v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return json(['code'=>0,'data'=>$tags]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// api_post接口
|
|
||||||
function request_post($url = '', $param = '')
|
|
||||||
{
|
|
||||||
if (empty($url) || empty($param)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$postUrl = $url;
|
|
||||||
$curlPost = $param;
|
|
||||||
$curl = curl_init();//初始化curl
|
|
||||||
curl_setopt($curl, CURLOPT_URL,$postUrl);//抓取指定网页
|
|
||||||
curl_setopt($curl, CURLOPT_HEADER, 0);//设置header
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);//要求结果为字符串且输出到屏幕上
|
|
||||||
curl_setopt($curl, CURLOPT_POST, 1);//post提交方式
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $curlPost);
|
|
||||||
$data = curl_exec($curl);//运行curl
|
|
||||||
curl_close($curl);
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 文章置顶、加精、评论状态
|
// 文章置顶、加精、评论状态
|
||||||
public function jieset()
|
public function jieset()
|
||||||
{
|
{
|
||||||
@ -666,33 +539,6 @@ class Article extends BaseController
|
|||||||
return $content;
|
return $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* baidu push api
|
|
||||||
*
|
|
||||||
* @param string $link
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function baiduPush(string $link)
|
|
||||||
{
|
|
||||||
// baidu 接口
|
|
||||||
$api = config('taoler.baidu.push_api');
|
|
||||||
if(!empty($api)) {
|
|
||||||
$url[] = $link;
|
|
||||||
$ch = curl_init();
|
|
||||||
$options = array(
|
|
||||||
CURLOPT_URL => $api,
|
|
||||||
CURLOPT_POST => true,
|
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
|
||||||
CURLOPT_POSTFIELDS => implode("\n", $url),
|
|
||||||
CURLOPT_HTTPHEADER => array('Content-Type: text/plain'),
|
|
||||||
);
|
|
||||||
curl_setopt_array($ch, $options);
|
|
||||||
curl_exec($ch);
|
|
||||||
curl_close($ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function userZanArticle()
|
public function userZanArticle()
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: TaoLer <alipey_tao@qq.com>
|
* @Author: TaoLer <alipey_tao@qq.com>
|
||||||
* @Date: 2021-12-06 16:04:50
|
* @Date: 2021-12-06 16:04:50
|
||||||
* @LastEditTime: 2022-07-26 13:58:17
|
* @LastEditTime: 2022-07-30 08:58:51
|
||||||
* @LastEditors: TaoLer
|
* @LastEditors: TaoLer
|
||||||
* @Description: 前端路由设置
|
* @Description: 前端路由设置
|
||||||
* @FilePath: \github\TaoLer\app\index\route\route.php
|
* @FilePath: \github\TaoLer\app\index\route\route.php
|
||||||
@ -18,16 +18,16 @@ $cate_as = config('taoler.url_rewrite.cate_as');
|
|||||||
Route::get('captcha/[:config]','\\think\\captcha\\CaptchaController@index');
|
Route::get('captcha/[:config]','\\think\\captcha\\CaptchaController@index');
|
||||||
Route::rule('/', 'index'); // 首页访问路由
|
Route::rule('/', 'index'); // 首页访问路由
|
||||||
|
|
||||||
Route::get('index/reply','index/reply')->name('user_reply');
|
Route::get('index/reply$','index/reply')->name('user_reply');
|
||||||
Route::rule('search','Search/getSearch')->name('user_search');
|
Route::rule('search','Search/getSearch')->name('user_search');
|
||||||
Route::get('message/nums','message/nums')->name('user_message');
|
Route::get('message/nums$','message/nums')->name('user_message');
|
||||||
Route::get('tag/:tag', 'Tag/list')->name('tag_list');
|
Route::get('tag/:tag', 'Tag/list')->name('tag_list');
|
||||||
// 用户中心
|
// 用户中心
|
||||||
Route::group(function () {
|
Route::group(function () {
|
||||||
Route::get('u/:id$', 'user/home')->name('user_home');
|
Route::get('u/:id$', 'user/home')->name('user_home');
|
||||||
Route::get('user/index', 'user/index');
|
Route::get('user/index', 'user/index');
|
||||||
Route::get('user/set', 'user/set');
|
Route::get('user/set', 'user/set');
|
||||||
Route::get('user/message', 'user/message');
|
Route::get('user/message$', 'user/message');
|
||||||
Route::get('user/post', 'user/post');
|
Route::get('user/post', 'user/post');
|
||||||
Route::get('user/article','user/artList');
|
Route::get('user/article','user/artList');
|
||||||
Route::get('user/coll','user/collList');
|
Route::get('user/coll','user/collList');
|
||||||
@ -41,11 +41,11 @@ Route::group(function () {
|
|||||||
|
|
||||||
// 登录注册
|
// 登录注册
|
||||||
Route::group(function () {
|
Route::group(function () {
|
||||||
Route::rule('login','login/index')->name('user_login');
|
Route::rule('login$','login/index')->name('user_login');
|
||||||
Route::rule('forget','login/forget')->name('user_forget');
|
Route::rule('forget$','login/forget')->name('user_forget');
|
||||||
Route::rule('postcode','login/postcode');
|
Route::rule('postcode$','login/postcode');
|
||||||
Route::rule('sentemailcode','login/sentMailCode');
|
Route::rule('sentemailcode$','login/sentMailCode');
|
||||||
Route::rule('respass','login/respass');
|
Route::rule('respass$','login/respass');
|
||||||
Route::rule('reg$','Login/reg')->name('user_reg')
|
Route::rule('reg$','Login/reg')->name('user_reg')
|
||||||
->middleware(\app\middleware\CheckRegister::class);
|
->middleware(\app\middleware\CheckRegister::class);
|
||||||
});
|
});
|
||||||
@ -53,16 +53,18 @@ Route::group(function () {
|
|||||||
|
|
||||||
|
|
||||||
// article
|
// article
|
||||||
Route::group(function () use($detail_as,$cate_as){
|
Route::group('art',function () use($detail_as,$cate_as){
|
||||||
Route::rule('add/[:cate]','Article/add')->name('add_article');
|
Route::rule('add/[:cate]','Article/add')->name('add_article');
|
||||||
Route::rule('delete/[:id]','Article/delete');
|
Route::rule('delete/[:id]','Article/delete');
|
||||||
Route::rule('tags','Article/tags')->allowCrossDomain();
|
Route::rule('tags','Article/tags')->allowCrossDomain();
|
||||||
Route::rule('edit/[:id]','Article/edit');
|
Route::rule('edit/[:id]','Article/edit');
|
||||||
|
});
|
||||||
|
Route::group(function () use($detail_as,$cate_as){
|
||||||
// 动态路径路由会影响下面的路由,所以动态路由放下面
|
// 动态路径路由会影响下面的路由,所以动态路由放下面
|
||||||
Route::get($detail_as . ':id$', 'article/detail')->name('article_detail');
|
Route::get($detail_as . ':id$', 'article/detail')->name('article_detail');
|
||||||
Route::get($cate_as . '<ename>$','article/cate')->name('cate');
|
Route::get($cate_as . '<ename>$','article/cate')->name('cate');
|
||||||
Route::get($cate_as . '<ename>/<type>$', 'article/cate')->name('cate_type');
|
Route::get($cate_as . '<ename>/<type>$', 'article/cate')->name('cate_type');
|
||||||
Route::get($cate_as . '<ename>/<type>/<page>', 'article/cate')->name('cate_page');
|
Route::get($cate_as . '<ename>/<type>/<page>$', 'article/cate')->name('cate_page');
|
||||||
})->pattern([
|
})->pattern([
|
||||||
'ename' => '\w+',
|
'ename' => '\w+',
|
||||||
'type' => '\w+',
|
'type' => '\w+',
|
||||||
|
@ -36,7 +36,8 @@
|
|||||||
"overtrue/pinyin": "^4.0",
|
"overtrue/pinyin": "^4.0",
|
||||||
"yansongda/pay": "~3.1.0",
|
"yansongda/pay": "~3.1.0",
|
||||||
"guzzlehttp/guzzle": "7.0",
|
"guzzlehttp/guzzle": "7.0",
|
||||||
"php-di/php-di": "^6.4"
|
"php-di/php-di": "^6.4",
|
||||||
|
"workerman/phpsocket.io": "^1.1"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"symfony/var-dumper": "^4.2",
|
"symfony/var-dumper": "^4.2",
|
||||||
|
84
composer.lock
generated
84
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "fe8d6bf6748143aa416b16c9a3f5db98",
|
"content-hash": "8e024ab98b5bfe9d89eb50e675b21b20",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "bacon/bacon-qr-code",
|
"name": "bacon/bacon-qr-code",
|
||||||
@ -2259,6 +2259,88 @@
|
|||||||
},
|
},
|
||||||
"time": "2020-02-15T13:04:16+00:00"
|
"time": "2020-02-15T13:04:16+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "workerman/channel",
|
||||||
|
"version": "v1.1.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/walkor/channel.git",
|
||||||
|
"reference": "3df772d0d20d4cebfcfd621c33d1a1ab732db523"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/walkor/channel/zipball/3df772d0d20d4cebfcfd621c33d1a1ab732db523",
|
||||||
|
"reference": "3df772d0d20d4cebfcfd621c33d1a1ab732db523",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"workerman/workerman": ">=4.0.12"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Channel\\": "./src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"homepage": "http://www.workerman.net",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/walkor/channel/issues",
|
||||||
|
"source": "https://github.com/walkor/channel/tree/v1.1.0"
|
||||||
|
},
|
||||||
|
"time": "2021-02-08T02:45:42+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "workerman/phpsocket.io",
|
||||||
|
"version": "v1.1.14",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/walkor/phpsocket.io.git",
|
||||||
|
"reference": "a5758da4d55b4744a4cc9c956816d88ce385601e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/walkor/phpsocket.io/zipball/a5758da4d55b4744a4cc9c956816d88ce385601e",
|
||||||
|
"reference": "a5758da4d55b4744a4cc9c956816d88ce385601e",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"workerman/channel": ">=1.0.0",
|
||||||
|
"workerman/workerman": ">=3.5.16"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PHPSocketIO\\": "./src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"homepage": "http://www.workerman.net",
|
||||||
|
"keywords": [
|
||||||
|
"Socket.io"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/walkor/phpsocket.io/issues",
|
||||||
|
"source": "https://github.com/walkor/phpsocket.io/tree/v1.1.14"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://opencollective.com/walkor",
|
||||||
|
"type": "open_collective"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.patreon.com/walkor",
|
||||||
|
"type": "patreon"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2022-02-24T03:33:45+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "workerman/workerman",
|
"name": "workerman/workerman",
|
||||||
"version": "v4.0.41",
|
"version": "v4.0.41",
|
||||||
|
@ -16,7 +16,7 @@ return [
|
|||||||
// 应用名,此项不可更改
|
// 应用名,此项不可更改
|
||||||
'appname' => 'TaoLer',
|
'appname' => 'TaoLer',
|
||||||
// 版本配置
|
// 版本配置
|
||||||
'version' => '1.9.20',
|
'version' => '1.9.21',
|
||||||
// 加盐
|
// 加盐
|
||||||
'salt' => 'taoler',
|
'salt' => 'taoler',
|
||||||
// 数据库备份目录
|
// 数据库备份目录
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* @Author: TaoLer <317927823@qq.com>
|
||||||
|
* @Date: 2021-12-06 16:04:50
|
||||||
|
* @LastEditTime: 2022-07-29 16:22:20
|
||||||
|
* @LastEditors: TaoLer
|
||||||
|
* @Description: 优化版
|
||||||
|
* @FilePath: \github\TaoLer\public\static\admin\lib\index.js
|
||||||
|
* Copyright (c) 2020~2022 https://www.aieok.com All rights reserved.
|
||||||
|
*/
|
||||||
/**
|
/**
|
||||||
|
|
||||||
@Name:layuiAdmin iframe版主入口
|
@Name:layuiAdmin iframe版主入口
|
||||||
@ -11,13 +20,14 @@ layui.extend({
|
|||||||
setter: 'config' //配置模块
|
setter: 'config' //配置模块
|
||||||
,admin: 'lib/admin' //核心模块
|
,admin: 'lib/admin' //核心模块
|
||||||
,view: 'lib/view' //视图渲染模块
|
,view: 'lib/view' //视图渲染模块
|
||||||
|
,editor: '{/}/addons/taonyeditor/js/taonyeditor'
|
||||||
}).define(['setter', 'admin'], function(exports){
|
}).define(['setter', 'admin'], function(exports){
|
||||||
var setter = layui.setter
|
var setter = layui.setter
|
||||||
,element = layui.element
|
,element = layui.element
|
||||||
,admin = layui.admin
|
,admin = layui.admin
|
||||||
,tabsPage = admin.tabsPage
|
,tabsPage = admin.tabsPage
|
||||||
,view = layui.view
|
,view = layui.view
|
||||||
|
,editor = layui.editor
|
||||||
//打开标签页
|
//打开标签页
|
||||||
,openTabsPage = function(url, text){
|
,openTabsPage = function(url, text){
|
||||||
//遍历页签选项卡
|
//遍历页签选项卡
|
||||||
|
@ -20,7 +20,7 @@ var forms = table.render({
|
|||||||
,{field: 'hot', title: '加精', templet: '#buttonHot', width: 80, align: 'center'}
|
,{field: 'hot', title: '加精', templet: '#buttonHot', width: 80, align: 'center'}
|
||||||
,{field: 'reply', title: '禁评', templet: '#buttonReply', width: 80, align: 'center'}
|
,{field: 'reply', title: '禁评', templet: '#buttonReply', width: 80, align: 'center'}
|
||||||
,{field: 'check', title: '审帖', templet: '#buttonCheck', width: 95, align: 'center'}
|
,{field: 'check', title: '审帖', templet: '#buttonCheck', width: 95, align: 'center'}
|
||||||
,{title: '操作', width: 60, align: 'center', toolbar: '#table-forum-list'}
|
,{title: '操作', width: 110, align: 'center', toolbar: '#table-forum-list'}
|
||||||
]]
|
]]
|
||||||
,page: true
|
,page: true
|
||||||
,limit: 15
|
,limit: 15
|
||||||
@ -65,45 +65,30 @@ var forms = table.render({
|
|||||||
layer.open({
|
layer.open({
|
||||||
type: 2
|
type: 2
|
||||||
,title: '编辑帖子'
|
,title: '编辑帖子'
|
||||||
,content: '/admin/Forum/listform?id='+ data.id
|
,content: forumEdit + '?id='+ data.id
|
||||||
,area: ['550px', '400px']
|
,area: ['100%', '100%']
|
||||||
,btn: ['确定', '取消']
|
,btn: ['确定', '取消']
|
||||||
,resize: false
|
,resize: false
|
||||||
,yes: function(index, layero){
|
,yes: function(index, layero){
|
||||||
var iframeWindow = window['layui-layer-iframe'+ index]
|
var iframeWindow = window['layui-layer-iframe'+ index]
|
||||||
,submitID = 'LAY-app-forum-submit'
|
,submitID = 'article-edit'
|
||||||
,submit = layero.find('iframe').contents().find("#layuiadmin-form-list")
|
,submit = layero.find('iframe').contents().find('#'+ submitID)
|
||||||
,poster = submit.find('input[name="poster"]').val()
|
|
||||||
,content = submit.find('input[name="content"]').val()
|
|
||||||
,avatar = submit.find('input[name="avatar"]').val();
|
|
||||||
|
|
||||||
//监听提交
|
//监听提交
|
||||||
iframeWindow.layui.form.on('submit('+ submitID +')', function(data){
|
iframeWindow.layui.form.on('submit('+ submitID +')', function(data){
|
||||||
var field = data.field; //获取提交的字段
|
var field = data.field; //获取提交的字段
|
||||||
|
|
||||||
//提交 Ajax 成功后,静态更新表格中的数据
|
|
||||||
//$.ajax({});
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type:"post",
|
type:"post",
|
||||||
url:"/admin/Forum/listform",
|
url: forumEdit,
|
||||||
data:{"id":data.id,"poster":name,"sort":sort,"ename":ename},
|
data: field,
|
||||||
daType:"json",
|
daType:"json",
|
||||||
success:function (data){
|
success:function (data){
|
||||||
if (data.code == 0) {
|
if (data.code == 0) {
|
||||||
layer.msg(data.msg,{
|
layer.msg(data.msg,{icon:6,time:2000});
|
||||||
icon:6,
|
|
||||||
time:2000
|
|
||||||
}, function(){
|
|
||||||
location.reload();
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
layer.open({
|
layer.open({title:'编辑失败',content:data.msg,icon:5,anim:6});
|
||||||
tiele:'修改失败',
|
};
|
||||||
content:data.msg,
|
|
||||||
icon:5,
|
|
||||||
anim:6
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -113,9 +98,6 @@ var forms = table.render({
|
|||||||
|
|
||||||
submit.trigger('click');
|
submit.trigger('click');
|
||||||
}
|
}
|
||||||
,success: function(layero, index){
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -264,8 +246,8 @@ var forms = table.render({
|
|||||||
,sort = othis.find('input[name="sort"]').val()
|
,sort = othis.find('input[name="sort"]').val()
|
||||||
,tags = othis.find('input[name="tags"]').val()
|
,tags = othis.find('input[name="tags"]').val()
|
||||||
,ename = othis.find('input[name="ename"]').val()
|
,ename = othis.find('input[name="ename"]').val()
|
||||||
,detpl = othis.find('select[name="detpl"]').val()
|
,detpl = othis.find('select[name="detpl"]').val()
|
||||||
,icon = othis.find('input[name="icon"]').val()
|
,icon = othis.find('input[name="icon"]').val()
|
||||||
,desc = othis.find('input[name="desc"]').val();
|
,desc = othis.find('input[name="desc"]').val();
|
||||||
|
|
||||||
if(!tags.replace(/\s/g, '')) return;
|
if(!tags.replace(/\s/g, '')) return;
|
||||||
|
@ -539,3 +539,8 @@ html{background-color: #f2f2f2; color: #666;}
|
|||||||
.xm-body .xm-option.hide-icon.selected {
|
.xm-body .xm-option.hide-icon.selected {
|
||||||
background-color: #1890ff !important;
|
background-color: #1890ff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 发文章标题下百度词条 */
|
||||||
|
.bdsug{height: auto; position: absolute; left: 0; top: 30px; z-index: 100; background: #fff; border-radius: 0 0 10px 10px; border: 1px solid #dadade!important; border-top: 0!important; box-shadow: none;}
|
||||||
|
.bdsug ul{display: block;margin: 5px 2px 0; padding: 5px 0 7px; background: 0 0; border-top: 0px solid #f5f5f6;}
|
||||||
|
.bdsug ul>li{margin-top: 0;height:30px;line-height: 25px;}
|
||||||
|
@ -12,7 +12,6 @@ i{font-style: normal;}
|
|||||||
|
|
||||||
/* 布局 */
|
/* 布局 */
|
||||||
.layui-mm{position: fixed; top: 100px; bottom: 0;}
|
.layui-mm{position: fixed; top: 100px; bottom: 0;}
|
||||||
.site-menu,
|
|
||||||
.site-menu{box-shadow: none; border-left: none;}
|
.site-menu{box-shadow: none; border-left: none;}
|
||||||
|
|
||||||
/* 文档 */
|
/* 文档 */
|
||||||
|
@ -995,6 +995,7 @@ layui.define(['layer', 'laytpl', 'form', 'element', 'upload', 'util', 'imgcom'],
|
|||||||
util.fixbar({
|
util.fixbar({
|
||||||
bar1: ''
|
bar1: ''
|
||||||
,bgcolor: '#009688'
|
,bgcolor: '#009688'
|
||||||
|
,css: {right: 10, bottom: 100}
|
||||||
,click: function(type){
|
,click: function(type){
|
||||||
//添加文章
|
//添加文章
|
||||||
if(type === 'bar1'){
|
if(type === 'bar1'){
|
||||||
|
2
vendor/composer/autoload_psr4.php
vendored
2
vendor/composer/autoload_psr4.php
vendored
@ -35,6 +35,7 @@ return array(
|
|||||||
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
|
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
|
||||||
'PhpDocReader\\' => array($vendorDir . '/php-di/phpdoc-reader/src/PhpDocReader'),
|
'PhpDocReader\\' => array($vendorDir . '/php-di/phpdoc-reader/src/PhpDocReader'),
|
||||||
'Phinx\\' => array($vendorDir . '/topthink/think-migration/phinx/src/Phinx'),
|
'Phinx\\' => array($vendorDir . '/topthink/think-migration/phinx/src/Phinx'),
|
||||||
|
'PHPSocketIO\\' => array($vendorDir . '/workerman/phpsocket.io/src'),
|
||||||
'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'),
|
'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'),
|
||||||
'Overtrue\\Pinyin\\' => array($vendorDir . '/overtrue/pinyin/src'),
|
'Overtrue\\Pinyin\\' => array($vendorDir . '/overtrue/pinyin/src'),
|
||||||
'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'),
|
'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'),
|
||||||
@ -49,5 +50,6 @@ return array(
|
|||||||
'Endroid\\QrCode\\' => array($vendorDir . '/endroid/qr-code/src'),
|
'Endroid\\QrCode\\' => array($vendorDir . '/endroid/qr-code/src'),
|
||||||
'DI\\' => array($vendorDir . '/php-di/php-di/src'),
|
'DI\\' => array($vendorDir . '/php-di/php-di/src'),
|
||||||
'DASPRiD\\Enum\\' => array($vendorDir . '/dasprid/enum/src'),
|
'DASPRiD\\Enum\\' => array($vendorDir . '/dasprid/enum/src'),
|
||||||
|
'Channel\\' => array($vendorDir . '/workerman/channel/src'),
|
||||||
'BaconQrCode\\' => array($vendorDir . '/bacon/bacon-qr-code/src'),
|
'BaconQrCode\\' => array($vendorDir . '/bacon/bacon-qr-code/src'),
|
||||||
);
|
);
|
||||||
|
13
vendor/composer/autoload_static.php
vendored
13
vendor/composer/autoload_static.php
vendored
@ -82,6 +82,7 @@ class ComposerStaticInit1b32198725235c8d6500c87262ef30c2
|
|||||||
'Psr\\Cache\\' => 10,
|
'Psr\\Cache\\' => 10,
|
||||||
'PhpDocReader\\' => 13,
|
'PhpDocReader\\' => 13,
|
||||||
'Phinx\\' => 6,
|
'Phinx\\' => 6,
|
||||||
|
'PHPSocketIO\\' => 12,
|
||||||
'PHPMailer\\PHPMailer\\' => 20,
|
'PHPMailer\\PHPMailer\\' => 20,
|
||||||
),
|
),
|
||||||
'O' =>
|
'O' =>
|
||||||
@ -118,6 +119,10 @@ class ComposerStaticInit1b32198725235c8d6500c87262ef30c2
|
|||||||
'DI\\' => 3,
|
'DI\\' => 3,
|
||||||
'DASPRiD\\Enum\\' => 13,
|
'DASPRiD\\Enum\\' => 13,
|
||||||
),
|
),
|
||||||
|
'C' =>
|
||||||
|
array (
|
||||||
|
'Channel\\' => 8,
|
||||||
|
),
|
||||||
'B' =>
|
'B' =>
|
||||||
array (
|
array (
|
||||||
'BaconQrCode\\' => 12,
|
'BaconQrCode\\' => 12,
|
||||||
@ -245,6 +250,10 @@ class ComposerStaticInit1b32198725235c8d6500c87262ef30c2
|
|||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/topthink/think-migration/phinx/src/Phinx',
|
0 => __DIR__ . '/..' . '/topthink/think-migration/phinx/src/Phinx',
|
||||||
),
|
),
|
||||||
|
'PHPSocketIO\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/workerman/phpsocket.io/src',
|
||||||
|
),
|
||||||
'PHPMailer\\PHPMailer\\' =>
|
'PHPMailer\\PHPMailer\\' =>
|
||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/phpmailer/phpmailer/src',
|
0 => __DIR__ . '/..' . '/phpmailer/phpmailer/src',
|
||||||
@ -301,6 +310,10 @@ class ComposerStaticInit1b32198725235c8d6500c87262ef30c2
|
|||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/dasprid/enum/src',
|
0 => __DIR__ . '/..' . '/dasprid/enum/src',
|
||||||
),
|
),
|
||||||
|
'Channel\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/workerman/channel/src',
|
||||||
|
),
|
||||||
'BaconQrCode\\' =>
|
'BaconQrCode\\' =>
|
||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/bacon/bacon-qr-code/src',
|
0 => __DIR__ . '/..' . '/bacon/bacon-qr-code/src',
|
||||||
|
88
vendor/composer/installed.json
vendored
88
vendor/composer/installed.json
vendored
@ -2849,6 +2849,94 @@
|
|||||||
},
|
},
|
||||||
"install-path": "../wamkj/thinkphp6.0-databackup"
|
"install-path": "../wamkj/thinkphp6.0-databackup"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "workerman/channel",
|
||||||
|
"version": "v1.1.0",
|
||||||
|
"version_normalized": "1.1.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/walkor/channel.git",
|
||||||
|
"reference": "3df772d0d20d4cebfcfd621c33d1a1ab732db523"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/walkor/channel/zipball/3df772d0d20d4cebfcfd621c33d1a1ab732db523",
|
||||||
|
"reference": "3df772d0d20d4cebfcfd621c33d1a1ab732db523",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"workerman/workerman": ">=4.0.12"
|
||||||
|
},
|
||||||
|
"time": "2021-02-08T02:45:42+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Channel\\": "./src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"homepage": "http://www.workerman.net",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/walkor/channel/issues",
|
||||||
|
"source": "https://github.com/walkor/channel/tree/v1.1.0"
|
||||||
|
},
|
||||||
|
"install-path": "../workerman/channel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "workerman/phpsocket.io",
|
||||||
|
"version": "v1.1.14",
|
||||||
|
"version_normalized": "1.1.14.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/walkor/phpsocket.io.git",
|
||||||
|
"reference": "a5758da4d55b4744a4cc9c956816d88ce385601e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/walkor/phpsocket.io/zipball/a5758da4d55b4744a4cc9c956816d88ce385601e",
|
||||||
|
"reference": "a5758da4d55b4744a4cc9c956816d88ce385601e",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"workerman/channel": ">=1.0.0",
|
||||||
|
"workerman/workerman": ">=3.5.16"
|
||||||
|
},
|
||||||
|
"time": "2022-02-24T03:33:45+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PHPSocketIO\\": "./src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"homepage": "http://www.workerman.net",
|
||||||
|
"keywords": [
|
||||||
|
"Socket.io"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/walkor/phpsocket.io/issues",
|
||||||
|
"source": "https://github.com/walkor/phpsocket.io/tree/v1.1.14"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://opencollective.com/walkor",
|
||||||
|
"type": "open_collective"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.patreon.com/walkor",
|
||||||
|
"type": "patreon"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"install-path": "../workerman/phpsocket.io"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "workerman/workerman",
|
"name": "workerman/workerman",
|
||||||
"version": "v4.0.41",
|
"version": "v4.0.41",
|
||||||
|
22
vendor/composer/installed.php
vendored
22
vendor/composer/installed.php
vendored
@ -3,7 +3,7 @@
|
|||||||
'name' => 'taoser/taoler',
|
'name' => 'taoser/taoler',
|
||||||
'pretty_version' => 'dev-master',
|
'pretty_version' => 'dev-master',
|
||||||
'version' => 'dev-master',
|
'version' => 'dev-master',
|
||||||
'reference' => '13f9cb05575d0a81b1392ce7fc500a595ca7290a',
|
'reference' => '9c56fe07e9c108768ac6d8a254dc3e838186eab1',
|
||||||
'type' => 'project',
|
'type' => 'project',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
@ -301,7 +301,7 @@
|
|||||||
'taoser/taoler' => array(
|
'taoser/taoler' => array(
|
||||||
'pretty_version' => 'dev-master',
|
'pretty_version' => 'dev-master',
|
||||||
'version' => 'dev-master',
|
'version' => 'dev-master',
|
||||||
'reference' => '13f9cb05575d0a81b1392ce7fc500a595ca7290a',
|
'reference' => '9c56fe07e9c108768ac6d8a254dc3e838186eab1',
|
||||||
'type' => 'project',
|
'type' => 'project',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
@ -433,6 +433,24 @@
|
|||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
|
'workerman/channel' => array(
|
||||||
|
'pretty_version' => 'v1.1.0',
|
||||||
|
'version' => '1.1.0.0',
|
||||||
|
'reference' => '3df772d0d20d4cebfcfd621c33d1a1ab732db523',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../workerman/channel',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'workerman/phpsocket.io' => array(
|
||||||
|
'pretty_version' => 'v1.1.14',
|
||||||
|
'version' => '1.1.14.0',
|
||||||
|
'reference' => 'a5758da4d55b4744a4cc9c956816d88ce385601e',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../workerman/phpsocket.io',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
'workerman/workerman' => array(
|
'workerman/workerman' => array(
|
||||||
'pretty_version' => 'v4.0.41',
|
'pretty_version' => 'v4.0.41',
|
||||||
'version' => '4.0.41.0',
|
'version' => '4.0.41.0',
|
||||||
|
2
vendor/services.php
vendored
2
vendor/services.php
vendored
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// This file is automatically generated at:2022-07-26 15:34:50
|
// This file is automatically generated at:2022-07-29 21:13:15
|
||||||
declare (strict_types = 1);
|
declare (strict_types = 1);
|
||||||
return array (
|
return array (
|
||||||
0 => 'taoser\\addons\\Service',
|
0 => 'taoser\\addons\\Service',
|
||||||
|
101
vendor/workerman/channel/README.md
vendored
Normal file
101
vendor/workerman/channel/README.md
vendored
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
# Channel
|
||||||
|
基于订阅的多进程通讯组件,用于workerman进程间通讯或者服务器集群通讯,类似redis订阅发布机制。基于workerman开发。
|
||||||
|
|
||||||
|
Channel 提供两种通讯形式,分别是发布订阅的事件机制和消息队列机制。
|
||||||
|
|
||||||
|
它们的主要区别是:
|
||||||
|
- 事件机制是消息发出后,所有订阅该事件的客户端都能收到消息。
|
||||||
|
- 消息队列机制是消息发出后,所有订阅该消息的客户端只有一个会收到消息,如果客户端忙消息会进行排队直到有客户端闲置后重新取到消息。
|
||||||
|
- 需要注意的是 Channel 只是提供一种通讯方式,本身并不提供消息确认、重试、延迟、持久化等功能,请根据实际情况合理使用。
|
||||||
|
|
||||||
|
# 手册地址
|
||||||
|
[Channel手册](http://doc.workerman.net/components/channel.html)
|
||||||
|
|
||||||
|
# 服务端
|
||||||
|
```php
|
||||||
|
use Workerman\Worker;
|
||||||
|
|
||||||
|
//Tcp 通讯方式
|
||||||
|
$channel_server = new Channel\Server('0.0.0.0', 2206);
|
||||||
|
|
||||||
|
//Unix Domain Socket 通讯方式
|
||||||
|
//$channel_server = new Channel\Server('unix:///tmp/workerman-channel.sock');
|
||||||
|
|
||||||
|
if(!defined('GLOBAL_START'))
|
||||||
|
{
|
||||||
|
Worker::runAll();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# 客户端
|
||||||
|
```php
|
||||||
|
use Workerman\Worker;
|
||||||
|
|
||||||
|
$worker = new Worker();
|
||||||
|
$worker->onWorkerStart = function()
|
||||||
|
{
|
||||||
|
// Channel客户端连接到Channel服务端
|
||||||
|
Channel\Client::connect('<Channel服务端ip>', 2206);
|
||||||
|
|
||||||
|
// 使用 Unix Domain Socket 通讯
|
||||||
|
//Channel\Client::connect('unix:///tmp/workerman-channel.sock');
|
||||||
|
|
||||||
|
// 要订阅的事件名称(名称可以为任意的数字和字符串组合)
|
||||||
|
$event_name = 'event_xxxx';
|
||||||
|
// 订阅某个自定义事件并注册回调,收到事件后会自动触发此回调
|
||||||
|
Channel\Client::on($event_name, function($event_data){
|
||||||
|
var_dump($event_data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
$worker->onMessage = function($connection, $data)
|
||||||
|
{
|
||||||
|
// 要发布的事件名称
|
||||||
|
$event_name = 'event_xxxx';
|
||||||
|
// 事件数据(数据格式可以为数字、字符串、数组),会传递给客户端回调函数作为参数
|
||||||
|
$event_data = array('some data.', 'some data..');
|
||||||
|
// 发布某个自定义事件,订阅这个事件的客户端会收到事件数据,并触发客户端对应的事件回调
|
||||||
|
Channel\Client::publish($event_name, $event_data);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!defined('GLOBAL_START'))
|
||||||
|
{
|
||||||
|
Worker::runAll();
|
||||||
|
}
|
||||||
|
````
|
||||||
|
|
||||||
|
## 消息队列示例
|
||||||
|
```php
|
||||||
|
use Workerman\Worker;
|
||||||
|
use Workerman\Timer;
|
||||||
|
|
||||||
|
$worker = new Worker();
|
||||||
|
$worker->name = 'Producer';
|
||||||
|
$worker->onWorkerStart = function()
|
||||||
|
{
|
||||||
|
Client::connect();
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
Timer::add(1, function() {
|
||||||
|
Client::enqueue('queue', 'Hello World '.time());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$mq = new Worker();
|
||||||
|
$mq->name = 'Consumer';
|
||||||
|
$mq->count = 4;
|
||||||
|
$mq->onWorkerStart = function($worker) {
|
||||||
|
Client::connect();
|
||||||
|
|
||||||
|
//订阅消息 queue
|
||||||
|
Client::watch('queue', function($data) use ($worker) {
|
||||||
|
echo "Worker {$worker->id} get queue: $data\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
//10 秒后取消订阅该消息
|
||||||
|
Timer::add(10, function() {
|
||||||
|
Client::unwatch('queue');
|
||||||
|
}, [], false);
|
||||||
|
};
|
||||||
|
|
||||||
|
Worker::runAll();
|
||||||
|
```
|
12
vendor/workerman/channel/composer.json
vendored
Normal file
12
vendor/workerman/channel/composer.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name" : "workerman/channel",
|
||||||
|
"type" : "library",
|
||||||
|
"homepage": "http://www.workerman.net",
|
||||||
|
"license" : "MIT",
|
||||||
|
"require": {
|
||||||
|
"workerman/workerman" : ">=4.0.12"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {"Channel\\": "./src"}
|
||||||
|
}
|
||||||
|
}
|
391
vendor/workerman/channel/src/Client.php
vendored
Normal file
391
vendor/workerman/channel/src/Client.php
vendored
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
<?php
|
||||||
|
namespace Channel;
|
||||||
|
|
||||||
|
use Workerman\Connection\AsyncTcpConnection;
|
||||||
|
use Workerman\Lib\Timer;
|
||||||
|
use Workerman\Protocols\Frame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Channel/Client
|
||||||
|
* @version 1.0.7
|
||||||
|
*/
|
||||||
|
class Client
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* onMessage.
|
||||||
|
* @var callback
|
||||||
|
*/
|
||||||
|
public static $onMessage = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* onConnect
|
||||||
|
* @var callback
|
||||||
|
*/
|
||||||
|
public static $onConnect = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* onClose
|
||||||
|
* @var callback
|
||||||
|
*/
|
||||||
|
public static $onClose = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connction to channel server.
|
||||||
|
* @var \Workerman\Connection\TcpConnection
|
||||||
|
*/
|
||||||
|
protected static $_remoteConnection = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Channel server ip.
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected static $_remoteIp = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Channel server port.
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected static $_remotePort = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reconnect timer.
|
||||||
|
* @var Timer
|
||||||
|
*/
|
||||||
|
protected static $_reconnectTimer = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ping timer.
|
||||||
|
* @var Timer
|
||||||
|
*/
|
||||||
|
protected static $_pingTimer = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All event callback.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected static $_events = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All queue callback.
|
||||||
|
* @var callable
|
||||||
|
*/
|
||||||
|
protected static $_queues = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected static $_isWorkermanEnv = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ping interval.
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public static $pingInterval = 25;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to channel server
|
||||||
|
* @param string $ip Channel server ip address or unix domain socket address
|
||||||
|
* Ip like (TCP): 192.168.1.100
|
||||||
|
* Unix domain socket like: unix:///tmp/workerman-channel.sock
|
||||||
|
* @param int $port Port to connect when use tcp
|
||||||
|
*/
|
||||||
|
public static function connect($ip = '127.0.0.1', $port = 2206)
|
||||||
|
{
|
||||||
|
if (self::$_remoteConnection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$_remoteIp = $ip;
|
||||||
|
self::$_remotePort = $port;
|
||||||
|
|
||||||
|
if (PHP_SAPI !== 'cli' || !class_exists('Workerman\Worker', false)) {
|
||||||
|
self::$_isWorkermanEnv = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For workerman environment.
|
||||||
|
if (self::$_isWorkermanEnv) {
|
||||||
|
if (strpos($ip, 'unix://') === false) {
|
||||||
|
$conn = new AsyncTcpConnection('frame://' . self::$_remoteIp . ':' . self::$_remotePort);
|
||||||
|
} else {
|
||||||
|
$conn = new AsyncTcpConnection($ip);
|
||||||
|
$conn->protocol = Frame::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
$conn->onClose = [self::class, 'onRemoteClose'];
|
||||||
|
$conn->onConnect = [self::class, 'onRemoteConnect'];
|
||||||
|
$conn->onMessage = [self::class , 'onRemoteMessage'];
|
||||||
|
$conn->connect();
|
||||||
|
|
||||||
|
if (empty(self::$_pingTimer)) {
|
||||||
|
self::$_pingTimer = Timer::add(self::$pingInterval, 'Channel\Client::ping');
|
||||||
|
}
|
||||||
|
// Not workerman environment.
|
||||||
|
} else {
|
||||||
|
$remote = strpos($ip, 'unix://') === false ? 'tcp://'.self::$_remoteIp.':'.self::$_remotePort : $ip;
|
||||||
|
$conn = stream_socket_client($remote, $code, $message, 5);
|
||||||
|
if (!$conn) {
|
||||||
|
throw new \Exception($message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$_remoteConnection = $conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* onRemoteMessage.
|
||||||
|
* @param \Workerman\Connection\TcpConnection $connection
|
||||||
|
* @param string $data
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public static function onRemoteMessage($connection, $data)
|
||||||
|
{
|
||||||
|
$data = unserialize($data);
|
||||||
|
$type = $data['type'];
|
||||||
|
$event = $data['channel'];
|
||||||
|
$event_data = $data['data'];
|
||||||
|
|
||||||
|
$callback = null;
|
||||||
|
|
||||||
|
if ($type == 'event') {
|
||||||
|
if (!empty(self::$_events[$event])) {
|
||||||
|
call_user_func(self::$_events[$event], $event_data);
|
||||||
|
} elseif (!empty(Client::$onMessage)) {
|
||||||
|
call_user_func(Client::$onMessage, $event, $event_data);
|
||||||
|
} else {
|
||||||
|
throw new \Exception("event:$event have not callback");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isset(self::$_queues[$event])) {
|
||||||
|
call_user_func(self::$_queues[$event], $event_data);
|
||||||
|
} else {
|
||||||
|
throw new \Exception("queue:$event have not callback");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ping.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function ping()
|
||||||
|
{
|
||||||
|
if(self::$_remoteConnection)
|
||||||
|
{
|
||||||
|
self::$_remoteConnection->send('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* onRemoteClose.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function onRemoteClose()
|
||||||
|
{
|
||||||
|
echo "Waring channel connection closed and try to reconnect\n";
|
||||||
|
self::$_remoteConnection = null;
|
||||||
|
self::clearTimer();
|
||||||
|
self::$_reconnectTimer = Timer::add(1, 'Channel\Client::connect', array(self::$_remoteIp, self::$_remotePort));
|
||||||
|
if (self::$onClose) {
|
||||||
|
call_user_func(Client::$onClose);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* onRemoteConnect.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function onRemoteConnect()
|
||||||
|
{
|
||||||
|
$all_event_names = array_keys(self::$_events);
|
||||||
|
if($all_event_names)
|
||||||
|
{
|
||||||
|
self::subscribe($all_event_names);
|
||||||
|
}
|
||||||
|
self::clearTimer();
|
||||||
|
|
||||||
|
if (self::$onConnect) {
|
||||||
|
call_user_func(Client::$onConnect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clearTimer.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function clearTimer()
|
||||||
|
{
|
||||||
|
if (!self::$_isWorkermanEnv) {
|
||||||
|
throw new \Exception('Channel\\Client not support clearTimer method when it is not in the workerman environment.');
|
||||||
|
}
|
||||||
|
if(self::$_reconnectTimer)
|
||||||
|
{
|
||||||
|
Timer::del(self::$_reconnectTimer);
|
||||||
|
self::$_reconnectTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On.
|
||||||
|
* @param string $event
|
||||||
|
* @param callback $callback
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public static function on($event, $callback)
|
||||||
|
{
|
||||||
|
if (!is_callable($callback)) {
|
||||||
|
throw new \Exception('callback is not callable for event.');
|
||||||
|
}
|
||||||
|
self::$_events[$event] = $callback;
|
||||||
|
self::subscribe($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe.
|
||||||
|
* @param string $events
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function subscribe($events)
|
||||||
|
{
|
||||||
|
$events = (array)$events;
|
||||||
|
self::send(array('type' => 'subscribe', 'channels'=>$events));
|
||||||
|
foreach ($events as $event) {
|
||||||
|
if(!isset(self::$_events[$event])) {
|
||||||
|
self::$_events[$event] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe.
|
||||||
|
* @param string $events
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function unsubscribe($events)
|
||||||
|
{
|
||||||
|
$events = (array)$events;
|
||||||
|
self::send(array('type' => 'unsubscribe', 'channels'=>$events));
|
||||||
|
foreach($events as $event) {
|
||||||
|
unset(self::$_events[$event]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publish.
|
||||||
|
* @param string $events
|
||||||
|
* @param mixed $data
|
||||||
|
*/
|
||||||
|
public static function publish($events, $data)
|
||||||
|
{
|
||||||
|
self::sendAnyway(array('type' => 'publish', 'channels' => (array)$events, 'data' => $data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Watch a channel of queue
|
||||||
|
* @param string|array $channels
|
||||||
|
* @param callable $callback
|
||||||
|
* @param boolean $autoReserve Auto reserve after callback finished.
|
||||||
|
* But sometime you may don't want reserve immediately, or in some asynchronous job,
|
||||||
|
* you want reserve in finished callback, so you should set $autoReserve to false
|
||||||
|
* and call Client::reserve() after watch() and in finish callback manually.
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public static function watch($channels, $callback, $autoReserve=true)
|
||||||
|
{
|
||||||
|
if (!is_callable($callback)) {
|
||||||
|
throw new \Exception('callback is not callable for watch.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($autoReserve) {
|
||||||
|
$callback = static function($data) use ($callback) {
|
||||||
|
try {
|
||||||
|
call_user_func($callback, $data);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw $e;
|
||||||
|
} catch (\Error $e) {
|
||||||
|
throw $e;
|
||||||
|
} finally {
|
||||||
|
self::reserve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
$channels = (array)$channels;
|
||||||
|
self::send(array('type' => 'watch', 'channels'=>$channels));
|
||||||
|
|
||||||
|
foreach ($channels as $channel) {
|
||||||
|
self::$_queues[$channel] = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($autoReserve) {
|
||||||
|
self::reserve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unwatch a channel of queue
|
||||||
|
* @param string $channel
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public static function unwatch($channels)
|
||||||
|
{
|
||||||
|
$channels = (array)$channels;
|
||||||
|
self::send(array('type' => 'unwatch', 'channels'=>$channels));
|
||||||
|
foreach ($channels as $channel) {
|
||||||
|
if (isset(self::$_queues[$channel])) {
|
||||||
|
unset(self::$_queues[$channel]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put data to queue
|
||||||
|
* @param string|array $channels
|
||||||
|
* @param mixed $data
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public static function enqueue($channels, $data)
|
||||||
|
{
|
||||||
|
self::sendAnyway(array('type' => 'enqueue', 'channels' => (array)$channels, 'data' => $data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start reserve queue manual
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public static function reserve()
|
||||||
|
{
|
||||||
|
self::send(array('type' => 'reserve'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send through workerman environment
|
||||||
|
* @param $data
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
protected static function send($data)
|
||||||
|
{
|
||||||
|
if (!self::$_isWorkermanEnv) {
|
||||||
|
throw new \Exception("Channel\\Client not support {$data['type']} method when it is not in the workerman environment.");
|
||||||
|
}
|
||||||
|
self::connect(self::$_remoteIp, self::$_remotePort);
|
||||||
|
self::$_remoteConnection->send(serialize($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send from any environment
|
||||||
|
* @param $data
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
protected static function sendAnyway($data)
|
||||||
|
{
|
||||||
|
self::connect(self::$_remoteIp, self::$_remotePort);
|
||||||
|
$body = serialize($data);
|
||||||
|
if (self::$_isWorkermanEnv) {
|
||||||
|
self::$_remoteConnection->send($body);
|
||||||
|
} else {
|
||||||
|
$buffer = pack('N', 4+strlen($body)) . $body;
|
||||||
|
fwrite(self::$_remoteConnection, $buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
89
vendor/workerman/channel/src/Queue.php
vendored
Normal file
89
vendor/workerman/channel/src/Queue.php
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Channel;
|
||||||
|
|
||||||
|
use Workerman\Connection\TcpConnection;
|
||||||
|
|
||||||
|
class Queue
|
||||||
|
{
|
||||||
|
|
||||||
|
public $name = 'default';
|
||||||
|
public $watcher = array();
|
||||||
|
public $consumer = array();
|
||||||
|
protected $queue = null;
|
||||||
|
|
||||||
|
public function __construct($name)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->queue = new \SplQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TcpConnection $connection
|
||||||
|
*/
|
||||||
|
public function addWatch($connection)
|
||||||
|
{
|
||||||
|
if (!isset($this->watcher[$connection->id])) {
|
||||||
|
$this->watcher[$connection->id] = $connection;
|
||||||
|
$connection->watchs[] = $this->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TcpConnection $connection
|
||||||
|
*/
|
||||||
|
public function removeWatch($connection)
|
||||||
|
{
|
||||||
|
if (isset($connection->watchs) && in_array($this->name, $connection->watchs)) {
|
||||||
|
$idx = array_search($this->name, $connection->watchs);
|
||||||
|
unset($connection->watchs[$idx]);
|
||||||
|
}
|
||||||
|
if (isset($this->watcher[$connection->id])) {
|
||||||
|
unset($this->watcher[$connection->id]);
|
||||||
|
}
|
||||||
|
if (isset($this->consumer[$connection->id])) {
|
||||||
|
unset($this->consumer[$connection->id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TcpConnection $connection
|
||||||
|
*/
|
||||||
|
public function addConsumer($connection)
|
||||||
|
{
|
||||||
|
if (isset($this->watcher[$connection->id]) && !isset($this->consumer[$connection->id])) {
|
||||||
|
$this->consumer[$connection->id] = $connection;
|
||||||
|
}
|
||||||
|
$this->dispatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enqueue($data)
|
||||||
|
{
|
||||||
|
$this->queue->enqueue($data);
|
||||||
|
$this->dispatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function dispatch()
|
||||||
|
{
|
||||||
|
if ($this->queue->isEmpty() || count($this->consumer) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!$this->queue->isEmpty()) {
|
||||||
|
$data = $this->queue->dequeue();
|
||||||
|
$idx = key($this->consumer);
|
||||||
|
$connection = $this->consumer[$idx];
|
||||||
|
unset($this->consumer[$idx]);
|
||||||
|
$connection->send(serialize(array('type'=>'queue', 'channel'=>$this->name, 'data' => $data)));
|
||||||
|
if (count($this->consumer) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEmpty()
|
||||||
|
{
|
||||||
|
return empty($this->watcher) && $this->queue->isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
163
vendor/workerman/channel/src/Server.php
vendored
Normal file
163
vendor/workerman/channel/src/Server.php
vendored
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
<?php
|
||||||
|
namespace Channel;
|
||||||
|
|
||||||
|
use Workerman\Protocols\Frame;
|
||||||
|
use Workerman\Worker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Channel server.
|
||||||
|
*/
|
||||||
|
class Server
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Worker instance.
|
||||||
|
* @var Worker
|
||||||
|
*/
|
||||||
|
protected $_worker = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues
|
||||||
|
* @var Queue[]
|
||||||
|
*/
|
||||||
|
protected $_queues = array();
|
||||||
|
|
||||||
|
private $ip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct.
|
||||||
|
* @param string $ip Bind ip address or unix domain socket.
|
||||||
|
* Bind unix domain socket use 'unix:///tmp/channel.sock'
|
||||||
|
* @param int $port Tcp port to bind, only used when listen on tcp.
|
||||||
|
*/
|
||||||
|
public function __construct($ip = '0.0.0.0', $port = 2206)
|
||||||
|
{
|
||||||
|
if (strpos($ip, 'unix:') === false) {
|
||||||
|
$worker = new Worker("frame://$ip:$port");
|
||||||
|
} else {
|
||||||
|
$worker = new Worker($ip);
|
||||||
|
$worker->protocol = Frame::class;
|
||||||
|
}
|
||||||
|
$this->ip = $ip;
|
||||||
|
$worker->count = 1;
|
||||||
|
$worker->name = 'ChannelServer';
|
||||||
|
$worker->channels = array();
|
||||||
|
$worker->onMessage = array($this, 'onMessage') ;
|
||||||
|
$worker->onClose = array($this, 'onClose');
|
||||||
|
$this->_worker = $worker;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* onClose
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function onClose($connection)
|
||||||
|
{
|
||||||
|
if (!empty($connection->channels)) {
|
||||||
|
foreach ($connection->channels as $channel) {
|
||||||
|
unset($this->_worker->channels[$channel][$connection->id]);
|
||||||
|
if (empty($this->_worker->channels[$channel])) {
|
||||||
|
unset($this->_worker->channels[$channel]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($connection->watchs)) {
|
||||||
|
foreach ($connection->watchs as $channel) {
|
||||||
|
if (isset($this->_queues[$channel])) {
|
||||||
|
$this->_queues[$channel]->removeWatch($connection);
|
||||||
|
if ($this->_queues[$channel]->isEmpty()) {
|
||||||
|
unset($this->_queues[$channel]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* onMessage.
|
||||||
|
* @param \Workerman\Connection\TcpConnection $connection
|
||||||
|
* @param string $data
|
||||||
|
*/
|
||||||
|
public function onMessage($connection, $data)
|
||||||
|
{
|
||||||
|
if(!$data)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$worker = $this->_worker;
|
||||||
|
$data = unserialize($data);
|
||||||
|
$type = $data['type'];
|
||||||
|
switch($type)
|
||||||
|
{
|
||||||
|
case 'subscribe':
|
||||||
|
foreach($data['channels'] as $channel)
|
||||||
|
{
|
||||||
|
$connection->channels[$channel] = $channel;
|
||||||
|
$worker->channels[$channel][$connection->id] = $connection;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'unsubscribe':
|
||||||
|
foreach($data['channels'] as $channel) {
|
||||||
|
if (isset($connection->channels[$channel])) {
|
||||||
|
unset($connection->channels[$channel]);
|
||||||
|
}
|
||||||
|
if (isset($worker->channels[$channel][$connection->id])) {
|
||||||
|
unset($worker->channels[$channel][$connection->id]);
|
||||||
|
if (empty($worker->channels[$channel])) {
|
||||||
|
unset($worker->channels[$channel]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'publish':
|
||||||
|
foreach ($data['channels'] as $channel) {
|
||||||
|
if (empty($worker->channels[$channel])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$buffer = serialize(array('type' => 'event', 'channel' => $channel, 'data' => $data['data']))."\n";
|
||||||
|
foreach ($worker->channels[$channel] as $connection) {
|
||||||
|
$connection->send($buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'watch':
|
||||||
|
foreach ($data['channels'] as $channel) {
|
||||||
|
$this->getQueue($channel)->addWatch($connection);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'unwatch':
|
||||||
|
foreach ($data['channels'] as $channel) {
|
||||||
|
if (isset($this->_queues[$channel])) {
|
||||||
|
$this->_queues[$channel]->removeWatch($connection);
|
||||||
|
if ($this->_queues[$channel]->isEmpty()) {
|
||||||
|
unset($this->_queues[$channel]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'enqueue':
|
||||||
|
foreach ($data['channels'] as $channel) {
|
||||||
|
$this->getQueue($channel)->enqueue($data['data']);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'reserve':
|
||||||
|
if (isset($connection->watchs)) {
|
||||||
|
foreach ($connection->watchs as $channel) {
|
||||||
|
if (isset($this->_queues[$channel])) {
|
||||||
|
$this->_queues[$channel]->addConsumer($connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getQueue($channel)
|
||||||
|
{
|
||||||
|
if (isset($this->_queues[$channel])) {
|
||||||
|
return $this->_queues[$channel];
|
||||||
|
}
|
||||||
|
return ($this->_queues[$channel] = new Queue($channel));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
53
vendor/workerman/channel/test/queue.php
vendored
Normal file
53
vendor/workerman/channel/test/queue.php
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Channel\Client;
|
||||||
|
use Channel\Server;
|
||||||
|
use Workerman\Worker;
|
||||||
|
use Workerman\Lib\Timer;
|
||||||
|
|
||||||
|
// composer autoload
|
||||||
|
include __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
$channel_server = new Server();
|
||||||
|
|
||||||
|
$worker = new Worker();
|
||||||
|
$worker->name = 'Event';
|
||||||
|
$worker->onWorkerStart = function()
|
||||||
|
{
|
||||||
|
Client::connect();
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
$timerId = Timer::add(0.01, function() use (&$timerId, &$count) {
|
||||||
|
Client::publish('test event', 'some data');
|
||||||
|
$count++;
|
||||||
|
Client::enqueue('task-queue', time());
|
||||||
|
if ($count == 1000) {
|
||||||
|
Timer::del($timerId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Timer::add(10, function() {
|
||||||
|
Client::enqueue('task-queue', 'hello every 10 seconds');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$mq = new Worker();
|
||||||
|
$mq->name = 'Queue';
|
||||||
|
$mq->count = 4;
|
||||||
|
$mq->onWorkerStart = function($worker) {
|
||||||
|
Client::connect();
|
||||||
|
$countDown = 20;
|
||||||
|
$id = 1;
|
||||||
|
Client::watch('task-queue', function($data) use ($worker, &$countDown, &$id) {
|
||||||
|
echo "[$id] Worker {$worker->id} get queue: $data\n";
|
||||||
|
sleep(0.2);
|
||||||
|
$countDown--;
|
||||||
|
$id++;
|
||||||
|
if ($worker->id > 1 && $countDown == 0) {
|
||||||
|
Client::unwatch('task-queue');
|
||||||
|
}
|
||||||
|
Timer::add(1, [Client::class, 'reserve'], [], false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Worker::runAll();
|
28
vendor/workerman/channel/test/server.php
vendored
Normal file
28
vendor/workerman/channel/test/server.php
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Channel\Client;
|
||||||
|
use Channel\Server;
|
||||||
|
use Workerman\Worker;
|
||||||
|
use Workerman\Lib\Timer;
|
||||||
|
|
||||||
|
// composer autoload
|
||||||
|
include __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
$channel_server = new Server();
|
||||||
|
|
||||||
|
$worker = new Worker();
|
||||||
|
$worker->onWorkerStart = function()
|
||||||
|
{
|
||||||
|
Client::connect();
|
||||||
|
|
||||||
|
Client::on('test event', function($event_data){
|
||||||
|
echo 'test event triggered event_data :';
|
||||||
|
var_dump($event_data);
|
||||||
|
});
|
||||||
|
|
||||||
|
Timer::add(2, function(){
|
||||||
|
Client::publish('test event', 'some data');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Worker::runAll();
|
4
vendor/workerman/phpsocket.io/.github/FUNDING.yml
vendored
Normal file
4
vendor/workerman/phpsocket.io/.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
open_collective: walkor
|
||||||
|
patreon: walkor
|
6
vendor/workerman/phpsocket.io/.gitignore
vendored
Normal file
6
vendor/workerman/phpsocket.io/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.buildpath
|
||||||
|
.project
|
||||||
|
.settings/org.eclipse.php.core.prefs
|
||||||
|
vendor
|
||||||
|
examples/vendor
|
||||||
|
composer.lock
|
162
vendor/workerman/phpsocket.io/README.md
vendored
Normal file
162
vendor/workerman/phpsocket.io/README.md
vendored
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
# phpsocket.io
|
||||||
|
A server side alternative implementation of [socket.io](https://github.com/socketio/socket.io) in PHP based on [Workerman](https://github.com/walkor/Workerman).<br>
|
||||||
|
|
||||||
|
# Notice
|
||||||
|
Only support socket.io v1.3.0 or greater. <br>
|
||||||
|
This project is just translate socket.io by [workerman](https://github.com/walkor/Workerman).<br>
|
||||||
|
More api just see http://socket.io/docs/server-api/
|
||||||
|
|
||||||
|
# Install
|
||||||
|
composer require workerman/phpsocket.io
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
## Simple chat
|
||||||
|
start.php
|
||||||
|
```php
|
||||||
|
|
||||||
|
use Workerman\Worker;
|
||||||
|
use PHPSocketIO\SocketIO;
|
||||||
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
// Listen port 2021 for socket.io client
|
||||||
|
$io = new SocketIO(2021);
|
||||||
|
$io->on('connection', function ($socket) use ($io) {
|
||||||
|
$socket->on('chat message', function ($msg) use ($io) {
|
||||||
|
$io->emit('chat message', $msg);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Worker::runAll();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Another chat demo
|
||||||
|
|
||||||
|
https://github.com/walkor/phpsocket.io/blob/master/examples/chat/start_io.php
|
||||||
|
```php
|
||||||
|
|
||||||
|
use Workerman\Worker;
|
||||||
|
use PHPSocketIO\SocketIO;
|
||||||
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
// Listen port 2020 for socket.io client
|
||||||
|
$io = new SocketIO(2020);
|
||||||
|
$io->on('connection', function ($socket) {
|
||||||
|
$socket->addedUser = false;
|
||||||
|
|
||||||
|
// When the client emits 'new message', this listens and executes
|
||||||
|
$socket->on('new message', function ($data) use ($socket) {
|
||||||
|
// We tell the client to execute 'new message'
|
||||||
|
$socket->broadcast->emit('new message', array(
|
||||||
|
'username' => $socket->username,
|
||||||
|
'message' => $data
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
// When the client emits 'add user', this listens and executes
|
||||||
|
$socket->on('add user', function ($username) use ($socket) {
|
||||||
|
global $usernames, $numUsers;
|
||||||
|
|
||||||
|
// We store the username in the socket session for this client
|
||||||
|
$socket->username = $username;
|
||||||
|
// Add the client's username to the global list
|
||||||
|
$usernames[$username] = $username;
|
||||||
|
++$numUsers;
|
||||||
|
|
||||||
|
$socket->addedUser = true;
|
||||||
|
$socket->emit('login', array(
|
||||||
|
'numUsers' => $numUsers
|
||||||
|
));
|
||||||
|
|
||||||
|
// echo globally (all clients) that a person has connected
|
||||||
|
$socket->broadcast->emit('user joined', array(
|
||||||
|
'username' => $socket->username,
|
||||||
|
'numUsers' => $numUsers
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
// When the client emits 'typing', we broadcast it to others
|
||||||
|
$socket->on('typing', function () use ($socket) {
|
||||||
|
$socket->broadcast->emit('typing', array(
|
||||||
|
'username' => $socket->username
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
// When the client emits 'stop typing', we broadcast it to others
|
||||||
|
$socket->on('stop typing', function () use ($socket) {
|
||||||
|
$socket->broadcast->emit('stop typing', array(
|
||||||
|
'username' => $socket->username
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
// When the user disconnects, perform this
|
||||||
|
$socket->on('disconnect', function () use ($socket) {
|
||||||
|
global $usernames, $numUsers;
|
||||||
|
|
||||||
|
// Remove the username from global usernames list
|
||||||
|
if ($socket->addedUser) {
|
||||||
|
unset($usernames[$socket->username]);
|
||||||
|
--$numUsers;
|
||||||
|
|
||||||
|
// echo globally that this client has left
|
||||||
|
$socket->broadcast->emit('user left', array(
|
||||||
|
'username' => $socket->username,
|
||||||
|
'numUsers' => $numUsers
|
||||||
|
));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Worker::runAll();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Enable SSL for https
|
||||||
|
**```(phpsocket.io>=1.1.1 && workerman>=3.3.7 required)```**
|
||||||
|
|
||||||
|
start.php
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Workerman\Worker;
|
||||||
|
use PHPSocketIO\SocketIO;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
// SSL context
|
||||||
|
$context = array(
|
||||||
|
'ssl' => array(
|
||||||
|
'local_cert' => '/your/path/of/server.pem',
|
||||||
|
'local_pk' => '/your/path/of/server.key',
|
||||||
|
'verify_peer' => false
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$io = new SocketIO(2021, $context);
|
||||||
|
|
||||||
|
$io->on('connection', function ($connection) use ($io) {
|
||||||
|
echo "New connection coming\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
Worker::runAll();
|
||||||
|
```
|
||||||
|
|
||||||
|
# 手册
|
||||||
|
[中文手册](https://github.com/walkor/phpsocket.io/tree/master/docs/zh)
|
||||||
|
|
||||||
|
# Livedemo
|
||||||
|
[chat demo](http://demos.workerman.net/phpsocketio-chat/)
|
||||||
|
|
||||||
|
# Run chat example
|
||||||
|
cd examples/chat
|
||||||
|
|
||||||
|
## Start
|
||||||
|
```php start.php start``` for debug mode
|
||||||
|
|
||||||
|
```php start.php start -d ``` for daemon mode
|
||||||
|
|
||||||
|
## Stop
|
||||||
|
```php start.php stop```
|
||||||
|
|
||||||
|
## Status
|
||||||
|
```php start.php status```
|
||||||
|
|
||||||
|
# License
|
||||||
|
MIT
|
14
vendor/workerman/phpsocket.io/composer.json
vendored
Normal file
14
vendor/workerman/phpsocket.io/composer.json
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name" : "workerman/phpsocket.io",
|
||||||
|
"type" : "library",
|
||||||
|
"keywords": ["socket.io"],
|
||||||
|
"homepage": "http://www.workerman.net",
|
||||||
|
"license" : "MIT",
|
||||||
|
"require": {
|
||||||
|
"workerman/workerman" : ">=3.5.16",
|
||||||
|
"workerman/channel" : ">=1.0.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {"PHPSocketIO\\": "./src"}
|
||||||
|
}
|
||||||
|
}
|
3
vendor/workerman/phpsocket.io/docs/README.md
vendored
Normal file
3
vendor/workerman/phpsocket.io/docs/README.md
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
## Documentation
|
||||||
|
|
||||||
|
[中文](./zh/)
|
259
vendor/workerman/phpsocket.io/docs/zh/README.md
vendored
Normal file
259
vendor/workerman/phpsocket.io/docs/zh/README.md
vendored
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
# phpsocket.io手册
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
请使用composer集成phpsocket.io。
|
||||||
|
|
||||||
|
脚本中引用vendor中的autoload.php实现SocketIO相关类的加载。例如
|
||||||
|
```php
|
||||||
|
require_once '/你的vendor路径/autoload.php';
|
||||||
|
```
|
||||||
|
|
||||||
|
## 服务端和客户端连接
|
||||||
|
**创建一个SocketIO服务端**
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
use Workerman\Worker;
|
||||||
|
use PHPSocketIO\SocketIO;
|
||||||
|
|
||||||
|
// 创建socket.io服务端,监听3120端口
|
||||||
|
$io = new SocketIO(3120);
|
||||||
|
// 当有客户端连接时打印一行文字
|
||||||
|
$io->on('connection', function($socket)use($io){
|
||||||
|
echo "new connection coming\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
Worker::runAll();
|
||||||
|
```
|
||||||
|
**客户端**
|
||||||
|
```javascript
|
||||||
|
<script src='https://cdn.bootcss.com/socket.io/2.0.3/socket.io.js'></script>
|
||||||
|
<script>
|
||||||
|
// 如果服务端不在本机,请把127.0.0.1改成服务端ip
|
||||||
|
var socket = io('http://127.0.0.1:3120');
|
||||||
|
// 当连接服务端成功时触发connect默认事件
|
||||||
|
socket.on('connect', function(){
|
||||||
|
console.log('connect success');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 自定义事件
|
||||||
|
socket.io主要是通过事件来进行通讯交互的。
|
||||||
|
|
||||||
|
socket连接除了自带的connect,message,disconnect三个事件以外,在服务端和客户端开发者可以自定义其它事件。
|
||||||
|
|
||||||
|
服务端和客户端都通过emit方法触发对端的事件。
|
||||||
|
|
||||||
|
例如下面的代码在服务端定义了一个```chat message```事件,事件参数为```$msg```。
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
use Workerman\Worker;
|
||||||
|
use PHPSocketIO\SocketIO;
|
||||||
|
|
||||||
|
$io = new SocketIO(3120);
|
||||||
|
// 当有客户端连接时
|
||||||
|
$io->on('connection', function($socket)use($io){
|
||||||
|
// 定义chat message事件回调函数
|
||||||
|
$socket->on('chat message', function($msg)use($io){
|
||||||
|
// 触发所有客户端定义的chat message from server事件
|
||||||
|
$io->emit('chat message from server', $msg);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Worker::runAll();
|
||||||
|
```
|
||||||
|
|
||||||
|
客户端通过下面的方法触发服务端的chat message事件。
|
||||||
|
```javascript
|
||||||
|
<script src='//cdn.bootcss.com/socket.io/1.3.7/socket.io.js'></script>
|
||||||
|
<script>
|
||||||
|
// 连接服务端
|
||||||
|
var socket = io('http://127.0.0.1:3120');
|
||||||
|
// 触发服务端的chat message事件
|
||||||
|
socket.emit('chat message', '这个是消息内容...');
|
||||||
|
// 服务端通过emit('chat message from server', $msg)触发客户端的chat message from server事件
|
||||||
|
socket.on('chat message from server', function(msg){
|
||||||
|
console.log('get message:' + msg + ' from server');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## workerStart事件
|
||||||
|
phpsocket.io提供了workerStart事件回调,也就是当进程启动后准备好接受客户端链接时触发的回调。
|
||||||
|
一个进程生命周期只会触发一次。可以在这里设置一些全局的事情,比如开一个新的Worker端口等等。
|
||||||
|
```php
|
||||||
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
use Workerman\Worker;
|
||||||
|
use PHPSocketIO\SocketIO;
|
||||||
|
|
||||||
|
$io = new SocketIO(9120);
|
||||||
|
|
||||||
|
// 监听一个http端口,通过http协议访问这个端口可以向所有客户端推送数据(url类似http://ip:9191?msg=xxxx)
|
||||||
|
$io->on('workerStart', function()use($io) {
|
||||||
|
$inner_http_worker = new Worker('http://0.0.0.0:9191');
|
||||||
|
$inner_http_worker->onMessage = function($http_connection, $data)use($io){
|
||||||
|
if(!isset($_GET['msg'])) {
|
||||||
|
return $http_connection->send('fail, $_GET["msg"] not found');
|
||||||
|
}
|
||||||
|
$io->emit('chat message', $_GET['msg']);
|
||||||
|
$http_connection->send('ok');
|
||||||
|
};
|
||||||
|
$inner_http_worker->listen();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 当有客户端连接时
|
||||||
|
$io->on('connection', function($socket)use($io){
|
||||||
|
// 定义chat message事件回调函数
|
||||||
|
$socket->on('chat message', function($msg)use($io){
|
||||||
|
// 触发所有客户端定义的chat message from server事件
|
||||||
|
$io->emit('chat message from server', $msg);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Worker::runAll();
|
||||||
|
```
|
||||||
|
phpsocket.io启动后开内部http端口通过phpsocket.io向客户端推送数据参考 [web-msg-sender](http://www.workerman.net/web-sender)。
|
||||||
|
|
||||||
|
## 分组
|
||||||
|
socket.io提供分组功能,允许向某个分组发送事件,例如向某个房间广播数据。
|
||||||
|
|
||||||
|
1、加入分组(一个连接可以加入多个分组)
|
||||||
|
```php
|
||||||
|
$socket->join('group name');
|
||||||
|
```
|
||||||
|
2、离开分组(连接断开时会自动从分组中离开)
|
||||||
|
```php
|
||||||
|
$socket->leave('group name');
|
||||||
|
```
|
||||||
|
|
||||||
|
## 向客户端发送事件的各种方法
|
||||||
|
$io是SocketIO对象。$socket是客户端连接
|
||||||
|
|
||||||
|
$data可以是数字和字符串,也可以是数组。当$data是数组时,客户端会自动转换为javascript对象。
|
||||||
|
|
||||||
|
同理如果客户端向服务端emit某个事件传递的是一个javascript对象,在服务端接收时会自动转换为php数组。
|
||||||
|
|
||||||
|
1、向当前客户端发送事件
|
||||||
|
```php
|
||||||
|
$socket->emit('event name', $data);
|
||||||
|
```
|
||||||
|
2、向所有客户端发送事件
|
||||||
|
```php
|
||||||
|
$io->emit('event name', $data);
|
||||||
|
```
|
||||||
|
3、向所有客户端发送事件,但不包括当前连接。
|
||||||
|
```php
|
||||||
|
$socket->broadcast->emit('event name', $data);
|
||||||
|
```
|
||||||
|
|
||||||
|
4、向某个分组的所有客户端发送事件
|
||||||
|
```php
|
||||||
|
$io->to('group name')->emit('event name', $data);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 获取客户端ip
|
||||||
|
```php
|
||||||
|
$io->on('connection', function($socket)use($io){
|
||||||
|
var_dump($socket->conn->remoteAddress);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## 关闭链接
|
||||||
|
```php
|
||||||
|
$socket->disconnect();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 限制连接域名
|
||||||
|
当我们想指定特定域名的页面才能连接,可以用$io->origins方法来设置域名白名单。
|
||||||
|
```php
|
||||||
|
$io = new SocketIO(2020);
|
||||||
|
$io->origins('http://example.com:8080');
|
||||||
|
```
|
||||||
|
多个域名时用空格分隔,类似
|
||||||
|
```php
|
||||||
|
$io = new SocketIO(2020);
|
||||||
|
$io->origins('http://workerman.net http://www.workerman.net');
|
||||||
|
```
|
||||||
|
|
||||||
|
## 支持SSL(https wss)
|
||||||
|
SSL支持有两种方法,workerman原生和nginx代理
|
||||||
|
### workerman原生支持
|
||||||
|
SSL 要求workerman>=3.3.7 phpsocket.io>=1.1.1
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
use Workerman\Worker;
|
||||||
|
use PHPSocketIO\SocketIO;
|
||||||
|
|
||||||
|
// 传入ssl选项,包含证书的路径
|
||||||
|
$context = array(
|
||||||
|
'ssl' => array(
|
||||||
|
'local_cert' => '/your/path/of/server.pem',
|
||||||
|
'local_pk' => '/your/path/of/server.key',
|
||||||
|
'verify_peer' => false,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$io = new SocketIO(2120, $context);
|
||||||
|
|
||||||
|
$io->on('connection', function($socket)use($io){
|
||||||
|
echo "new connection coming\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
Worker::runAll();
|
||||||
|
```
|
||||||
|
**注意:**<br>
|
||||||
|
1、证书是要验证域名的,所以客户端链接时要指定域名才能顺利的建立链接。<br>
|
||||||
|
2、客户端连接时不能再用http方式,要改成https类似下面这样。
|
||||||
|
```javascript
|
||||||
|
<script>
|
||||||
|
var socket = io('https://yoursite.com:2120');
|
||||||
|
//.....
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
### nginx代理SSL
|
||||||
|
|
||||||
|
**前提条件及准备工作:**
|
||||||
|
|
||||||
|
1、已经安装nginx,版本不低于1.3
|
||||||
|
|
||||||
|
2、假设phpsocket.io监听的是2120端口
|
||||||
|
|
||||||
|
3、已经申请了证书(pem/crt文件及key文件)放在了/etc/nginx/conf.d/ssl下
|
||||||
|
|
||||||
|
4、打算利用nginx开启443端口对外提供ssl代理服务(端口可以根据需要修改)
|
||||||
|
|
||||||
|
**nginx配置类似如下:**
|
||||||
|
```
|
||||||
|
server {
|
||||||
|
listen 443;
|
||||||
|
|
||||||
|
ssl on;
|
||||||
|
ssl_certificate /etc/ssl/server.pem;
|
||||||
|
ssl_certificate_key /etc/ssl/server.key;
|
||||||
|
ssl_session_timeout 5m;
|
||||||
|
ssl_session_cache shared:SSL:50m;
|
||||||
|
ssl_protocols SSLv3 SSLv2 TLSv1 TLSv1.1 TLSv1.2;
|
||||||
|
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
|
||||||
|
|
||||||
|
location /socket.io
|
||||||
|
{
|
||||||
|
proxy_pass http://127.0.0.1:2120;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "Upgrade";
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
# location / {} 站点的其它配置...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**注意:**<br>
|
||||||
|
1、证书是要验证域名的,所以客户端链接时要指定域名才能顺利的建立链接。<br>
|
||||||
|
2、客户端连接时不能再用http方式,要改成https类似下面这样。
|
||||||
|
```javascript
|
||||||
|
<script>
|
||||||
|
var socket = io('https://yoursite.com');
|
||||||
|
//.....
|
||||||
|
</scrip
|
20
vendor/workerman/phpsocket.io/examples/chat/README.md
vendored
Normal file
20
vendor/workerman/phpsocket.io/examples/chat/README.md
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# For chat demo
|
||||||
|
## start
|
||||||
|
```php start.php start``` for debug mode
|
||||||
|
|
||||||
|
```php start.php start -d``` for daemon mode
|
||||||
|
|
||||||
|
## stop
|
||||||
|
```php start.php stop```
|
||||||
|
|
||||||
|
## satus
|
||||||
|
```php start.php status```
|
||||||
|
|
||||||
|
## restart
|
||||||
|
``` php start.php restart```
|
||||||
|
|
||||||
|
## reload
|
||||||
|
``` php start.php reload```
|
||||||
|
|
||||||
|
## connections
|
||||||
|
``` php start.php connections```
|
28
vendor/workerman/phpsocket.io/examples/chat/public/index.html
vendored
Normal file
28
vendor/workerman/phpsocket.io/examples/chat/public/index.html
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Socket.IO Chat Example</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ul class="pages">
|
||||||
|
<li class="chat page">
|
||||||
|
<div class="chatArea">
|
||||||
|
<ul class="messages"></ul>
|
||||||
|
</div>
|
||||||
|
<input class="inputMessage" placeholder="Type here..."/>
|
||||||
|
</li>
|
||||||
|
<li class="login page">
|
||||||
|
<div class="form">
|
||||||
|
<h3 class="title">What's your nickname?</h3>
|
||||||
|
<input class="usernameInput" type="text" maxlength="14" />
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<script src="/jquery.min.js"></script>
|
||||||
|
<script src="/socket.io-client/socket.io.js"></script>
|
||||||
|
<script src="/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
4
vendor/workerman/phpsocket.io/examples/chat/public/jquery.min.js
vendored
Normal file
4
vendor/workerman/phpsocket.io/examples/chat/public/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
282
vendor/workerman/phpsocket.io/examples/chat/public/main.js
vendored
Normal file
282
vendor/workerman/phpsocket.io/examples/chat/public/main.js
vendored
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
$(function() {
|
||||||
|
var FADE_TIME = 150; // ms
|
||||||
|
var TYPING_TIMER_LENGTH = 400; // ms
|
||||||
|
var COLORS = [
|
||||||
|
'#e21400', '#91580f', '#f8a700', '#f78b00',
|
||||||
|
'#58dc00', '#287b00', '#a8f07a', '#4ae8c4',
|
||||||
|
'#3b88eb', '#3824aa', '#a700ff', '#d300e7'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Initialize variables
|
||||||
|
var $window = $(window);
|
||||||
|
var $usernameInput = $('.usernameInput'); // Input for username
|
||||||
|
var $messages = $('.messages'); // Messages area
|
||||||
|
var $inputMessage = $('.inputMessage'); // Input message input box
|
||||||
|
|
||||||
|
var $loginPage = $('.login.page'); // The login page
|
||||||
|
var $chatPage = $('.chat.page'); // The chatroom page
|
||||||
|
|
||||||
|
// Prompt for setting a username
|
||||||
|
var username;
|
||||||
|
var connected = false;
|
||||||
|
var typing = false;
|
||||||
|
var lastTypingTime;
|
||||||
|
var $currentInput = $usernameInput.focus();
|
||||||
|
|
||||||
|
var socket = io('http://'+document.domain+':2020');
|
||||||
|
|
||||||
|
const addParticipantsMessage = (data) => {
|
||||||
|
var message = '';
|
||||||
|
if (data.numUsers === 1) {
|
||||||
|
message += "there's 1 participant";
|
||||||
|
} else {
|
||||||
|
message += "there are " + data.numUsers + " participants";
|
||||||
|
}
|
||||||
|
log(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the client's username
|
||||||
|
const setUsername = () => {
|
||||||
|
username = cleanInput($usernameInput.val().trim());
|
||||||
|
|
||||||
|
// If the username is valid
|
||||||
|
if (username) {
|
||||||
|
$loginPage.fadeOut();
|
||||||
|
$chatPage.show();
|
||||||
|
$loginPage.off('click');
|
||||||
|
$currentInput = $inputMessage.focus();
|
||||||
|
|
||||||
|
// Tell the server your username
|
||||||
|
socket.emit('add user', username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sends a chat message
|
||||||
|
const sendMessage = () => {
|
||||||
|
var message = $inputMessage.val();
|
||||||
|
// Prevent markup from being injected into the message
|
||||||
|
message = cleanInput(message);
|
||||||
|
// if there is a non-empty message and a socket connection
|
||||||
|
if (message && connected) {
|
||||||
|
$inputMessage.val('');
|
||||||
|
addChatMessage({
|
||||||
|
username: username,
|
||||||
|
message: message
|
||||||
|
});
|
||||||
|
// tell server to execute 'new message' and send along one parameter
|
||||||
|
socket.emit('new message', message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log a message
|
||||||
|
const log = (message, options) => {
|
||||||
|
var $el = $('<li>').addClass('log').text(message);
|
||||||
|
addMessageElement($el, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds the visual chat message to the message list
|
||||||
|
const addChatMessage = (data, options) => {
|
||||||
|
// Don't fade the message in if there is an 'X was typing'
|
||||||
|
var $typingMessages = getTypingMessages(data);
|
||||||
|
options = options || {};
|
||||||
|
if ($typingMessages.length !== 0) {
|
||||||
|
options.fade = false;
|
||||||
|
$typingMessages.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
var $usernameDiv = $('<span class="username"/>')
|
||||||
|
.text(data.username)
|
||||||
|
.css('color', getUsernameColor(data.username));
|
||||||
|
var $messageBodyDiv = $('<span class="messageBody">')
|
||||||
|
.text(data.message);
|
||||||
|
|
||||||
|
var typingClass = data.typing ? 'typing' : '';
|
||||||
|
var $messageDiv = $('<li class="message"/>')
|
||||||
|
.data('username', data.username)
|
||||||
|
.addClass(typingClass)
|
||||||
|
.append($usernameDiv, $messageBodyDiv);
|
||||||
|
|
||||||
|
addMessageElement($messageDiv, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds the visual chat typing message
|
||||||
|
const addChatTyping = (data) => {
|
||||||
|
data.typing = true;
|
||||||
|
data.message = 'is typing';
|
||||||
|
addChatMessage(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes the visual chat typing message
|
||||||
|
const removeChatTyping = (data) => {
|
||||||
|
getTypingMessages(data).fadeOut(function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a message element to the messages and scrolls to the bottom
|
||||||
|
// el - The element to add as a message
|
||||||
|
// options.fade - If the element should fade-in (default = true)
|
||||||
|
// options.prepend - If the element should prepend
|
||||||
|
// all other messages (default = false)
|
||||||
|
const addMessageElement = (el, options) => {
|
||||||
|
var $el = $(el);
|
||||||
|
|
||||||
|
// Setup default options
|
||||||
|
if (!options) {
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
if (typeof options.fade === 'undefined') {
|
||||||
|
options.fade = true;
|
||||||
|
}
|
||||||
|
if (typeof options.prepend === 'undefined') {
|
||||||
|
options.prepend = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply options
|
||||||
|
if (options.fade) {
|
||||||
|
$el.hide().fadeIn(FADE_TIME);
|
||||||
|
}
|
||||||
|
if (options.prepend) {
|
||||||
|
$messages.prepend($el);
|
||||||
|
} else {
|
||||||
|
$messages.append($el);
|
||||||
|
}
|
||||||
|
$messages[0].scrollTop = $messages[0].scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevents input from having injected markup
|
||||||
|
const cleanInput = (input) => {
|
||||||
|
return $('<div/>').text(input).html();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the typing event
|
||||||
|
const updateTyping = () => {
|
||||||
|
if (connected) {
|
||||||
|
if (!typing) {
|
||||||
|
typing = true;
|
||||||
|
socket.emit('typing');
|
||||||
|
}
|
||||||
|
lastTypingTime = (new Date()).getTime();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
var typingTimer = (new Date()).getTime();
|
||||||
|
var timeDiff = typingTimer - lastTypingTime;
|
||||||
|
if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
|
||||||
|
socket.emit('stop typing');
|
||||||
|
typing = false;
|
||||||
|
}
|
||||||
|
}, TYPING_TIMER_LENGTH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the 'X is typing' messages of a user
|
||||||
|
const getTypingMessages = (data) => {
|
||||||
|
return $('.typing.message').filter(function (i) {
|
||||||
|
return $(this).data('username') === data.username;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the color of a username through our hash function
|
||||||
|
const getUsernameColor = (username) => {
|
||||||
|
// Compute hash code
|
||||||
|
var hash = 7;
|
||||||
|
for (var i = 0; i < username.length; i++) {
|
||||||
|
hash = username.charCodeAt(i) + (hash << 5) - hash;
|
||||||
|
}
|
||||||
|
// Calculate color
|
||||||
|
var index = Math.abs(hash % COLORS.length);
|
||||||
|
return COLORS[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keyboard events
|
||||||
|
|
||||||
|
$window.keydown(event => {
|
||||||
|
// Auto-focus the current input when a key is typed
|
||||||
|
if (!(event.ctrlKey || event.metaKey || event.altKey)) {
|
||||||
|
$currentInput.focus();
|
||||||
|
}
|
||||||
|
// When the client hits ENTER on their keyboard
|
||||||
|
if (event.which === 13) {
|
||||||
|
if (username) {
|
||||||
|
sendMessage();
|
||||||
|
socket.emit('stop typing');
|
||||||
|
typing = false;
|
||||||
|
} else {
|
||||||
|
setUsername();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$inputMessage.on('input', () => {
|
||||||
|
updateTyping();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Click events
|
||||||
|
|
||||||
|
// Focus input when clicking anywhere on login page
|
||||||
|
$loginPage.click(() => {
|
||||||
|
$currentInput.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Focus input when clicking on the message input's border
|
||||||
|
$inputMessage.click(() => {
|
||||||
|
$inputMessage.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Socket events
|
||||||
|
|
||||||
|
// Whenever the server emits 'login', log the login message
|
||||||
|
socket.on('login', (data) => {
|
||||||
|
connected = true;
|
||||||
|
// Display the welcome message
|
||||||
|
var message = "Welcome to Socket.IO Chat – ";
|
||||||
|
log(message, {
|
||||||
|
prepend: true
|
||||||
|
});
|
||||||
|
addParticipantsMessage(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Whenever the server emits 'new message', update the chat body
|
||||||
|
socket.on('new message', (data) => {
|
||||||
|
addChatMessage(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Whenever the server emits 'user joined', log it in the chat body
|
||||||
|
socket.on('user joined', (data) => {
|
||||||
|
log(data.username + ' joined');
|
||||||
|
addParticipantsMessage(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Whenever the server emits 'user left', log it in the chat body
|
||||||
|
socket.on('user left', (data) => {
|
||||||
|
log(data.username + ' left');
|
||||||
|
addParticipantsMessage(data);
|
||||||
|
removeChatTyping(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Whenever the server emits 'typing', show the typing message
|
||||||
|
socket.on('typing', (data) => {
|
||||||
|
addChatTyping(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Whenever the server emits 'stop typing', kill the typing message
|
||||||
|
socket.on('stop typing', (data) => {
|
||||||
|
removeChatTyping(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('disconnect', () => {
|
||||||
|
log('you have been disconnected');
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('reconnect', () => {
|
||||||
|
log('you have been reconnected');
|
||||||
|
if (username) {
|
||||||
|
socket.emit('add user', username);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('reconnect_error', () => {
|
||||||
|
log('attempt to reconnect has failed');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
22
vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/LICENSE
vendored
Normal file
22
vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/LICENSE
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Guillermo Rauch
|
||||||
|
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
92
vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/index.js
vendored
Normal file
92
vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/index.js
vendored
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var url = require('./url');
|
||||||
|
var parser = require('socket.io-parser');
|
||||||
|
var Manager = require('./manager');
|
||||||
|
var debug = require('debug')('socket.io-client');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module exports.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = exports = lookup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Managers cache.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var cache = exports.managers = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up an existing `Manager` for multiplexing.
|
||||||
|
* If the user summons:
|
||||||
|
*
|
||||||
|
* `io('http://localhost/a');`
|
||||||
|
* `io('http://localhost/b');`
|
||||||
|
*
|
||||||
|
* We reuse the existing instance based on same scheme/port/host,
|
||||||
|
* and we initialize sockets for each namespace.
|
||||||
|
*
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
function lookup(uri, opts) {
|
||||||
|
if (typeof uri == 'object') {
|
||||||
|
opts = uri;
|
||||||
|
uri = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = opts || {};
|
||||||
|
|
||||||
|
var parsed = url(uri);
|
||||||
|
var source = parsed.source;
|
||||||
|
var id = parsed.id;
|
||||||
|
var path = parsed.path;
|
||||||
|
var sameNamespace = cache[id] && path in cache[id].nsps;
|
||||||
|
var newConnection = opts.forceNew || opts['force new connection'] ||
|
||||||
|
false === opts.multiplex || sameNamespace;
|
||||||
|
|
||||||
|
var io;
|
||||||
|
|
||||||
|
if (newConnection) {
|
||||||
|
debug('ignoring socket cache for %s', source);
|
||||||
|
io = Manager(source, opts);
|
||||||
|
} else {
|
||||||
|
if (!cache[id]) {
|
||||||
|
debug('new io instance for %s', source);
|
||||||
|
cache[id] = Manager(source, opts);
|
||||||
|
}
|
||||||
|
io = cache[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
return io.socket(parsed.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protocol version.
|
||||||
|
*
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.protocol = parser.protocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `connect`.
|
||||||
|
*
|
||||||
|
* @param {String} uri
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.connect = lookup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose constructors for standalone build.
|
||||||
|
*
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.Manager = require('./manager');
|
||||||
|
exports.Socket = require('./socket');
|
539
vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/manager.js
vendored
Normal file
539
vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/manager.js
vendored
Normal file
@ -0,0 +1,539 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var url = require('./url');
|
||||||
|
var eio = require('engine.io-client');
|
||||||
|
var Socket = require('./socket');
|
||||||
|
var Emitter = require('component-emitter');
|
||||||
|
var parser = require('socket.io-parser');
|
||||||
|
var on = require('./on');
|
||||||
|
var bind = require('component-bind');
|
||||||
|
var object = require('object-component');
|
||||||
|
var debug = require('debug')('socket.io-client:manager');
|
||||||
|
var indexOf = require('indexof');
|
||||||
|
var Backoff = require('backo2');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module exports
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = Manager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `Manager` constructor.
|
||||||
|
*
|
||||||
|
* @param {String} engine instance or engine uri/opts
|
||||||
|
* @param {Object} options
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
function Manager(uri, opts){
|
||||||
|
if (!(this instanceof Manager)) return new Manager(uri, opts);
|
||||||
|
if (uri && ('object' == typeof uri)) {
|
||||||
|
opts = uri;
|
||||||
|
uri = undefined;
|
||||||
|
}
|
||||||
|
opts = opts || {};
|
||||||
|
|
||||||
|
opts.path = opts.path || '/socket.io';
|
||||||
|
this.nsps = {};
|
||||||
|
this.subs = [];
|
||||||
|
this.opts = opts;
|
||||||
|
this.reconnection(opts.reconnection !== false);
|
||||||
|
this.reconnectionAttempts(opts.reconnectionAttempts || Infinity);
|
||||||
|
this.reconnectionDelay(opts.reconnectionDelay || 1000);
|
||||||
|
this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000);
|
||||||
|
this.randomizationFactor(opts.randomizationFactor || 0.5);
|
||||||
|
this.backoff = new Backoff({
|
||||||
|
min: this.reconnectionDelay(),
|
||||||
|
max: this.reconnectionDelayMax(),
|
||||||
|
jitter: this.randomizationFactor()
|
||||||
|
});
|
||||||
|
this.timeout(null == opts.timeout ? 20000 : opts.timeout);
|
||||||
|
this.readyState = 'closed';
|
||||||
|
this.uri = uri;
|
||||||
|
this.connected = [];
|
||||||
|
this.lastPing = null;
|
||||||
|
this.encoding = false;
|
||||||
|
this.packetBuffer = [];
|
||||||
|
this.encoder = new parser.Encoder();
|
||||||
|
this.decoder = new parser.Decoder();
|
||||||
|
this.autoConnect = opts.autoConnect !== false;
|
||||||
|
if (this.autoConnect) this.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Propagate given event to sockets and emit on `this`
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.emitAll = function() {
|
||||||
|
this.emit.apply(this, arguments);
|
||||||
|
for (var nsp in this.nsps) {
|
||||||
|
this.nsps[nsp].emit.apply(this.nsps[nsp], arguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update `socket.id` of all sockets
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.updateSocketIds = function(){
|
||||||
|
for (var nsp in this.nsps) {
|
||||||
|
this.nsps[nsp].id = this.engine.id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mix in `Emitter`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Emitter(Manager.prototype);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the `reconnection` config.
|
||||||
|
*
|
||||||
|
* @param {Boolean} true/false if it should automatically reconnect
|
||||||
|
* @return {Manager} self or value
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.reconnection = function(v){
|
||||||
|
if (!arguments.length) return this._reconnection;
|
||||||
|
this._reconnection = !!v;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the reconnection attempts config.
|
||||||
|
*
|
||||||
|
* @param {Number} max reconnection attempts before giving up
|
||||||
|
* @return {Manager} self or value
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.reconnectionAttempts = function(v){
|
||||||
|
if (!arguments.length) return this._reconnectionAttempts;
|
||||||
|
this._reconnectionAttempts = v;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the delay between reconnections.
|
||||||
|
*
|
||||||
|
* @param {Number} delay
|
||||||
|
* @return {Manager} self or value
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.reconnectionDelay = function(v){
|
||||||
|
if (!arguments.length) return this._reconnectionDelay;
|
||||||
|
this._reconnectionDelay = v;
|
||||||
|
this.backoff && this.backoff.setMin(v);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Manager.prototype.randomizationFactor = function(v){
|
||||||
|
if (!arguments.length) return this._randomizationFactor;
|
||||||
|
this._randomizationFactor = v;
|
||||||
|
this.backoff && this.backoff.setJitter(v);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the maximum delay between reconnections.
|
||||||
|
*
|
||||||
|
* @param {Number} delay
|
||||||
|
* @return {Manager} self or value
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.reconnectionDelayMax = function(v){
|
||||||
|
if (!arguments.length) return this._reconnectionDelayMax;
|
||||||
|
this._reconnectionDelayMax = v;
|
||||||
|
this.backoff && this.backoff.setMax(v);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the connection timeout. `false` to disable
|
||||||
|
*
|
||||||
|
* @return {Manager} self or value
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.timeout = function(v){
|
||||||
|
if (!arguments.length) return this._timeout;
|
||||||
|
this._timeout = v;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts trying to reconnect if reconnection is enabled and we have not
|
||||||
|
* started reconnecting yet
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.maybeReconnectOnOpen = function() {
|
||||||
|
// Only try to reconnect if it's the first time we're connecting
|
||||||
|
if (!this.reconnecting && this._reconnection && this.backoff.attempts === 0) {
|
||||||
|
// keeps reconnection from firing twice for the same reconnection loop
|
||||||
|
this.reconnect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current transport `socket`.
|
||||||
|
*
|
||||||
|
* @param {Function} optional, callback
|
||||||
|
* @return {Manager} self
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.open =
|
||||||
|
Manager.prototype.connect = function(fn){
|
||||||
|
debug('readyState %s', this.readyState);
|
||||||
|
if (~this.readyState.indexOf('open')) return this;
|
||||||
|
|
||||||
|
debug('opening %s', this.uri);
|
||||||
|
this.engine = eio(this.uri, this.opts);
|
||||||
|
var socket = this.engine;
|
||||||
|
var self = this;
|
||||||
|
this.readyState = 'opening';
|
||||||
|
this.skipReconnect = false;
|
||||||
|
|
||||||
|
// emit `open`
|
||||||
|
var openSub = on(socket, 'open', function() {
|
||||||
|
self.onopen();
|
||||||
|
fn && fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
// emit `connect_error`
|
||||||
|
var errorSub = on(socket, 'error', function(data){
|
||||||
|
debug('connect_error');
|
||||||
|
self.cleanup();
|
||||||
|
self.readyState = 'closed';
|
||||||
|
self.emitAll('connect_error', data);
|
||||||
|
if (fn) {
|
||||||
|
var err = new Error('Connection error');
|
||||||
|
err.data = data;
|
||||||
|
fn(err);
|
||||||
|
} else {
|
||||||
|
// Only do this if there is no fn to handle the error
|
||||||
|
self.maybeReconnectOnOpen();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// emit `connect_timeout`
|
||||||
|
if (false !== this._timeout) {
|
||||||
|
var timeout = this._timeout;
|
||||||
|
debug('connect attempt will timeout after %d', timeout);
|
||||||
|
|
||||||
|
// set timer
|
||||||
|
var timer = setTimeout(function(){
|
||||||
|
debug('connect attempt timed out after %d', timeout);
|
||||||
|
openSub.destroy();
|
||||||
|
socket.close();
|
||||||
|
socket.emit('error', 'timeout');
|
||||||
|
self.emitAll('connect_timeout', timeout);
|
||||||
|
}, timeout);
|
||||||
|
|
||||||
|
this.subs.push({
|
||||||
|
destroy: function(){
|
||||||
|
clearTimeout(timer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.subs.push(openSub);
|
||||||
|
this.subs.push(errorSub);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon transport open.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.onopen = function(){
|
||||||
|
debug('open');
|
||||||
|
|
||||||
|
// clear old subs
|
||||||
|
this.cleanup();
|
||||||
|
|
||||||
|
// mark as open
|
||||||
|
this.readyState = 'open';
|
||||||
|
this.emit('open');
|
||||||
|
|
||||||
|
// add new subs
|
||||||
|
var socket = this.engine;
|
||||||
|
this.subs.push(on(socket, 'data', bind(this, 'ondata')));
|
||||||
|
this.subs.push(on(socket, 'ping', bind(this, 'onping')));
|
||||||
|
this.subs.push(on(socket, 'pong', bind(this, 'onpong')));
|
||||||
|
this.subs.push(on(socket, 'error', bind(this, 'onerror')));
|
||||||
|
this.subs.push(on(socket, 'close', bind(this, 'onclose')));
|
||||||
|
this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded')));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon a ping.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.onping = function(){
|
||||||
|
this.lastPing = new Date;
|
||||||
|
this.emitAll('ping');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon a packet.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.onpong = function(){
|
||||||
|
this.emitAll('pong', new Date - this.lastPing);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called with data.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.ondata = function(data){
|
||||||
|
this.decoder.add(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when parser fully decodes a packet.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.ondecoded = function(packet) {
|
||||||
|
this.emit('packet', packet);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon socket error.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.onerror = function(err){
|
||||||
|
debug('error', err);
|
||||||
|
this.emitAll('error', err);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new socket for the given `nsp`.
|
||||||
|
*
|
||||||
|
* @return {Socket}
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.socket = function(nsp){
|
||||||
|
var socket = this.nsps[nsp];
|
||||||
|
if (!socket) {
|
||||||
|
socket = new Socket(this, nsp);
|
||||||
|
this.nsps[nsp] = socket;
|
||||||
|
var self = this;
|
||||||
|
socket.on('connect', function(){
|
||||||
|
socket.id = self.engine.id;
|
||||||
|
if (!~indexOf(self.connected, socket)) {
|
||||||
|
self.connected.push(socket);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return socket;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon a socket close.
|
||||||
|
*
|
||||||
|
* @param {Socket} socket
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.destroy = function(socket){
|
||||||
|
var index = indexOf(this.connected, socket);
|
||||||
|
if (~index) this.connected.splice(index, 1);
|
||||||
|
if (this.connected.length) return;
|
||||||
|
|
||||||
|
this.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a packet.
|
||||||
|
*
|
||||||
|
* @param {Object} packet
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.packet = function(packet){
|
||||||
|
debug('writing packet %j', packet);
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (!self.encoding) {
|
||||||
|
// encode, then write to engine with result
|
||||||
|
self.encoding = true;
|
||||||
|
this.encoder.encode(packet, function(encodedPackets) {
|
||||||
|
for (var i = 0; i < encodedPackets.length; i++) {
|
||||||
|
self.engine.write(encodedPackets[i], packet.options);
|
||||||
|
}
|
||||||
|
self.encoding = false;
|
||||||
|
self.processPacketQueue();
|
||||||
|
});
|
||||||
|
} else { // add packet to the queue
|
||||||
|
self.packetBuffer.push(packet);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If packet buffer is non-empty, begins encoding the
|
||||||
|
* next packet in line.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.processPacketQueue = function() {
|
||||||
|
if (this.packetBuffer.length > 0 && !this.encoding) {
|
||||||
|
var pack = this.packetBuffer.shift();
|
||||||
|
this.packet(pack);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up transport subscriptions and packet buffer.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.cleanup = function(){
|
||||||
|
debug('cleanup');
|
||||||
|
|
||||||
|
var sub;
|
||||||
|
while (sub = this.subs.shift()) sub.destroy();
|
||||||
|
|
||||||
|
this.packetBuffer = [];
|
||||||
|
this.encoding = false;
|
||||||
|
this.lastPing = null;
|
||||||
|
|
||||||
|
this.decoder.destroy();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the current socket.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.close =
|
||||||
|
Manager.prototype.disconnect = function(){
|
||||||
|
debug('disconnect');
|
||||||
|
this.skipReconnect = true;
|
||||||
|
this.reconnecting = false;
|
||||||
|
if ('opening' == this.readyState) {
|
||||||
|
// `onclose` will not fire because
|
||||||
|
// an open event never happened
|
||||||
|
this.cleanup();
|
||||||
|
}
|
||||||
|
this.backoff.reset();
|
||||||
|
this.readyState = 'closed';
|
||||||
|
if (this.engine) this.engine.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon engine close.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.onclose = function(reason){
|
||||||
|
debug('onclose');
|
||||||
|
|
||||||
|
this.cleanup();
|
||||||
|
this.backoff.reset();
|
||||||
|
this.readyState = 'closed';
|
||||||
|
this.emit('close', reason);
|
||||||
|
|
||||||
|
if (this._reconnection && !this.skipReconnect) {
|
||||||
|
this.reconnect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt a reconnection.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.reconnect = function(){
|
||||||
|
if (this.reconnecting || this.skipReconnect) return this;
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (this.backoff.attempts >= this._reconnectionAttempts) {
|
||||||
|
debug('reconnect failed');
|
||||||
|
this.backoff.reset();
|
||||||
|
this.emitAll('reconnect_failed');
|
||||||
|
this.reconnecting = false;
|
||||||
|
} else {
|
||||||
|
var delay = this.backoff.duration();
|
||||||
|
debug('will wait %dms before reconnect attempt', delay);
|
||||||
|
|
||||||
|
this.reconnecting = true;
|
||||||
|
var timer = setTimeout(function(){
|
||||||
|
if (self.skipReconnect) return;
|
||||||
|
|
||||||
|
debug('attempting reconnect');
|
||||||
|
self.emitAll('reconnect_attempt', self.backoff.attempts);
|
||||||
|
self.emitAll('reconnecting', self.backoff.attempts);
|
||||||
|
|
||||||
|
// check again for the case socket closed in above events
|
||||||
|
if (self.skipReconnect) return;
|
||||||
|
|
||||||
|
self.open(function(err){
|
||||||
|
if (err) {
|
||||||
|
debug('reconnect attempt error');
|
||||||
|
self.reconnecting = false;
|
||||||
|
self.reconnect();
|
||||||
|
self.emitAll('reconnect_error', err.data);
|
||||||
|
} else {
|
||||||
|
debug('reconnect success');
|
||||||
|
self.onreconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, delay);
|
||||||
|
|
||||||
|
this.subs.push({
|
||||||
|
destroy: function(){
|
||||||
|
clearTimeout(timer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon successful reconnect.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Manager.prototype.onreconnect = function(){
|
||||||
|
var attempt = this.backoff.attempts;
|
||||||
|
this.reconnecting = false;
|
||||||
|
this.backoff.reset();
|
||||||
|
this.updateSocketIds();
|
||||||
|
this.emitAll('reconnect', attempt);
|
||||||
|
};
|
24
vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/on.js
vendored
Normal file
24
vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/on.js
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Module exports.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = on;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for subscriptions.
|
||||||
|
*
|
||||||
|
* @param {Object|EventEmitter} obj with `Emitter` mixin or `EventEmitter`
|
||||||
|
* @param {String} event name
|
||||||
|
* @param {Function} callback
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
function on(obj, ev, fn) {
|
||||||
|
obj.on(ev, fn);
|
||||||
|
return {
|
||||||
|
destroy: function(){
|
||||||
|
obj.removeListener(ev, fn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
411
vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/socket.js
vendored
Normal file
411
vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/socket.js
vendored
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var parser = require('socket.io-parser');
|
||||||
|
var Emitter = require('component-emitter');
|
||||||
|
var toArray = require('to-array');
|
||||||
|
var on = require('./on');
|
||||||
|
var bind = require('component-bind');
|
||||||
|
var debug = require('debug')('socket.io-client:socket');
|
||||||
|
var hasBin = require('has-binary');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module exports.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = exports = Socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal events (blacklisted).
|
||||||
|
* These events can't be emitted by the user.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
var events = {
|
||||||
|
connect: 1,
|
||||||
|
connect_error: 1,
|
||||||
|
connect_timeout: 1,
|
||||||
|
disconnect: 1,
|
||||||
|
error: 1,
|
||||||
|
reconnect: 1,
|
||||||
|
reconnect_attempt: 1,
|
||||||
|
reconnect_failed: 1,
|
||||||
|
reconnect_error: 1,
|
||||||
|
reconnecting: 1,
|
||||||
|
ping: 1,
|
||||||
|
pong: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut to `Emitter#emit`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var emit = Emitter.prototype.emit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `Socket` constructor.
|
||||||
|
*
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
function Socket(io, nsp){
|
||||||
|
this.io = io;
|
||||||
|
this.nsp = nsp;
|
||||||
|
this.json = this; // compat
|
||||||
|
this.ids = 0;
|
||||||
|
this.acks = {};
|
||||||
|
if (this.io.autoConnect) this.open();
|
||||||
|
this.receiveBuffer = [];
|
||||||
|
this.sendBuffer = [];
|
||||||
|
this.connected = false;
|
||||||
|
this.disconnected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mix in `Emitter`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Emitter(Socket.prototype);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to open, close and packet events
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.subEvents = function() {
|
||||||
|
if (this.subs) return;
|
||||||
|
|
||||||
|
var io = this.io;
|
||||||
|
this.subs = [
|
||||||
|
on(io, 'open', bind(this, 'onopen')),
|
||||||
|
on(io, 'packet', bind(this, 'onpacket')),
|
||||||
|
on(io, 'close', bind(this, 'onclose'))
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Opens" the socket.
|
||||||
|
*
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.open =
|
||||||
|
Socket.prototype.connect = function(){
|
||||||
|
if (this.connected) return this;
|
||||||
|
|
||||||
|
this.subEvents();
|
||||||
|
this.io.open(); // ensure open
|
||||||
|
if ('open' == this.io.readyState) this.onopen();
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a `message` event.
|
||||||
|
*
|
||||||
|
* @return {Socket} self
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.send = function(){
|
||||||
|
var args = toArray(arguments);
|
||||||
|
args.unshift('message');
|
||||||
|
this.emit.apply(this, args);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override `emit`.
|
||||||
|
* If the event is in `events`, it's emitted normally.
|
||||||
|
*
|
||||||
|
* @param {String} event name
|
||||||
|
* @return {Socket} self
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.emit = function(ev){
|
||||||
|
if (events.hasOwnProperty(ev)) {
|
||||||
|
emit.apply(this, arguments);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
var args = toArray(arguments);
|
||||||
|
var parserType = parser.EVENT; // default
|
||||||
|
if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary
|
||||||
|
var packet = { type: parserType, data: args };
|
||||||
|
|
||||||
|
packet.options = {};
|
||||||
|
packet.options.compress = !this.flags || false !== this.flags.compress;
|
||||||
|
|
||||||
|
// event ack callback
|
||||||
|
if ('function' == typeof args[args.length - 1]) {
|
||||||
|
debug('emitting packet with ack id %d', this.ids);
|
||||||
|
this.acks[this.ids] = args.pop();
|
||||||
|
packet.id = this.ids++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.connected) {
|
||||||
|
this.packet(packet);
|
||||||
|
} else {
|
||||||
|
this.sendBuffer.push(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete this.flags;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a packet.
|
||||||
|
*
|
||||||
|
* @param {Object} packet
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.packet = function(packet){
|
||||||
|
packet.nsp = this.nsp;
|
||||||
|
this.io.packet(packet);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon engine `open`.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.onopen = function(){
|
||||||
|
debug('transport is open - connecting');
|
||||||
|
|
||||||
|
// write connect packet if necessary
|
||||||
|
if ('/' != this.nsp) {
|
||||||
|
this.packet({ type: parser.CONNECT, options: { compress: true } });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon engine `close`.
|
||||||
|
*
|
||||||
|
* @param {String} reason
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.onclose = function(reason){
|
||||||
|
debug('close (%s)', reason);
|
||||||
|
this.connected = false;
|
||||||
|
this.disconnected = true;
|
||||||
|
delete this.id;
|
||||||
|
this.emit('disconnect', reason);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called with socket packet.
|
||||||
|
*
|
||||||
|
* @param {Object} packet
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.onpacket = function(packet){
|
||||||
|
if (packet.nsp != this.nsp) return;
|
||||||
|
|
||||||
|
switch (packet.type) {
|
||||||
|
case parser.CONNECT:
|
||||||
|
this.onconnect();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case parser.EVENT:
|
||||||
|
this.onevent(packet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case parser.BINARY_EVENT:
|
||||||
|
this.onevent(packet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case parser.ACK:
|
||||||
|
this.onack(packet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case parser.BINARY_ACK:
|
||||||
|
this.onack(packet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case parser.DISCONNECT:
|
||||||
|
this.ondisconnect();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case parser.ERROR:
|
||||||
|
this.emit('error', packet.data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon a server event.
|
||||||
|
*
|
||||||
|
* @param {Object} packet
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.onevent = function(packet){
|
||||||
|
var args = packet.data || [];
|
||||||
|
debug('emitting event %j', args);
|
||||||
|
|
||||||
|
if (null != packet.id) {
|
||||||
|
debug('attaching ack callback to event');
|
||||||
|
args.push(this.ack(packet.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.connected) {
|
||||||
|
emit.apply(this, args);
|
||||||
|
} else {
|
||||||
|
this.receiveBuffer.push(args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces an ack callback to emit with an event.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.ack = function(id){
|
||||||
|
var self = this;
|
||||||
|
var sent = false;
|
||||||
|
return function(){
|
||||||
|
// prevent double callbacks
|
||||||
|
if (sent) return;
|
||||||
|
sent = true;
|
||||||
|
var args = toArray(arguments);
|
||||||
|
debug('sending ack %j', args);
|
||||||
|
|
||||||
|
var type = hasBin(args) ? parser.BINARY_ACK : parser.ACK;
|
||||||
|
self.packet({
|
||||||
|
type: type,
|
||||||
|
id: id,
|
||||||
|
data: args,
|
||||||
|
options: { compress: true }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon a server acknowlegement.
|
||||||
|
*
|
||||||
|
* @param {Object} packet
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.onack = function(packet){
|
||||||
|
var ack = this.acks[packet.id];
|
||||||
|
if ('function' == typeof ack) {
|
||||||
|
debug('calling ack %s with %j', packet.id, packet.data);
|
||||||
|
ack.apply(this, packet.data);
|
||||||
|
delete this.acks[packet.id];
|
||||||
|
} else {
|
||||||
|
debug('bad ack %s', packet.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon server connect.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.onconnect = function(){
|
||||||
|
this.connected = true;
|
||||||
|
this.disconnected = false;
|
||||||
|
this.emit('connect');
|
||||||
|
this.emitBuffered();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit buffered events (received and emitted).
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.emitBuffered = function(){
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < this.receiveBuffer.length; i++) {
|
||||||
|
emit.apply(this, this.receiveBuffer[i]);
|
||||||
|
}
|
||||||
|
this.receiveBuffer = [];
|
||||||
|
|
||||||
|
for (i = 0; i < this.sendBuffer.length; i++) {
|
||||||
|
this.packet(this.sendBuffer[i]);
|
||||||
|
}
|
||||||
|
this.sendBuffer = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon server disconnect.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.ondisconnect = function(){
|
||||||
|
debug('server disconnect (%s)', this.nsp);
|
||||||
|
this.destroy();
|
||||||
|
this.onclose('io server disconnect');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon forced client/server side disconnections,
|
||||||
|
* this method ensures the manager stops tracking us and
|
||||||
|
* that reconnections don't get triggered for this.
|
||||||
|
*
|
||||||
|
* @api private.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.destroy = function(){
|
||||||
|
if (this.subs) {
|
||||||
|
// clean subscriptions to avoid reconnections
|
||||||
|
for (var i = 0; i < this.subs.length; i++) {
|
||||||
|
this.subs[i].destroy();
|
||||||
|
}
|
||||||
|
this.subs = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.io.destroy(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects the socket manually.
|
||||||
|
*
|
||||||
|
* @return {Socket} self
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.close =
|
||||||
|
Socket.prototype.disconnect = function(){
|
||||||
|
if (this.connected) {
|
||||||
|
debug('performing disconnect (%s)', this.nsp);
|
||||||
|
this.packet({ type: parser.DISCONNECT, options: { compress: true } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove socket from pool
|
||||||
|
this.destroy();
|
||||||
|
|
||||||
|
if (this.connected) {
|
||||||
|
// fire events
|
||||||
|
this.onclose('io client disconnect');
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the compress flag.
|
||||||
|
*
|
||||||
|
* @param {Boolean} if `true`, compresses the sending data
|
||||||
|
* @return {Socket} self
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
Socket.prototype.compress = function(compress){
|
||||||
|
this.flags = this.flags || {};
|
||||||
|
this.flags.compress = compress;
|
||||||
|
return this;
|
||||||
|
};
|
73
vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/url.js
vendored
Normal file
73
vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/lib/url.js
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var parseuri = require('parseuri');
|
||||||
|
var debug = require('debug')('socket.io-client:url');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module exports.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL parser.
|
||||||
|
*
|
||||||
|
* @param {String} url
|
||||||
|
* @param {Object} An object meant to mimic window.location.
|
||||||
|
* Defaults to window.location.
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
function url(uri, loc){
|
||||||
|
var obj = uri;
|
||||||
|
|
||||||
|
// default to window.location
|
||||||
|
var loc = loc || global.location;
|
||||||
|
if (null == uri) uri = loc.protocol + '//' + loc.host;
|
||||||
|
|
||||||
|
// relative path support
|
||||||
|
if ('string' == typeof uri) {
|
||||||
|
if ('/' == uri.charAt(0)) {
|
||||||
|
if ('/' == uri.charAt(1)) {
|
||||||
|
uri = loc.protocol + uri;
|
||||||
|
} else {
|
||||||
|
uri = loc.host + uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^(https?|wss?):\/\//.test(uri)) {
|
||||||
|
debug('protocol-less url %s', uri);
|
||||||
|
if ('undefined' != typeof loc) {
|
||||||
|
uri = loc.protocol + '//' + uri;
|
||||||
|
} else {
|
||||||
|
uri = 'https://' + uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse
|
||||||
|
debug('parse %s', uri);
|
||||||
|
obj = parseuri(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we treat `localhost:80` and `localhost` equally
|
||||||
|
if (!obj.port) {
|
||||||
|
if (/^(http|ws)$/.test(obj.protocol)) {
|
||||||
|
obj.port = '80';
|
||||||
|
}
|
||||||
|
else if (/^(http|ws)s$/.test(obj.protocol)) {
|
||||||
|
obj.port = '443';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.path = obj.path || '/';
|
||||||
|
|
||||||
|
// define unique id
|
||||||
|
obj.id = obj.protocol + '://' + obj.host + ':' + obj.port;
|
||||||
|
// define href
|
||||||
|
obj.href = obj.protocol + '://' + obj.host + (loc && loc.port == obj.port ? '' : (':' + obj.port));
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
7006
vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/socket.io.js
vendored
Normal file
7006
vendor/workerman/phpsocket.io/examples/chat/public/socket.io-client/socket.io.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
149
vendor/workerman/phpsocket.io/examples/chat/public/style.css
vendored
Normal file
149
vendor/workerman/phpsocket.io/examples/chat/public/style.css
vendored
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
/* Fix user-agent */
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-weight: 300;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, input {
|
||||||
|
font-family:
|
||||||
|
"HelveticaNeue-Light",
|
||||||
|
"Helvetica Neue Light",
|
||||||
|
"Helvetica Neue",
|
||||||
|
Helvetica,
|
||||||
|
Arial,
|
||||||
|
"Lucida Grande",
|
||||||
|
sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pages */
|
||||||
|
|
||||||
|
.pages {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Login Page */
|
||||||
|
|
||||||
|
.login.page {
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login.page .form {
|
||||||
|
height: 100px;
|
||||||
|
margin-top: -100px;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
top: 50%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login.page .form .usernameInput {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 2px solid #fff;
|
||||||
|
outline: none;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
text-align: center;
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login.page .title {
|
||||||
|
font-size: 200%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login.page .usernameInput {
|
||||||
|
font-size: 200%;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login.page .title, .login.page .usernameInput {
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chat page */
|
||||||
|
|
||||||
|
.chat.page {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Font */
|
||||||
|
|
||||||
|
.messages {
|
||||||
|
font-size: 150%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputMessage {
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log {
|
||||||
|
color: gray;
|
||||||
|
font-size: 70%;
|
||||||
|
margin: 5px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Messages */
|
||||||
|
|
||||||
|
.chatArea {
|
||||||
|
height: 100%;
|
||||||
|
padding-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
overflow-y: scroll;
|
||||||
|
padding: 10px 20px 10px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.typing .messageBody {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.username {
|
||||||
|
font-weight: 700;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-right: 15px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input */
|
||||||
|
|
||||||
|
.inputMessage {
|
||||||
|
border: 10px solid #000;
|
||||||
|
bottom: 0;
|
||||||
|
height: 60px;
|
||||||
|
left: 0;
|
||||||
|
outline: none;
|
||||||
|
padding-left: 10px;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
12
vendor/workerman/phpsocket.io/examples/chat/start.php
vendored
Normal file
12
vendor/workerman/phpsocket.io/examples/chat/start.php
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
use Workerman\Worker;
|
||||||
|
use Workerman\WebServer;
|
||||||
|
use Workerman\Autoloader;
|
||||||
|
use PHPSocketIO\SocketIO;
|
||||||
|
|
||||||
|
define('GLOBAL_START', true);
|
||||||
|
|
||||||
|
require_once __DIR__ . '/start_web.php';
|
||||||
|
require_once __DIR__ . '/start_io.php';
|
||||||
|
|
||||||
|
Worker::runAll();
|
73
vendor/workerman/phpsocket.io/examples/chat/start_io.php
vendored
Normal file
73
vendor/workerman/phpsocket.io/examples/chat/start_io.php
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
use Workerman\Worker;
|
||||||
|
use Workerman\WebServer;
|
||||||
|
use Workerman\Autoloader;
|
||||||
|
use PHPSocketIO\SocketIO;
|
||||||
|
|
||||||
|
// composer autoload
|
||||||
|
require_once join(DIRECTORY_SEPARATOR, array(__DIR__, "..", "..", "vendor", "autoload.php"));
|
||||||
|
|
||||||
|
$io = new SocketIO(2020);
|
||||||
|
$io->on('connection', function($socket){
|
||||||
|
$socket->addedUser = false;
|
||||||
|
// when the client emits 'new message', this listens and executes
|
||||||
|
$socket->on('new message', function ($data)use($socket){
|
||||||
|
// we tell the client to execute 'new message'
|
||||||
|
$socket->broadcast->emit('new message', array(
|
||||||
|
'username'=> $socket->username,
|
||||||
|
'message'=> $data
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
// when the client emits 'add user', this listens and executes
|
||||||
|
$socket->on('add user', function ($username) use($socket){
|
||||||
|
if ($socket->addedUser)
|
||||||
|
return;
|
||||||
|
global $usernames, $numUsers;
|
||||||
|
// we store the username in the socket session for this client
|
||||||
|
$socket->username = $username;
|
||||||
|
++$numUsers;
|
||||||
|
$socket->addedUser = true;
|
||||||
|
$socket->emit('login', array(
|
||||||
|
'numUsers' => $numUsers
|
||||||
|
));
|
||||||
|
// echo globally (all clients) that a person has connected
|
||||||
|
$socket->broadcast->emit('user joined', array(
|
||||||
|
'username' => $socket->username,
|
||||||
|
'numUsers' => $numUsers
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
// when the client emits 'typing', we broadcast it to others
|
||||||
|
$socket->on('typing', function () use($socket) {
|
||||||
|
$socket->broadcast->emit('typing', array(
|
||||||
|
'username' => $socket->username
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
// when the client emits 'stop typing', we broadcast it to others
|
||||||
|
$socket->on('stop typing', function () use($socket) {
|
||||||
|
$socket->broadcast->emit('stop typing', array(
|
||||||
|
'username' => $socket->username
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
// when the user disconnects.. perform this
|
||||||
|
$socket->on('disconnect', function () use($socket) {
|
||||||
|
global $usernames, $numUsers;
|
||||||
|
if($socket->addedUser) {
|
||||||
|
--$numUsers;
|
||||||
|
|
||||||
|
// echo globally that this client has left
|
||||||
|
$socket->broadcast->emit('user left', array(
|
||||||
|
'username' => $socket->username,
|
||||||
|
'numUsers' => $numUsers
|
||||||
|
));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!defined('GLOBAL_START')) {
|
||||||
|
Worker::runAll();
|
||||||
|
}
|
62
vendor/workerman/phpsocket.io/examples/chat/start_web.php
vendored
Normal file
62
vendor/workerman/phpsocket.io/examples/chat/start_web.php
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
use Workerman\Worker;
|
||||||
|
use Workerman\Protocols\Http\Request;
|
||||||
|
use Workerman\Protocols\Http\Response;
|
||||||
|
use Workerman\Connection\TcpConnection;
|
||||||
|
|
||||||
|
// composer autoload
|
||||||
|
require_once join(DIRECTORY_SEPARATOR, array(__DIR__, "..", "..", "vendor", "autoload.php"));
|
||||||
|
|
||||||
|
$web = new Worker('http://0.0.0.0:2022');
|
||||||
|
$web->name = 'web';
|
||||||
|
|
||||||
|
define('WEBROOT', __DIR__ . DIRECTORY_SEPARATOR . 'public');
|
||||||
|
|
||||||
|
$web->onMessage = function (TcpConnection $connection, Request $request) {
|
||||||
|
$path = $request->path();
|
||||||
|
if ($path === '/') {
|
||||||
|
$connection->send(exec_php_file(WEBROOT.'/index.html'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$file = realpath(WEBROOT. $path);
|
||||||
|
if (false === $file) {
|
||||||
|
$connection->send(new Response(404, array(), '<h3>404 Not Found</h3>'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Security check! Very important!!!
|
||||||
|
if (strpos($file, WEBROOT) !== 0) {
|
||||||
|
$connection->send(new Response(400));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (\pathinfo($file, PATHINFO_EXTENSION) === 'php') {
|
||||||
|
$connection->send(exec_php_file($file));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$if_modified_since = $request->header('if-modified-since');
|
||||||
|
if (!empty($if_modified_since)) {
|
||||||
|
// Check 304.
|
||||||
|
$info = \stat($file);
|
||||||
|
$modified_time = $info ? \date('D, d M Y H:i:s', $info['mtime']) . ' ' . \date_default_timezone_get() : '';
|
||||||
|
if ($modified_time === $if_modified_since) {
|
||||||
|
$connection->send(new Response(304));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$connection->send((new Response())->withFile($file));
|
||||||
|
};
|
||||||
|
|
||||||
|
function exec_php_file($file) {
|
||||||
|
\ob_start();
|
||||||
|
// Try to include php file.
|
||||||
|
try {
|
||||||
|
include $file;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
echo $e;
|
||||||
|
}
|
||||||
|
return \ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!defined('GLOBAL_START')) {
|
||||||
|
Worker::runAll();
|
||||||
|
}
|
124
vendor/workerman/phpsocket.io/src/ChannelAdapter.php
vendored
Normal file
124
vendor/workerman/phpsocket.io/src/ChannelAdapter.php
vendored
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO;
|
||||||
|
class ChannelAdapter extends DefaultAdapter
|
||||||
|
{
|
||||||
|
protected $_channelId = null;
|
||||||
|
|
||||||
|
public static $ip = '127.0.0.1';
|
||||||
|
|
||||||
|
public static $port = 2206;
|
||||||
|
|
||||||
|
public function __construct($nsp)
|
||||||
|
{
|
||||||
|
parent::__construct($nsp);
|
||||||
|
$this->_channelId = (function_exists('random_int') ? random_int(1, 10000000): rand(1, 10000000)) . "-" . (function_exists('posix_getpid') ? posix_getpid(): 1);
|
||||||
|
\Channel\Client::connect(self::$ip, self::$port);
|
||||||
|
\Channel\Client::$onMessage = array($this, 'onChannelMessage');
|
||||||
|
\Channel\Client::subscribe("socket.io#/#");
|
||||||
|
Debug::debug('ChannelAdapter __construct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
Debug::debug('ChannelAdapter __destruct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add($id ,$room)
|
||||||
|
{
|
||||||
|
$this->sids[$id][$room] = true;
|
||||||
|
$this->rooms[$room][$id] = true;
|
||||||
|
$channel = "socket.io#/#$room#";
|
||||||
|
\Channel\Client::subscribe($channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function del($id, $room)
|
||||||
|
{
|
||||||
|
unset($this->sids[$id][$room]);
|
||||||
|
unset($this->rooms[$room][$id]);
|
||||||
|
if(empty($this->rooms[$room]))
|
||||||
|
{
|
||||||
|
unset($this->rooms[$room]);
|
||||||
|
$channel = "socket.io#/#$room#";
|
||||||
|
\Channel\Client::unsubscribe($channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delAll($id)
|
||||||
|
{
|
||||||
|
$rooms = isset($this->sids[$id]) ? array_keys($this->sids[$id]) : array();
|
||||||
|
if($rooms)
|
||||||
|
{
|
||||||
|
foreach($rooms as $room)
|
||||||
|
{
|
||||||
|
if(isset($this->rooms[$room][$id]))
|
||||||
|
{
|
||||||
|
unset($this->rooms[$room][$id]);
|
||||||
|
$channel = "socket.io#/#$room#";
|
||||||
|
\Channel\Client::unsubscribe($channel);
|
||||||
|
}
|
||||||
|
if(isset($this->rooms[$room]) && empty($this->rooms[$room]))
|
||||||
|
{
|
||||||
|
unset($this->rooms[$room]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($this->sids[$id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onChannelMessage($channel, $msg)
|
||||||
|
{
|
||||||
|
if($this->_channelId === array_shift($msg))
|
||||||
|
{
|
||||||
|
//echo "ignore same channel_id \n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$packet = $msg[0];
|
||||||
|
|
||||||
|
$opts = $msg[1];
|
||||||
|
|
||||||
|
if(!$packet)
|
||||||
|
{
|
||||||
|
echo "invalid channel:$channel packet \n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(empty($packet['nsp']))
|
||||||
|
{
|
||||||
|
$packet['nsp'] = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
if($packet['nsp'] != $this->nsp->name)
|
||||||
|
{
|
||||||
|
echo "ignore different namespace {$packet['nsp']} != {$this->nsp->name}\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->broadcast($packet, $opts, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broadcast($packet, $opts, $remote = false)
|
||||||
|
{
|
||||||
|
parent::broadcast($packet, $opts);
|
||||||
|
if (!$remote)
|
||||||
|
{
|
||||||
|
$packet['nsp'] = '/';
|
||||||
|
|
||||||
|
if(!empty($opts['rooms']))
|
||||||
|
{
|
||||||
|
foreach($opts['rooms'] as $room)
|
||||||
|
{
|
||||||
|
$chn = "socket.io#/#$room#";
|
||||||
|
$msg = array($this->_channelId, $packet, $opts);
|
||||||
|
\Channel\Client::publish($chn, $msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$chn = "socket.io#/#";
|
||||||
|
$msg = array($this->_channelId, $packet, $opts);
|
||||||
|
\Channel\Client::publish($chn, $msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
260
vendor/workerman/phpsocket.io/src/Client.php
vendored
Normal file
260
vendor/workerman/phpsocket.io/src/Client.php
vendored
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO;
|
||||||
|
use PHPSocketIO\Parser\Parser;
|
||||||
|
|
||||||
|
class Client
|
||||||
|
{
|
||||||
|
public $server = null;
|
||||||
|
public $conn = null;
|
||||||
|
public $encoder = null;
|
||||||
|
public $decoder = null;
|
||||||
|
public $id = null;
|
||||||
|
public $request = null;
|
||||||
|
public $nsps = array();
|
||||||
|
public $connectBuffer = array();
|
||||||
|
public function __construct($server, $conn)
|
||||||
|
{
|
||||||
|
$this->server = $server;
|
||||||
|
$this->conn = $conn;
|
||||||
|
$this->encoder = new \PHPSocketIO\Parser\Encoder();
|
||||||
|
$this->decoder = new \PHPSocketIO\Parser\Decoder();
|
||||||
|
$this->id = $conn->id;
|
||||||
|
$this->request = $conn->request;
|
||||||
|
$this->setup();
|
||||||
|
Debug::debug('Client __construct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
Debug::debug('Client __destruct');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up event listeners.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function setup(){
|
||||||
|
$this->decoder->on('decoded', array($this,'ondecoded'));
|
||||||
|
$this->conn->on('data', array($this,'ondata'));
|
||||||
|
$this->conn->on('error', array($this, 'onerror'));
|
||||||
|
$this->conn->on('close' ,array($this, 'onclose'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects a client to a namespace.
|
||||||
|
*
|
||||||
|
* @param {String} namespace name
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function connect($name){
|
||||||
|
if (!isset($this->server->nsps[$name]))
|
||||||
|
{
|
||||||
|
$this->packet(array('type'=> Parser::ERROR, 'nsp'=> $name, 'data'=> 'Invalid namespace'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$nsp = $this->server->of($name);
|
||||||
|
if ('/' !== $name && !isset($this->nsps['/']))
|
||||||
|
{
|
||||||
|
$this->connectBuffer[$name] = $name;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$nsp->add($this, $nsp, array($this, 'nspAdd'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function nspAdd($socket, $nsp)
|
||||||
|
{
|
||||||
|
$this->sockets[$socket->id] = $socket;
|
||||||
|
$this->nsps[$nsp->name] = $socket;
|
||||||
|
if ('/' === $nsp->name && $this->connectBuffer)
|
||||||
|
{
|
||||||
|
foreach($this->connectBuffer as $name)
|
||||||
|
{
|
||||||
|
$this->connect($name);
|
||||||
|
}
|
||||||
|
$this->connectBuffer = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects from all namespaces and closes transport.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function disconnect()
|
||||||
|
{
|
||||||
|
foreach($this->sockets as $socket)
|
||||||
|
{
|
||||||
|
$socket->disconnect();
|
||||||
|
}
|
||||||
|
$this->sockets = array();
|
||||||
|
$this->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a socket. Called by each `Socket`.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function remove($socket)
|
||||||
|
{
|
||||||
|
if(isset($this->sockets[$socket->id]))
|
||||||
|
{
|
||||||
|
$nsp = $this->sockets[$socket->id]->nsp->name;
|
||||||
|
unset($this->sockets[$socket->id]);
|
||||||
|
unset($this->nsps[$nsp]);
|
||||||
|
} else {
|
||||||
|
//echo('ignoring remove for '. $socket->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the underlying connection.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
if (empty($this->conn)) return;
|
||||||
|
if('open' === $this->conn->readyState)
|
||||||
|
{
|
||||||
|
//echo('forcing transport close');
|
||||||
|
$this->conn->close();
|
||||||
|
$this->onclose('forced server close');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a packet to the transport.
|
||||||
|
*
|
||||||
|
* @param {Object} packet object
|
||||||
|
* @param {Object} options
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
public function packet($packet, $preEncoded = false, $volatile = false)
|
||||||
|
{
|
||||||
|
if(!empty($this->conn) && 'open' === $this->conn->readyState)
|
||||||
|
{
|
||||||
|
if (!$preEncoded)
|
||||||
|
{
|
||||||
|
// not broadcasting, need to encode
|
||||||
|
$encodedPackets = $this->encoder->encode($packet);
|
||||||
|
$this->writeToEngine($encodedPackets, $volatile);
|
||||||
|
} else { // a broadcast pre-encodes a packet
|
||||||
|
$this->writeToEngine($packet);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// todo check
|
||||||
|
// echo('ignoring packet write ' . var_export($packet, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function writeToEngine($encodedPackets, $volatile = false)
|
||||||
|
{
|
||||||
|
if($volatile)echo new \Exception('volatile');
|
||||||
|
if ($volatile && !$this->conn->transport->writable) return;
|
||||||
|
// todo check
|
||||||
|
if(isset($encodedPackets['nsp']))unset($encodedPackets['nsp']);
|
||||||
|
foreach($encodedPackets as $packet)
|
||||||
|
{
|
||||||
|
$this->conn->write($packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called with incoming transport data.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function ondata($data)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// todo chek '2["chat message","2"]' . "\0" . ''
|
||||||
|
$this->decoder->add(trim($data));
|
||||||
|
} catch(\Exception $e) {
|
||||||
|
$this->onerror($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when parser fully decodes a packet.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function ondecoded($packet)
|
||||||
|
{
|
||||||
|
if(Parser::CONNECT == $packet['type'])
|
||||||
|
{
|
||||||
|
$this->connect($packet['nsp']);
|
||||||
|
} else {
|
||||||
|
if(isset($this->nsps[$packet['nsp']]))
|
||||||
|
{
|
||||||
|
$this->nsps[$packet['nsp']]->onpacket($packet);
|
||||||
|
} else {
|
||||||
|
//echo('no socket for namespace ' . $packet['nsp']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles an error.
|
||||||
|
*
|
||||||
|
* @param {Objcet} error object
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function onerror($err)
|
||||||
|
{
|
||||||
|
foreach($this->sockets as $socket)
|
||||||
|
{
|
||||||
|
$socket->onerror($err);
|
||||||
|
}
|
||||||
|
$this->onclose('client error');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon transport close.
|
||||||
|
*
|
||||||
|
* @param {String} reason
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function onclose($reason)
|
||||||
|
{
|
||||||
|
if (empty($this->conn)) return;
|
||||||
|
// ignore a potential subsequent `close` event
|
||||||
|
$this->destroy();
|
||||||
|
|
||||||
|
// `nsps` and `sockets` are cleaned up seamlessly
|
||||||
|
foreach($this->sockets as $socket)
|
||||||
|
{
|
||||||
|
$socket->onclose($reason);
|
||||||
|
}
|
||||||
|
$this->sockets = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up event listeners.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function destroy()
|
||||||
|
{
|
||||||
|
if (!$this->conn) return;
|
||||||
|
$this->conn->removeAllListeners();
|
||||||
|
$this->decoder->removeAllListeners();
|
||||||
|
$this->encoder->removeAllListeners();
|
||||||
|
$this->server = $this->conn = $this->encoder = $this->decoder = $this->request = $this->nsps = null;
|
||||||
|
}
|
||||||
|
}
|
12
vendor/workerman/phpsocket.io/src/Debug.php
vendored
Normal file
12
vendor/workerman/phpsocket.io/src/Debug.php
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO;
|
||||||
|
|
||||||
|
class Debug
|
||||||
|
{
|
||||||
|
public static function debug($var)
|
||||||
|
{
|
||||||
|
global $debug;
|
||||||
|
if($debug)
|
||||||
|
echo var_export($var, true)."\n";
|
||||||
|
}
|
||||||
|
}
|
106
vendor/workerman/phpsocket.io/src/DefaultAdapter.php
vendored
Normal file
106
vendor/workerman/phpsocket.io/src/DefaultAdapter.php
vendored
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO;
|
||||||
|
class DefaultAdapter
|
||||||
|
{
|
||||||
|
|
||||||
|
public $nsp = null;
|
||||||
|
public $rooms = array();
|
||||||
|
public $sids = array();
|
||||||
|
public $encoder = null;
|
||||||
|
public function __construct($nsp)
|
||||||
|
{
|
||||||
|
$this->nsp = $nsp;
|
||||||
|
$this->encoder = new Parser\Encoder();
|
||||||
|
Debug::debug('DefaultAdapter __construct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
Debug::debug('DefaultAdapter __destruct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add($id, $room)
|
||||||
|
{
|
||||||
|
$this->sids[$id][$room] = true;
|
||||||
|
$this->rooms[$room][$id] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function del($id, $room)
|
||||||
|
{
|
||||||
|
unset($this->sids[$id][$room]);
|
||||||
|
unset($this->rooms[$room][$id]);
|
||||||
|
if(empty($this->rooms[$room]))
|
||||||
|
{
|
||||||
|
unset($this->rooms[$room]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delAll($id)
|
||||||
|
{
|
||||||
|
$rooms = array_keys(isset($this->sids[$id]) ? $this->sids[$id] : array());
|
||||||
|
foreach($rooms as $room)
|
||||||
|
{
|
||||||
|
$this->del($id, $room);
|
||||||
|
}
|
||||||
|
unset($this->sids[$id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broadcast($packet, $opts, $remote = false)
|
||||||
|
{
|
||||||
|
$rooms = isset($opts['rooms']) ? $opts['rooms'] : array();
|
||||||
|
$except = isset($opts['except']) ? $opts['except'] : array();
|
||||||
|
$flags = isset($opts['flags']) ? $opts['flags'] : array();
|
||||||
|
$packetOpts = array(
|
||||||
|
'preEncoded' => true,
|
||||||
|
'volatile' => isset($flags['volatile']) ? $flags['volatile'] : null,
|
||||||
|
'compress' => isset($flags['compress']) ? $flags['compress'] : null
|
||||||
|
);
|
||||||
|
$packet['nsp'] = $this->nsp->name;
|
||||||
|
$encodedPackets = $this->encoder->encode($packet);
|
||||||
|
if($rooms)
|
||||||
|
{
|
||||||
|
$ids = array();
|
||||||
|
foreach($rooms as $i=>$room)
|
||||||
|
{
|
||||||
|
if(!isset($this->rooms[$room]))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$room = $this->rooms[$room];
|
||||||
|
foreach($room as $id=>$item)
|
||||||
|
{
|
||||||
|
if(isset($ids[$id]) || isset($except[$id]))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(isset($this->nsp->connected[$id]))
|
||||||
|
{
|
||||||
|
$ids[$id] = true;
|
||||||
|
$this->nsp->connected[$id]->packet($encodedPackets, $packetOpts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach($this->sids as $id=>$sid)
|
||||||
|
{
|
||||||
|
if(isset($except[$id])) continue;
|
||||||
|
if(isset($this->nsp->connected[$id]))
|
||||||
|
{
|
||||||
|
$socket = $this->nsp->connected[$id];
|
||||||
|
$volatile = isset($flags['volatile']) ? $flags['volatile'] : null;
|
||||||
|
$socket->packet($encodedPackets, true, $volatile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clients($rooms, $fn) {
|
||||||
|
$sids = array();
|
||||||
|
foreach ($rooms as $room) {
|
||||||
|
$sids = array_merge($sids, $this->rooms[$room]);
|
||||||
|
}
|
||||||
|
$fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
309
vendor/workerman/phpsocket.io/src/Engine/Engine.php
vendored
Normal file
309
vendor/workerman/phpsocket.io/src/Engine/Engine.php
vendored
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO\Engine;
|
||||||
|
use \PHPSocketIO\Engine\Transports\Polling;
|
||||||
|
use \PHPSocketIO\Engine\Transports\PollingXHR;
|
||||||
|
use \PHPSocketIO\Engine\Transports\WebSocket;
|
||||||
|
use \PHPSocketIO\Event\Emitter;
|
||||||
|
use \PHPSocketIO\Debug;
|
||||||
|
class Engine extends Emitter
|
||||||
|
{
|
||||||
|
public $pingTimeout = 60;
|
||||||
|
public $pingInterval = 25;
|
||||||
|
public $upgradeTimeout = 5;
|
||||||
|
public $transports = array();
|
||||||
|
public $allowUpgrades = array();
|
||||||
|
public $allowRequest = array();
|
||||||
|
public $clients = array();
|
||||||
|
public $origins = '*:*';
|
||||||
|
public static $allowTransports = array(
|
||||||
|
'polling' => 'polling',
|
||||||
|
'websocket' => 'websocket'
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $errorMessages = array(
|
||||||
|
'Transport unknown',
|
||||||
|
'Session ID unknown',
|
||||||
|
'Bad handshake method',
|
||||||
|
'Bad request'
|
||||||
|
);
|
||||||
|
|
||||||
|
const ERROR_UNKNOWN_TRANSPORT = 0;
|
||||||
|
|
||||||
|
const ERROR_UNKNOWN_SID = 1;
|
||||||
|
|
||||||
|
const ERROR_BAD_HANDSHAKE_METHOD = 2;
|
||||||
|
|
||||||
|
const ERROR_BAD_REQUEST = 3;
|
||||||
|
|
||||||
|
public function __construct($opts = array())
|
||||||
|
{
|
||||||
|
$ops_map = array(
|
||||||
|
'pingTimeout',
|
||||||
|
'pingInterval',
|
||||||
|
'upgradeTimeout',
|
||||||
|
'transports',
|
||||||
|
'allowUpgrades',
|
||||||
|
'allowRequest'
|
||||||
|
);
|
||||||
|
foreach($ops_map as $key)
|
||||||
|
{
|
||||||
|
if(isset($opts[$key]))
|
||||||
|
{
|
||||||
|
$this->$key = $opts[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Debug::debug('Engine __construct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
Debug::debug('Engine __destruct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleRequest($req, $res)
|
||||||
|
{
|
||||||
|
$this->prepare($req);
|
||||||
|
$req->res = $res;
|
||||||
|
$this->verify($req, $res, false, array($this, 'dealRequest'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dealRequest($err, $success, $req)
|
||||||
|
{
|
||||||
|
if (!$success)
|
||||||
|
{
|
||||||
|
self::sendErrorMessage($req, $req->res, $err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($req->_query['sid']))
|
||||||
|
{
|
||||||
|
$this->clients[$req->_query['sid']]->transport->onRequest($req);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->handshake($req->_query['transport'], $req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function sendErrorMessage($req, $res, $code)
|
||||||
|
{
|
||||||
|
$headers = array('Content-Type'=> 'application/json');
|
||||||
|
if(isset($req->headers['origin']))
|
||||||
|
{
|
||||||
|
$headers['Access-Control-Allow-Credentials'] = 'true';
|
||||||
|
$headers['Access-Control-Allow-Origin'] = $req->headers['origin'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$headers['Access-Control-Allow-Origin'] = '*';
|
||||||
|
}
|
||||||
|
|
||||||
|
$res->writeHead(403, '', $headers);
|
||||||
|
$res->end(json_encode(array(
|
||||||
|
'code' => $code,
|
||||||
|
'message' => isset(self::$errorMessages[$code]) ? self::$errorMessages[$code] : $code
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function verify($req, $res, $upgrade, $fn)
|
||||||
|
{
|
||||||
|
if(!isset($req->_query['transport']) || !isset(self::$allowTransports[$req->_query['transport']]))
|
||||||
|
{
|
||||||
|
return call_user_func($fn, self::ERROR_UNKNOWN_TRANSPORT, false, $req, $res);
|
||||||
|
}
|
||||||
|
$transport = $req->_query['transport'];
|
||||||
|
$sid = isset($req->_query['sid']) ? $req->_query['sid'] : '';
|
||||||
|
/*if ($transport === 'websocket' && empty($sid)) {
|
||||||
|
return call_user_func($fn, self::ERROR_UNKNOWN_TRANSPORT, false, $req, $res);
|
||||||
|
}*/
|
||||||
|
if($sid)
|
||||||
|
{
|
||||||
|
if(!isset($this->clients[$sid]))
|
||||||
|
{
|
||||||
|
return call_user_func($fn, self::ERROR_UNKNOWN_SID, false, $req, $res);
|
||||||
|
}
|
||||||
|
if(!$upgrade && $this->clients[$sid]->transport->name !== $transport)
|
||||||
|
{
|
||||||
|
return call_user_func($fn, self::ERROR_BAD_REQUEST, false, $req, $res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if('GET' !== $req->method)
|
||||||
|
{
|
||||||
|
return call_user_func($fn, self::ERROR_BAD_HANDSHAKE_METHOD, false, $req, $res);
|
||||||
|
}
|
||||||
|
return $this->checkRequest($req, $res, $fn);
|
||||||
|
}
|
||||||
|
call_user_func($fn, null, true, $req, $res);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkRequest($req, $res, $fn)
|
||||||
|
{
|
||||||
|
if ($this->origins === "*:*" || empty($this->origins))
|
||||||
|
{
|
||||||
|
return call_user_func($fn, null, true, $req, $res);
|
||||||
|
}
|
||||||
|
$origin = null;
|
||||||
|
if (isset($req->headers['origin']))
|
||||||
|
{
|
||||||
|
$origin = $req->headers['origin'];
|
||||||
|
}
|
||||||
|
else if(isset($req->headers['referer']))
|
||||||
|
{
|
||||||
|
$origin = $req->headers['referer'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// file:// URLs produce a null Origin which can't be authorized via echo-back
|
||||||
|
if ('null' === $origin || null === $origin) {
|
||||||
|
return call_user_func($fn, null, true, $req, $res);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($origin)
|
||||||
|
{
|
||||||
|
$parts = parse_url($origin);
|
||||||
|
$defaultPort = 'https:' === $parts['scheme'] ? 443 : 80;
|
||||||
|
$parts['port'] = isset($parts['port']) ? $parts['port'] : $defaultPort;
|
||||||
|
$allowed_origins = explode(' ', $this->origins);
|
||||||
|
foreach( $allowed_origins as $allow_origin ){
|
||||||
|
$ok =
|
||||||
|
$allow_origin === $parts['scheme'] . '://' . $parts['host'] . ':' . $parts['port'] ||
|
||||||
|
$allow_origin === $parts['scheme'] . '://' . $parts['host'] ||
|
||||||
|
$allow_origin === $parts['scheme'] . '://' . $parts['host'] . ':*' ||
|
||||||
|
$allow_origin === '*:' . $parts['port'];
|
||||||
|
if($ok){
|
||||||
|
# 只需要有一个白名单通过,则都通过
|
||||||
|
return call_user_func($fn, null, $ok, $req, $res);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
call_user_func($fn, null, false, $req, $res);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function prepare($req)
|
||||||
|
{
|
||||||
|
if(!isset($req->_query))
|
||||||
|
{
|
||||||
|
$info = parse_url($req->url);
|
||||||
|
if(isset($info['query']))
|
||||||
|
{
|
||||||
|
parse_str($info['query'], $req->_query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handshake($transport, $req)
|
||||||
|
{
|
||||||
|
$id = bin2hex(pack('d', microtime(true)).pack('N', function_exists('random_int') ? random_int(1, 100000000): rand(1, 100000000)));
|
||||||
|
if ($transport == 'websocket') {
|
||||||
|
$transport = '\\PHPSocketIO\\Engine\\Transports\\WebSocket';
|
||||||
|
}
|
||||||
|
elseif (isset($req->_query['j']))
|
||||||
|
{
|
||||||
|
$transport = '\\PHPSocketIO\\Engine\\Transports\\PollingJsonp';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$transport = '\\PHPSocketIO\\Engine\\Transports\\PollingXHR';
|
||||||
|
}
|
||||||
|
|
||||||
|
$transport = new $transport($req);
|
||||||
|
|
||||||
|
$transport->supportsBinary = !isset($req->_query['b64']);
|
||||||
|
|
||||||
|
$socket = new Socket($id, $this, $transport, $req);
|
||||||
|
|
||||||
|
/* $transport->on('headers', function(&$headers)use($id)
|
||||||
|
{
|
||||||
|
$headers['Set-Cookie'] = "io=$id";
|
||||||
|
}); */
|
||||||
|
|
||||||
|
$transport->onRequest($req);
|
||||||
|
|
||||||
|
$this->clients[$id] = $socket;
|
||||||
|
$socket->once('close', array($this, 'onSocketClose'));
|
||||||
|
$this->emit('connection', $socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onSocketClose($id)
|
||||||
|
{
|
||||||
|
unset($this->clients[$id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attach($worker)
|
||||||
|
{
|
||||||
|
$this->server = $worker;
|
||||||
|
$worker->onConnect = array($this, 'onConnect');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onConnect($connection)
|
||||||
|
{
|
||||||
|
$connection->onRequest = array($this, 'handleRequest');
|
||||||
|
$connection->onWebSocketConnect = array($this, 'onWebSocketConnect');
|
||||||
|
// clean
|
||||||
|
$connection->onClose = function($connection)
|
||||||
|
{
|
||||||
|
if(!empty($connection->httpRequest))
|
||||||
|
{
|
||||||
|
$connection->httpRequest->destroy();
|
||||||
|
$connection->httpRequest = null;
|
||||||
|
}
|
||||||
|
if(!empty($connection->httpResponse))
|
||||||
|
{
|
||||||
|
$connection->httpResponse->destroy();
|
||||||
|
$connection->httpResponse = null;
|
||||||
|
}
|
||||||
|
if(!empty($connection->onRequest))
|
||||||
|
{
|
||||||
|
$connection->onRequest = null;
|
||||||
|
}
|
||||||
|
if(!empty($connection->onWebSocketConnect))
|
||||||
|
{
|
||||||
|
$connection->onWebSocketConnect = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onWebSocketConnect($connection, $req, $res)
|
||||||
|
{
|
||||||
|
$this->prepare($req);
|
||||||
|
$this->verify($req, $res, true, array($this, 'dealWebSocketConnect'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dealWebSocketConnect($err, $success, $req, $res)
|
||||||
|
{
|
||||||
|
if (!$success)
|
||||||
|
{
|
||||||
|
self::sendErrorMessage($req, $res, $err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(isset($req->_query['sid']))
|
||||||
|
{
|
||||||
|
if(!isset($this->clients[$req->_query['sid']]))
|
||||||
|
{
|
||||||
|
self::sendErrorMessage($req, $res, 'upgrade attempt for closed client');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$client = $this->clients[$req->_query['sid']];
|
||||||
|
if($client->upgrading)
|
||||||
|
{
|
||||||
|
self::sendErrorMessage($req, $res, 'transport has already been trying to upgrade');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if($client->upgraded)
|
||||||
|
{
|
||||||
|
self::sendErrorMessage($req, $res, 'transport had already been upgraded');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$transport = new WebSocket($req);
|
||||||
|
$client->maybeUpgrade($transport);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->handshake($req->_query['transport'], $req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
303
vendor/workerman/phpsocket.io/src/Engine/Parser.php
vendored
Normal file
303
vendor/workerman/phpsocket.io/src/Engine/Parser.php
vendored
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO\Engine;
|
||||||
|
use \PHPSocketIO\Debug;
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
Debug::debug('Engine/Parser __construct');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static $packets=array(
|
||||||
|
'open'=> 0 // non-ws
|
||||||
|
, 'close'=> 1 // non-ws
|
||||||
|
, 'ping'=> 2
|
||||||
|
, 'pong'=> 3
|
||||||
|
, 'message'=> 4
|
||||||
|
, 'upgrade'=> 5
|
||||||
|
, 'noop'=> 6
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $packetsList = array(
|
||||||
|
'open',
|
||||||
|
'close',
|
||||||
|
'ping',
|
||||||
|
'pong',
|
||||||
|
'message',
|
||||||
|
'upgrade',
|
||||||
|
'noop'
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $err = array(
|
||||||
|
'type' => 'error',
|
||||||
|
'data' => 'parser error'
|
||||||
|
);
|
||||||
|
|
||||||
|
public static function encodePacket($packet)
|
||||||
|
{
|
||||||
|
$data = !isset($packet['data']) ? '' : $packet['data'];
|
||||||
|
return self::$packets[$packet['type']].$data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a packet with binary data in a base64 string
|
||||||
|
*
|
||||||
|
* @param {Object} packet, has `type` and `data`
|
||||||
|
* @return {String} base64 encoded message
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static function encodeBase64Packet($packet)
|
||||||
|
{
|
||||||
|
$data = isset($packet['data']) ? '' : $packet['data'];
|
||||||
|
return $message = 'b' . self::$packets[$packet['type']] . base64_encode($packet['data']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a packet. Data also available as an ArrayBuffer if requested.
|
||||||
|
*
|
||||||
|
* @return {Object} with `type` and `data` (if any)
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static function decodePacket($data, $binaryType = null, $utf8decode = true)
|
||||||
|
{
|
||||||
|
// String data todo check if (typeof data == 'string' || data === undefined)
|
||||||
|
if ($data[0] === 'b')
|
||||||
|
{
|
||||||
|
return self::decodeBase64Packet(substr($data, 1), $binaryType);
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $data[0];
|
||||||
|
if (!isset(self::$packetsList[$type]))
|
||||||
|
{
|
||||||
|
return self::$err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($data[1]))
|
||||||
|
{
|
||||||
|
return array('type'=> self::$packetsList[$type], 'data'=> substr($data, 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return array('type'=> self::$packetsList[$type]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a packet encoded in a base64 string.
|
||||||
|
*
|
||||||
|
* @param {String} base64 encoded message
|
||||||
|
* @return {Object} with `type` and `data` (if any)
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static function decodeBase64Packet($msg, $binaryType)
|
||||||
|
{
|
||||||
|
$type = self::$packetsList[$msg[0]];
|
||||||
|
$data = base64_decode(substr($data, 1));
|
||||||
|
return array('type'=> $type, 'data'=> $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes multiple messages (payload).
|
||||||
|
*
|
||||||
|
* <length>:data
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* 11:hello world2:hi
|
||||||
|
*
|
||||||
|
* If any contents are binary, they will be encoded as base64 strings. Base64
|
||||||
|
* encoded strings are marked with a b before the length specifier
|
||||||
|
*
|
||||||
|
* @param {Array} packets
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static function encodePayload($packets, $supportsBinary = null)
|
||||||
|
{
|
||||||
|
if ($supportsBinary)
|
||||||
|
{
|
||||||
|
return self::encodePayloadAsBinary($packets);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$packets)
|
||||||
|
{
|
||||||
|
return '0:';
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = '';
|
||||||
|
foreach($packets as $msg)
|
||||||
|
{
|
||||||
|
$results .= self::encodeOne($msg, $supportsBinary);
|
||||||
|
}
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function encodeOne($packet, $supportsBinary = null, $result = null)
|
||||||
|
{
|
||||||
|
$message = self::encodePacket($packet, $supportsBinary, true);
|
||||||
|
return strlen($message) . ':' . $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decodes data when a payload is maybe expected. Possible binary contents are
|
||||||
|
* decoded from their base64 representation
|
||||||
|
*
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static function decodePayload($data, $binaryType = null)
|
||||||
|
{
|
||||||
|
if(!preg_match('/^\d+:\d/',$data))
|
||||||
|
{
|
||||||
|
return self::decodePayloadAsBinary($data, $binaryType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($data === '')
|
||||||
|
{
|
||||||
|
// parser error - ignoring payload
|
||||||
|
return self::$err;
|
||||||
|
}
|
||||||
|
|
||||||
|
$length = '';//, n, msg;
|
||||||
|
|
||||||
|
for ($i = 0, $l = strlen($data); $i < $l; $i++)
|
||||||
|
{
|
||||||
|
$chr = $data[$i];
|
||||||
|
|
||||||
|
if (':' != $chr)
|
||||||
|
{
|
||||||
|
$length .= $chr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ('' == $length || ($length != ($n = intval($length))))
|
||||||
|
{
|
||||||
|
// parser error - ignoring payload
|
||||||
|
return self::$err;
|
||||||
|
}
|
||||||
|
|
||||||
|
$msg = substr($data, $i + 1/*, $n*/);
|
||||||
|
|
||||||
|
/*if ($length != strlen($msg))
|
||||||
|
{
|
||||||
|
// parser error - ignoring payload
|
||||||
|
return self::$err;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if (isset($msg[0]))
|
||||||
|
{
|
||||||
|
$packet = self::decodePacket($msg, $binaryType, true);
|
||||||
|
|
||||||
|
if (self::$err['type'] == $packet['type'] && self::$err['data'] == $packet['data'])
|
||||||
|
{
|
||||||
|
// parser error in individual packet - ignoring payload
|
||||||
|
return self::$err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// advance cursor
|
||||||
|
$i += $n;
|
||||||
|
$length = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($length !== '')
|
||||||
|
{
|
||||||
|
// parser error - ignoring payload
|
||||||
|
echo new \Exception('parser error');
|
||||||
|
return self::$err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes multiple messages (payload) as binary.
|
||||||
|
*
|
||||||
|
* <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
|
||||||
|
* 255><data>
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
|
||||||
|
*
|
||||||
|
* @param {Array} packets
|
||||||
|
* @return {Buffer} encoded payload
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static function encodePayloadAsBinary($packets)
|
||||||
|
{
|
||||||
|
$results = '';
|
||||||
|
foreach($packets as $msg)
|
||||||
|
{
|
||||||
|
$results .= self::encodeOneAsBinary($msg);
|
||||||
|
}
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function encodeOneAsBinary($p)
|
||||||
|
{
|
||||||
|
// todo is string or arraybuf
|
||||||
|
$packet = self::encodePacket($p, true, true);
|
||||||
|
$encodingLength = ''.strlen($packet);
|
||||||
|
$sizeBuffer = chr(0);
|
||||||
|
for ($i = 0; $i < strlen($encodingLength); $i++)
|
||||||
|
{
|
||||||
|
$sizeBuffer .= chr($encodingLength[$i]);
|
||||||
|
}
|
||||||
|
$sizeBuffer .= chr(255);
|
||||||
|
return $sizeBuffer.$packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decodes data when a payload is maybe expected. Strings are decoded by
|
||||||
|
* interpreting each byte as a key code for entries marked to start with 0. See
|
||||||
|
* description of encodePayloadAsBinary
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static function decodePayloadAsBinary($data, $binaryType = null)
|
||||||
|
{
|
||||||
|
$bufferTail = $data;
|
||||||
|
$buffers = array();
|
||||||
|
|
||||||
|
while (strlen($bufferTail) > 0)
|
||||||
|
{
|
||||||
|
$strLen = '';
|
||||||
|
$isString = $bufferTail[0] == 0;
|
||||||
|
$numberTooLong = false;
|
||||||
|
for ($i = 1; ; $i++)
|
||||||
|
{
|
||||||
|
$tail = ord($bufferTail[$i]);
|
||||||
|
if ($tail === 255) break;
|
||||||
|
// 310 = char length of Number.MAX_VALUE
|
||||||
|
if (strlen($strLen) > 310)
|
||||||
|
{
|
||||||
|
$numberTooLong = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$strLen .= $tail;
|
||||||
|
}
|
||||||
|
if($numberTooLong) return self::$err;
|
||||||
|
$bufferTail = substr($bufferTail, strlen($strLen) + 1);
|
||||||
|
|
||||||
|
$msgLength = intval($strLen, 10);
|
||||||
|
|
||||||
|
$msg = substr($bufferTail, 1, $msgLength + 1);
|
||||||
|
$buffers[] = $msg;
|
||||||
|
$bufferTail = substr($bufferTail, $msgLength + 1);
|
||||||
|
}
|
||||||
|
$total = count($buffers);
|
||||||
|
$packets = array();
|
||||||
|
foreach($buffers as $i => $buffer)
|
||||||
|
{
|
||||||
|
$packets[] = self::decodePacket($buffer, $binaryType, true);
|
||||||
|
}
|
||||||
|
return $packets;
|
||||||
|
}
|
||||||
|
}
|
51
vendor/workerman/phpsocket.io/src/Engine/Protocols/Http/Request.php
vendored
Normal file
51
vendor/workerman/phpsocket.io/src/Engine/Protocols/Http/Request.php
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO\Engine\Protocols\Http;
|
||||||
|
|
||||||
|
class Request
|
||||||
|
{
|
||||||
|
public $onData = null;
|
||||||
|
|
||||||
|
public $onEnd = null;
|
||||||
|
|
||||||
|
public $httpVersion = null;
|
||||||
|
|
||||||
|
public $headers = array();
|
||||||
|
|
||||||
|
public $rawHeaders = null;
|
||||||
|
|
||||||
|
public $method = null;
|
||||||
|
|
||||||
|
public $url = null;
|
||||||
|
|
||||||
|
public $connection = null;
|
||||||
|
|
||||||
|
public function __construct($connection, $raw_head)
|
||||||
|
{
|
||||||
|
$this->connection = $connection;
|
||||||
|
$this->parseHead($raw_head);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parseHead($raw_head)
|
||||||
|
{
|
||||||
|
$header_data = explode("\r\n", $raw_head);
|
||||||
|
list($this->method, $this->url, $protocol) = explode(' ', $header_data[0]);
|
||||||
|
list($null, $this->httpVersion) = explode('/', $protocol);
|
||||||
|
unset($header_data[0]);
|
||||||
|
foreach($header_data as $content)
|
||||||
|
{
|
||||||
|
if(empty($content))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->rawHeaders[] = $content;
|
||||||
|
list($key, $value) = explode(':', $content, 2);
|
||||||
|
$this->headers[strtolower($key)] = trim($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy()
|
||||||
|
{
|
||||||
|
$this->onData = $this->onEnd = $this->onClose = null;
|
||||||
|
$this->connection = null;
|
||||||
|
}
|
||||||
|
}
|
206
vendor/workerman/phpsocket.io/src/Engine/Protocols/Http/Response.php
vendored
Normal file
206
vendor/workerman/phpsocket.io/src/Engine/Protocols/Http/Response.php
vendored
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO\Engine\Protocols\Http;
|
||||||
|
|
||||||
|
class Response
|
||||||
|
{
|
||||||
|
public $statusCode = 200;
|
||||||
|
|
||||||
|
protected $_statusPhrase = null;
|
||||||
|
|
||||||
|
protected $_connection = null;
|
||||||
|
|
||||||
|
protected $_headers = array();
|
||||||
|
|
||||||
|
public $headersSent = false;
|
||||||
|
|
||||||
|
public $writable = true;
|
||||||
|
|
||||||
|
protected $_buffer = '';
|
||||||
|
|
||||||
|
public function __construct($connection)
|
||||||
|
{
|
||||||
|
$this->_connection = $connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initHeader()
|
||||||
|
{
|
||||||
|
$this->_headers['Connection'] = 'keep-alive';
|
||||||
|
$this->_headers['Content-Type'] = 'Content-Type: text/html;charset=utf-8';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function writeHead($status_code, $reason_phrase = '', $headers = null)
|
||||||
|
{
|
||||||
|
if($this->headersSent)
|
||||||
|
{
|
||||||
|
echo "header has already send\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$this->statusCode = $status_code;
|
||||||
|
if($reason_phrase)
|
||||||
|
{
|
||||||
|
$this->_statusPhrase = $reason_phrase;
|
||||||
|
}
|
||||||
|
if($headers)
|
||||||
|
{
|
||||||
|
foreach($headers as $key=>$val)
|
||||||
|
{
|
||||||
|
$this->_headers[$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->_buffer = $this->getHeadBuffer();
|
||||||
|
$this->headersSent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHeadBuffer()
|
||||||
|
{
|
||||||
|
if(!$this->_statusPhrase)
|
||||||
|
{
|
||||||
|
$this->_statusPhrase = isset(self::$codes[$this->statusCode]) ? self::$codes[$this->statusCode] : '';
|
||||||
|
}
|
||||||
|
$head_buffer = "HTTP/1.1 $this->statusCode $this->_statusPhrase\r\n";
|
||||||
|
if(!isset($this->_headers['Content-Length']) && !isset($this->_headers['Transfer-Encoding']))
|
||||||
|
{
|
||||||
|
$head_buffer .= "Transfer-Encoding: chunked\r\n";
|
||||||
|
}
|
||||||
|
if(!isset($this->_headers['Connection']))
|
||||||
|
{
|
||||||
|
$head_buffer .= "Connection: keep-alive\r\n";
|
||||||
|
}
|
||||||
|
foreach($this->_headers as $key=>$val)
|
||||||
|
{
|
||||||
|
if($key === 'Set-Cookie' && is_array($val))
|
||||||
|
{
|
||||||
|
foreach($val as $v)
|
||||||
|
{
|
||||||
|
$head_buffer .= "Set-Cookie: $v\r\n";
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$head_buffer .= "$key: $val\r\n";
|
||||||
|
}
|
||||||
|
return $head_buffer."\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setHeader($key, $val)
|
||||||
|
{
|
||||||
|
$this->_headers[$key] = $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHeader($name)
|
||||||
|
{
|
||||||
|
return isset($this->_headers[$name]) ? $this->_headers[$name] : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeHeader($name)
|
||||||
|
{
|
||||||
|
unset($this->_headers[$name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write($chunk)
|
||||||
|
{
|
||||||
|
if(!isset($this->_headers['Content-Length']))
|
||||||
|
{
|
||||||
|
$chunk = dechex(strlen($chunk)) . "\r\n" . $chunk . "\r\n";
|
||||||
|
}
|
||||||
|
if(!$this->headersSent)
|
||||||
|
{
|
||||||
|
$head_buffer = $this->getHeadBuffer();
|
||||||
|
$this->_buffer = $head_buffer . $chunk;
|
||||||
|
$this->headersSent = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->_buffer .= $chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function end($data = null)
|
||||||
|
{
|
||||||
|
if(!$this->writable)
|
||||||
|
{
|
||||||
|
echo new \Exception('unwirtable');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if($data !== null)
|
||||||
|
{
|
||||||
|
$this->write($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$this->headersSent)
|
||||||
|
{
|
||||||
|
$head_buffer = $this->getHeadBuffer();
|
||||||
|
$this->_buffer = $head_buffer;
|
||||||
|
$this->headersSent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($this->_headers['Content-Length']))
|
||||||
|
{
|
||||||
|
$ret = $this->_connection->send($this->_buffer . "0\r\n\r\n", true);
|
||||||
|
$this->destroy();
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
$ret = $this->_connection->send($this->_buffer, true);
|
||||||
|
$this->destroy();
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy()
|
||||||
|
{
|
||||||
|
if(!empty($this->_connection->httpRequest))
|
||||||
|
{
|
||||||
|
$this->_connection->httpRequest->destroy();
|
||||||
|
}
|
||||||
|
if(!empty($this->_connection))
|
||||||
|
{
|
||||||
|
$this->_connection->httpResponse = $this->_connection->httpRequest = null;
|
||||||
|
}
|
||||||
|
$this->_connection = null;
|
||||||
|
$this->writable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static $codes = array(
|
||||||
|
100 => 'Continue',
|
||||||
|
101 => 'Switching Protocols',
|
||||||
|
200 => 'OK',
|
||||||
|
201 => 'Created',
|
||||||
|
202 => 'Accepted',
|
||||||
|
203 => 'Non-Authoritative Information',
|
||||||
|
204 => 'No Content',
|
||||||
|
205 => 'Reset Content',
|
||||||
|
206 => 'Partial Content',
|
||||||
|
300 => 'Multiple Choices',
|
||||||
|
301 => 'Moved Permanently',
|
||||||
|
302 => 'Found',
|
||||||
|
303 => 'See Other',
|
||||||
|
304 => 'Not Modified',
|
||||||
|
305 => 'Use Proxy',
|
||||||
|
306 => '(Unused)',
|
||||||
|
307 => 'Temporary Redirect',
|
||||||
|
400 => 'Bad Request',
|
||||||
|
401 => 'Unauthorized',
|
||||||
|
402 => 'Payment Required',
|
||||||
|
403 => 'Forbidden',
|
||||||
|
404 => 'Not Found',
|
||||||
|
405 => 'Method Not Allowed',
|
||||||
|
406 => 'Not Acceptable',
|
||||||
|
407 => 'Proxy Authentication Required',
|
||||||
|
408 => 'Request Timeout',
|
||||||
|
409 => 'Conflict',
|
||||||
|
410 => 'Gone',
|
||||||
|
411 => 'Length Required',
|
||||||
|
412 => 'Precondition Failed',
|
||||||
|
413 => 'Request Entity Too Large',
|
||||||
|
414 => 'Request-URI Too Long',
|
||||||
|
415 => 'Unsupported Media Type',
|
||||||
|
416 => 'Requested Range Not Satisfiable',
|
||||||
|
417 => 'Expectation Failed',
|
||||||
|
422 => 'Unprocessable Entity',
|
||||||
|
423 => 'Locked',
|
||||||
|
500 => 'Internal Server Error',
|
||||||
|
501 => 'Not Implemented',
|
||||||
|
502 => 'Bad Gateway',
|
||||||
|
503 => 'Service Unavailable',
|
||||||
|
504 => 'Gateway Timeout',
|
||||||
|
505 => 'HTTP Version Not Supported',
|
||||||
|
);
|
||||||
|
}
|
209
vendor/workerman/phpsocket.io/src/Engine/Protocols/SocketIO.php
vendored
Normal file
209
vendor/workerman/phpsocket.io/src/Engine/Protocols/SocketIO.php
vendored
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO\Engine\Protocols;
|
||||||
|
use \PHPSocketIO\Engine\Protocols\WebSocket;
|
||||||
|
use \PHPSocketIO\Engine\Protocols\Http\Request;
|
||||||
|
use \PHPSocketIO\Engine\Protocols\Http\Response;
|
||||||
|
use \Workerman\Connection\TcpConnection;
|
||||||
|
class SocketIO
|
||||||
|
{
|
||||||
|
public static function input($http_buffer, $connection)
|
||||||
|
{
|
||||||
|
if(!empty($connection->hasReadedHead))
|
||||||
|
{
|
||||||
|
return strlen($http_buffer);
|
||||||
|
}
|
||||||
|
$pos = strpos($http_buffer, "\r\n\r\n");
|
||||||
|
if(!$pos)
|
||||||
|
{
|
||||||
|
if(strlen($http_buffer) >= $connection->maxPackageSize)
|
||||||
|
{
|
||||||
|
$connection->close("HTTP/1.1 400 bad request\r\n\r\nheader too long");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
$head_len = $pos + 4;
|
||||||
|
$raw_head = substr($http_buffer, 0, $head_len);
|
||||||
|
$raw_body = substr($http_buffer, $head_len);
|
||||||
|
$req = new Request($connection, $raw_head);
|
||||||
|
$res = new Response($connection);
|
||||||
|
$connection->httpRequest = $req;
|
||||||
|
$connection->httpResponse = $res;
|
||||||
|
$connection->hasReadedHead = true;
|
||||||
|
TcpConnection::$statistics['total_request']++;
|
||||||
|
$connection->onClose = '\PHPSocketIO\Engine\Protocols\SocketIO::emitClose';
|
||||||
|
if(isset($req->headers['upgrade']) && strtolower($req->headers['upgrade']) === 'websocket')
|
||||||
|
{
|
||||||
|
$connection->consumeRecvBuffer(strlen($http_buffer));
|
||||||
|
WebSocket::dealHandshake($connection, $req, $res);
|
||||||
|
self::cleanup($connection);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(!empty($connection->onRequest))
|
||||||
|
{
|
||||||
|
$connection->consumeRecvBuffer(strlen($http_buffer));
|
||||||
|
self::emitRequest($connection, $req, $res);
|
||||||
|
if($req->method === 'GET' || $req->method === 'OPTIONS')
|
||||||
|
{
|
||||||
|
self::emitEnd($connection, $req);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST
|
||||||
|
if('\PHPSocketIO\Engine\Protocols\SocketIO::onData' !== $connection->onMessage)
|
||||||
|
{
|
||||||
|
$connection->onMessage = '\PHPSocketIO\Engine\Protocols\SocketIO::onData';
|
||||||
|
}
|
||||||
|
if(!$raw_body)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
self::onData($connection, $raw_body);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if($req->method === 'GET')
|
||||||
|
{
|
||||||
|
return $pos + 4;
|
||||||
|
}
|
||||||
|
elseif(isset($req->headers['content-length']))
|
||||||
|
{
|
||||||
|
return $req->headers['content-length'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$connection->close("HTTP/1.1 400 bad request\r\n\r\ntrunk not support");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function onData($connection, $data)
|
||||||
|
{
|
||||||
|
$req = $connection->httpRequest;
|
||||||
|
self::emitData($connection, $req, $data);
|
||||||
|
if((isset($req->headers['content-length']) && $req->headers['content-length'] <= strlen($data))
|
||||||
|
|| substr($data, -5) === "0\r\n\r\n")
|
||||||
|
{
|
||||||
|
self::emitEnd($connection, $req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function emitRequest($connection, $req, $res)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
call_user_func($connection->onRequest, $req, $res);
|
||||||
|
}
|
||||||
|
catch(\Exception $e)
|
||||||
|
{
|
||||||
|
echo $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function emitClose($connection)
|
||||||
|
{
|
||||||
|
$req = $connection->httpRequest;
|
||||||
|
if(isset($req->onClose))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
call_user_func($req->onClose, $req);
|
||||||
|
}
|
||||||
|
catch(\Exception $e)
|
||||||
|
{
|
||||||
|
echo $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$res = $connection->httpResponse;
|
||||||
|
if(isset($res->onClose))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
call_user_func($res->onClose, $res);
|
||||||
|
}
|
||||||
|
catch(\Exception $e)
|
||||||
|
{
|
||||||
|
echo $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self::cleanup($connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function cleanup($connection)
|
||||||
|
{
|
||||||
|
if(!empty($connection->onRequest))
|
||||||
|
{
|
||||||
|
$connection->onRequest = null;
|
||||||
|
}
|
||||||
|
if(!empty($connection->onWebSocketConnect))
|
||||||
|
{
|
||||||
|
$connection->onWebSocketConnect = null;
|
||||||
|
}
|
||||||
|
if(!empty($connection->httpRequest))
|
||||||
|
{
|
||||||
|
$connection->httpRequest->destroy();
|
||||||
|
$connection->httpRequest = null;
|
||||||
|
}
|
||||||
|
if(!empty($connection->httpResponse))
|
||||||
|
{
|
||||||
|
$connection->httpResponse->destroy();
|
||||||
|
$connection->httpResponse = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function emitData($connection, $req, $data)
|
||||||
|
{
|
||||||
|
if(isset($req->onData))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
call_user_func($req->onData, $req, $data);
|
||||||
|
}
|
||||||
|
catch(\Exception $e)
|
||||||
|
{
|
||||||
|
echo $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function emitEnd($connection, $req)
|
||||||
|
{
|
||||||
|
if(isset($req->onEnd))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
call_user_func($req->onEnd, $req);
|
||||||
|
}
|
||||||
|
catch(\Exception $e)
|
||||||
|
{
|
||||||
|
echo $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$connection->hasReadedHead = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function encode($buffer, $connection)
|
||||||
|
{
|
||||||
|
if(!isset($connection->onRequest))
|
||||||
|
{
|
||||||
|
$connection->httpResponse->setHeader('Content-Length', strlen($buffer));
|
||||||
|
return $connection->httpResponse->getHeadBuffer() . $buffer;
|
||||||
|
}
|
||||||
|
return $buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function decode($http_buffer, $connection)
|
||||||
|
{
|
||||||
|
if(isset($connection->onRequest))
|
||||||
|
{
|
||||||
|
return $http_buffer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list($head, $body) = explode("\r\n\r\n", $http_buffer, 2);
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
86
vendor/workerman/phpsocket.io/src/Engine/Protocols/WebSocket.php
vendored
Normal file
86
vendor/workerman/phpsocket.io/src/Engine/Protocols/WebSocket.php
vendored
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is part of workerman.
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @author walkor<walkor@workerman.net>
|
||||||
|
* @copyright walkor<walkor@workerman.net>
|
||||||
|
* @link http://www.workerman.net/
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
namespace PHPSocketIO\Engine\Protocols;
|
||||||
|
|
||||||
|
use \PHPSocketIO\Engine\Protocols\Http\Request;
|
||||||
|
use \PHPSocketIO\Engine\Protocols\Http\Response;
|
||||||
|
use \PHPSocketIO\Engine\Protocols\WebSocket\RFC6455;
|
||||||
|
use \Workerman\Connection\TcpConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket 协议服务端解包和打包
|
||||||
|
*/
|
||||||
|
class WebSocket
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 最小包头
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
const MIN_HEAD_LEN = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查包的完整性
|
||||||
|
* @param string $buffer
|
||||||
|
*/
|
||||||
|
public static function input($buffer, $connection)
|
||||||
|
{
|
||||||
|
if(strlen($buffer) < self::MIN_HEAD_LEN)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// flash policy file
|
||||||
|
if(0 === strpos($buffer,'<policy'))
|
||||||
|
{
|
||||||
|
$policy_xml = '<?xml version="1.0"?><cross-domain-policy><site-control permitted-cross-domain-policies="all"/><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>'."\0";
|
||||||
|
$connection->send($policy_xml, true);
|
||||||
|
$connection->consumeRecvBuffer(strlen($buffer));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// http head
|
||||||
|
$pos = strpos($buffer, "\r\n\r\n");
|
||||||
|
if(!$pos)
|
||||||
|
{
|
||||||
|
if(strlen($buffer)>=TcpConnection::$maxPackageSize)
|
||||||
|
{
|
||||||
|
$connection->close("HTTP/1.1 400 bad request\r\n\r\nheader too long");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
$req = new Request($connection, $buffer);
|
||||||
|
$res = new Response($connection);
|
||||||
|
$connection->consumeRecvBuffer(strlen($buffer));
|
||||||
|
return self::dealHandshake($connection, $req, $res);
|
||||||
|
$connection->consumeRecvBuffer($pos+4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理websocket握手
|
||||||
|
* @param string $buffer
|
||||||
|
* @param TcpConnection $connection
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function dealHandshake($connection, $req, $res)
|
||||||
|
{
|
||||||
|
if(isset($req->headers['sec-websocket-key1']))
|
||||||
|
{
|
||||||
|
$res->writeHead(400);
|
||||||
|
$res->end("Not support");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
$connection->protocol = 'PHPSocketIO\Engine\Protocols\WebSocket\RFC6455';
|
||||||
|
return RFC6455::dealHandshake($connection, $req, $res);
|
||||||
|
}
|
||||||
|
}
|
335
vendor/workerman/phpsocket.io/src/Engine/Protocols/WebSocket/RFC6455.php
vendored
Normal file
335
vendor/workerman/phpsocket.io/src/Engine/Protocols/WebSocket/RFC6455.php
vendored
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is part of workerman.
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @author walkor<walkor@workerman.net>
|
||||||
|
* @copyright walkor<walkor@workerman.net>
|
||||||
|
* @link http://www.workerman.net/
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
namespace PHPSocketIO\Engine\Protocols\WebSocket;
|
||||||
|
|
||||||
|
use Workerman\Connection\ConnectionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket 协议服务端解包和打包
|
||||||
|
*/
|
||||||
|
class RFC6455 implements \Workerman\Protocols\ProtocolInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* websocket头部最小长度
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
const MIN_HEAD_LEN = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* websocket blob类型
|
||||||
|
* @var char
|
||||||
|
*/
|
||||||
|
const BINARY_TYPE_BLOB = "\x81";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* websocket arraybuffer类型
|
||||||
|
* @var char
|
||||||
|
*/
|
||||||
|
const BINARY_TYPE_ARRAYBUFFER = "\x82";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查包的完整性
|
||||||
|
* @param string $buffer
|
||||||
|
*/
|
||||||
|
public static function input($buffer, ConnectionInterface $connection)
|
||||||
|
{
|
||||||
|
// 数据长度
|
||||||
|
$recv_len = strlen($buffer);
|
||||||
|
// 长度不够
|
||||||
|
if($recv_len < self::MIN_HEAD_LEN)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// $connection->websocketCurrentFrameLength有值说明当前fin为0,则缓冲websocket帧数据
|
||||||
|
if($connection->websocketCurrentFrameLength)
|
||||||
|
{
|
||||||
|
// 如果当前帧数据未收全,则继续收
|
||||||
|
if($connection->websocketCurrentFrameLength > $recv_len)
|
||||||
|
{
|
||||||
|
// 返回0,因为不清楚完整的数据包长度,需要等待fin=1的帧
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$data_len = ord($buffer[1]) & 127;
|
||||||
|
$firstbyte = ord($buffer[0]);
|
||||||
|
$is_fin_frame = $firstbyte>>7;
|
||||||
|
$opcode = $firstbyte & 0xf;
|
||||||
|
switch($opcode)
|
||||||
|
{
|
||||||
|
// 附加数据帧 @todo 实现附加数据帧
|
||||||
|
case 0x0:
|
||||||
|
break;
|
||||||
|
// 文本数据帧
|
||||||
|
case 0x1:
|
||||||
|
break;
|
||||||
|
// 二进制数据帧
|
||||||
|
case 0x2:
|
||||||
|
break;
|
||||||
|
// 关闭的包
|
||||||
|
case 0x8:
|
||||||
|
// 如果有设置onWebSocketClose回调,尝试执行
|
||||||
|
if(isset($connection->onWebSocketClose))
|
||||||
|
{
|
||||||
|
call_user_func($connection->onWebSocketClose, $connection);
|
||||||
|
}
|
||||||
|
// 默认行为是关闭连接
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$connection->close();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
// ping的包
|
||||||
|
case 0x9:
|
||||||
|
// 如果有设置onWebSocketPing回调,尝试执行
|
||||||
|
if(isset($connection->onWebSocketPing))
|
||||||
|
{
|
||||||
|
call_user_func($connection->onWebSocketPing, $connection);
|
||||||
|
}
|
||||||
|
// 默认发送pong
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$connection->send(pack('H*', '8a00'), true);
|
||||||
|
}
|
||||||
|
// 从接受缓冲区中消费掉该数据包
|
||||||
|
if(!$data_len)
|
||||||
|
{
|
||||||
|
$connection->consumeRecvBuffer(self::MIN_HEAD_LEN);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// pong的包
|
||||||
|
case 0xa:
|
||||||
|
// 如果有设置onWebSocketPong回调,尝试执行
|
||||||
|
if(isset($connection->onWebSocketPong))
|
||||||
|
{
|
||||||
|
call_user_func($connection->onWebSocketPong, $connection);
|
||||||
|
}
|
||||||
|
// 从接受缓冲区中消费掉该数据包
|
||||||
|
if(!$data_len)
|
||||||
|
{
|
||||||
|
$connection->consumeRecvBuffer(self::MIN_HEAD_LEN);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// 错误的opcode
|
||||||
|
default :
|
||||||
|
echo "error opcode $opcode and close websocket connection\n";
|
||||||
|
$connection->close();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// websocket二进制数据
|
||||||
|
$head_len = self::MIN_HEAD_LEN;
|
||||||
|
if ($data_len === 126) {
|
||||||
|
$head_len = 8;
|
||||||
|
if($head_len > $recv_len)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
$pack = unpack('ntotal_len', substr($buffer, 2, 2));
|
||||||
|
$data_len = $pack['total_len'];
|
||||||
|
} else if ($data_len === 127) {
|
||||||
|
$head_len = 14;
|
||||||
|
if($head_len > $recv_len)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
$arr = unpack('N2', substr($buffer, 2, 8));
|
||||||
|
$data_len = $arr[1]*4294967296 + $arr[2];
|
||||||
|
}
|
||||||
|
$current_frame_length = $head_len + $data_len;
|
||||||
|
if($is_fin_frame)
|
||||||
|
{
|
||||||
|
return $current_frame_length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$connection->websocketCurrentFrameLength = $current_frame_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收到的数据刚好是一个frame
|
||||||
|
if($connection->websocketCurrentFrameLength == $recv_len)
|
||||||
|
{
|
||||||
|
self::decode($buffer, $connection);
|
||||||
|
$connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
|
||||||
|
$connection->websocketCurrentFrameLength = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// 收到的数据大于一个frame
|
||||||
|
elseif($connection->websocketCurrentFrameLength < $recv_len)
|
||||||
|
{
|
||||||
|
self::decode(substr($buffer, 0, $connection->websocketCurrentFrameLength), $connection);
|
||||||
|
$connection->consumeRecvBuffer($connection->websocketCurrentFrameLength);
|
||||||
|
$current_frame_length = $connection->websocketCurrentFrameLength;
|
||||||
|
$connection->websocketCurrentFrameLength = 0;
|
||||||
|
// 继续读取下一个frame
|
||||||
|
return self::input(substr($buffer, $current_frame_length), $connection);
|
||||||
|
}
|
||||||
|
// 收到的数据不足一个frame
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打包
|
||||||
|
* @param string $buffer
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function encode($buffer, ConnectionInterface $connection)
|
||||||
|
{
|
||||||
|
$len = strlen($buffer);
|
||||||
|
if(empty($connection->websocketHandshake))
|
||||||
|
{
|
||||||
|
// 默认是utf8文本格式
|
||||||
|
$connection->websocketType = self::BINARY_TYPE_BLOB;
|
||||||
|
}
|
||||||
|
|
||||||
|
$first_byte = $connection->websocketType;
|
||||||
|
|
||||||
|
if($len<=125)
|
||||||
|
{
|
||||||
|
$encode_buffer = $first_byte.chr($len).$buffer;
|
||||||
|
}
|
||||||
|
else if($len<=65535)
|
||||||
|
{
|
||||||
|
$encode_buffer = $first_byte.chr(126).pack("n", $len).$buffer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$encode_buffer = $first_byte.chr(127).pack("xxxxN", $len).$buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 还没握手不能发数据,先将数据缓冲起来,等握手完毕后发送
|
||||||
|
if(empty($connection->websocketHandshake))
|
||||||
|
{
|
||||||
|
if(empty($connection->websocketTmpData))
|
||||||
|
{
|
||||||
|
// 临时数据缓冲
|
||||||
|
$connection->websocketTmpData = '';
|
||||||
|
}
|
||||||
|
$connection->websocketTmpData .= $encode_buffer;
|
||||||
|
// 返回空,阻止发送
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $encode_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解包
|
||||||
|
* @param string $buffer
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function decode($buffer, ConnectionInterface $connection)
|
||||||
|
{
|
||||||
|
$len = $masks = $data = $decoded = null;
|
||||||
|
$len = ord($buffer[1]) & 127;
|
||||||
|
if ($len === 126) {
|
||||||
|
$masks = substr($buffer, 4, 4);
|
||||||
|
$data = substr($buffer, 8);
|
||||||
|
} else if ($len === 127) {
|
||||||
|
$masks = substr($buffer, 10, 4);
|
||||||
|
$data = substr($buffer, 14);
|
||||||
|
} else {
|
||||||
|
$masks = substr($buffer, 2, 4);
|
||||||
|
$data = substr($buffer, 6);
|
||||||
|
}
|
||||||
|
for ($index = 0; $index < strlen($data); $index++) {
|
||||||
|
$decoded .= $data[$index] ^ $masks[$index % 4];
|
||||||
|
}
|
||||||
|
if($connection->websocketCurrentFrameLength)
|
||||||
|
{
|
||||||
|
$connection->websocketDataBuffer .= $decoded;
|
||||||
|
return $connection->websocketDataBuffer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$decoded = $connection->websocketDataBuffer . $decoded;
|
||||||
|
$connection->websocketDataBuffer = '';
|
||||||
|
return $decoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理websocket握手
|
||||||
|
* @param string $buffer
|
||||||
|
* @param TcpConnection $connection
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function dealHandshake($connection, $req, $res)
|
||||||
|
{
|
||||||
|
$headers = array();
|
||||||
|
if(isset($connection->onWebSocketConnect))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
call_user_func_array($connection->onWebSocketConnect, array($connection, $req, $res));
|
||||||
|
}
|
||||||
|
catch (\Exception $e)
|
||||||
|
{
|
||||||
|
echo $e;
|
||||||
|
}
|
||||||
|
if(!$res->writable)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($req->headers['sec-websocket-key']))
|
||||||
|
{
|
||||||
|
$sec_websocket_key = $req->headers['sec-websocket-key'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$res->writeHead(400);
|
||||||
|
$res->end('<b>400 Bad Request</b><br>Upgrade to websocket but Sec-WebSocket-Key not found.');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标记已经握手
|
||||||
|
$connection->websocketHandshake = true;
|
||||||
|
// 缓冲fin为0的包,直到fin为1
|
||||||
|
$connection->websocketDataBuffer = '';
|
||||||
|
// 当前数据帧的长度,可能是fin为0的帧,也可能是fin为1的帧
|
||||||
|
$connection->websocketCurrentFrameLength = 0;
|
||||||
|
// 当前帧的数据缓冲
|
||||||
|
$connection->websocketCurrentFrameBuffer = '';
|
||||||
|
// blob or arraybuffer
|
||||||
|
$connection->websocketType = self::BINARY_TYPE_BLOB;
|
||||||
|
|
||||||
|
$sec_websocket_accept = base64_encode(sha1($sec_websocket_key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11',true));
|
||||||
|
$headers['Content-Length'] = 0;
|
||||||
|
$headers['Upgrade'] = 'websocket';
|
||||||
|
$headers['Sec-WebSocket-Version'] = 13;
|
||||||
|
$headers['Connection'] = 'Upgrade';
|
||||||
|
$headers['Sec-WebSocket-Accept'] = $sec_websocket_accept;
|
||||||
|
$res->writeHead(101, '', $headers);
|
||||||
|
$res->end();
|
||||||
|
|
||||||
|
// 握手后有数据要发送
|
||||||
|
if(!empty($connection->websocketTmpData))
|
||||||
|
{
|
||||||
|
$connection->send($connection->websocketTmpData, true);
|
||||||
|
$connection->websocketTmpData = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
380
vendor/workerman/phpsocket.io/src/Engine/Socket.php
vendored
Normal file
380
vendor/workerman/phpsocket.io/src/Engine/Socket.php
vendored
Normal file
@ -0,0 +1,380 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO\Engine;
|
||||||
|
use \PHPSocketIO\Event\Emitter;
|
||||||
|
use \Workerman\Lib\Timer;
|
||||||
|
use \PHPSocketIO\Debug;
|
||||||
|
class Socket extends Emitter
|
||||||
|
{
|
||||||
|
public $id = 0;
|
||||||
|
public $server = null;
|
||||||
|
public $upgrading = false;
|
||||||
|
public $upgraded = false;
|
||||||
|
public $readyState = 'opening';
|
||||||
|
public $writeBuffer = array();
|
||||||
|
public $packetsFn = array();
|
||||||
|
public $sentCallbackFn = array();
|
||||||
|
public $request = null;
|
||||||
|
public $remoteAddress = '';
|
||||||
|
public $checkIntervalTimer = null;
|
||||||
|
public $upgradeTimeoutTimer = null;
|
||||||
|
public $pingTimeoutTimer = null;
|
||||||
|
|
||||||
|
public function __construct($id, $server, $transport, $req)
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
$this->server = $server;
|
||||||
|
$this->request = $req;
|
||||||
|
$this->remoteAddress = $req->connection->getRemoteIp().':'.$req->connection->getRemotePort();
|
||||||
|
$this->setTransport($transport);
|
||||||
|
$this->onOpen();
|
||||||
|
Debug::debug('Engine/Socket __construct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
Debug::debug('Engine/Socket __destruct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function maybeUpgrade($transport)
|
||||||
|
{
|
||||||
|
$this->upgrading = true;
|
||||||
|
$this->upgradeTimeoutTimer = Timer::add(
|
||||||
|
$this->server->upgradeTimeout,
|
||||||
|
array($this, 'upgradeTimeoutCallback'),
|
||||||
|
array($transport), false
|
||||||
|
);
|
||||||
|
$this->upgradeTransport = $transport;
|
||||||
|
$transport->on('packet', array($this, 'onUpgradePacket'));
|
||||||
|
$transport->once('close', array($this, 'onUpgradeTransportClose'));
|
||||||
|
$transport->once('error', array($this, 'onUpgradeTransportError'));
|
||||||
|
$this->once('close', array($this, 'onUpgradeTransportClose'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onUpgradePacket($packet)
|
||||||
|
{
|
||||||
|
if(empty($this->upgradeTransport))
|
||||||
|
{
|
||||||
|
$this->onError('upgradeTransport empty');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if('ping' === $packet['type'] && (isset($packet['data']) && 'probe' === $packet['data']))
|
||||||
|
{
|
||||||
|
$this->upgradeTransport->send(array(array('type'=> 'pong', 'data'=> 'probe')));
|
||||||
|
//$this->transport->shouldClose = function(){};
|
||||||
|
if ($this->checkIntervalTimer) {
|
||||||
|
Timer::del($this->checkIntervalTimer);
|
||||||
|
}
|
||||||
|
$this->checkIntervalTimer = Timer::add(0.5, array($this, 'check'));
|
||||||
|
}
|
||||||
|
else if('upgrade' === $packet['type'] && $this->readyState !== 'closed')
|
||||||
|
{
|
||||||
|
$this->upgradeCleanup();
|
||||||
|
$this->upgraded = true;
|
||||||
|
$this->clearTransport();
|
||||||
|
$this->transport->destroy();
|
||||||
|
$this->setTransport($this->upgradeTransport);
|
||||||
|
$this->emit('upgrade', $this->upgradeTransport);
|
||||||
|
$this->upgradeTransport = null;
|
||||||
|
$this->setPingTimeout();
|
||||||
|
$this->flush();
|
||||||
|
if($this->readyState === 'closing')
|
||||||
|
{
|
||||||
|
$this->transport->close(array($this, 'onClose'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!empty($this->upgradeTransport))
|
||||||
|
{
|
||||||
|
$this->upgradeCleanup();
|
||||||
|
$this->upgradeTransport->close();
|
||||||
|
$this->upgradeTransport = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function upgradeCleanup()
|
||||||
|
{
|
||||||
|
$this->upgrading = false;
|
||||||
|
Timer::del($this->checkIntervalTimer);
|
||||||
|
Timer::del($this->upgradeTimeoutTimer);
|
||||||
|
if(!empty($this->upgradeTransport))
|
||||||
|
{
|
||||||
|
$this->upgradeTransport->removeListener('packet', array($this, 'onUpgradePacket'));
|
||||||
|
$this->upgradeTransport->removeListener('close', array($this, 'onUpgradeTransportClose'));
|
||||||
|
$this->upgradeTransport->removeListener('error', array($this, 'onUpgradeTransportError'));
|
||||||
|
}
|
||||||
|
$this->removeListener('close', array($this, 'onUpgradeTransportClose'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onUpgradeTransportClose()
|
||||||
|
{
|
||||||
|
$this->onUpgradeTransportError('transport closed');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onUpgradeTransportError($err)
|
||||||
|
{
|
||||||
|
//echo $err;
|
||||||
|
$this->upgradeCleanup();
|
||||||
|
if($this->upgradeTransport)
|
||||||
|
{
|
||||||
|
$this->upgradeTransport->close();
|
||||||
|
$this->upgradeTransport = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function upgradeTimeoutCallback($transport)
|
||||||
|
{
|
||||||
|
//echo("client did not complete upgrade - closing transport\n");
|
||||||
|
$this->upgradeCleanup();
|
||||||
|
if('open' === $transport->readyState)
|
||||||
|
{
|
||||||
|
$transport->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTransport($transport)
|
||||||
|
{
|
||||||
|
$this->transport = $transport;
|
||||||
|
$this->transport->once('error', array($this, 'onError'));
|
||||||
|
$this->transport->on('packet', array($this, 'onPacket'));
|
||||||
|
$this->transport->on('drain', array($this, 'flush'));
|
||||||
|
$this->transport->once('close', array($this, 'onClose'));
|
||||||
|
//this function will manage packet events (also message callbacks)
|
||||||
|
$this->setupSendCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onOpen()
|
||||||
|
{
|
||||||
|
$this->readyState = 'open';
|
||||||
|
|
||||||
|
// sends an `open` packet
|
||||||
|
$this->transport->sid = $this->id;
|
||||||
|
$this->sendPacket('open', json_encode(array(
|
||||||
|
'sid'=> $this->id
|
||||||
|
, 'upgrades' => $this->getAvailableUpgrades()
|
||||||
|
, 'pingInterval'=> $this->server->pingInterval*1000
|
||||||
|
, 'pingTimeout'=> $this->server->pingTimeout*1000
|
||||||
|
)));
|
||||||
|
|
||||||
|
$this->emit('open');
|
||||||
|
$this->setPingTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onPacket($packet)
|
||||||
|
{
|
||||||
|
if ('open' === $this->readyState) {
|
||||||
|
// export packet event
|
||||||
|
$this->emit('packet', $packet);
|
||||||
|
|
||||||
|
// Reset ping timeout on any packet, incoming data is a good sign of
|
||||||
|
// other side's liveness
|
||||||
|
$this->setPingTimeout();
|
||||||
|
switch ($packet['type']) {
|
||||||
|
|
||||||
|
case 'ping':
|
||||||
|
$this->sendPacket('pong');
|
||||||
|
$this->emit('heartbeat');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'error':
|
||||||
|
$this->onClose('parse error');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'message':
|
||||||
|
$this->emit('data', $packet['data']);
|
||||||
|
$this->emit('message', $packet['data']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo('packet received with closed socket');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function check()
|
||||||
|
{
|
||||||
|
if('polling' == $this->transport->name && $this->transport->writable)
|
||||||
|
{
|
||||||
|
$this->transport->send(array(array('type' => 'noop')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onError($err)
|
||||||
|
{
|
||||||
|
$this->onClose('transport error', $err);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPingTimeout()
|
||||||
|
{
|
||||||
|
if ($this->pingTimeoutTimer) {
|
||||||
|
Timer::del($this->pingTimeoutTimer);
|
||||||
|
}
|
||||||
|
$this->pingTimeoutTimer = Timer::add(
|
||||||
|
$this->server->pingInterval + $this->server->pingTimeout ,
|
||||||
|
array($this, 'pingTimeoutCallback'), null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pingTimeoutCallback()
|
||||||
|
{
|
||||||
|
$this->transport->close();
|
||||||
|
$this->onClose('ping timeout');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function clearTransport()
|
||||||
|
{
|
||||||
|
$this->transport->close();
|
||||||
|
Timer::del($this->pingTimeoutTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onClose($reason = '', $description = null)
|
||||||
|
{
|
||||||
|
if ('closed' !== $this->readyState)
|
||||||
|
{
|
||||||
|
Timer::del($this->pingTimeoutTimer);
|
||||||
|
Timer::del($this->checkIntervalTimer);
|
||||||
|
$this->checkIntervalTimer = null;
|
||||||
|
Timer::del($this->upgradeTimeoutTimer);
|
||||||
|
// clean writeBuffer in next tick, so developers can still
|
||||||
|
// grab the writeBuffer on 'close' event
|
||||||
|
$this->writeBuffer = array();
|
||||||
|
$this->packetsFn = array();
|
||||||
|
$this->sentCallbackFn = array();
|
||||||
|
$this->clearTransport();
|
||||||
|
$this->readyState = 'closed';
|
||||||
|
$this->emit('close', $this->id, $reason, $description);
|
||||||
|
$this->server = null;
|
||||||
|
$this->request = null;
|
||||||
|
$this->upgradeTransport = null;
|
||||||
|
$this->removeAllListeners();
|
||||||
|
if(!empty($this->transport))
|
||||||
|
{
|
||||||
|
$this->transport->removeAllListeners();
|
||||||
|
$this->transport = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send($data, $options, $callback)
|
||||||
|
{
|
||||||
|
$this->sendPacket('message', $data, $callback);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write($data, $options = array(), $callback = null)
|
||||||
|
{
|
||||||
|
return $this->send($data, $options, $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sendPacket($type, $data = null, $callback = null)
|
||||||
|
{
|
||||||
|
if('closing' !== $this->readyState)
|
||||||
|
{
|
||||||
|
$packet = array(
|
||||||
|
'type'=> $type
|
||||||
|
);
|
||||||
|
if($data !== null)
|
||||||
|
{
|
||||||
|
$packet['data'] = $data;
|
||||||
|
}
|
||||||
|
// exports packetCreate event
|
||||||
|
$this->emit('packetCreate', $packet);
|
||||||
|
$this->writeBuffer[] = $packet;
|
||||||
|
//add send callback to object
|
||||||
|
if($callback)
|
||||||
|
{
|
||||||
|
$this->packetsFn[] = $callback;
|
||||||
|
}
|
||||||
|
$this->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function flush()
|
||||||
|
{
|
||||||
|
if ('closed' !== $this->readyState && $this->transport->writable
|
||||||
|
&& $this->writeBuffer)
|
||||||
|
{
|
||||||
|
$this->emit('flush', $this->writeBuffer);
|
||||||
|
$this->server->emit('flush', $this, $this->writeBuffer);
|
||||||
|
$wbuf = $this->writeBuffer;
|
||||||
|
$this->writeBuffer = array();
|
||||||
|
if($this->packetsFn)
|
||||||
|
{
|
||||||
|
if(!empty($this->transport->supportsFraming))
|
||||||
|
{
|
||||||
|
$this->sentCallbackFn[] = $this->packetsFn;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// @todo check
|
||||||
|
$this->sentCallbackFn[]=$this->packetsFn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->packetsFn = array();
|
||||||
|
$this->transport->send($wbuf);
|
||||||
|
$this->emit('drain');
|
||||||
|
if($this->server)
|
||||||
|
{
|
||||||
|
$this->server->emit('drain', $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAvailableUpgrades()
|
||||||
|
{
|
||||||
|
return array('websocket');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
if ('open' !== $this->readyState)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->readyState = 'closing';
|
||||||
|
|
||||||
|
if ($this->writeBuffer) {
|
||||||
|
$this->once('drain', array($this, 'closeTransport'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->closeTransport();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function closeTransport()
|
||||||
|
{
|
||||||
|
//todo onClose.bind(this, 'forced close'));
|
||||||
|
$this->transport->close(array($this, 'onClose'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setupSendCallback()
|
||||||
|
{
|
||||||
|
$self = $this;
|
||||||
|
//the message was sent successfully, execute the callback
|
||||||
|
$this->transport->on('drain', array($this, 'onDrainCallback'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onDrainCallback()
|
||||||
|
{
|
||||||
|
if ($this->sentCallbackFn)
|
||||||
|
{
|
||||||
|
$seqFn = array_shift($this->sentCallbackFn);
|
||||||
|
if(is_callable($seqFn))
|
||||||
|
{
|
||||||
|
echo('executing send callback');
|
||||||
|
call_user_func($seqFn, $this->transport);
|
||||||
|
}else if (is_array($seqFn)) {
|
||||||
|
echo('executing batch send callback');
|
||||||
|
foreach($seqFn as $fn)
|
||||||
|
{
|
||||||
|
call_user_func($fn, $this->transport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
vendor/workerman/phpsocket.io/src/Engine/Transport.php
vendored
Normal file
79
vendor/workerman/phpsocket.io/src/Engine/Transport.php
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO\Engine;
|
||||||
|
use \PHPSocketIO\Event\Emitter;
|
||||||
|
use \PHPSocketIO\Debug;
|
||||||
|
class Transport extends Emitter
|
||||||
|
{
|
||||||
|
public $readyState = 'opening';
|
||||||
|
public $req = null;
|
||||||
|
public $res = null;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
Debug::debug('Transport __construct no access !!!!');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
Debug::debug('Transport __destruct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function noop()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onRequest($req)
|
||||||
|
{
|
||||||
|
$this->req = $req;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close($fn = null)
|
||||||
|
{
|
||||||
|
$this->readyState = 'closing';
|
||||||
|
$fn = $fn ? $fn : array($this, 'noop');
|
||||||
|
$this->doClose($fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onError($msg, $desc = '')
|
||||||
|
{
|
||||||
|
if ($this->listeners('error'))
|
||||||
|
{
|
||||||
|
$err = array(
|
||||||
|
'type' => 'TransportError',
|
||||||
|
'description' => $desc,
|
||||||
|
);
|
||||||
|
$this->emit('error', $err);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo("ignored transport error $msg $desc\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onPacket($packet)
|
||||||
|
{
|
||||||
|
$this->emit('packet', $packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onData($data)
|
||||||
|
{
|
||||||
|
$this->onPacket(Parser::decodePacket($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onClose()
|
||||||
|
{
|
||||||
|
$this->req = $this->res = null;
|
||||||
|
$this->readyState = 'closed';
|
||||||
|
$this->emit('close');
|
||||||
|
$this->removeAllListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy()
|
||||||
|
{
|
||||||
|
$this->req = $this->res = null;
|
||||||
|
$this->readyState = 'closed';
|
||||||
|
$this->removeAllListeners();
|
||||||
|
$this->shouldClose = null;
|
||||||
|
}
|
||||||
|
}
|
208
vendor/workerman/phpsocket.io/src/Engine/Transports/Polling.php
vendored
Normal file
208
vendor/workerman/phpsocket.io/src/Engine/Transports/Polling.php
vendored
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO\Engine\Transports;
|
||||||
|
use PHPSocketIO\Engine\Transport;
|
||||||
|
use PHPSocketIO\Engine\Parser;
|
||||||
|
use \PHPSocketIO\Debug;
|
||||||
|
class Polling extends Transport
|
||||||
|
{
|
||||||
|
public $name = 'polling';
|
||||||
|
public $chunks = '';
|
||||||
|
public $shouldClose = null;
|
||||||
|
public $writable = false;
|
||||||
|
public function onRequest($req)
|
||||||
|
{
|
||||||
|
$res = $req->res;
|
||||||
|
|
||||||
|
if ('GET' === $req->method)
|
||||||
|
{
|
||||||
|
$this->onPollRequest($req, $res);
|
||||||
|
}
|
||||||
|
else if('POST' === $req->method)
|
||||||
|
{
|
||||||
|
$this->onDataRequest($req, $res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$res->writeHead(500);
|
||||||
|
$res->end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onPollRequest($req, $res)
|
||||||
|
{
|
||||||
|
if($this->req)
|
||||||
|
{
|
||||||
|
echo ('request overlap');
|
||||||
|
// assert: this.res, '.req and .res should be (un)set together'
|
||||||
|
$this->onError('overlap from client');
|
||||||
|
$res->writeHead(500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->req = $req;
|
||||||
|
$this->res = $res;
|
||||||
|
|
||||||
|
|
||||||
|
$req->onClose = array($this, 'pollRequestOnClose');
|
||||||
|
$req->cleanup = array($this, 'pollRequestClean');
|
||||||
|
|
||||||
|
$this->writable = true;
|
||||||
|
$this->emit('drain');
|
||||||
|
|
||||||
|
// if we're still writable but had a pending close, trigger an empty send
|
||||||
|
if ($this->writable && $this->shouldClose)
|
||||||
|
{
|
||||||
|
echo('triggering empty send to append close packet');
|
||||||
|
$this->send(array(array('type'=>'noop')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pollRequestOnClose()
|
||||||
|
{
|
||||||
|
$this->onError('poll connection closed prematurely');
|
||||||
|
$this->pollRequestClean();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pollRequestClean()
|
||||||
|
{
|
||||||
|
if(isset($this->req))
|
||||||
|
{
|
||||||
|
$this->req->res = null;
|
||||||
|
$this->req->onClose = $this->req->cleanup = null;
|
||||||
|
$this->req = $this->res = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onDataRequest($req, $res)
|
||||||
|
{
|
||||||
|
if(isset($this->dataReq))
|
||||||
|
{
|
||||||
|
// assert: this.dataRes, '.dataReq and .dataRes should be (un)set together'
|
||||||
|
$this->onError('data request overlap from client');
|
||||||
|
$res->writeHead(500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->dataReq = $req;
|
||||||
|
$this->dataRes = $res;
|
||||||
|
$req->onClose = array($this, 'dataRequestOnClose');
|
||||||
|
$req->onData = array($this, 'dataRequestOnData');
|
||||||
|
$req->onEnd = array($this, 'dataRequestOnEnd');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataRequestCleanup()
|
||||||
|
{
|
||||||
|
$this->chunks = '';
|
||||||
|
$this->dataReq->res = null;
|
||||||
|
$this->dataReq->onClose = $this->dataReq->onData = $this->dataReq->onEnd = null;
|
||||||
|
$this->dataReq = $this->dataRes = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataRequestOnClose()
|
||||||
|
{
|
||||||
|
$this->dataRequestCleanup();
|
||||||
|
$this->onError('data request connection closed prematurely');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataRequestOnData($req, $data)
|
||||||
|
{
|
||||||
|
$this->chunks .= $data;
|
||||||
|
// todo maxHttpBufferSize
|
||||||
|
/*if(strlen($this->chunks) > $this->maxHttpBufferSize)
|
||||||
|
{
|
||||||
|
$this->chunks = '';
|
||||||
|
$req->connection->destroy();
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataRequestOnEnd ()
|
||||||
|
{
|
||||||
|
$this->onData($this->chunks);
|
||||||
|
|
||||||
|
$headers = array(
|
||||||
|
'Content-Type'=> 'text/html',
|
||||||
|
'Content-Length'=> 2,
|
||||||
|
'X-XSS-Protection' => '0',
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->dataRes->writeHead(200, '', $this->headers($this->dataReq, $headers));
|
||||||
|
$this->dataRes->end('ok');
|
||||||
|
$this->dataRequestCleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onData($data)
|
||||||
|
{
|
||||||
|
$packets = Parser::decodePayload($data);
|
||||||
|
if(isset($packets['type']))
|
||||||
|
{
|
||||||
|
if('close' === $packets['type'])
|
||||||
|
{
|
||||||
|
$this->onClose();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$packets = array($packets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($packets as $packet)
|
||||||
|
{
|
||||||
|
$this->onPacket($packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onClose()
|
||||||
|
{
|
||||||
|
if($this->writable)
|
||||||
|
{
|
||||||
|
// close pending poll request
|
||||||
|
$this->send(array(array('type'=> 'noop')));
|
||||||
|
}
|
||||||
|
parent::onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send($packets)
|
||||||
|
{
|
||||||
|
$this->writable = false;
|
||||||
|
if($this->shouldClose)
|
||||||
|
{
|
||||||
|
echo('appending close packet to payload');
|
||||||
|
$packets[] = array('type'=>'close');
|
||||||
|
call_user_func($this->shouldClose);
|
||||||
|
$this->shouldClose = null;
|
||||||
|
}
|
||||||
|
$data = Parser::encodePayload($packets, $this->supportsBinary);
|
||||||
|
$this->write($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write($data)
|
||||||
|
{
|
||||||
|
$this->doWrite($data);
|
||||||
|
if(!empty($this->req->cleanup))
|
||||||
|
{
|
||||||
|
call_user_func($this->req->cleanup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doClose($fn)
|
||||||
|
{
|
||||||
|
if(!empty($this->dataReq))
|
||||||
|
{
|
||||||
|
//echo('aborting ongoing data request');
|
||||||
|
$this->dataReq->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->writable)
|
||||||
|
{
|
||||||
|
//echo('transport writable - closing right away');
|
||||||
|
$this->send(array(array('type'=> 'close')));
|
||||||
|
call_user_func($fn);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//echo("transport not writable - buffering orderly close\n");
|
||||||
|
$this->shouldClose = $fn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
vendor/workerman/phpsocket.io/src/Engine/Transports/PollingJsonp.php
vendored
Normal file
63
vendor/workerman/phpsocket.io/src/Engine/Transports/PollingJsonp.php
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO\Engine\Transports;
|
||||||
|
use \PHPSocketIO\Debug;
|
||||||
|
class PollingJsonp extends Polling
|
||||||
|
{
|
||||||
|
public $head = null;
|
||||||
|
public $foot = ');';
|
||||||
|
|
||||||
|
public function __construct($req)
|
||||||
|
{
|
||||||
|
$j = isset($req->_query['j']) ? preg_replace('/[^0-9]/', '', $req->_query['j']) : '';
|
||||||
|
$this->head = "___eio[ $j ](";
|
||||||
|
Debug::debug('PollingJsonp __construct');
|
||||||
|
}
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
Debug::debug('PollingJsonp __destruct');
|
||||||
|
}
|
||||||
|
public function onData($data)
|
||||||
|
{
|
||||||
|
$parsed_data = null;
|
||||||
|
parse_str($data, $parsed_data);
|
||||||
|
$data = $parsed_data['d'];
|
||||||
|
// todo check
|
||||||
|
//client will send already escaped newlines as \\\\n and newlines as \\n
|
||||||
|
// \\n must be replaced with \n and \\\\n with \\n
|
||||||
|
/*data = data.replace(rSlashes, function(match, slashes) {
|
||||||
|
return slashes ? match : '\n';
|
||||||
|
});*/
|
||||||
|
call_user_func(array($this, 'parent::onData'), preg_replace('/\\\\n/', '\\n', $data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doWrite($data)
|
||||||
|
{
|
||||||
|
$js = json_encode($data);
|
||||||
|
//$js = preg_replace(array('/\u2028/', '/\u2029/'), array('\\u2028', '\\u2029'), $js);
|
||||||
|
|
||||||
|
// prepare response
|
||||||
|
$data = $this->head . $js . $this->foot;
|
||||||
|
|
||||||
|
// explicit UTF-8 is required for pages not served under utf
|
||||||
|
$headers = array(
|
||||||
|
'Content-Type'=> 'text/javascript; charset=UTF-8',
|
||||||
|
'Content-Length'=> strlen($data),
|
||||||
|
'X-XSS-Protection'=>'0'
|
||||||
|
);
|
||||||
|
if(empty($this->res)){echo new \Exception('empty $this->res');return;}
|
||||||
|
$this->res->writeHead(200, '',$this->headers($this->req, $headers));
|
||||||
|
$this->res->end($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function headers($req, $headers = array())
|
||||||
|
{
|
||||||
|
$listeners = $this->listeners('headers');
|
||||||
|
foreach($listeners as $listener)
|
||||||
|
{
|
||||||
|
$listener($headers);
|
||||||
|
}
|
||||||
|
return $headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
70
vendor/workerman/phpsocket.io/src/Engine/Transports/PollingXHR.php
vendored
Normal file
70
vendor/workerman/phpsocket.io/src/Engine/Transports/PollingXHR.php
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO\Engine\Transports;
|
||||||
|
use \PHPSocketIO\Debug;
|
||||||
|
class PollingXHR extends Polling
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
Debug::debug('PollingXHR __construct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
Debug::debug('PollingXHR __destruct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onRequest($req)
|
||||||
|
{
|
||||||
|
if('OPTIONS' === $req->method)
|
||||||
|
{
|
||||||
|
$res = $req->res;
|
||||||
|
$headers = $this->headers($req);
|
||||||
|
$headers['Access-Control-Allow-Headers'] = 'Content-Type';
|
||||||
|
$res->writeHead(200, '', $headers);
|
||||||
|
$res->end();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parent::onRequest($req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doWrite($data)
|
||||||
|
{
|
||||||
|
// explicit UTF-8 is required for pages not served under utf todo
|
||||||
|
//$content_type = $isString
|
||||||
|
// ? 'text/plain; charset=UTF-8'
|
||||||
|
// : 'application/octet-stream';
|
||||||
|
$content_type = preg_match('/^\d+:/', $data) ? 'text/plain; charset=UTF-8' : 'application/octet-stream';
|
||||||
|
$content_length = strlen($data);
|
||||||
|
$headers = array(
|
||||||
|
'Content-Type'=> $content_type,
|
||||||
|
'Content-Length'=> $content_length,
|
||||||
|
'X-XSS-Protection' => '0',
|
||||||
|
);
|
||||||
|
if(empty($this->res)){echo new \Exception('empty this->res');return;}
|
||||||
|
$this->res->writeHead(200, '', $this->headers($this->req, $headers));
|
||||||
|
$this->res->end($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function headers($req, $headers = array())
|
||||||
|
{
|
||||||
|
if(isset($req->headers['origin']))
|
||||||
|
{
|
||||||
|
$headers['Access-Control-Allow-Credentials'] = 'true';
|
||||||
|
$headers['Access-Control-Allow-Origin'] = $req->headers['origin'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$headers['Access-Control-Allow-Origin'] = '*';
|
||||||
|
}
|
||||||
|
$listeners = $this->listeners('headers');
|
||||||
|
foreach($listeners as $listener)
|
||||||
|
{
|
||||||
|
$listener($headers);
|
||||||
|
}
|
||||||
|
return $headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
58
vendor/workerman/phpsocket.io/src/Engine/Transports/WebSocket.php
vendored
Normal file
58
vendor/workerman/phpsocket.io/src/Engine/Transports/WebSocket.php
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO\Engine\Transports;
|
||||||
|
use \PHPSocketIO\Engine\Transport;
|
||||||
|
use \PHPSocketIO\Engine\Parser;
|
||||||
|
use \PHPSocketIO\Debug;
|
||||||
|
class WebSocket extends Transport
|
||||||
|
{
|
||||||
|
public $writable = true;
|
||||||
|
public $supportsFraming = true;
|
||||||
|
public $supportsBinary = true;
|
||||||
|
public $name = 'websocket';
|
||||||
|
public function __construct($req)
|
||||||
|
{
|
||||||
|
$this->socket = $req->connection;
|
||||||
|
$this->socket->onMessage = array($this, 'onData2');
|
||||||
|
$this->socket->onClose = array($this, 'onClose');
|
||||||
|
$this->socket->onError = array($this, 'onError2');
|
||||||
|
Debug::debug('WebSocket __construct');
|
||||||
|
}
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
Debug::debug('WebSocket __destruct');
|
||||||
|
}
|
||||||
|
public function onData2($connection, $data)
|
||||||
|
{
|
||||||
|
call_user_func(array($this, 'parent::onData'), $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onError2($conection, $code, $msg)
|
||||||
|
{
|
||||||
|
call_user_func(array($this, 'parent::onClose'), $code, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send($packets)
|
||||||
|
{
|
||||||
|
foreach($packets as $packet)
|
||||||
|
{
|
||||||
|
$data = Parser::encodePacket($packet, $this->supportsBinary);
|
||||||
|
if ($this->socket) {
|
||||||
|
$this->socket->send($data);
|
||||||
|
$this->emit('drain');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doClose($fn = null)
|
||||||
|
{
|
||||||
|
if($this->socket)
|
||||||
|
{
|
||||||
|
$this->socket->close();
|
||||||
|
$this->socket = null;
|
||||||
|
if(!empty($fn))
|
||||||
|
{
|
||||||
|
call_user_func($fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
107
vendor/workerman/phpsocket.io/src/Event/Emitter.php
vendored
Normal file
107
vendor/workerman/phpsocket.io/src/Event/Emitter.php
vendored
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO\Event;
|
||||||
|
|
||||||
|
use PHPSocketIO\Debug;
|
||||||
|
|
||||||
|
class Emitter
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
Debug::debug('Emitter __construct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
Debug::debug('Emitter __destruct');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [event=>[[listener1, once?], [listener2,once?], ..], ..]
|
||||||
|
*/
|
||||||
|
protected $_eventListenerMap = array();
|
||||||
|
|
||||||
|
public function on($event_name, $listener)
|
||||||
|
{
|
||||||
|
$this->emit('newListener', $event_name, $listener);
|
||||||
|
$this->_eventListenerMap[$event_name][] = array($listener, 0);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function once($event_name, $listener)
|
||||||
|
{
|
||||||
|
$this->_eventListenerMap[$event_name][] = array($listener, 1);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeListener($event_name, $listener)
|
||||||
|
{
|
||||||
|
if(!isset($this->_eventListenerMap[$event_name]))
|
||||||
|
{
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
foreach($this->_eventListenerMap[$event_name] as $key=>$item)
|
||||||
|
{
|
||||||
|
if($item[0] === $listener)
|
||||||
|
{
|
||||||
|
$this->emit('removeListener', $event_name, $listener);
|
||||||
|
unset($this->_eventListenerMap[$event_name][$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(empty($this->_eventListenerMap[$event_name]))
|
||||||
|
{
|
||||||
|
unset($this->_eventListenerMap[$event_name]);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeAllListeners($event_name = null)
|
||||||
|
{
|
||||||
|
$this->emit('removeListener', $event_name);
|
||||||
|
if(null === $event_name)
|
||||||
|
{
|
||||||
|
$this->_eventListenerMap = array();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
unset($this->_eventListenerMap[$event_name]);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listeners($event_name)
|
||||||
|
{
|
||||||
|
if(empty($this->_eventListenerMap[$event_name]))
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
$listeners = array();
|
||||||
|
foreach($this->_eventListenerMap[$event_name] as $item)
|
||||||
|
{
|
||||||
|
$listeners[] = $item[0];
|
||||||
|
}
|
||||||
|
return $listeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function emit($event_name = null)
|
||||||
|
{
|
||||||
|
if(empty($event_name) || empty($this->_eventListenerMap[$event_name]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foreach($this->_eventListenerMap[$event_name] as $key=>$item)
|
||||||
|
{
|
||||||
|
$args = func_get_args();
|
||||||
|
unset($args[0]);
|
||||||
|
call_user_func_array($item[0], $args);
|
||||||
|
// once ?
|
||||||
|
if($item[1])
|
||||||
|
{
|
||||||
|
unset($this->_eventListenerMap[$event_name][$key]);
|
||||||
|
if(empty($this->_eventListenerMap[$event_name]))
|
||||||
|
{
|
||||||
|
unset($this->_eventListenerMap[$event_name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
163
vendor/workerman/phpsocket.io/src/Nsp.php
vendored
Normal file
163
vendor/workerman/phpsocket.io/src/Nsp.php
vendored
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO;
|
||||||
|
use PHPSocketIO\Event\Emitter;
|
||||||
|
use PHPSocketIO\Parser\Parser;
|
||||||
|
class Nsp extends Emitter
|
||||||
|
{
|
||||||
|
public $name = null;
|
||||||
|
public $server = null;
|
||||||
|
public $rooms = array();
|
||||||
|
public $flags = array();
|
||||||
|
public $sockets = array();
|
||||||
|
public $connected = array();
|
||||||
|
public $fns = array();
|
||||||
|
public $ids = 0;
|
||||||
|
public $acks = array();
|
||||||
|
public static $events = array(
|
||||||
|
'connect' => 'connect', // for symmetry with client
|
||||||
|
'connection' => 'connection',
|
||||||
|
'newListener' => 'newListener'
|
||||||
|
);
|
||||||
|
|
||||||
|
//public static $flags = array('json','volatile');
|
||||||
|
|
||||||
|
public function __construct($server, $name)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->server = $server;
|
||||||
|
$this->initAdapter();
|
||||||
|
Debug::debug('Nsp __construct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
Debug::debug('Nsp __destruct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initAdapter()
|
||||||
|
{
|
||||||
|
$adapter_name = $this->server->adapter();
|
||||||
|
$this->adapter = new $adapter_name($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function to($name)
|
||||||
|
{
|
||||||
|
if(!isset($this->rooms[$name]))
|
||||||
|
{
|
||||||
|
$this->rooms[$name] = $name;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function in($name)
|
||||||
|
{
|
||||||
|
return $this->to($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function add($client, $nsp, $fn)
|
||||||
|
{
|
||||||
|
$socket_name = $this->server->socket();
|
||||||
|
$socket = new $socket_name($this, $client);
|
||||||
|
if('open' === $client->conn->readyState)
|
||||||
|
{
|
||||||
|
$this->sockets[$socket->id]=$socket;
|
||||||
|
$socket->onconnect();
|
||||||
|
if(!empty($fn)) call_user_func($fn, $socket, $nsp);
|
||||||
|
$this->emit('connect', $socket);
|
||||||
|
$this->emit('connection', $socket);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo('next called after client was closed - ignoring socket');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a client. Called by each `Socket`.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function remove($socket)
|
||||||
|
{
|
||||||
|
// todo $socket->id
|
||||||
|
unset($this->sockets[$socket->id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits to all clients.
|
||||||
|
*
|
||||||
|
* @return {Namespace} self
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function emit($ev = null)
|
||||||
|
{
|
||||||
|
$args = func_get_args();
|
||||||
|
if (isset(self::$events[$ev]))
|
||||||
|
{
|
||||||
|
call_user_func_array(array(__CLASS__, 'parent::emit'), $args);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// set up packet object
|
||||||
|
|
||||||
|
$parserType = Parser::EVENT; // default
|
||||||
|
//if (self::hasBin($args)) { $parserType = Parser::BINARY_EVENT; } // binary
|
||||||
|
|
||||||
|
$packet = array('type'=> $parserType, 'data'=> $args );
|
||||||
|
|
||||||
|
if (is_callable(end($args)))
|
||||||
|
{
|
||||||
|
echo('Callbacks are not supported when broadcasting');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->adapter->broadcast($packet, array(
|
||||||
|
'rooms'=> $this->rooms,
|
||||||
|
'flags'=> $this->flags
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->rooms = array();
|
||||||
|
$this->flags = array();;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send()
|
||||||
|
{
|
||||||
|
$args = func_get_args();
|
||||||
|
array_unshift($args, 'message');
|
||||||
|
$this->emit($args);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write()
|
||||||
|
{
|
||||||
|
$args = func_get_args();
|
||||||
|
return call_user_func_array(array($this, 'send'), $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clients($fn)
|
||||||
|
{
|
||||||
|
$this->adapter->clients($this->rooms, $fn);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the compress flag.
|
||||||
|
*
|
||||||
|
* @param {Boolean} if `true`, compresses the sending data
|
||||||
|
* @return {Socket} self
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function compress($compress)
|
||||||
|
{
|
||||||
|
$this->flags['compress'] = $compress;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
140
vendor/workerman/phpsocket.io/src/Parser/Decoder.php
vendored
Normal file
140
vendor/workerman/phpsocket.io/src/Parser/Decoder.php
vendored
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO\Parser;
|
||||||
|
use \PHPSocketIO\Parser\Parser;
|
||||||
|
use \PHPSocketIO\Event\Emitter;
|
||||||
|
use \PHPSocketIO\Debug;
|
||||||
|
class Decoder extends Emitter
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
Debug::debug('Decoder __construct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
Debug::debug('Decoder __destruct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add($obj)
|
||||||
|
{
|
||||||
|
if (is_string($obj))
|
||||||
|
{
|
||||||
|
$packet = self::decodeString($obj);
|
||||||
|
if(Parser::BINARY_EVENT == $packet['type'] || Parser::BINARY_ACK == $packet['type'])
|
||||||
|
{
|
||||||
|
// binary packet's json todo BinaryReconstructor
|
||||||
|
$this->reconstructor = new BinaryReconstructor(packet);
|
||||||
|
|
||||||
|
// no attachments, labeled binary but no binary data to follow
|
||||||
|
if ($this->reconstructor->reconPack->attachments === 0)
|
||||||
|
{
|
||||||
|
$this->emit('decoded', $packet);
|
||||||
|
}
|
||||||
|
} else { // non-binary full packet
|
||||||
|
$this->emit('decoded', $packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isBuf($obj) || !empty($obj['base64']))
|
||||||
|
{ // raw binary data
|
||||||
|
if (!$this->reconstructor)
|
||||||
|
{
|
||||||
|
throw new \Exception('got binary data when not reconstructing a packet');
|
||||||
|
} else {
|
||||||
|
$packet = $this->reconstructor->takeBinaryData($obj);
|
||||||
|
if ($packet)
|
||||||
|
{ // received final buffer
|
||||||
|
$this->reconstructor = null;
|
||||||
|
$this->emit('decoded', $packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new \Exception('Unknown type: ' + obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function decodeString($str)
|
||||||
|
{
|
||||||
|
$p = array();
|
||||||
|
$i = 0;
|
||||||
|
|
||||||
|
// look up type
|
||||||
|
$p['type'] = $str[0];
|
||||||
|
if(!isset(Parser::$types[$p['type']])) return self::error();
|
||||||
|
|
||||||
|
// look up attachments if type binary
|
||||||
|
if(Parser::BINARY_EVENT == $p['type'] || Parser::BINARY_ACK == $p['type'])
|
||||||
|
{
|
||||||
|
$buf = '';
|
||||||
|
while ($str[++$i] != '-')
|
||||||
|
{
|
||||||
|
$buf .= $str[$i];
|
||||||
|
if($i == strlen(str)) break;
|
||||||
|
}
|
||||||
|
if ($buf != intval($buf) || $str[$i] != '-')
|
||||||
|
{
|
||||||
|
throw new \Exception('Illegal attachments');
|
||||||
|
}
|
||||||
|
$p['attachments'] = intval($buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// look up namespace (if any)
|
||||||
|
if(isset($str[$i + 1]) && '/' === $str[$i + 1])
|
||||||
|
{
|
||||||
|
$p['nsp'] = '';
|
||||||
|
while (++$i)
|
||||||
|
{
|
||||||
|
if ($i === strlen($str)) break;
|
||||||
|
$c = $str[$i];
|
||||||
|
if (',' === $c) break;
|
||||||
|
$p['nsp'] .= $c;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$p['nsp'] = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
// look up id
|
||||||
|
if(isset($str[$i+1]))
|
||||||
|
{
|
||||||
|
$next = $str[$i+1];
|
||||||
|
if ('' !== $next && strval((int)$next) === strval($next))
|
||||||
|
{
|
||||||
|
$p['id'] = '';
|
||||||
|
while (++$i)
|
||||||
|
{
|
||||||
|
$c = $str[$i];
|
||||||
|
if (null == $c || strval((int)$c) != strval($c))
|
||||||
|
{
|
||||||
|
--$i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$p['id'] .= $str[$i];
|
||||||
|
if($i == strlen($str)) break;
|
||||||
|
}
|
||||||
|
$p['id'] = (int)$p['id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// look up json data
|
||||||
|
if (isset($str[++$i]))
|
||||||
|
{
|
||||||
|
// todo try
|
||||||
|
$p['data'] = json_decode(substr($str, $i), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $p;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function error()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'type'=> Parser::ERROR,
|
||||||
|
'data'=> 'parser error'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
76
vendor/workerman/phpsocket.io/src/Parser/Encoder.php
vendored
Normal file
76
vendor/workerman/phpsocket.io/src/Parser/Encoder.php
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO\Parser;
|
||||||
|
use \PHPSocketIO\Parser\Parser;
|
||||||
|
use \PHPSocketIO\Event\Emitter;
|
||||||
|
use \PHPSocketIO\Debug;
|
||||||
|
class Encoder extends Emitter
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
Debug::debug('Encoder __construct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
Debug::debug('Encoder __destruct');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function encode($obj)
|
||||||
|
{
|
||||||
|
if(Parser::BINARY_EVENT == $obj['type'] || Parser::BINARY_ACK == $obj['type'])
|
||||||
|
{
|
||||||
|
echo new \Exception("not support BINARY_EVENT BINARY_ACK");
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$encoding = self::encodeAsString($obj);
|
||||||
|
return array($encoding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function encodeAsString($obj) {
|
||||||
|
$str = '';
|
||||||
|
$nsp = false;
|
||||||
|
|
||||||
|
// first is type
|
||||||
|
$str .= $obj['type'];
|
||||||
|
|
||||||
|
// attachments if we have them
|
||||||
|
if (Parser::BINARY_EVENT == $obj['type'] || Parser::BINARY_ACK == $obj['type'])
|
||||||
|
{
|
||||||
|
$str .= $obj['attachments'];
|
||||||
|
$str .= '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have a namespace other than `/`
|
||||||
|
// we append it followed by a comma `,`
|
||||||
|
if (!empty($obj['nsp']) && '/' !== $obj['nsp'])
|
||||||
|
{
|
||||||
|
$nsp = true;
|
||||||
|
$str .= $obj['nsp'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// immediately followed by the id
|
||||||
|
if (isset($obj['id']))
|
||||||
|
{
|
||||||
|
if($nsp)
|
||||||
|
{
|
||||||
|
$str .= ',';
|
||||||
|
$nsp = false;
|
||||||
|
}
|
||||||
|
$str .= $obj['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// json data
|
||||||
|
if(isset($obj['data']))
|
||||||
|
{
|
||||||
|
if ($nsp) $str .= ',';
|
||||||
|
$str .= json_encode($obj['data']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $str;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
63
vendor/workerman/phpsocket.io/src/Parser/Parser.php
vendored
Normal file
63
vendor/workerman/phpsocket.io/src/Parser/Parser.php
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO\Parser;
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Packet type `connect`.
|
||||||
|
*
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
const CONNECT = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet type `disconnect`.
|
||||||
|
*
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
const DISCONNECT = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet type `event`.
|
||||||
|
*
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
const EVENT = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet type `ack`.
|
||||||
|
*
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
const ACK = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet type `error`.
|
||||||
|
*
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
const ERROR = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet type 'binary event'
|
||||||
|
*
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
const BINARY_EVENT = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet type `binary ack`. For acks with binary arguments.
|
||||||
|
*
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
const BINARY_ACK = 6;
|
||||||
|
|
||||||
|
public static $types = array(
|
||||||
|
'CONNECT',
|
||||||
|
'DISCONNECT',
|
||||||
|
'EVENT',
|
||||||
|
'BINARY_EVENT',
|
||||||
|
'ACK',
|
||||||
|
'BINARY_ACK',
|
||||||
|
'ERROR'
|
||||||
|
);
|
||||||
|
}
|
473
vendor/workerman/phpsocket.io/src/Socket.php
vendored
Normal file
473
vendor/workerman/phpsocket.io/src/Socket.php
vendored
Normal file
@ -0,0 +1,473 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO;
|
||||||
|
use PHPSocketIO\Event\Emitter;
|
||||||
|
use PHPSocketIO\Parser\Parser;
|
||||||
|
class Socket extends Emitter
|
||||||
|
{
|
||||||
|
public $nsp = null;
|
||||||
|
public $server = null;
|
||||||
|
public $adapter = null;
|
||||||
|
public $id = null;
|
||||||
|
public $path = '/';
|
||||||
|
public $request = null;
|
||||||
|
public $client = null;
|
||||||
|
public $conn = null;
|
||||||
|
public $rooms = array();
|
||||||
|
public $_rooms = array();
|
||||||
|
public $flags = array();
|
||||||
|
public $acks = array();
|
||||||
|
public $connected = true;
|
||||||
|
public $disconnected = false;
|
||||||
|
|
||||||
|
public static $events = array(
|
||||||
|
'error'=>'error',
|
||||||
|
'connect' => 'connect',
|
||||||
|
'disconnect' => 'disconnect',
|
||||||
|
'newListener' => 'newListener',
|
||||||
|
'removeListener' => 'removeListener'
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $flagsMap = array(
|
||||||
|
'json' => 'json',
|
||||||
|
'volatile' => 'volatile',
|
||||||
|
'broadcast' => 'broadcast'
|
||||||
|
);
|
||||||
|
|
||||||
|
public function __construct($nsp, $client)
|
||||||
|
{
|
||||||
|
$this->nsp = $nsp;
|
||||||
|
$this->server = $nsp->server;
|
||||||
|
$this->adapter = $this->nsp->adapter;
|
||||||
|
$this->id = ($nsp->name !== '/') ? $nsp->name .'#' .$client->id : $client->id;
|
||||||
|
$this->request = $client->request;
|
||||||
|
$this->client = $client;
|
||||||
|
$this->conn = $client->conn;
|
||||||
|
$this->handshake = $this->buildHandshake();
|
||||||
|
Debug::debug('IO Socket __construct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
Debug::debug('IO Socket __destruct');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildHandshake()
|
||||||
|
{
|
||||||
|
//todo check this->request->_query
|
||||||
|
$info = !empty($this->request->url) ? parse_url($this->request->url) : array();
|
||||||
|
$query = array();
|
||||||
|
if(isset($info['query']))
|
||||||
|
{
|
||||||
|
parse_str($info['query'], $query);
|
||||||
|
}
|
||||||
|
return array(
|
||||||
|
'headers' => isset($this->request->headers) ? $this->request->headers : array(),
|
||||||
|
'time'=> date('D M d Y H:i:s') . ' GMT',
|
||||||
|
'address'=> $this->conn->remoteAddress,
|
||||||
|
'xdomain'=> isset($this->request->headers['origin']),
|
||||||
|
'secure' => !empty($this->request->connection->encrypted),
|
||||||
|
'issued' => time(),
|
||||||
|
'url' => isset($this->request->url) ? $this->request->url : '',
|
||||||
|
'query' => $query,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($name)
|
||||||
|
{
|
||||||
|
if($name === 'broadcast')
|
||||||
|
{
|
||||||
|
$this->flags['broadcast'] = true;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function emit($ev = null)
|
||||||
|
{
|
||||||
|
$args = func_get_args();
|
||||||
|
if (isset(self::$events[$ev]))
|
||||||
|
{
|
||||||
|
call_user_func_array(array(__CLASS__, 'parent::emit'), $args);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$packet = array();
|
||||||
|
// todo check
|
||||||
|
//$packet['type'] = hasBin($args) ? Parser::BINARY_EVENT : Parser::EVENT;
|
||||||
|
$packet['type'] = Parser::EVENT;
|
||||||
|
$packet['data'] = $args;
|
||||||
|
$flags = $this->flags;
|
||||||
|
// access last argument to see if it's an ACK callback
|
||||||
|
if (is_callable(end($args)))
|
||||||
|
{
|
||||||
|
if ($this->_rooms || isset($flags['broadcast']))
|
||||||
|
{
|
||||||
|
throw new \Exception('Callbacks are not supported when broadcasting');
|
||||||
|
}
|
||||||
|
echo('emitting packet with ack id ' . $this->nsp->ids);
|
||||||
|
$this->acks[$this->nsp->ids] = array_pop($args);
|
||||||
|
$packet['id'] = $this->nsp->ids++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->_rooms || !empty($flags['broadcast']))
|
||||||
|
{
|
||||||
|
$this->adapter->broadcast($packet, array(
|
||||||
|
'except' => array($this->id => $this->id),
|
||||||
|
'rooms'=> $this->_rooms,
|
||||||
|
'flags' => $flags
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// dispatch packet
|
||||||
|
$this->packet($packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset flags
|
||||||
|
$this->_rooms = array();
|
||||||
|
$this->flags = array();
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Targets a room when broadcasting.
|
||||||
|
*
|
||||||
|
* @param {String} name
|
||||||
|
* @return {Socket} self
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function to($name)
|
||||||
|
{
|
||||||
|
if(!isset($this->_rooms[$name]))
|
||||||
|
{
|
||||||
|
$this->_rooms[$name] = $name;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function in($name)
|
||||||
|
{
|
||||||
|
return $this->to($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a `message` event.
|
||||||
|
*
|
||||||
|
* @return {Socket} self
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function send()
|
||||||
|
{
|
||||||
|
$args = func_get_args();
|
||||||
|
array_unshift($args, 'message');
|
||||||
|
call_user_func_array(array($this, 'emit'), $args);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write()
|
||||||
|
{
|
||||||
|
$args = func_get_args();
|
||||||
|
array_unshift($args, 'message');
|
||||||
|
call_user_func_array(array($this, 'emit'), $args);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a packet.
|
||||||
|
*
|
||||||
|
* @param {Object} packet object
|
||||||
|
* @param {Object} options
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function packet($packet, $preEncoded = false)
|
||||||
|
{
|
||||||
|
if (!$this->nsp || !$this->client) return;
|
||||||
|
$packet['nsp'] = $this->nsp->name;
|
||||||
|
//$volatile = !empty(self::$flagsMap['volatile']);
|
||||||
|
$volatile = false;
|
||||||
|
$this->client->packet($packet, $preEncoded, $volatile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Joins a room.
|
||||||
|
*
|
||||||
|
* @param {String} room
|
||||||
|
* @param {Function} optional, callback
|
||||||
|
* @return {Socket} self
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function join($room)
|
||||||
|
{
|
||||||
|
if (!$this->connected) return $this;
|
||||||
|
if(isset($this->rooms[$room])) return $this;
|
||||||
|
$this->adapter->add($this->id, $room);
|
||||||
|
$this->rooms[$room] = $room;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leaves a room.
|
||||||
|
*
|
||||||
|
* @param {String} room
|
||||||
|
* @param {Function} optional, callback
|
||||||
|
* @return {Socket} self
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function leave($room)
|
||||||
|
{
|
||||||
|
$this->adapter->del($this->id, $room);
|
||||||
|
unset($this->rooms[$room]);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leave all rooms.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function leaveAll()
|
||||||
|
{
|
||||||
|
$this->adapter->delAll($this->id);
|
||||||
|
$this->rooms = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by `Namespace` upon succesful
|
||||||
|
* middleware execution (ie: authorization).
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function onconnect()
|
||||||
|
{
|
||||||
|
$this->nsp->connected[$this->id] = $this;
|
||||||
|
$this->join($this->id);
|
||||||
|
$this->packet(array(
|
||||||
|
'type' => Parser::CONNECT)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called with each packet. Called by `Client`.
|
||||||
|
*
|
||||||
|
* @param {Object} packet
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function onpacket($packet)
|
||||||
|
{
|
||||||
|
switch ($packet['type'])
|
||||||
|
{
|
||||||
|
case Parser::EVENT:
|
||||||
|
$this->onevent($packet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Parser::BINARY_EVENT:
|
||||||
|
$this->onevent($packet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Parser::ACK:
|
||||||
|
$this->onack($packet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Parser::BINARY_ACK:
|
||||||
|
$this->onack($packet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Parser::DISCONNECT:
|
||||||
|
$this->ondisconnect();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Parser::ERROR:
|
||||||
|
$this->emit('error', $packet['data']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon event packet.
|
||||||
|
*
|
||||||
|
* @param {Object} packet object
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function onevent($packet)
|
||||||
|
{
|
||||||
|
$args = isset($packet['data']) ? $packet['data'] : array();
|
||||||
|
if (!empty($packet['id']) || (isset($packet['id']) && $packet['id'] === 0))
|
||||||
|
{
|
||||||
|
$args[] = $this->ack($packet['id']);
|
||||||
|
}
|
||||||
|
call_user_func_array(array(__CLASS__, 'parent::emit'), $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces an ack callback to emit with an event.
|
||||||
|
*
|
||||||
|
* @param {Number} packet id
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function ack($id)
|
||||||
|
{
|
||||||
|
$self = $this;
|
||||||
|
$sent = false;
|
||||||
|
return function()use(&$sent, $id, $self){
|
||||||
|
// prevent double callbacks
|
||||||
|
if ($sent) return;
|
||||||
|
$args = func_get_args();
|
||||||
|
$type = $this->hasBin($args) ? Parser::BINARY_ACK : Parser::ACK;
|
||||||
|
$self->packet(array(
|
||||||
|
'id' => $id,
|
||||||
|
'type' => $type,
|
||||||
|
'data' => $args
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon ack packet.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function onack($packet)
|
||||||
|
{
|
||||||
|
$ack = $this->acks[$packet['id']];
|
||||||
|
if (is_callable($ack))
|
||||||
|
{
|
||||||
|
call_user_func($ack, $packet['data']);
|
||||||
|
unset($this->acks[$packet['id']]);
|
||||||
|
} else {
|
||||||
|
echo ('bad ack '. packet.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon client disconnect packet.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function ondisconnect()
|
||||||
|
{
|
||||||
|
//echo('got disconnect packet');
|
||||||
|
$this->onclose('client namespace disconnect');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a client error.
|
||||||
|
*
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function onerror($err)
|
||||||
|
{
|
||||||
|
if ($this->listeners('error'))
|
||||||
|
{
|
||||||
|
$this->emit('error', $err);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//echo('Missing error handler on `socket`.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called upon closing. Called by `Client`.
|
||||||
|
*
|
||||||
|
* @param {String} reason
|
||||||
|
* @param {Error} optional error object
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function onclose($reason)
|
||||||
|
{
|
||||||
|
if (!$this->connected) return $this;
|
||||||
|
$this->emit('disconnect', $reason);
|
||||||
|
$this->leaveAll();
|
||||||
|
$this->nsp->remove($this);
|
||||||
|
$this->client->remove($this);
|
||||||
|
$this->connected = false;
|
||||||
|
$this->disconnected = true;
|
||||||
|
unset($this->nsp->connected[$this->id]);
|
||||||
|
// ....
|
||||||
|
$this->nsp = null;
|
||||||
|
$this->server = null;
|
||||||
|
$this->adapter = null;
|
||||||
|
$this->request = null;
|
||||||
|
$this->client = null;
|
||||||
|
$this->conn = null;
|
||||||
|
$this->removeAllListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces an `error` packet.
|
||||||
|
*
|
||||||
|
* @param {Object} error object
|
||||||
|
* @api private
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function error($err)
|
||||||
|
{
|
||||||
|
$this->packet(array(
|
||||||
|
'type' => Parser::ERROR, 'data' => $err )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects this client.
|
||||||
|
*
|
||||||
|
* @param {Boolean} if `true`, closes the underlying connection
|
||||||
|
* @return {Socket} self
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function disconnect( $close = false )
|
||||||
|
{
|
||||||
|
if (!$this->connected) return $this;
|
||||||
|
if ($close)
|
||||||
|
{
|
||||||
|
$this->client->disconnect();
|
||||||
|
} else {
|
||||||
|
$this->packet(array(
|
||||||
|
'type'=> Parser::DISCONNECT
|
||||||
|
));
|
||||||
|
$this->onclose('server namespace disconnect');
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the compress flag.
|
||||||
|
*
|
||||||
|
* @param {Boolean} if `true`, compresses the sending data
|
||||||
|
* @return {Socket} self
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function compress($compress)
|
||||||
|
{
|
||||||
|
$this->flags['compress'] = $compress;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function hasBin($args) {
|
||||||
|
$hasBin = false;
|
||||||
|
|
||||||
|
array_walk_recursive($args, function($item, $key) use ($hasBin) {
|
||||||
|
if (!ctype_print($item)) {
|
||||||
|
$hasBin = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return $hasBin;
|
||||||
|
}
|
||||||
|
}
|
171
vendor/workerman/phpsocket.io/src/SocketIO.php
vendored
Normal file
171
vendor/workerman/phpsocket.io/src/SocketIO.php
vendored
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
<?php
|
||||||
|
namespace PHPSocketIO;
|
||||||
|
use Workerman\Worker;
|
||||||
|
use PHPSocketIO\Engine\Engine;
|
||||||
|
class SocketIO
|
||||||
|
{
|
||||||
|
public $nsps = array();
|
||||||
|
protected $_nsp = null;
|
||||||
|
protected $_socket = null;
|
||||||
|
protected $_adapter = null;
|
||||||
|
public $eio = null;
|
||||||
|
public $engine = null;
|
||||||
|
protected $_origins = '*:*';
|
||||||
|
protected $_path = null;
|
||||||
|
|
||||||
|
public function __construct($port = null, $opts = array())
|
||||||
|
{
|
||||||
|
$nsp = isset($opts['nsp']) ? $opts['nsp'] : '\PHPSocketIO\Nsp';
|
||||||
|
$this->nsp($nsp);
|
||||||
|
|
||||||
|
$socket = isset($opts['socket']) ? $opts['socket'] : '\PHPSocketIO\Socket';
|
||||||
|
$this->socket($socket);
|
||||||
|
|
||||||
|
$adapter = isset($opts['adapter']) ? $opts['adapter'] : '\PHPSocketIO\DefaultAdapter';
|
||||||
|
$this->adapter($adapter);
|
||||||
|
if(isset($opts['origins']))
|
||||||
|
{
|
||||||
|
$this->origins($opts['origins']);
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($opts['nsp'], $opts['socket'], $opts['adapter'], $opts['origins']);
|
||||||
|
|
||||||
|
$this->sockets = $this->of('/');
|
||||||
|
|
||||||
|
if(!class_exists('Protocols\SocketIO'))
|
||||||
|
{
|
||||||
|
class_alias('PHPSocketIO\Engine\Protocols\SocketIO', 'Protocols\SocketIO');
|
||||||
|
}
|
||||||
|
if($port)
|
||||||
|
{
|
||||||
|
$worker = new Worker('SocketIO://0.0.0.0:'.$port, $opts);
|
||||||
|
$worker->name = 'PHPSocketIO';
|
||||||
|
|
||||||
|
if(isset($opts['ssl'])) {
|
||||||
|
$worker->transport = 'ssl';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->attach($worker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function nsp($v = null)
|
||||||
|
{
|
||||||
|
if (empty($v)) return $this->_nsp;
|
||||||
|
$this->_nsp = $v;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function socket($v = null)
|
||||||
|
{
|
||||||
|
if (empty($v)) return $this->_socket;
|
||||||
|
$this->_socket = $v;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function adapter($v = null)
|
||||||
|
{
|
||||||
|
if (empty($v)) return $this->_adapter;
|
||||||
|
$this->_adapter = $v;
|
||||||
|
foreach($this->nsps as $nsp)
|
||||||
|
{
|
||||||
|
$nsp->initAdapter();
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function origins($v = null)
|
||||||
|
{
|
||||||
|
if ($v === null) return $this->_origins;
|
||||||
|
$this->_origins = $v;
|
||||||
|
if(isset($this->engine)) {
|
||||||
|
$this->engine->origins = $this->_origins;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attach($srv, $opts = array())
|
||||||
|
{
|
||||||
|
$engine = new Engine();
|
||||||
|
$this->eio = $engine->attach($srv, $opts);
|
||||||
|
|
||||||
|
// Export http server
|
||||||
|
$this->worker = $srv;
|
||||||
|
|
||||||
|
// bind to engine events
|
||||||
|
$this->bind($engine);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bind($engine)
|
||||||
|
{
|
||||||
|
$this->engine = $engine;
|
||||||
|
$this->engine->on('connection', array($this, 'onConnection'));
|
||||||
|
$this->engine->origins = $this->_origins;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function of($name, $fn = null)
|
||||||
|
{
|
||||||
|
if($name[0] !== '/')
|
||||||
|
{
|
||||||
|
$name = "/$name";
|
||||||
|
}
|
||||||
|
if(empty($this->nsps[$name]))
|
||||||
|
{
|
||||||
|
$nsp_name = $this->nsp();
|
||||||
|
$this->nsps[$name] = new $nsp_name($this, $name);
|
||||||
|
}
|
||||||
|
if ($fn)
|
||||||
|
{
|
||||||
|
$this->nsps[$name]->on('connect', $fn);
|
||||||
|
}
|
||||||
|
return $this->nsps[$name];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onConnection($engine_socket)
|
||||||
|
{
|
||||||
|
$client = new Client($this, $engine_socket);
|
||||||
|
$client->connect('/');
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function on()
|
||||||
|
{
|
||||||
|
$args = array_pad(func_get_args(), 2, null);
|
||||||
|
|
||||||
|
if ($args[0] === 'workerStart') {
|
||||||
|
$this->worker->onWorkerStart = $args[1];
|
||||||
|
} else if ($args[0] === 'workerStop') {
|
||||||
|
$this->worker->onWorkerStop = $args[1];
|
||||||
|
} else if ($args[0] !== null) {
|
||||||
|
return call_user_func_array(array($this->sockets, 'on'), $args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function in()
|
||||||
|
{
|
||||||
|
return call_user_func_array(array($this->sockets, 'in'), func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function to()
|
||||||
|
{
|
||||||
|
return call_user_func_array(array($this->sockets, 'to'), func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function emit()
|
||||||
|
{
|
||||||
|
return call_user_func_array(array($this->sockets, 'emit'), func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send()
|
||||||
|
{
|
||||||
|
return call_user_func_array(array($this->sockets, 'send'), func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write()
|
||||||
|
{
|
||||||
|
return call_user_func_array(array($this->sockets, 'write'), func_get_args());
|
||||||
|
}
|
||||||
|
}
|
15
vendor/workerman/phpsocket.io/src/autoload.php
vendored
Normal file
15
vendor/workerman/phpsocket.io/src/autoload.php
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
spl_autoload_register(function($name){
|
||||||
|
$path = str_replace('\\', DIRECTORY_SEPARATOR ,$name);
|
||||||
|
$path = str_replace('PHPSocketIO', '', $path);
|
||||||
|
if(is_file($class_file = __DIR__ . "/$path.php"))
|
||||||
|
{
|
||||||
|
require_once($class_file);
|
||||||
|
if(class_exists($name, false))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
28
vendor/workerman/phpsocket.io/tests/emitter.php
vendored
Normal file
28
vendor/workerman/phpsocket.io/tests/emitter.php
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
$rootPath = join(DIRECTORY_SEPARATOR, array(__DIR__,".."));
|
||||||
|
include join(DIRECTORY_SEPARATOR, array($rootPath,"vendor","autoload.php"));
|
||||||
|
include join(DIRECTORY_SEPARATOR, array($rootPath,"src","Event","Emitter.php"));
|
||||||
|
|
||||||
|
ini_set('display_errors', 'on');
|
||||||
|
$emitter = new PHPSocketIO\Event\Emitter;
|
||||||
|
$func = function($arg1, $arg2)
|
||||||
|
{
|
||||||
|
var_dump($arg1, $arg2);
|
||||||
|
};
|
||||||
|
$emitter->on('removeListener', function($event_name, $func){echo $event_name,':',var_export($func, true),"removed\n";});
|
||||||
|
$emitter->on('newListener', function($event_name, $func){echo $event_name,':',var_export($func, true)," added\n";});
|
||||||
|
$emitter->on('test', $func);
|
||||||
|
$emitter->on('test', $func);
|
||||||
|
$emitter->emit('test', 1 ,2);
|
||||||
|
echo "----------------------\n";
|
||||||
|
$emitter->once('test', $func);
|
||||||
|
$emitter->emit('test', 3 ,4);
|
||||||
|
echo "----------------------\n";
|
||||||
|
$emitter->emit('test', 4 ,4);
|
||||||
|
echo "----------------------\n";
|
||||||
|
$emitter->removeListener('test', $func)->emit('test', 5 ,6);
|
||||||
|
echo "----------------------\n";
|
||||||
|
$emitter->on('test2', function(){echo "test2\n";});
|
||||||
|
|
||||||
|
var_dump($emitter->listeners('test2'));
|
||||||
|
|
@ -1,3 +1,12 @@
|
|||||||
|
<!--
|
||||||
|
* @Author: TaoLer <317927823@qq.com>
|
||||||
|
* @Date: 2021-12-06 16:04:51
|
||||||
|
* @LastEditTime: 2022-07-30 07:24:25
|
||||||
|
* @LastEditors: TaoLer
|
||||||
|
* @Description: 优化版
|
||||||
|
* @FilePath: \github\TaoLer\view\404.html
|
||||||
|
* Copyright (c) 2020~2022 https://www.aieok.com All rights reserved.
|
||||||
|
-->
|
||||||
{extend name="public/base" /}
|
{extend name="public/base" /}
|
||||||
|
|
||||||
{block name="title"}404 - {$sysInfo.webname}{/block}
|
{block name="title"}404 - {$sysInfo.webname}{/block}
|
||||||
@ -16,22 +25,7 @@
|
|||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block name="script"}
|
{block name="script"}
|
||||||
<script>
|
404
|
||||||
|
|
||||||
layui.cache.user = {
|
|
||||||
username: '{$user.name??'游客'}'
|
|
||||||
,uid: {$user.id ? $user.id : -1}
|
|
||||||
,avatar: '{if condition="$user['user_img'] neq ''"}{$user['user_img']}{else /}/static/res/images/avatar/00.jpg{/if}'
|
|
||||||
,experience: 83
|
|
||||||
,sex: '{if condition="$user['sex'] eq 0"}男{else/}女{/if}'
|
|
||||||
};
|
|
||||||
layui.config({
|
|
||||||
version: "3.0.0"
|
|
||||||
,base: '/static/res/mods/'
|
|
||||||
}).extend({
|
|
||||||
fly: 'index'
|
|
||||||
}).use('fly');
|
|
||||||
</script>
|
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,10 +10,7 @@
|
|||||||
<meta property="bytedance:lrDate_time" content="{$lrDate_time|date='c'}" />
|
<meta property="bytedance:lrDate_time" content="{$lrDate_time|date='c'}" />
|
||||||
<meta property="bytedance:updated_time" content="{$article.update_time|date='c'}" />
|
<meta property="bytedance:updated_time" content="{$article.update_time|date='c'}" />
|
||||||
{/block}
|
{/block}
|
||||||
{block name="link"}<link rel="stylesheet" href="{$Request.domain}/static/res/css/plyr.css" charset="utf-8">
|
{block name="link"}<link rel="stylesheet" href="{$Request.domain}/static/res/css/plyr.css" charset="utf-8">{/block}
|
||||||
<link rel="stylesheet" type="text/css" id="mce-u0" href="http://www.tp6.com/addons/taonyeditor/tinymce/skins/ui/oxide/content.min.css">
|
|
||||||
<link rel="stylesheet" type="text/css" id="mce-u1" href="http://www.tp6.com/addons/taonyeditor/tinymce/skins/content/default/content.min.css">
|
|
||||||
{/block}
|
|
||||||
{block name="column"}<div class="layui-hide-xs">{include file="/public/column" /}</div>{/block}
|
{block name="column"}<div class="layui-hide-xs">{include file="/public/column" /}</div>{/block}
|
||||||
{block name="content"}
|
{block name="content"}
|
||||||
<div class="layui-container">
|
<div class="layui-container">
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<!--
|
<!--
|
||||||
* @Author: TaoLer <alipay_tao@qq.com>
|
* @Author: TaoLer <alipay_tao@qq.com>
|
||||||
* @Date: 2021-12-06 16:04:51
|
* @Date: 2021-12-06 16:04:51
|
||||||
* @LastEditTime: 2022-07-26 13:43:10
|
* @LastEditTime: 2022-07-30 07:20:01
|
||||||
* @LastEditors: TaoLer
|
* @LastEditors: TaoLer
|
||||||
* @Description: 搜索引擎SEO优化设置
|
* @Description: 搜索引擎SEO优化设置
|
||||||
* @FilePath: \github\TaoLer\view\taoler\index\public\base.html
|
* @FilePath: \github\TaoLer\view\taoler\index\public\base.html
|
||||||
@ -73,7 +73,7 @@
|
|||||||
fly: 'index'
|
fly: 'index'
|
||||||
}).use('fly');
|
}).use('fly');
|
||||||
</script>
|
</script>
|
||||||
{block name="script"}{/block}
|
{block name="script"} {/block}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
<!--
|
||||||
|
* @Author: TaoLer <317927823@qq.com>
|
||||||
|
* @Date: 2021-12-06 16:04:51
|
||||||
|
* @LastEditTime: 2022-07-30 08:27:32
|
||||||
|
* @LastEditors: TaoLer
|
||||||
|
* @Description: 优化版
|
||||||
|
* @FilePath: \github\TaoLer\view\taoler\index\public\column.html
|
||||||
|
* Copyright (c) 2020~2022 https://www.aieok.com All rights reserved.
|
||||||
|
-->
|
||||||
<div class="fly-panel fly-column layui-hide-xs">
|
<div class="fly-panel fly-column layui-hide-xs">
|
||||||
<div class="layui-container">
|
<div class="layui-container">
|
||||||
<ul class="layui-clear">
|
<ul class="layui-clear">
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
{//websocket统计脚本}
|
{//websocket统计脚本}
|
||||||
<div style="text-align:center;color:#999;font-size:14px;padding:0 0 10px;" id="online_count"></div>
|
<div style="text-align:center;color:#999;font-size:14px;padding:0 0 10px;" id="online_count"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{:hook('showLeftLayer')}
|
{:hook('showLeftLayer')}
|
||||||
<script>
|
<script>
|
||||||
var $ = layui.jquery;
|
var $ = layui.jquery;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<!--
|
<!--
|
||||||
* @Author: TaoLer <alipay_tao@qq.com>
|
* @Author: TaoLer <alipay_tao@qq.com>
|
||||||
* @Date: 2021-12-06 16:04:51
|
* @Date: 2021-12-06 16:04:51
|
||||||
* @LastEditTime: 2022-07-19 11:05:29
|
* @LastEditTime: 2022-07-30 07:08:19
|
||||||
* @LastEditors: TaoLer
|
* @LastEditors: TaoLer
|
||||||
* @Description: 搜索引擎SEO优化设置
|
* @Description: 搜索引擎SEO优化设置
|
||||||
* @FilePath: \TaoLer\view\taoler\index\public\user.html
|
* @FilePath: \github\TaoLer\view\taoler\index\public\user.html
|
||||||
* Copyright (c) 2020~2022 https://www.aieok.com All rights reserved.
|
* Copyright (c) 2020~2022 https://www.aieok.com All rights reserved.
|
||||||
-->
|
-->
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@ -34,7 +34,7 @@
|
|||||||
<script>
|
<script>
|
||||||
layui.cache.page = 'user';
|
layui.cache.page = 'user';
|
||||||
layui.cache.user = {
|
layui.cache.user = {
|
||||||
username: "{$user.name??'游客'}"
|
username: "{$user.name ??'游客'}"
|
||||||
,uid: "{$user.id ?? -1}"
|
,uid: "{$user.id ?? -1}"
|
||||||
,avatar: "{$user['user_img'] ?? '/static/res/images/avatar/00.jpg'}"
|
,avatar: "{$user['user_img'] ?? '/static/res/images/avatar/00.jpg'}"
|
||||||
,experience: "{$user.point ?? ''}"
|
,experience: "{$user.point ?? ''}"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user