├── README.md ├── app ├── config │ └── kuxin.php ├── controller │ ├── index.php │ └── novel.php ├── provider │ └── format.php └── rule │ ├── custom │ ├── .gitignore │ └── customqidian.php │ ├── kernel.php │ └── yc │ └── qidian.php ├── env.example.php ├── kuxin ├── alipay │ ├── alipay.php │ └── pay.php ├── block.php ├── cache.php ├── cache │ ├── file.php │ ├── memcache.php │ ├── memcached.php │ ├── redis.php │ └── yac.php ├── config.php ├── console.php ├── console │ └── migrate.php ├── controller.php ├── cookie.php ├── db │ ├── migrate.php │ ├── mysql.php │ └── orm.php ├── di.php ├── filter.php ├── helper │ ├── arr.php │ ├── big5.php │ ├── collect.php │ ├── collection.php │ ├── data │ │ └── pinyin.dat │ ├── emoji.php │ ├── file.php │ ├── http.php │ ├── image.php │ ├── json.php │ ├── jsonp.php │ ├── math.php │ ├── pinyin.php │ ├── serialize.php │ ├── str.php │ ├── upload.php │ ├── url.php │ ├── verify.php │ └── xml.php ├── input.php ├── kuxin.php ├── loader.php ├── log.php ├── model.php ├── oauth │ ├── oauth.php │ ├── qq.php │ ├── weibo.php │ └── weixin.php ├── plugin.php ├── registry.php ├── request.php ├── response.php ├── rewrite.php ├── router.php ├── session.php ├── storage.php ├── storage │ ├── cos.php │ ├── file.php │ └── oss.php ├── tpl │ └── error.php ├── view.php └── weixin │ ├── js.php │ ├── oauth.php │ ├── pay.php │ ├── resource.php │ └── weixin.php └── public ├── .htaccess ├── favicon.ico ├── githook.php ├── index.php └── robots.txt /README.md: -------------------------------------------------------------------------------- 1 | # RuleApi 2 | PTCMS 小说规则API接口程序 3 | 4 | # 适用场景 5 | - Json格式为PTCMS专用采集格式 6 | - Xml规则可以用于任何程序的采集,如关关、杰奇、YGB 7 | 8 | 9 | # 安装说明 10 | - 开箱即用 ,只需要把 `env.example.php` 改名为 `env.php` 修改对应配置项 11 | 12 | # 使用 13 | 14 | ## Json格式 15 | 16 | ### 列表接口 17 | - 请求方式:GET 18 | - 请求路径:`/novel/getlist.json` 19 | - 请求参数: 20 | ``` 21 | { 22 | site:qidian 23 | } 24 | ``` 25 | - 请求示例 26 | `http://api.ptcms.com/novel/getlist.json?site=qidian` 27 | 28 | ### 信息接口 29 | - 请求方式:GET 30 | - 请求路径:`/novel/getinfo.json` 31 | - 请求参数: 32 | ``` 33 | { 34 | site:qidian, 35 | novelid:书号, 36 | } 37 | ``` 38 | - 请求示例 39 | `http://api.ptcms.com/novel/getinfo.json?site=qidian&novelid=1` 40 | 41 | ### 目录接口 42 | - 请求方式:GET 43 | - 请求路径:`/novel/getinfo.json` 44 | - 请求参数: 45 | ``` 46 | { 47 | site:qidian, 48 | novelid:书号, 49 | } 50 | ``` 51 | - 请求示例 52 | `http://api.ptcms.com/novel/getdir.json?site=qidian&novelid=1` 53 | 54 | ### 章节接口 55 | - 请求方式:GET 56 | - 请求路径:`/novel/getchapter.json` 57 | - 请求参数: 58 | ``` 59 | { 60 | site:qidian, 61 | novelid:书号, 62 | chapterid:章节ID, 63 | } 64 | ``` 65 | - 请求示例 66 | `http://api.ptcms.com/novel/getchapter.json?site=qidian&novelid=1&chapterid=1` 67 | 68 | ### 下载接口 69 | - 请求方式:GET 70 | - 请求路径:`/novel/getdown.json` 71 | - 请求参数: 72 | ``` 73 | { 74 | site:qidian, 75 | novelid:书号, 76 | } 77 | ``` 78 | - 请求示例 79 | `http://api.ptcms.com/novel/getdown.json?site=qidian&novelid=1` 80 | 81 | ### 搜索接口 82 | - 请求方式:GET 83 | - 请求路径:`/novel/getsearch.json` 84 | - 请求参数: 85 | ``` 86 | { 87 | site:qidian, 88 | name:书名, 89 | author:作者, 90 | } 91 | ``` 92 | `name`,`author` 两个需要有一个填写 93 | - 请求示例 94 | `http://api.ptcms.com/novel/getsearch.json?site=qidian&name=极品家丁&author=` 95 | 96 | ## Xml格式 97 | 把Json格式接口地址后缀`.json`改为`.xml`即可 98 | 99 | # 新增规则 100 | 参考`app\rule\custom\customqidian.php` 文件新增规则,以此为例,增加的新规则`site`的值为`customqidian` 101 | 102 | # 代理使用 103 | 目前已支持芝麻代理 104 | 使用前请先修改`app\controller\index.php`文件`proxy`方法的代理获取url 105 | 在规则文件中增加`protected $useProxy=1;` 即可对规则启用代理功能 106 | 更新代理则是定时访问 `http://www.ptcms.com/index/proxy` 107 | 108 | # 章节缓存 109 | ## 开关 110 | 在`env.php`定义常量`CHAPTER_POWER` 如`const CHAPTER_POWER = true;` 111 | 112 | ## 路径 113 | 修改配置文件`app\config\kuxin.php`中`storage.txt`的配置 114 | -------------------------------------------------------------------------------- /app/controller/index.php: -------------------------------------------------------------------------------- 1 | get('http.proxy.info'); 29 | $info = json_decode($info, true); 30 | if ($info && $info['expire'] > time()) { 31 | Config::set('http.proxy.power', 1); 32 | Config::set('http.proxy.host', $info['ip']); 33 | Config::set('http.proxy.port', $info['port']); 34 | Config::set('http.proxy.type', CURLPROXY_SOCKS5); 35 | $urls = ['http://myip.ipip.net']; 36 | foreach ($urls as $url) { 37 | $res = Http::get($url); 38 | if (strpos($res, $info['ip'])) { 39 | echo(date('Y-m-d H:i:s ') . '代理可用 [' . $info['ip'] . ':' . $info['port'] . '] 过期时间 [ ' . date('Y-m-d H:i:s', $info['expire']) . ' ] ' . trim($res) . PHP_EOL); 40 | exit; 41 | } 42 | } 43 | echo date('Y-m-d H:i:s ') . '代理不可用,尝试获取新代理 [' . $info['ip'] . ':' . $info['port'] . '] ' . trim($res), PHP_EOL; 44 | } else { 45 | echo date('Y-m-d H:i:s ') . '代理过期', PHP_EOL; 46 | } 47 | for ($i = 1; $i <= 5; $i++) { 48 | $url = 'http://webapi.http.zhimacangku.com/getip?num=1&type=2&pro=&city=0&yys=0&port=2&pack=35657&ts=1&ys=0&cs=0&lb=1&sb=0&pb=5&mr=1®ions='; 49 | 50 | $data = file_get_contents($url); 51 | $data = json_decode($data, true); 52 | if (empty($data['data']['0']['ip'])) { 53 | sleep(2); 54 | continue; 55 | } 56 | Config::set('http.proxy.power', 1); 57 | Config::set('http.proxy.host', $data['data']['0']['ip']); 58 | Config::set('http.proxy.port', $data['data']['0']['port']); 59 | Config::set('http.proxy.type', CURLPROXY_SOCKS5); 60 | $urls = ['http://myip.ipip.net']; 61 | foreach ($urls as $url) { 62 | $res = Http::get($url); 63 | if (strpos($res, $data['data']['0']['ip'])) { 64 | $expire = strtotime($data['data']['0']['expire_time']) - 130; 65 | DI::Cache()->set('http.proxy.info', json_encode([ 66 | 'ip' => $data['data']['0']['ip'], 67 | 'port' => $data['data']['0']['port'], 68 | 'expire' => $expire, 69 | ]), 3600); 70 | exit(date('Y-m-d H:i:s ') . '获取新代理成功 [' . $data['data']['0']['ip'] . ':' . $data['data']['0']['port'] . '] 过期时间 [ ' . date('Y-m-d H:i:s', $expire) . ' ] ' . PHP_EOL); 71 | } 72 | } 73 | echo date('Y-m-d H:i:s ') . '获取新代理不可用,尝试获取新代理 [' . $data['data']['0']['ip'] . ':' . $data['data']['0']['port'] . ']' . trim($res), PHP_EOL; 74 | sleep(3); 75 | } 76 | exit(date('Y-m-d H:i:s ') . '无可用代理' . PHP_EOL); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/provider/format.php: -------------------------------------------------------------------------------- 1 | }isU", "", $content); 19 | $content = str_replace("温馨提示:按回车[Enter]键返回书目,按←键返回上一页,按→键进入下一页。", "", $content); 20 | $content = preg_replace("/我是超级[^<]+?nv92[^<]+?/U", "", $content); 21 | //完全过滤js 22 | $content = preg_replace('//', '', $content); 23 | 24 | $content = str_ireplace(['www.', '.com', '.cn', '.cc', '.org', '.me', '.net', 'http', '://'], '', $content); 25 | $content = str_ireplace(['', '
', '
'], "\n", $content); 27 | $content = preg_replace('/<\/p>\s*

/i', "\n", $content); 28 | $content = trim(str_ireplace(['

', '

', "\r"], ["\n", "\n", "\n"], $content)); 29 | $content = str_replace("\r", "\n", $content); 30 | $content = preg_replace("{\s+  \s+}", "\n", $content); 31 | $content = preg_replace("/\n{2,}/", "\n", $content); 32 | // 去除空格 33 | $content = trim(str_replace([' ', ' ', ' '], ' ', $content)); 34 | $t = explode("\n", $content); 35 | foreach ($t as $k => $tt) { 36 | $tt = trim($tt); 37 | $tt = preg_replace('/^( |,|、|。|;|:|?|!)+/', '', $tt); 38 | $tt = preg_replace('/( )+$/', '', $tt); 39 | $tt = trim($tt); 40 | if (!$tt) 41 | unset($t[$k]); 42 | $t[$k] = $tt; 43 | } 44 | $content = implode("\n\n", $t); 45 | // 去除其他html 46 | $content = strip_tags($content, ''); 47 | // 加换行及空格 48 | $content = (trim($content)); 49 | return str_replace("\n", "
", $content); 50 | } 51 | 52 | //整理简介 53 | public static function intro($str) 54 | { 55 | $str = htmlspecialchars_decode(htmlspecialchars_decode($str)); 56 | $str = str_ireplace(['
', '
', '
'], "\n", $str); 57 | $str = str_replace(["\r", '\n', '\r'], "\n", $str); 58 | $str = preg_replace("/\n{2,}/", "\n", $str); 59 | //完全过滤js 60 | $str = preg_replace('//', '', $str); 61 | $str = strip_tags($str); 62 | $from = ["。", "!", "?", "!", "?", '.', '…']; 63 | $str = Filter::safetext($str); 64 | $str = trim(str_replace([' ', ' ', ' '], '', $str)); 65 | $t = explode("\n", $str); 66 | $last = 0; 67 | foreach ($t as $k => $tt) { 68 | $tt = trim($tt); 69 | if ($last > 0 && (in_array($tt, $from) || in_array($tt, ['”', '"', "'", ")", "("]))) { 70 | $t[$last] .= $tt; 71 | unset($t[$k]); 72 | continue; 73 | }; 74 | if (!$tt) { 75 | unset($t[$k]); 76 | continue; 77 | } 78 | if (in_array($tt{0}, [',', '?', ':', ';', ';', ':'])) { 79 | $tt = preg_replace('/^[,?:;;:]/', '', $tt); 80 | } 81 | $t[$k] = $tt; 82 | $last = $k; 83 | } 84 | return implode("\n", $t); 85 | } 86 | 87 | /** 88 | * 整理章节名 89 | * 90 | * @param $name 91 | * @return mixed 92 | */ 93 | public static function name($name) 94 | { 95 | $name = str_replace([' ', ' '], ' ', urldecode($name)); 96 | $name = strip_tags(htmlspecialchars_decode(strip_tags($name))); 97 | //部分小说章节获取不全 去除尾部的省略号 98 | $name = preg_replace('/\.+$/', '', $name); 99 | $name = preg_replace('/…+$/', '', $name); 100 | // 补齐符号 101 | $arr = [ 102 | ['<', '>'], 103 | ['[', ']'], 104 | ['(', ')'], 105 | ['{', '}'], 106 | ['〈', '〉'], 107 | ['《', '》'], 108 | ['(', ')'], 109 | ['「', '」'], 110 | ['『', '』'], 111 | ['[', ']'], 112 | ['〖', '〗'], 113 | ['【', '】'], 114 | ['{', '}'], 115 | ]; 116 | foreach ($arr as $v) { 117 | if (strpos($name, $v['0']) !== false && strpos($name, $v['1']) === false) { 118 | //$name.=$v['1']; 119 | //break; 120 | $name = str_replace($v['0'], ' ', $name); 121 | } 122 | } 123 | $name = preg_replace('/(\s| )+/', ' ', $name); 124 | $name = preg_replace('/^[\d\s\.]+?第/', '第', $name); 125 | return $name; 126 | } 127 | 128 | public static function clearNovelName($name) 129 | { 130 | $name = trim(strip_tags($name)); 131 | $name = str_replace(['_塔读文学网', '_甜悦读', '_南京阅明', '_逐浪小说', '_杭州趣阅', '_凤凰网', '_博易创为', '_飞卢', '_品书网', '_蔷薇书院', '_幻文小说网', '_黑岩', '_岳麓', '_红杉万橡', '_二层楼', '_酷匠网', '_酷匠网', '_杭州悦蓝', '_一千零一页', '_书海', '_华夏天空', '_凤鸣轩', '_瑞隆文化', '_中润', '_铁血科技', '_永正'], '', $name); 132 | $name = str_replace(['(塔读文学网)', '(塔读文学网1)', '(甜悦读)', '(南京阅明)', '(逐浪小说)', '(杭州趣阅)', '(凤凰网)', '(博易创为)', '(飞卢)', '(品书网)', '(蔷薇书院)', '(幻文小说网)', '(黑岩)', '(岳麓)', '(红杉万橡)', '(二层楼)', '(酷匠网)', '(酷匠网)', '(杭州悦蓝)', '(一千零一页)', '(书海)', '(华夏天空)', '(凤鸣轩)', '(瑞隆文化)', '(中润)', '(铁血科技)', '(永正)'], '', $name); 133 | $name = str_replace(['(书坊)', '《', '》', '(合作)'], '', $name); 134 | return $name; 135 | } 136 | 137 | public static function clearAuthorName($name) 138 | { 139 | $name = trim(strip_tags($name)); 140 | $name = str_replace(['_塔读文学网', '_甜悦读', '_南京阅明', '_逐浪小说', '_杭州趣阅', '_凤凰网', '_博易创为', '_飞卢', '_品书网', '_蔷薇书院', '_幻文小说网', '_黑岩', '_岳麓', '_红杉万橡', '_二层楼', '_酷匠网', '_酷匠网', '_杭州悦蓝', '_一千零一页', '_书海', '_华夏天空', '_凤鸣轩', '_瑞隆文化', '_中润', '_铁血科技', '_永正'], '', $name); 141 | $name = str_replace(['(书坊)', '《', '》', '(合作)'], '', $name); 142 | $name = $name ?: '匿名'; 143 | return $name; 144 | } 145 | 146 | public static function showIntro($str) 147 | { 148 | $str = str_replace('
', '
  ', nl2br($str)); 149 | $str = str_replace("\n", '', $str); 150 | return '  ' . $str; 151 | } 152 | 153 | public static function showChapter($str) 154 | { 155 | return '  ' . str_replace("\n", '

  ', $str); 156 | } 157 | } -------------------------------------------------------------------------------- /app/rule/custom/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ptcms/RuleApi/9a493d8deb726e33cf702be183ca52871470a593/app/rule/custom/.gitignore -------------------------------------------------------------------------------- /app/rule/custom/customqidian.php: -------------------------------------------------------------------------------- 1 | (.+?) $v) { 28 | $list[] = [ 29 | 'novelid' => $novelid[$k], 30 | 'novelname' => str_replace('(书坊)', '', $novelname[$k]), 31 | 'chapterid' => $chapterid[$k], 32 | ]; 33 | } 34 | return $list; 35 | } else { 36 | if (isset($_GET['debug'])) { 37 | var_dump($content, $novelid, $novelname, $chapterid); 38 | } 39 | return sprintf('获取数目不一致:novelname %s novelid %s chapterid %s 长度 %s', count($novelname), count($novelid), count($chapterid), strlen($content)); 40 | } 41 | } 42 | 43 | public function getInfo($novelid) 44 | { 45 | $url = 'https://m.qidian.com/book/' . $novelid; 46 | $content = Collect::getContent($url); 47 | if (!$content || strlen($content) < 1000) { 48 | return '获取内容失败:链接' . $url . ' 长度' . strlen($content); 49 | } 50 | $content = str_replace('', '', $content); 51 | $data = [ 52 | 'novelname' => Format::clearNovelName(strip_tags(Collect::getMatch('

(.+?)

', $content))), 53 | 'author' => Format::clearNovelName(strip_tags(Collect::getMatch('

(.+?)<', $content))), 54 | 'intro' => Format::intro(Collect::getMatch(['rule' => '', 'option' => 'is'], $content)), 55 | 'cover' => strip_tags(Collect::getMatch('
\s* strpos($content, '起点女生网') ? '女生' : '男生', 57 | 'category' => strip_tags(Collect::getMatch('

(.+?)/.+?

\s*

', $content)), 58 | 'subcategory' => strip_tags(Collect::getMatch('

.+?/(.+?)

\s*

', $content)), 59 | 'isover' => strip_tags(Collect::getMatch('\|(.+?)

', $content)), 60 | 'num_words' => floor((Collect::getMatch('

\s*

([\d\.]*?)万字', $content)) * 10000), 61 | 'tag' => implode(',', Collect::getMatchAll('/search\?kw=&tag=(.+?)"', $content)), 62 | ]; 63 | $data['cover'] = (substr($data['cover'], 0, 2) == '//' ? 'https:' : '') . $data['cover']; 64 | if ($data['cover'] && substr($data['cover'], -4) == '/300') { 65 | $data['cover'] = substr($data['cover'], 0, -4) . '/180'; 66 | } 67 | if (empty($data['novelname'])) { 68 | return '获取小说书名失败:url ' . $url . ' 内容长度:' . strlen($content); 69 | } 70 | return $data; 71 | } 72 | 73 | public function getDir($novelid) 74 | { 75 | $dir = []; 76 | $url = "https://m.qidian.com/book/{$novelid}/catalog"; 77 | $content = Collect::getContent($url); 78 | if (empty($content)) { 79 | return '获取内容失败:链接 ' . $url; 80 | } 81 | $jscontent = Collect::getMatch(['rule' => 'g_data\.volumes\s*\=\s+(.+?)\];\s+', 'option' => 'isU'], $content) . ']'; 82 | 83 | if (!$jscontent) { 84 | return '获取js代码失败:链接 ' . $url; 85 | } 86 | $data = Json::decode($jscontent); 87 | if (empty($data)) { 88 | return '解析内容失败:链接 ' . $url; 89 | } else { 90 | if($data[0]['vN']=='作品相关'){ 91 | array_shift($data); 92 | } 93 | foreach ($data as $k => $v) { 94 | if (isset($v['cs'])) { 95 | foreach ($v['cs'] as $c) { 96 | $dir[] = [ 97 | 'id' => $c['id'], 98 | 'name' => $c['cN'], 99 | 'url' => $c['id'], 100 | ]; 101 | } 102 | } 103 | } 104 | return $dir; 105 | } 106 | 107 | } 108 | 109 | public function getChapter($param) 110 | { 111 | $url = "https://m.qidian.com/book/{$param['novelid']}/{$param['url']}"; 112 | $content = Collect::getContent($url); 113 | $jscontent = collect::getMatch('g_data.chapter = (.+?);\s*g_data', $content); 114 | $jscontent = Json::decode($jscontent); 115 | $chapterContent = $jscontent['content'] . "\n\n" . $jscontent['authorWords']['content']; 116 | return [ 117 | 'name' => $jscontent['chapterName'], 118 | 'content' => $chapterContent, 119 | 'url' => $url, 120 | 'isvip' => $jscontent['vipStatus'], 121 | ]; 122 | } 123 | 124 | public function getDown($novelid) 125 | { 126 | return [ 127 | 'epub' => [ 128 | 'url' => 'http://download.qidian.com/epub/' . $novelid . '.epub', 129 | ], 130 | ]; 131 | } 132 | 133 | } -------------------------------------------------------------------------------- /app/rule/kernel.php: -------------------------------------------------------------------------------- 1 | useProxy) { 19 | $this->setProxy(); 20 | } 21 | $userAgents = Config::get('useragents'); 22 | Config::set('http.user_agent', Arr::rand($userAgents)); 23 | } 24 | 25 | protected function setProxy() 26 | { 27 | $info = DI::Cache()->get('http.proxy.info'); 28 | $info = json_decode($info, true); 29 | if ($info && $info['expire'] > time()) { 30 | Config::set('http.proxy.power', 1); 31 | Config::set('http.proxy.host', $info['ip']); 32 | Config::set('http.proxy.port', $info['port']); 33 | Config::set('http.proxy.type', CURLPROXY_SOCKS5); 34 | } 35 | } 36 | 37 | /** 38 | * @param int $page 39 | * @reutrn array() 40 | */ 41 | abstract public function getlist($page); 42 | 43 | /** 44 | * @param $novelid 45 | * @return array 46 | */ 47 | abstract public function getinfo($novelid); 48 | 49 | /** 50 | * @param $novelid 51 | * @return array 52 | */ 53 | abstract public function getdir($novelid); 54 | 55 | /** 56 | * @param $novelid 57 | * @return array 58 | */ 59 | public function getDown($novelid) 60 | { 61 | return []; 62 | } 63 | 64 | /** 65 | * @param $novelid 66 | * @return array 67 | */ 68 | public function getSearch($novelname, $author) 69 | { 70 | return []; 71 | } 72 | 73 | /** 74 | * @param $param 75 | * @return array 76 | */ 77 | abstract public function getchapter($param); 78 | 79 | //获取json数据 80 | protected function getjson($url) 81 | { 82 | return Json::decode(trim(Http::get($url))); 83 | } 84 | 85 | public function getDownFileSize($url) 86 | { 87 | $content = (Http::get($url, [], [], [ 88 | CURLOPT_HEADER => 1, 89 | CURLOPT_NOBODY => 1, 90 | ])); 91 | if (strpos($content, 'Content-Length')) { 92 | return Collect::getMatch('Content-Length:\s*(\d+)\s+', $content); 93 | } 94 | return 0; 95 | } 96 | } -------------------------------------------------------------------------------- /app/rule/yc/qidian.php: -------------------------------------------------------------------------------- 1 | (.+?) $v) { 33 | $list[] = [ 34 | 'novelid' => $novelid[$k], 35 | 'novelname' => str_replace('(书坊)', '', $novelname[$k]), 36 | 'chapterid' => $chapterid[$k], 37 | ]; 38 | } 39 | return $list; 40 | } else { 41 | if (isset($_GET['debug'])) { 42 | var_dump($content, $novelid, $novelname, $chapterid); 43 | } 44 | return sprintf('获取数目不一致:novelname %s novelid %s chapterid %s 长度 %s', count($novelname), count($novelid), count($chapterid), strlen($content)); 45 | } 46 | } 47 | 48 | public function getInfo($novelid) 49 | { 50 | $url = 'https://m.qidian.com/book/' . $novelid; 51 | $content = Collect::getContent($url); 52 | if (!$content || strlen($content) < 1000) { 53 | return '获取内容失败:链接' . $url . ' 长度' . strlen($content); 54 | } 55 | $content = str_replace('', '', $content); 56 | $data = [ 57 | 'novelname' => Format::clearNovelName(strip_tags(Collect::getMatch('

(.+?)

', $content))), 58 | 'author' => Format::clearNovelName(strip_tags(Collect::getMatch('

(.+?)<', $content))), 59 | 'intro' => Format::intro(Collect::getMatch(['rule' => '', 'option' => 'is'], $content)), 60 | 'cover' => strip_tags(Collect::getMatch('
\s* strpos($content, '起点女生网') ? '女生' : '男生', 62 | 'category' => strip_tags(Collect::getMatch('

(.+?)/.+?

\s*

', $content)), 63 | 'subcategory' => strip_tags(Collect::getMatch('

.+?/(.+?)

\s*

', $content)), 64 | 'isover' => strip_tags(Collect::getMatch('\|(.+?)

', $content)), 65 | 'num_words' => floor((Collect::getMatch('

\s*

([\d\.]*?)万字', $content)) * 10000), 66 | 'tag' => implode(',', Collect::getMatchAll('/search\?kw=&tag=(.+?)"', $content)), 67 | ]; 68 | $data['cover'] = (substr($data['cover'], 0, 2) == '//' ? 'https:' : '') . $data['cover']; 69 | if ($data['cover'] && substr($data['cover'], -4) == '/300') { 70 | $data['cover'] = substr($data['cover'], 0, -4) . '/180'; 71 | } 72 | if (empty($data['novelname'])) { 73 | return '获取小说书名失败:url ' . $url . ' 内容长度:' . strlen($content); 74 | } 75 | return $data; 76 | } 77 | 78 | public function getDir($novelid) 79 | { 80 | $dir = []; 81 | $url = "https://m.qidian.com/book/{$novelid}/catalog"; 82 | $content = Collect::getContent($url); 83 | if (empty($content)) { 84 | return '获取内容失败:链接 ' . $url; 85 | } 86 | $jscontent = Collect::getMatch(['rule' => 'g_data\.volumes\s*\=\s+(.+?)\];\s+', 'option' => 'isU'], $content) . ']'; 87 | 88 | if (!$jscontent) { 89 | return '获取js代码失败:链接 ' . $url; 90 | } 91 | $data = Json::decode($jscontent); 92 | if (empty($data)) { 93 | return '解析内容失败:链接 ' . $url; 94 | } else { 95 | if ($data[0]['vN'] == '作品相关') { 96 | array_shift($data); 97 | } 98 | foreach ($data as $k => $v) { 99 | if (isset($v['cs'])) { 100 | foreach ($v['cs'] as $c) { 101 | $dir[] = [ 102 | 'id' => $c['id'], 103 | 'name' => $c['cN'], 104 | 'url' => $c['id'], 105 | ]; 106 | } 107 | } 108 | } 109 | return $dir; 110 | } 111 | 112 | } 113 | 114 | public function getChapter($param) 115 | { 116 | $url = "https://m.qidian.com/book/{$param['novelid']}/{$param['chapterid']}"; 117 | $content = Collect::getContent($url); 118 | $jscontent = collect::getMatch('g_data.chapter = (.+?);\s*g_data', $content); 119 | $jscontent = Json::decode($jscontent); 120 | $chapterContent = $jscontent['content'] . "\n\n" . $jscontent['authorWords']['content']; 121 | return [ 122 | 'name' => $jscontent['chapterName'], 123 | 'content' => Format::chapter($chapterContent), 124 | 'url' => $url, 125 | 'isvip' => $jscontent['vipStatus'], 126 | ]; 127 | } 128 | 129 | public function getDown($novelid) 130 | { 131 | return [ 132 | 'epub' => [ 133 | 'url' => 'http://download.qidian.com/epub/' . $novelid . '.epub', 134 | ], 135 | ]; 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /env.example.php: -------------------------------------------------------------------------------- 1 | appId = $appId ?: Config::get('alipay.appid'); 23 | $this->appSecret = $appSecret ?: Config::get('alipay.appsecret'); 24 | } 25 | 26 | /** 27 | * @param $appId 28 | * @param $appSecret 29 | * @return static 30 | */ 31 | public static function I($appId = null, $appSecret = null) 32 | { 33 | $class = static::class; 34 | return Loader::instance($class, [$appId, $appSecret]); 35 | } 36 | 37 | public function sign($data) { 38 | ksort($data); 39 | reset($data); 40 | $arg = ''; 41 | foreach ($data as $key => $value) { 42 | if ($key != 'sign_type' && $key != 'sign' && $value!='') { 43 | $arg .= "$key=$value&"; 44 | } 45 | } 46 | return md5(substr($arg, 0, -1) . $this->appSecret); 47 | } 48 | } -------------------------------------------------------------------------------- /kuxin/alipay/pay.php: -------------------------------------------------------------------------------- 1 | 'create_direct_pay_by_user', 20 | '_input_charset' => 'utf-8', 21 | 'partner' => $this->appId, 22 | 'format' => 'xml', 23 | 'v' => '2.0', 24 | 'sign_type' => 'MD5', 25 | 'notify_url' => '', 26 | 'return_url' => '', 27 | 'out_trade_no' => '', 28 | 'subject' => '', 29 | 'body' => '', 30 | 'payment_type' => '1', 31 | 'total_fee' => '', 32 | 'seller_id' => $this->appId, 33 | 'seller_email' => Config::get('alipay.email'), 34 | 'show_url' => '', 35 | ], $data); 36 | $data['sign'] = $this->sign($data); 37 | return self::PC_PAY_URL . '?' . http_build_query($data); 38 | } 39 | 40 | public function getWapUrl($param) 41 | { 42 | $req_data = ''; 43 | $req_data .= '' . $param['notify_url'] . ''; 44 | $req_data .= '' . $param['return_url'] . ''; 45 | $req_data .= '' . Config::get('pay_alipay_email') . ''; 46 | $req_data .= '' . $param['out_trade_no'] . ''; 47 | $req_data .= '' . $param['subject'] . ''; 48 | $req_data .= '' . $param['total_fee'] . ''; 49 | $req_data .= ''; 50 | 51 | $data = [ 52 | 'service' => 'alipay.wap.trade.create.direct', 53 | '_input_charset' => 'utf-8', 54 | 'partner' => $this->appId, 55 | 'sec_id' => 'MD5', 56 | 'req_id' => date('Ymdhis'), 57 | 'req_data' => $req_data, 58 | 'v' => '2.0', 59 | 'format' => 'xml', 60 | ]; 61 | $data['sign'] = $this->sign($data); 62 | $res = urldecode(Http::post(self::WAP_PAY_URL, $data)); 63 | parse_str($res, $param); 64 | if (!empty($param['res_data'])) { 65 | $data = Xml::decode($param['res_data']); 66 | //业务详细 67 | $req_data = '' . $data['request_token'] . ''; 68 | $data = [ 69 | 'service' => 'alipay.wap.auth.authAndExecute', 70 | '_input_charset' => 'utf-8', 71 | 'partner' => $this->appId, 72 | 'sec_id' => 'MD5', 73 | 'req_id' => date('Ymdhis'), 74 | 'req_data' => $req_data, 75 | 'v' => '2.0', 76 | 'format' => 'xml', 77 | ]; 78 | $data['sign'] = $this->sign($data); 79 | return self::WAP_PAY_URL . '?' . http_build_query($data); 80 | } 81 | return false; 82 | } 83 | 84 | 85 | public function notify($param) 86 | { 87 | unset($param['m'], $param['c'], $param['a'], $param['s'], $param['f']); 88 | $sign = $this->sign($param); 89 | if ($sign == $param['sign']) { 90 | if ($param['trade_status'] == 'TRADE_FINISHED' || $param['trade_status'] == 'TRADE_SUCCESS') { 91 | return ['status' => 1, 'info' => 'success', 'money' => $param['total_fee'], 'time' => strtotime(isset($param['gmt_payment'])) ? $param['gmt_payment'] : $param['notify_time']]; 92 | } else { 93 | return ['status' => 0, 'info' => 'success']; 94 | } 95 | } else { 96 | return ['status' => 0, 'info' => 'fail']; 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /kuxin/block.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class Block 14 | { 15 | 16 | /** 17 | * 获取区块 18 | * @param string $name 19 | * @param array|null $param 20 | * @return array|mixed|string 21 | */ 22 | public static function show(string $name, ?array $param = []) 23 | { 24 | Block::clearCache('comment.list', 22); 25 | $cacheKey = md5(self::getUniqId($name, self::getParamId($param)) . '_' . Json::encode($param)); 26 | $data = DI::Cache()->debugGet($cacheKey, function ($key) use ($name, $param) { 27 | if (strpos($name, '.')) { 28 | $var = explode('.', $name); 29 | } else { 30 | $var = [$name, 'index']; 31 | } 32 | $method = array_pop($var); 33 | $class = '\\App\\Block\\' . implode('\\', $var); 34 | $block = Loader::instance($class); 35 | if (!$block || !is_callable([$block, $method])) { 36 | trigger_error(sprintf('区块 %s 无法加载', $name), E_USER_ERROR); 37 | } 38 | $data = $block->$method($param); 39 | $cacheTime = $param['cachetime'] ?? $block->getCacheTime(); 40 | if ($cacheTime && $data) { 41 | DI::Cache()->set($key, $data, $cacheTime); 42 | } 43 | return $data; 44 | }); 45 | //随机数 46 | if (isset($param['randnum'])) { 47 | $randnum = Input::param('randnum', 'int', 10, $param); 48 | if ($randnum > count($data)) { 49 | $list = []; 50 | $keys = array_rand($data, $randnum); 51 | foreach ($keys as $v) { 52 | $list[] = $data[$v]; 53 | } 54 | $data = $list; 55 | } 56 | } 57 | // 定义了模板 58 | if (isset($param['template'])) { 59 | View::disableLayout(); 60 | $data = View::make($param['template'], $param); 61 | View::enableLayout(); 62 | } 63 | return $data; 64 | } 65 | 66 | /** 67 | * 获取参数中的id值 68 | * @param $param 69 | * @return string 70 | */ 71 | protected static function getParamId($param): string 72 | { 73 | if (isset($param['id'])) { 74 | return $param['id']; 75 | } elseif (isset($param['novelid'])) { 76 | return $param['novelid']; 77 | } elseif (isset($param['chapterid'])) { 78 | return $param['chapterid']; 79 | } elseif (isset($param['siteid'])) { 80 | return $param['siteid']; 81 | } elseif (isset($param['authorid'])) { 82 | return $param['authorid']; 83 | } elseif (isset($param['categoryid'])) { 84 | return $param['categoryid']; 85 | } elseif (isset($param['typeid'])) { 86 | return $param['typeid']; 87 | } else { 88 | return '0'; 89 | } 90 | } 91 | 92 | /** 93 | * 生成区块名的唯一id 用于缓存 便于清理缓存 94 | * @param string $name 95 | * @param int $id 96 | * @return string 97 | */ 98 | protected static function getUniqId(string $name, int $id = 0): string 99 | { 100 | static $_cache = []; 101 | $nameUniqId = self::getUniqNameId($name); 102 | $key = 'block_uniq_' . $nameUniqId . '_' . $id; 103 | if (isset($_cache[$key])) { 104 | return $_cache[$key]; 105 | } 106 | $data = DI::Cache()->get($key, function ($key) { 107 | $uniqid = uniqid(); 108 | DI::Cache()->set($key, $uniqid); 109 | return $uniqid; 110 | }); 111 | return $_cache[$key] = $data; 112 | } 113 | 114 | /** 115 | * 缓存设计 116 | * @param string $name 117 | * @return mixed 118 | */ 119 | protected static function getUniqNameId(string $name) 120 | { 121 | static $_cache = []; 122 | if (isset($_cache['block_uniq_' . $name])) { 123 | return $_cache['block_uniq_' . $name]; 124 | } 125 | $key = 'block_uniq_' . $name; 126 | $data = DI::Cache()->get($key, function ($key) { 127 | $uniqid = uniqid(); 128 | DI::Cache()->set($key, $uniqid); 129 | return $uniqid; 130 | }); 131 | return $_cache['block_uniq_' . $name] = $data; 132 | } 133 | 134 | /** 135 | * 更新区块的唯一 136 | * @param string $name 137 | * @param int $id 138 | */ 139 | public static function clearCache(string $name, ?int $id = null): void 140 | { 141 | if ($id !== null) { 142 | $nameUniqId = self::getUniqNameId($name); 143 | DI::Cache()->set('block_uniq_' . $nameUniqId . '_' . $id, uniqid() . uniqid()); 144 | } else { 145 | DI::Cache()->set('block_uniq_' . $name, uniqid()); 146 | } 147 | } 148 | } -------------------------------------------------------------------------------- /kuxin/cache.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class Cache 18 | { 19 | 20 | /** 21 | * @var \Kuxin\Cache\Memcache 22 | */ 23 | protected $handler = null; 24 | 25 | 26 | /** 27 | * Cache constructor. 28 | * 29 | * @param array $config 30 | */ 31 | public function __construct(array $config) 32 | { 33 | $class = '\\Kuxin\\Cache\\' . $config['driver']; 34 | $this->handler = Loader::instance($class, [$config['option']]); 35 | } 36 | 37 | /** 38 | * 设置缓存 39 | * 40 | * @param $key 41 | * @param $value 42 | * @param int $time 43 | * @return bool 44 | */ 45 | public function set(string $key, $value, $time = 0):bool 46 | { 47 | Registry::setInc('_cacheWrite'); 48 | return $this->handler->set($key, $value, (int)$time); 49 | } 50 | 51 | /** 52 | * 获取缓存 53 | * @param string $key 54 | * @param mixed|null $default 55 | * @return mixed 56 | */ 57 | public function get(string $key, $default = null) 58 | { 59 | Registry::setInc('_cacheRead'); 60 | $result = $this->handler->get($key); 61 | if ($result === null) { 62 | $result = (is_callable($default) ? $default($key) : $default); 63 | } else { 64 | Registry::setInc('_cacheHit'); 65 | } 66 | return $result; 67 | } 68 | 69 | /** 70 | * debug模式来获取缓存,debug模式不取缓存 71 | * 72 | * @param $key 73 | * @param mixed $default 74 | * @return mixed 75 | */ 76 | public function debugGet(string $key, $default = null) 77 | { 78 | Registry::setInc('_cacheRead'); 79 | $result = Config::get('app.debug') ? null : $this->handler->get($key); 80 | if ($result === null) { 81 | $result = (is_callable($default) ? $default($key) : $default); 82 | } else { 83 | Registry::setInc('_cacheHit'); 84 | } 85 | return $result; 86 | } 87 | 88 | /** 89 | * 删除缓存 90 | * 91 | * @param $key 92 | * @return bool 93 | */ 94 | public function remove(string $key): bool 95 | { 96 | return $this->handler->remove($key); 97 | } 98 | 99 | /** 100 | * 缓存计数 增加 101 | * 102 | * @param $key 103 | * @param int $len 104 | * @return mixed|bool|int 105 | */ 106 | public function inc(string $key, int $len = 1) 107 | { 108 | return $this->handler->inc($key, $len); 109 | } 110 | 111 | /** 112 | * 缓存计数 减少 113 | * 114 | * @param $key 115 | * @param int $len 116 | * @return mixed|bool|int 117 | */ 118 | public function dec(string $key, int $len = 1) 119 | { 120 | return $this->handler->dec($key, $len); 121 | } 122 | 123 | /** 124 | * 清空缓存 125 | */ 126 | public function clear(): void 127 | { 128 | $this->handler->clear(); 129 | } 130 | 131 | /** 132 | * @param $method 133 | * @param $args 134 | */ 135 | public function __call($method, $args) 136 | { 137 | if (is_callable([$this->handler, $method])) { 138 | call_user_func_array([$this->handler, $method], $args); 139 | } else { 140 | trigger_error('Cache中不存在的方法'); 141 | } 142 | } 143 | 144 | } 145 | 146 | 147 | -------------------------------------------------------------------------------- /kuxin/cache/file.php: -------------------------------------------------------------------------------- 1 | path = $option['path'] ?? KX_ROOT . '/storage/cache'; 27 | $this->prefix = $option['prefix'] ?? Config::get('cache.prefix', ''); 28 | } 29 | 30 | public function set(string $key, $value, int $time = 0) 31 | { 32 | $file = $this->key2file($key); 33 | $data['data'] = $value; 34 | $data['time'] = ($time == 0) ? 0 : ($_SERVER['REQUEST_TIME'] + $time); 35 | return file_put_contents($file, Serialize::encode($data)); 36 | } 37 | 38 | public function get(string $key) 39 | { 40 | $file = $this->key2file($key); 41 | if (is_file($file)) { 42 | $data = Serialize::decode(file_get_contents($file)); 43 | if ($data && ($data['time'] > 0 && $data['time'] < $_SERVER['REQUEST_TIME'])) { 44 | $this->remove($key); 45 | return null; 46 | } 47 | return $data['data']; 48 | } else { 49 | return null; 50 | } 51 | } 52 | 53 | public function remove(string $key) 54 | { 55 | $file = $this->key2file($key); 56 | if (is_file($file)) 57 | return unlink($file); 58 | return false; 59 | } 60 | 61 | public function inc(string $key, int $num = 1) 62 | { 63 | $data = $this->get($key); 64 | if ($data) { 65 | $data += $num; 66 | $this->set($key, $data); 67 | return $data; 68 | } 69 | return false; 70 | } 71 | 72 | public function dec(string $key, int $num = 1) 73 | { 74 | $data = $this->get($key); 75 | if ($data) { 76 | $data -= $num; 77 | $this->set($key, $data); 78 | return $data; 79 | } 80 | return false; 81 | } 82 | 83 | public function clear() 84 | { 85 | 86 | } 87 | 88 | protected function key2file(string $key) 89 | { 90 | if (is_array($key)) { 91 | $key = Serialize::encode($key); 92 | } 93 | $key = md5($key); 94 | $path = $this->path . '/' . $key{0} . '/' . $key{1} . '/'; 95 | if (!is_dir($path)) { 96 | mkdir($path, 0755, true); 97 | } 98 | $file = $path . $key . '.php'; 99 | return $file; 100 | } 101 | } -------------------------------------------------------------------------------- /kuxin/cache/memcache.php: -------------------------------------------------------------------------------- 1 | handler = new \Memcache(); 27 | $this->handler->connect(($option['host'] ?? '127.0.0.1'), ($option['port'] ?? '11211')); 28 | $this->prefix = $option['prefix'] ?? Config::get('cache.prefix', ''); 29 | } 30 | 31 | public function set(string $key, $value, $time = 0) 32 | { 33 | return $this->handler->set($this->prefix . $key, Serialize::encode($value), MEMCACHE_COMPRESSED, $time); 34 | } 35 | 36 | public function get(string $key) 37 | { 38 | $return = $this->handler->get($this->prefix . $key); 39 | if ($return === false) { 40 | return null; 41 | } elseif (is_string($return)) { 42 | return Serialize::decode($return); 43 | } else { 44 | return $return; 45 | } 46 | } 47 | 48 | public function remove(string $key) 49 | { 50 | return $this->handler->delete($this->prefix . $key); 51 | } 52 | 53 | public function inc(string $key, ?int $num = 1) 54 | { 55 | $key = $this->prefix . $key; 56 | if ($this->handler->get($key)) { 57 | return $this->handler->increment($key, $num); 58 | } 59 | $this->handler->set($key, $num); 60 | return $num; 61 | } 62 | 63 | public function dec(string $key, ?int $num = 1) 64 | { 65 | $key = $this->prefix . $key; 66 | if ($this->handler->get($key)) { 67 | return $this->handler->decrement($key, $num); 68 | } else { 69 | $this->handler->set($key, 0); 70 | return 0; 71 | } 72 | } 73 | 74 | public function clear() 75 | { 76 | $this->handler->flush(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /kuxin/cache/memcached.php: -------------------------------------------------------------------------------- 1 | handler = new \Memcached(); 28 | $this->handler->addServer($option['host'] ?? '127.0.0.1', $option['port'] ?? '11211'); 29 | $this->prefix = $option['prefix'] ?? Config::get('cache.prefix', ''); 30 | } 31 | 32 | public function set(string $key, $value, ?int $time = 0) 33 | { 34 | return $this->handler->set($this->prefix . $key, Serialize::encode($value), $time); 35 | } 36 | 37 | public function get(string $key) 38 | { 39 | $return = $this->handler->get($this->prefix . $key); 40 | if ($return === false) { 41 | return null; 42 | } elseif (is_string($return)) { 43 | return Serialize::decode($return); 44 | } else { 45 | return $return; 46 | } 47 | } 48 | 49 | public function remove(string $key) 50 | { 51 | return $this->handler->delete($this->prefix . $key); 52 | } 53 | 54 | public function inc(string $key, int $num = 1) 55 | { 56 | $key = $this->prefix . $key; 57 | if ($this->handler->get($key)) { 58 | return $this->handler->increment($key, $num); 59 | } 60 | $this->handler->set($key, $num); 61 | return $num; 62 | } 63 | 64 | public function dec(string $key, int $num = 1) 65 | { 66 | $key = $this->prefix . $key; 67 | if ($this->handler->get($key)) { 68 | return $this->handler->decrement($key, $num); 69 | } else { 70 | $this->handler->set($key, 0); 71 | return 0; 72 | } 73 | } 74 | 75 | public function clear() 76 | { 77 | $this->handler->flush(); 78 | } 79 | } -------------------------------------------------------------------------------- /kuxin/cache/redis.php: -------------------------------------------------------------------------------- 1 | handler = new \Redis; 27 | $this->handler->connect($option['host'] ?? '127.0.0.1', $option['port'] ?? '6379'); 28 | if (isset($option['password']) && $option['password'] !== null) { 29 | $this->handler->auth($option['password']); 30 | } 31 | $this->handler->select(($option['db'] ?? 0)); 32 | $this->prefix = $option['prefix'] ?? Config::get('cache.prefix', ''); 33 | } 34 | 35 | public function set(string $key, $value, int $time = 0) 36 | { 37 | $value = Serialize::encode($value); 38 | if (is_int($time) && $time) { 39 | return $this->handler->set($this->prefix . $key, $time, $value); 40 | } else { 41 | return $this->handler->set($this->prefix . $key, $value); 42 | } 43 | } 44 | 45 | public function get($key) 46 | { 47 | $return = $this->handler->get($this->prefix . $key); 48 | if ($return === false) { 49 | return null; 50 | } elseif (is_string($return)) { 51 | return Serialize::decode($return); 52 | } else { 53 | return $return; 54 | } 55 | } 56 | 57 | public function remove($key) 58 | { 59 | $this->handler->delete($this->prefix . $key); 60 | } 61 | 62 | public function inc($key, $num = 1) 63 | { 64 | return $this->handler->incrBy($this->prefix . $key, $num); 65 | } 66 | 67 | public function dec($key, $num = 1) 68 | { 69 | return $this->handler->decrBy($this->prefix . $key, $num); 70 | } 71 | 72 | public function clear() 73 | { 74 | $this->handler->flushDB(); 75 | } 76 | } -------------------------------------------------------------------------------- /kuxin/cache/yac.php: -------------------------------------------------------------------------------- 1 | prefix = $option['prefix'] ?? Config::get('cache.prefix', ''); 28 | $this->handler = new \Yac($this->prefix); 29 | } 30 | 31 | public function set($key, $value, $time = 0) 32 | { 33 | return $this->handler->set($key, Serialize::encode($value), $time); 34 | } 35 | 36 | public function get($key) 37 | { 38 | $return = $this->handler->get($key); 39 | if ($return === false) { 40 | return null; 41 | } elseif (is_string($return)) { 42 | return Serialize::decode($return); 43 | } else { 44 | return $return; 45 | } 46 | } 47 | 48 | public function remove($key) 49 | { 50 | return $this->handler->delete($key); 51 | } 52 | 53 | public function inc($key, $num = 1) 54 | { 55 | $data = $this->get($key); 56 | if ($data) { 57 | $data += $num; 58 | $this->set($key, $data); 59 | return $data; 60 | } 61 | return false; 62 | } 63 | 64 | public function dec($key, $num = 1) 65 | { 66 | $data = $this->get($key); 67 | if ($data) { 68 | $data -= $num; 69 | $this->set($key, $data); 70 | return $data; 71 | } 72 | return false; 73 | } 74 | 75 | public function clear() 76 | { 77 | Yac::flush(); 78 | } 79 | } -------------------------------------------------------------------------------- /kuxin/config.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Config 15 | { 16 | 17 | /** 18 | * 缓存存储变量 19 | * 20 | * @var array 21 | */ 22 | protected static $_config = []; 23 | 24 | /** 25 | * 获取参数 26 | * 27 | * @param string $name 参数名 28 | * @param mixed $defaultVar 默认值 29 | * @return mixed 30 | */ 31 | public static function get(string $name = '', $defaultVar = null) 32 | { 33 | if ($name == '') { 34 | return self::getAll(); 35 | } 36 | $name = strtolower($name); 37 | if (strpos($name, '.')) { 38 | //数组模式 找到返回 39 | $keys = explode('.', $name); 40 | $data = self::$_config; 41 | foreach ($keys as $name) { 42 | if (isset($data[$name])) { 43 | $data = $data[$name]; 44 | } else { 45 | return $defaultVar; 46 | } 47 | } 48 | return $data; 49 | } else { 50 | return self::$_config[$name] ?? $defaultVar; 51 | } 52 | } 53 | 54 | /** 55 | * 获取参数 56 | * 57 | * @return mixed 58 | */ 59 | public static function getAll() 60 | { 61 | return self::$_config; 62 | } 63 | 64 | 65 | /** 66 | * @param string $name 67 | * @param mixed $var 68 | */ 69 | public static function set(string $name, $var): void 70 | { 71 | //数组 调用注册方法 72 | if (is_array($name)) { 73 | self::register($name); 74 | } elseif (strpos($name, '.')) { 75 | $data = self::$_config; 76 | $tmp = &$data; 77 | $fields = explode('.', $name); 78 | foreach ($fields as $field) { 79 | $tmp = &$tmp[$field]; 80 | } 81 | $tmp = $var; 82 | self::$_config = $data; 83 | } else { 84 | self::$_config[$name] = $var; 85 | } 86 | } 87 | 88 | 89 | /** 90 | * 注册配置 91 | * 92 | * @param $config 93 | */ 94 | public static function register($config) 95 | { 96 | if (is_array($config)) { 97 | self::$_config = Arr::merge(self::$_config, $config); 98 | } 99 | } 100 | 101 | /** 102 | * 加载目录配置 103 | * 104 | * @param $dir 105 | */ 106 | public static function LoadDir($dir) 107 | { 108 | // todo 缓存所有配置 109 | $dir = rtrim($dir, '/'); 110 | $files = scandir($dir); 111 | foreach ($files as $file) { 112 | if ($file == '.' || $file == '..') { 113 | continue; 114 | } 115 | $config = Loader::import($dir . '/' . $file); 116 | if (is_array($config)) { 117 | self::$_config = Arr::merge(self::$_config, $config); 118 | } 119 | } 120 | } 121 | } -------------------------------------------------------------------------------- /kuxin/console.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Console 13 | { 14 | 15 | /** 16 | * 命令行参数 17 | * @var array 18 | */ 19 | protected $params = []; 20 | 21 | /** 22 | * Console constructor. 23 | */ 24 | public function __construct() 25 | { 26 | if((int)ini_get('memory_limit')<1024){ 27 | ini_set('memory_limit','1024M'); 28 | } 29 | set_time_limit(0); 30 | $this->params = Registry::get('cli_params', []); 31 | } 32 | 33 | /** 34 | * 初始化 35 | */ 36 | public function init() 37 | { 38 | 39 | } 40 | 41 | /** 42 | * 终端输出 43 | * 44 | * @param $text 45 | * @param $status 46 | * @param $line 47 | * @return mixed 48 | */ 49 | public function show(string $text, string $status = 'text', bool $line = true): void 50 | { 51 | printf(Response::terminal($text, $status, $line)); 52 | } 53 | 54 | /** 55 | * 终端输出 56 | * 57 | * @param string $text 58 | * @param bool $line 59 | * @return mixed 60 | */ 61 | public function info(string $text, bool $line = true): void 62 | { 63 | printf(Response::terminal($text, 'info', $line)); 64 | } 65 | 66 | /** 67 | * @param string $text 68 | * @param bool $line 69 | * @return mixed 70 | */ 71 | public function success(string $text, bool $line = true): void 72 | { 73 | printf(Response::terminal($text, 'success', $line)); 74 | } 75 | 76 | /** 77 | * @param string $text 78 | * @param bool $line 79 | * @return mixed 80 | */ 81 | public function warning(string $text, bool $line = true): void 82 | { 83 | printf(Response::terminal($text, 'success', $line)); 84 | } 85 | 86 | /** 87 | * @param string $text 88 | * @param bool $line 89 | * @return mixed 90 | */ 91 | public function error(string $text, bool $line = true): void 92 | { 93 | printf(Response::terminal($text, 'error', $line)); 94 | } 95 | 96 | 97 | /** 98 | * 获取参数 99 | * @param string $key 100 | * @param string $type 101 | * @param null $default 102 | * @return array|float|int|mixed|null|string 103 | */ 104 | public function param(string $key, string $type = 'int', $default = null) 105 | { 106 | return Input::param($key, $type, $default, $this->params); 107 | } 108 | 109 | /** 110 | * 终端给提示获取用户数据 111 | * @param string $text 112 | * @param string $status 113 | * @return string 114 | */ 115 | public function prompt(string $text = '请输入', string $status = 'text') 116 | { 117 | //提示输入 118 | fwrite(STDOUT, Response::terminal($text . ":", $status, false)); 119 | //获取用户输入数据 120 | $result = trim(fgets(STDIN)); 121 | return $result; 122 | } 123 | } -------------------------------------------------------------------------------- /kuxin/console/migrate.php: -------------------------------------------------------------------------------- 1 | db = DI::DB(); 24 | } 25 | 26 | public function up() 27 | { 28 | $silent = isset($this->params['-s']); 29 | if (false === $records = $this->db->fetchAll("select * from migrate")) { 30 | $res = $this->db->execute('CREATE TABLE `migrate` ( `name` varchar(180) NOT NULL,`time` int(11) DEFAULT NULL,PRIMARY KEY (`name`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;'); 31 | if ($res) { 32 | $records = []; 33 | } else { 34 | return $this->info('初始化失败'); 35 | } 36 | } 37 | $execed = array_column($records, 'name'); 38 | $noExec = []; 39 | foreach (scandir($this->path) as $file) { 40 | if ($file == '.' || $file == '..') { 41 | continue; 42 | } 43 | $name = substr($file, 0, -4); 44 | if (!in_array($name, $execed) && is_file($this->path . '/' . $name . '.php')) { 45 | $noExec[] = $name; 46 | } 47 | } 48 | if ($noExec) { 49 | $silent || $this->info('您将执行以下migrate'); 50 | $silent || $this->info(PHP_EOL . implode(PHP_EOL, $noExec) . PHP_EOL); 51 | if (Config::get('app.env') == 'production' && !$silent) { 52 | $input = $this->prompt('请输入 yes 来确认执行'); 53 | } else { 54 | $input = 'yes'; 55 | } 56 | if ($input == 'yes') { 57 | try { 58 | foreach ($noExec as $name) { 59 | $class = Loader::instance('App\\Migrate\\' . $name); 60 | if (is_callable([ $class, 'up' ])) { 61 | $class->up(); 62 | } 63 | $this->db->execute("INSERT INTO `migrate` (`name`, `time`) VALUES ('{$name}', {$_SERVER['REQUEST_TIME']});"); 64 | } 65 | $silent || $this->info(PHP_EOL . '本次命令执行成功', 'success'); 66 | } catch (\Exception $e) { 67 | $this->info(PHP_EOL . "执行失败: 文件[{$name}] " . $e->getMessage(), 'error'); 68 | } 69 | } else { 70 | $silent || $this->info('您取消了本次命令执行', 'warning'); 71 | } 72 | } else { 73 | $silent || $this->info('没有要执行的migrate', 'warning'); 74 | } 75 | } 76 | 77 | public function down() 78 | { 79 | $silent = isset($this->params['-s']); 80 | $maxRow = $this->db->fetch('select time from migrate order by time desc limit 1'); 81 | if ($maxRow) { 82 | $maxTime = $maxRow['time']; 83 | $records = $this->db->fetchAll("select name from migrate where time={$maxTime}"); 84 | $names = array_column($records, 'name'); 85 | $silent || $this->info('您将回滚以下migrate'); 86 | $silent || $this->info(PHP_EOL . implode(PHP_EOL, $names) . PHP_EOL); 87 | if (Config::get('app.env') == 'production' && !$silent) { 88 | $input = $this->prompt('请输入 yes 来确认执行'); 89 | } else { 90 | $input = 'yes'; 91 | } 92 | if ($input == 'yes') { 93 | try { 94 | foreach ($names as $name) { 95 | $class = Loader::instance('App\\Migrate\\' . $name); 96 | if (is_callable([ $class, 'down' ])) { 97 | $class->down(); 98 | } 99 | $this->db->execute("DELETE FROM `migrate` where name='{$name}';"); 100 | } 101 | $silent || $this->info(PHP_EOL . '本次命令执行成功', 'success'); 102 | } catch (\Exception $e) { 103 | $this->info(PHP_EOL . "执行失败: 文件[{$name}] " . $e->getMessage(), 'error'); 104 | } 105 | } else { 106 | $silent || $this->info('您取消了本次命令执行', 'warning'); 107 | } 108 | } else { 109 | $silent || $this->info('没有要执行的migrate', 'warning'); 110 | } 111 | } 112 | 113 | public function create() 114 | { 115 | $tableName = str_replace([ 'create_', 'insert_', 'alter_', 'delete_' ], '', trim($this->params['argv']['2'])); 116 | if ($tableName == '') { 117 | return $this->info('please input migrate name', 'error'); 118 | } 119 | 120 | $name = str_replace([ ' ', ':', '/', '\\' ], '_', trim($this->params['argv']['2'])); 121 | $filename = 'kx_' . date('YmdHis_') . $name; 122 | $file = $this->path . $filename . '.php'; 123 | $classname = ucfirst($filename); 124 | $content = <<create('{$tableName}',function(){ 140 | \$this->addComand("`id` int(10) unsigned NOT NULL AUTO_INCREMENT"); 141 | \$this->addComand("PRIMARY KEY (`id`)"); 142 | }); 143 | } 144 | 145 | /** 146 | * 回滚修改 147 | * @throws \Exception 148 | */ 149 | public function down() 150 | { 151 | \$this->drop('{$tableName}'); 152 | } 153 | } 154 | PHP; 155 | file_put_contents($file, $content); 156 | $this->info('创建migrate文件 [ ' . $filename . ' ] 成功', 'success'); 157 | } 158 | } -------------------------------------------------------------------------------- /kuxin/controller.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class Controller 18 | { 19 | public $disableActions = []; 20 | 21 | public function middleware() 22 | { 23 | return null; 24 | } 25 | 26 | public function init() 27 | { 28 | } 29 | 30 | /** 31 | * ajax返回 32 | * 33 | * @param $data 34 | * @param string $type 35 | * @return mixed 36 | */ 37 | public function ajax(array $data, string $type = 'json'): array 38 | { 39 | Response::setType($type); 40 | return $data; 41 | } 42 | 43 | /** 44 | * 跳转 45 | * 46 | * @param $url 47 | * @param int $code 48 | */ 49 | public function redirect(string $url, $code = 302): void 50 | { 51 | Response::redirect($url, $code); 52 | exit; 53 | } 54 | } -------------------------------------------------------------------------------- /kuxin/cookie.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Cookie 12 | { 13 | 14 | /** 15 | * 默认配置 16 | * 17 | * @var array 18 | */ 19 | protected static $option = [ 20 | 'prefix' => 'kuxin_', 21 | // cookie 保存时间 22 | 'expire' => 2592000, 23 | // cookie 保存路径 24 | 'path' => '/', 25 | // cookie 有效域名 26 | 'domain' => '', 27 | // cookie 启用安全传输 28 | 'secure' => false, 29 | // httponly设置 30 | 'httponly' => '', 31 | ]; 32 | 33 | public function __construct(array $config = []) 34 | { 35 | $this->init($config); 36 | } 37 | 38 | /** 39 | * 初始化 40 | * 41 | * @param array $config 42 | */ 43 | public static function init(array $config = []): void 44 | { 45 | $config = [ 46 | 'prefix' => Input::param('prefix', 'string', Config::get('cookie_prefix', 'PTCMS_'), $config), 47 | // cookie 保存时间 48 | 'expire' => Input::param('expire', 'int', Config::get('cookie_expire', 2592000), $config), 49 | // cookie 保存路径 50 | 'path' => Input::param('path', 'string', Config::get('cookie_path', '/'), $config), 51 | // cookie 有效域名 52 | 'domain' => Input::param('domain', 'string', Config::get('cookie_domain', ''), $config), 53 | // cookie 启用安全传输 54 | 'secure' => Input::param('secure', 'string', Config::get('cookie_secure', false), $config), 55 | // httponly设置 56 | 'httponly' => Input::param('httponly', 'string', Config::get('cookie_httponly', ''), $config), 57 | ]; 58 | self::$option = array_merge(self::$option, $config); 59 | } 60 | 61 | /** 62 | * 获取 63 | * 64 | * @param $name 65 | * @param mixed $default 66 | * @return mixed 67 | */ 68 | public static function get(string $name, $default = null) 69 | { 70 | $fullname = self::$option['prefix'] . $name; 71 | return $_COOKIE[$fullname] ?? ((is_callable($default) ? $default($name) : $default)); 72 | } 73 | 74 | /** 75 | * 设置cookie 76 | * @param string $name 77 | * @param string $value 78 | * @param array|null $option 79 | */ 80 | public static function set(string $name, string $value = '', $option = null): void 81 | { 82 | if (!is_null($option)) { 83 | if (is_numeric($option)) 84 | $option = ['expire' => $option]; 85 | elseif (is_string($option)) 86 | parse_str($option, $option); 87 | $config = array_merge(self::$option, array_change_key_case($option)); 88 | } else { 89 | $config = self::$option; 90 | } 91 | $name = self::$option['prefix'] . $name; 92 | $expire = !empty($config['expire']) ? time() + $config['expire'] : 0; 93 | setcookie($name, $value, $expire, $config['path'], $config['domain']); 94 | $_COOKIE[$name] = $value; 95 | } 96 | 97 | /** 98 | * 删除单个 99 | * 100 | * @param $name 101 | */ 102 | public static function remove(string $name): void 103 | { 104 | $name = self::$option['prefix'] . $name; 105 | setcookie($name, '', time() - 3600, self::$option['path'], self::$option['domain']); 106 | // 删除指定cookie 107 | unset($_COOKIE[$name]); 108 | } 109 | 110 | /** 111 | * 清空 112 | */ 113 | public static function clear(): void 114 | { 115 | foreach ($_COOKIE as $key => $val) { 116 | if (0 === stripos($key, self::$option['prefix'])) { 117 | setcookie($key, '', time() - 3600, self::$option['path'], self::$option['domain']); 118 | unset($_COOKIE[$key]); 119 | } 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /kuxin/db/migrate.php: -------------------------------------------------------------------------------- 1 | setDb($db); 18 | } 19 | 20 | /** 21 | * @param null $db 22 | * @throws \Exception 23 | */ 24 | public function setDb($db): void 25 | { 26 | if (is_string($db)) { 27 | $this->db = DI::DB($db); 28 | } else if ($db instanceof Mysql) { 29 | $this->db = $db; 30 | } else { 31 | throw new \Exception('param $db error'); 32 | } 33 | } 34 | 35 | /** 36 | * @param string $table 37 | * @param \Closure $func 38 | * @param string $engine 39 | * @return bool 40 | * @throws \Exception 41 | */ 42 | public function create(string $table, Closure $func, $engine = 'innodb') 43 | { 44 | if (!$table) { 45 | return false; 46 | } 47 | //执行回调函数 48 | $func(); 49 | 50 | $field = $this->combineCommand(); 51 | 52 | $engine = $engine ?: Config::get('database.engine', 'innodb'); 53 | 54 | if ($field) { 55 | $sql = "CREATE TABLE IF NOT EXISTS `{$table}` ({$field}) ENGINE={$engine} DEFAULT CHARSET=utf8;"; 56 | return $this->executeSql($sql); 57 | } 58 | } 59 | 60 | /** 61 | * @param string $table 62 | * @param \Closure $func 63 | * @return bool 64 | * @throws \Exception 65 | */ 66 | public function alter(string $table, Closure $func) 67 | { 68 | if (!$table) { 69 | return false; 70 | } 71 | //执行回调函数 72 | $func(); 73 | 74 | $this->comands = array_map(function ($k) use ($table) { 75 | return 'alter table ' . $table . ' ' . trim($k, ';') . ';'; 76 | }, $this->comands); 77 | $sql = $this->combineCommand(); 78 | return $this->executeSql($sql); 79 | } 80 | 81 | 82 | /** 83 | * @param string $table 84 | * @return bool 85 | * @throws \Exception 86 | */ 87 | public function drop(string $table) 88 | { 89 | if (!$table) { 90 | return false; 91 | } 92 | $sql = "DROP TABLE IF EXISTS {$table}"; 93 | return $this->executeSql($sql); 94 | } 95 | 96 | public function addComand(string $string): void 97 | { 98 | $this->comands[] = trim($string, ','); 99 | } 100 | 101 | /** 102 | * @return string 103 | */ 104 | protected function combineCommand(): string 105 | { 106 | $command = implode(',', $this->comands); 107 | $this->comands = []; 108 | return $command; 109 | } 110 | 111 | /** 112 | * @param string $sql 113 | * @return bool 114 | * @throws \Exception 115 | */ 116 | protected function executeSql(string $sql) 117 | { 118 | if ($this->db->execute($sql)) { 119 | return true; 120 | } else { 121 | throw new \Exception($this->db->errorInfo() . PHP_EOL . $this->db->getRealSql($sql)); 122 | } 123 | } 124 | 125 | /** 126 | * @param string $sql 127 | * @return array 128 | * @throws \Exception 129 | */ 130 | protected function fetchAll(string $sql) 131 | { 132 | if (($records = $this->db->fetchAll($sql)) !== false){ 133 | return $records; 134 | }else{ 135 | throw new \Exception($this->db->errorInfo() . PHP_EOL . $this->db->getRealSql($sql)); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /kuxin/db/orm.php: -------------------------------------------------------------------------------- 1 | attribute = $this->original = $this->find($id); 27 | return $this; 28 | } 29 | 30 | /** 31 | * 保存数据 32 | */ 33 | public function save() 34 | { 35 | $data = []; 36 | foreach ($this->attribute as $field => $value) { 37 | if ($field <> $this->getPk() && $value != $this->original[$field]) { 38 | $data[$field] = $value; 39 | } 40 | } 41 | if ($data) { 42 | $this->where([$this->getPk() => $this->original[$this->getPk()]])->update($data); 43 | } 44 | } 45 | 46 | /** 47 | * 修改器 设置数据对象的值 48 | * @access public 49 | * @param string $name 名称 50 | * @param mixed $value 值 51 | * @return void 52 | */ 53 | public function __set($name, $value) 54 | { 55 | if ($name != $this->getPk()) { 56 | $this->attribute[$name] = $value; 57 | } 58 | } 59 | 60 | /** 61 | * 获取器 获取数据对象的值 62 | * @access public 63 | * @param string $name 名称 64 | * @return mixed 65 | */ 66 | public function __get($name) 67 | { 68 | return $this->attribute[$name]; 69 | } 70 | 71 | /** 72 | * 检测数据对象的值 73 | * @access public 74 | * @param string $name 名称 75 | * @return boolean 76 | */ 77 | public function __isset($name) 78 | { 79 | return isset($this->attribute[$name]); 80 | } 81 | 82 | /** 83 | * 销毁数据对象的值 84 | * @access public 85 | * @param string $name 名称 86 | * @return void 87 | */ 88 | public function __unset($name) 89 | { 90 | unset($this->attribute[$name]); 91 | } 92 | 93 | // ArrayAccess 94 | public function offsetSet($name, $value) 95 | { 96 | $this->__set($name, $value); 97 | } 98 | 99 | public function offsetUnset($name) 100 | { 101 | $this->__unset($name); 102 | } 103 | 104 | public function offsetExists($name) 105 | { 106 | return $this->__isset($name); 107 | } 108 | 109 | public function offsetGet($name) 110 | { 111 | return $this->__get($name); 112 | } 113 | 114 | public function __debugInfo() 115 | { 116 | return $this->attribute ?: $this; 117 | } 118 | } -------------------------------------------------------------------------------- /kuxin/di.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class DI 12 | { 13 | 14 | /** 15 | * @param string $onnection 16 | * @return \Kuxin\Cache 17 | */ 18 | public static function Cache(?string $onnection = 'common'): \Kuxin\Cache 19 | { 20 | $hanlder = Registry::get("cache.{$onnection}"); 21 | if (!$hanlder) { 22 | $config = Config::get("cache.{$onnection}"); 23 | if ($config) { 24 | $hanlder = Loader::instance('\\Kuxin\\Cache', [$config]); 25 | if ($hanlder) { 26 | Registry::set("cache.{$onnection}", $hanlder); 27 | } 28 | } else { 29 | trigger_error("缓存节点配置[{$onnection}]不存在", E_USER_ERROR); 30 | } 31 | } 32 | return $hanlder; 33 | } 34 | 35 | /** 36 | * @param string $node 37 | * @return \Kuxin\Storage 38 | */ 39 | public static function Storage(string $node = 'common'): \Kuxin\Storage 40 | { 41 | $hanlder = Registry::get("storage.{$node}"); 42 | if (!$hanlder) { 43 | $config = Config::get("storage.{$node}"); 44 | if ($config) { 45 | $hanlder = Loader::instance('\\Kuxin\\Storage', [$config]); 46 | if ($hanlder) { 47 | Registry::set("storage.{$node}", $hanlder); 48 | } 49 | } else { 50 | trigger_error("Storage节点配置[{$node}]不存在", E_USER_ERROR); 51 | } 52 | } 53 | return $hanlder; 54 | } 55 | 56 | /** 57 | * @param string $node 58 | * @return \Kuxin\Db\Mysql 59 | */ 60 | public static function DB(string $node = 'common'): \Kuxin\Db\Mysql 61 | { 62 | $hanlder = Registry::get("db.{$node}"); 63 | if (!$hanlder) { 64 | $config = Config::get("database.{$node}"); 65 | if ($config) { 66 | $hanlder = Loader::instance('\\Kuxin\Db\\' . $config['driver'], [$config['option']]); 67 | if ($hanlder) { 68 | Registry::set("storage.{$node}", $hanlder); 69 | } 70 | } else { 71 | trigger_error("Db节点配置[{$node}]不存在", E_USER_ERROR); 72 | } 73 | } 74 | return $hanlder; 75 | } 76 | } -------------------------------------------------------------------------------- /kuxin/filter.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Filter 12 | { 13 | 14 | /** 15 | * 默认验证规则 16 | * 17 | * @var array 18 | */ 19 | protected static $validate = [ 20 | //必填 21 | 'require' => '/.+/', 22 | 'required' => '/.+/', 23 | 'string' => '/.+/', 24 | 'str' => '/.+/', 25 | 'mix' => '/.+/', 26 | 'mixed' => '/.+/', 27 | //邮箱 28 | 'email' => '/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/', 29 | //链接 30 | 'url' => '/^https?:\/\/[a-zA-Z0-9]+\.[a-zA-Z0-9]+[\/=\?%\-&_~`@\[\]\':+!]*([^<>\"\"])*$/', 31 | //货币 32 | 'currency' => '/^\d+(\.\d+)?$/', 33 | //数字 34 | 'number' => '/^\d+$/', 35 | //邮编 36 | 'zip' => '/^[0-9]\d{5}$/', 37 | //电话 38 | 'mobile' => '/^1[\d]{10}$/', 39 | //整型 40 | 'integer' => '/^[-\+]?\d+$/', 41 | 'int' => '/^[-\+]?\d+$/', 42 | //带小数点 43 | 'double' => '/^[-\+]?\d+(\.\d+)?$/', 44 | 'float' => '/^[-\+]?\d+(\.\d+)?$/', 45 | //英文字母 46 | 'english' => '/^[a-zA-Z]+$/', 47 | // 48 | 'key' => '/^[\w\-\#]+$/', 49 | //中文汉字 50 | 'chinese' => '/^[\x{4e00}-\x{9fa5}]+$/u', 51 | //拼音 52 | 'pinyin' => '/^[a-zA-Z0-9\-\_]+$/', 53 | //用户名 54 | 'username' => '/^(?!_)(?!.*?_$)[a-zA-Z0-9_\x{4e00}-\x{9fa5}]{3,15}$/u', 55 | //英文字符 56 | 'en' => '/^[a-zA-Z0-9_\s\-\.]+$/', 57 | //中文字符 58 | 'cn' => '/^[\w\s\-\x{4e00}-\x{9fa5}]+$/u', 59 | //安全字符串 60 | 'safestring' => '/^[^\$\?]+$/', 61 | ]; 62 | 63 | /** 64 | * 校验变量 65 | * 66 | * @param $value 67 | * @param $rule 68 | * @return mixed 69 | */ 70 | public static function check($value, $rule) 71 | { 72 | switch ($rule) { 73 | case 'mixed': 74 | case 'mix': 75 | break; 76 | case 'int': 77 | $value = (int)$value; 78 | break; 79 | case 'float': 80 | $value = (float)$value; 81 | break; 82 | case 'str': 83 | case 'string': 84 | $value = (string)$value; 85 | break; 86 | case 'arr': 87 | case 'array': 88 | $value = (array)$value; 89 | break; 90 | case 'time': 91 | $value = strtotime($value) ? $value : '0'; 92 | break; 93 | default: 94 | if (is_array($rule)) { 95 | if (!in_array($value, $rule)) { 96 | $value = null; 97 | } 98 | } elseif (false === Filter::regex($value, $rule)) { 99 | $value = null; 100 | }; 101 | } 102 | return $value; 103 | } 104 | 105 | 106 | /** 107 | * 判断是否符合正则 108 | * 109 | * @param $value 110 | * @param $rule 111 | * @return bool 112 | */ 113 | public static function regex($value, string $rule): bool 114 | { 115 | 116 | if (strpos($rule, '|')) { 117 | $rules = explode('|', $rule); 118 | } else { 119 | $rules = [$rule]; 120 | } 121 | foreach ($rules as $rule) { 122 | if (in_array($rule, ['unique', 'ignore'])) { 123 | continue; 124 | } 125 | if (isset(self::$validate[$rule])) { 126 | $rule = self::$validate[$rule]; 127 | } 128 | if (preg_match($rule, strval($value)) !== 1) { 129 | return false; 130 | } 131 | } 132 | return true; 133 | } 134 | 135 | /** 136 | * 安全的剔除字符 单行等 用于搜索 链接等地方 137 | * 138 | * @param $str 139 | * @return mixed|string 140 | */ 141 | public static function safeWord(string $str): string 142 | { 143 | if (strlen($str) == 0) { 144 | return ''; 145 | } 146 | $str = strip_tags($str); 147 | $badString = '~!@#$%^&*()+|=\\{}[];\'"/<>?'; 148 | $length = strlen($badString); 149 | $pos = 0; 150 | while ($pos < $length) { 151 | $str = str_replace($badString{$pos}, '', $str); 152 | $pos++; 153 | } 154 | return preg_replace('/([\:\r\n\t]+)/', '', $str); 155 | } 156 | 157 | /** 158 | * 过滤掉html字符 159 | * 160 | * @param string $text 161 | * @param string $tags 允许的html标签 162 | * @return mixed|string 163 | */ 164 | public static function safetext(string $text, string $tags = 'br'): string 165 | { 166 | $text = trim($text); 167 | //完全过滤注释 168 | $text = preg_replace('//', '', $text); 169 | //完全过滤动态代码 170 | $text = preg_replace('/<\?|\?' . '>/', '', $text); 171 | //完全过滤js 172 | $text = preg_replace('//', '', $text); 173 | $text = preg_replace('/\&#\d+;/', '', $text); 174 | $text = preg_replace('/\&#\w{4}/', '', $text); 175 | 176 | $text = str_replace('[', '[', $text); 177 | $text = str_replace(']', ']', $text); 178 | $text = str_replace('|', '|', $text); 179 | //br 180 | $text = preg_replace('//i', '[br]', $text); 181 | $text = preg_replace('//i', '[br]', $text); 182 | $text = preg_replace('/(\[br\]\s*){10,}/i', '[br]', $text); 183 | //过滤危险的属性,如:过滤on事件lang js 184 | while (preg_match('/(<[^><]+)( lang|on|action|background|codebase|dynsrc|lowsrc)[^><]+/i', $text, $mat)) { 185 | $text = str_replace($mat[0], $mat[1], $text); 186 | } 187 | while (preg_match('/(<[^><]+)(window\.|javascript:|js:|about:|file:|document\.|vbs:|cookie)([^><]*)/i', $text, $mat)) { 188 | $text = str_replace($mat[0], $mat[1] . $mat[3], $text); 189 | } 190 | //允许的HTML标签 191 | $text = preg_replace('/<(' . $tags . ')( [^><\[\]]*)>/i', '[\1\2]', $text); 192 | $text = preg_replace('/<\/(' . $tags . ')>/Ui', '[/\1]', $text); 193 | //过滤多余html 194 | $text = preg_replace('/<\/?(html|head|meta|link|base|basefont|body|bgsound|title|style|script|form|iframe|frame|frameset|applet|id|ilayer|layer|name|script|style|xml|table|td|th|tr|i|u|strong|img|p|br|div|strong|em|ul|ol|li|dl|dd|dt|a|b|strong)[^><]*>/i', '', $text); 195 | //过滤合法的html标签 196 | while (preg_match('/<([a-z]+)[^><\[\]]*>[^><]*<\/\1>/i', $text, $mat)) { 197 | $text = str_replace($mat[0], str_replace('>', ']', str_replace('<', '[', $mat[0])), $text); 198 | } 199 | //转换引号 200 | while (preg_match('/(\[[^\[\]]*=\s*)(\"|\')([^\2=\[\]]+)\2([^\[\]]*\])/i', $text, $mat)) { 201 | $text = str_replace($mat[0], $mat[1] . '|' . $mat[3] . '|' . $mat[4], $text); 202 | } 203 | //过滤错误的单个引号 204 | while (preg_match('/\[[^\[\]]*(\"|\')[^\[\]]*\]/i', $text, $mat)) { 205 | $text = str_replace($mat[0], str_replace($mat[1], '', $mat[0]), $text); 206 | } 207 | //转换其它所有不合法的 < > 208 | $text = str_replace('<', '<', $text); 209 | $text = str_replace('>', '>', $text); 210 | $text = str_replace('"', '"', $text); 211 | //反转换 212 | $text = str_replace('[', '<', $text); 213 | $text = str_replace(']', '>', $text); 214 | $text = str_replace('|', '"', $text); 215 | //过滤多余空格 216 | $text = str_replace(' ', ' ', $text); 217 | return $text; 218 | } 219 | 220 | /** 221 | * 深度过滤 去掉url 222 | * @param $text 223 | * @return string|string[]|null 224 | */ 225 | public static function clearUrl($text) 226 | { 227 | $text = self::safetext($text); 228 | return $text = preg_replace(self::$validate['ur;'], '', $text); 229 | } 230 | } -------------------------------------------------------------------------------- /kuxin/helper/big5.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Big5 12 | { 13 | 14 | private static $utf8 = "历与丑专业丛东丝丢两严丧个丬丰临为丽举么义乌乐乔习乡书买乱争于亏云亘亚产亩亲亵亸亿仅从仑仓仪们价众优伙会伛伞伟传伤伥伦伧伪伫体余佣佥侠侣侥侦侧侨侩侪侬俣俦俨俩俪俭债倾偬偻偾偿傥傧储傩儿兑兖党兰关兴兹养兽冁内冈册写军农冢冯冲决况冻净凄凉凌减凑凛几凤凫凭凯击凼凿刍划刘则刚创删别刬刭刽刿剀剂剐剑剥剧劝办务劢动励劲劳势勋勐勚匀匦匮区医华协单卖卢卤卧卫却卺厂厅历厉压厌厍厕厢厣厦厨厩厮县参叆叇双发变叙叠叶号叹叽吁后吓吕吗吣吨听启吴呒呓呕呖呗员呙呛呜咏咔咙咛咝咤咴咸哌响哑哒哓哔哕哗哙哜哝哟唛唝唠唡唢唣唤唿啧啬啭啮啰啴啸喷喽喾嗫呵嗳嘘嘤嘱噜噼嚣嚯团园囱围囵国图圆圣圹场坂坏块坚坛坜坝坞坟坠垄垅垆垒垦垧垩垫垭垯垱垲垴埘埙埚埝埯堑堕塆墙壮声壳壶壸处备复够头夸夹夺奁奂奋奖奥妆妇妈妩妪妫姗姜娄娅娆娇娈娱娲娴婳婴婵婶媪嫒嫔嫱嬷孙学孪宁宝实宠审宪宫宽宾寝对寻导寿将尔尘尧尴尸尽层屃屉届属屡屦屿岁岂岖岗岘岙岚岛岭岳岽岿峃峄峡峣峤峥峦崂崃崄崭嵘嵚嵛嵝嵴巅巩巯币帅师帏帐帘帜带帧帮帱帻帼幂幞干并广庄庆庐庑库应庙庞废庼廪开异弃张弥弪弯弹强归当录彟彦彻径徕御忆忏忧忾怀态怂怃怄怅怆怜总怼怿恋恳恶恸恹恺恻恼恽悦悫悬悭悯惊惧惨惩惫惬惭惮惯愍愠愤愦愿慑慭憷懑懒懔戆戋戏戗战戬户扎扑扦执扩扪扫扬扰抚抛抟抠抡抢护报担拟拢拣拥拦拧拨择挂挚挛挜挝挞挟挠挡挢挣挤挥挦捞损捡换捣据捻掳掴掷掸掺掼揸揽揿搀搁搂搅携摄摅摆摇摈摊撄撑撵撷撸撺擞攒敌敛数斋斓斗斩断无旧时旷旸昙昼昽显晋晒晓晔晕晖暂暧札术朴机杀杂权条来杨杩杰极构枞枢枣枥枧枨枪枫枭柜柠柽栀栅标栈栉栊栋栌栎栏树栖样栾桊桠桡桢档桤桥桦桧桨桩梦梼梾检棂椁椟椠椤椭楼榄榇榈榉槚槛槟槠横樯樱橥橱橹橼檐檩欢欤欧歼殁殇残殒殓殚殡殴毁毂毕毙毡毵氇气氢氩氲汇汉污汤汹沓沟没沣沤沥沦沧沨沩沪沵泞泪泶泷泸泺泻泼泽泾洁洒洼浃浅浆浇浈浉浊测浍济浏浐浑浒浓浔浕涂涌涛涝涞涟涠涡涢涣涤润涧涨涩淀渊渌渍渎渐渑渔渖渗温游湾湿溃溅溆溇滗滚滞滟滠满滢滤滥滦滨滩滪漤潆潇潋潍潜潴澜濑濒灏灭灯灵灾灿炀炉炖炜炝点炼炽烁烂烃烛烟烦烧烨烩烫烬热焕焖焘煅煳熘爱爷牍牦牵牺犊犟状犷犸犹狈狍狝狞独狭狮狯狰狱狲猃猎猕猡猪猫猬献獭玑玙玚玛玮环现玱玺珉珏珐珑珰珲琎琏琐琼瑶瑷璇璎瓒瓮瓯电画畅畲畴疖疗疟疠疡疬疮疯疱疴痈痉痒痖痨痪痫痴瘅瘆瘗瘘瘪瘫瘾瘿癞癣癫癯皑皱皲盏盐监盖盗盘眍眦眬着睁睐睑瞒瞩矫矶矾矿砀码砖砗砚砜砺砻砾础硁硅硕硖硗硙硚确硷碍碛碜碱碹磙礼祎祢祯祷祸禀禄禅离秃秆种积称秽秾稆税稣稳穑穷窃窍窑窜窝窥窦窭竖竞笃笋笔笕笺笼笾筑筚筛筜筝筹签简箓箦箧箨箩箪箫篑篓篮篱簖籁籴类籼粜粝粤粪粮糁糇紧絷纟纠纡红纣纤纥约级纨纩纪纫纬纭纮纯纰纱纲纳纴纵纶纷纸纹纺纻纼纽纾线绀绁绂练组绅细织终绉绊绋绌绍绎经绐绑绒结绔绕绖绗绘给绚绛络绝绞统绠绡绢绣绤绥绦继绨绩绪绫绬续绮绯绰绱绲绳维绵绶绷绸绹绺绻综绽绾绿缀缁缂缃缄缅缆缇缈缉缊缋缌缍缎缏缐缑缒缓缔缕编缗缘缙缚缛缜缝缞缟缠缡缢缣缤缥缦缧缨缩缪缫缬缭缮缯缰缱缲缳缴缵罂网罗罚罢罴羁羟羡翘翙翚耢耧耸耻聂聋职聍联聩聪肃肠肤肷肾肿胀胁胆胜胧胨胪胫胶脉脍脏脐脑脓脔脚脱脶脸腊腌腘腭腻腼腽腾膑臜舆舣舰舱舻艰艳艹艺节芈芗芜芦苁苇苈苋苌苍苎苏苘苹茎茏茑茔茕茧荆荐荙荚荛荜荞荟荠荡荣荤荥荦荧荨荩荪荫荬荭荮药莅莜莱莲莳莴莶获莸莹莺莼萚萝萤营萦萧萨葱蒇蒉蒋蒌蓝蓟蓠蓣蓥蓦蔷蔹蔺蔼蕲蕴薮藁藓虏虑虚虫虬虮虽虾虿蚀蚁蚂蚕蚝蚬蛊蛎蛏蛮蛰蛱蛲蛳蛴蜕蜗蜡蝇蝈蝉蝎蝼蝾螀螨蟏衅衔补衬衮袄袅袆袜袭袯装裆裈裢裣裤裥褛褴襁襕见观觃规觅视觇览觉觊觋觌觍觎觏觐觑觞触觯詟誉誊讠计订讣认讥讦讧讨让讪讫训议讯记讱讲讳讴讵讶讷许讹论讻讼讽设访诀证诂诃评诅识诇诈诉诊诋诌词诎诏诐译诒诓诔试诖诗诘诙诚诛诜话诞诟诠诡询诣诤该详诧诨诩诪诫诬语诮误诰诱诲诳说诵诶请诸诹诺读诼诽课诿谀谁谂调谄谅谆谇谈谊谋谌谍谎谏谐谑谒谓谔谕谖谗谘谙谚谛谜谝谞谟谠谡谢谣谤谥谦谧谨谩谪谫谬谭谮谯谰谱谲谳谴谵谶谷豮贝贞负贠贡财责贤败账货质贩贪贫贬购贮贯贰贱贲贳贴贵贶贷贸费贺贻贼贽贾贿赀赁赂赃资赅赆赇赈赉赊赋赌赍赎赏赐赑赒赓赔赕赖赗赘赙赚赛赜赝赞赟赠赡赢赣赪赵赶趋趱趸跃跄跖跞践跶跷跸跹跻踊踌踪踬踯蹑蹒蹰蹿躏躜躯车轧轨轩轪轫转轭轮软轰轱轲轳轴轵轶轷轸轹轺轻轼载轾轿辀辁辂较辄辅辆辇辈辉辊辋辌辍辎辏辐辑辒输辔辕辖辗辘辙辚辞辩辫边辽达迁过迈运还这进远违连迟迩迳迹适选逊递逦逻遗遥邓邝邬邮邹邺邻郁郄郏郐郑郓郦郧郸酝酦酱酽酾酿释里鉅鉴銮錾钆钇针钉钊钋钌钍钎钏钐钑钒钓钔钕钖钗钘钙钚钛钝钞钟钠钡钢钣钤钥钦钧钨钩钪钫钬钭钮钯钰钱钲钳钴钵钶钷钸钹钺钻钼钽钾钿铀铁铂铃铄铅铆铈铉铊铋铍铎铏铐铑铒铕铗铘铙铚铛铜铝铞铟铠铡铢铣铤铥铦铧铨铪铫铬铭铮铯铰铱铲铳铴铵银铷铸铹铺铻铼铽链铿销锁锂锃锄锅锆锇锈锉锊锋锌锍锎锏锐锑锒锓锔锕锖锗错锚锜锞锟锠锡锢锣锤锥锦锨锩锫锬锭键锯锰锱锲锳锴锵锶锷锸锹锺锻锼锽锾锿镀镁镂镃镆镇镈镉镊镌镍镎镏镐镑镒镕镖镗镙镚镛镜镝镞镟镠镡镢镣镤镥镦镧镨镩镪镫镬镭镮镯镰镱镲镳镴镶长门闩闪闫闬闭问闯闰闱闲闳间闵闶闷闸闹闺闻闼闽闾闿阀阁阂阃阄阅阆阇阈阉阊阋阌阍阎阏阐阑阒阓阔阕阖阗阘阙阚阛队阳阴阵阶际陆陇陈陉陕陧陨险随隐隶隽难雏雠雳雾霁霉霭靓静靥鞑鞒鞯鞴韦韧韨韩韪韫韬韵页顶顷顸项顺须顼顽顾顿颀颁颂颃预颅领颇颈颉颊颋颌颍颎颏颐频颒颓颔颕颖颗题颙颚颛颜额颞颟颠颡颢颣颤颥颦颧风飏飐飑飒飓飔飕飖飗飘飙飚飞飨餍饤饥饦饧饨饩饪饫饬饭饮饯饰饱饲饳饴饵饶饷饸饹饺饻饼饽饾饿馀馁馂馃馄馅馆馇馈馉馊馋馌馍馎馏馐馑馒馓馔馕马驭驮驯驰驱驲驳驴驵驶驷驸驹驺驻驼驽驾驿骀骁骂骃骄骅骆骇骈骉骊骋验骍骎骏骐骑骒骓骔骕骖骗骘骙骚骛骜骝骞骟骠骡骢骣骤骥骦骧髅髋髌鬓魇魉鱼鱽鱾鱿鲀鲁鲂鲄鲅鲆鲇鲈鲉鲊鲋鲌鲍鲎鲏鲐鲑鲒鲓鲔鲕鲖鲗鲘鲙鲚鲛鲜鲝鲞鲟鲠鲡鲢鲣鲤鲥鲦鲧鲨鲩鲪鲫鲬鲭鲮鲯鲰鲱鲲鲳鲴鲵鲶鲷鲸鲹鲺鲻鲼鲽鲾鲿鳀鳁鳂鳃鳄鳅鳆鳇鳈鳉鳊鳋鳌鳍鳎鳏鳐鳑鳒鳓鳔鳕鳖鳗鳘鳙鳛鳜鳝鳞鳟鳠鳡鳢鳣鸟鸠鸡鸢鸣鸤鸥鸦鸧鸨鸩鸪鸫鸬鸭鸮鸯鸰鸱鸲鸳鸴鸵鸶鸷鸸鸹鸺鸻鸼鸽鸾鸿鹀鹁鹂鹃鹄鹅鹆鹇鹈鹉鹊鹋鹌鹍鹎鹏鹐鹑鹒鹓鹔鹕鹖鹗鹘鹚鹛鹜鹝鹞鹟鹠鹡鹢鹣鹤鹥鹦鹧鹨鹩鹪鹫鹬鹭鹯鹰鹱鹲鹳鹴鹾麦麸黄黉黡黩黪黾鼋鼌鼍鼗鼹齄齐齑齿龀龁龂龃龄龅龆龇龈龉龊龋龌龙龚龛龟志制咨只里系范松没尝尝闹面准钟别闲干尽脏拼"; 15 | private static $big5 = "歷萬與醜專業叢東絲丟兩嚴喪個爿豐臨為麗舉麼義烏樂喬習鄉書買亂爭於虧雲亙亞產畝親褻嚲億僅從侖倉儀們價眾優夥會傴傘偉傳傷倀倫傖偽佇體餘傭僉俠侶僥偵側僑儈儕儂俁儔儼倆儷儉債傾傯僂僨償儻儐儲儺兒兌兗黨蘭關興茲養獸囅內岡冊寫軍農塚馮衝決況凍淨淒涼淩減湊凜幾鳳鳧憑凱擊氹鑿芻劃劉則剛創刪別剗剄劊劌剴劑剮劍剝劇勸辦務勱動勵勁勞勢勳猛勩勻匭匱區醫華協單賣盧鹵臥衛卻巹廠廳曆厲壓厭厙廁廂厴廈廚廄廝縣參靉靆雙發變敘疊葉號歎嘰籲後嚇呂嗎唚噸聽啟吳嘸囈嘔嚦唄員咼嗆嗚詠哢嚨嚀噝吒噅鹹呱響啞噠嘵嗶噦嘩噲嚌噥喲嘜嗊嘮啢嗩唕喚呼嘖嗇囀齧囉嘽嘯噴嘍嚳囁嗬噯噓嚶囑嚕劈囂謔團園囪圍圇國圖圓聖壙場阪壞塊堅壇壢壩塢墳墜壟壟壚壘墾坰堊墊埡墶壋塏堖塒塤堝墊垵塹墮壪牆壯聲殼壺壼處備複夠頭誇夾奪奩奐奮獎奧妝婦媽嫵嫗媯姍薑婁婭嬈嬌孌娛媧嫻嫿嬰嬋嬸媼嬡嬪嬙嬤孫學孿寧寶實寵審憲宮寬賓寢對尋導壽將爾塵堯尷屍盡層屭屜屆屬屢屨嶼歲豈嶇崗峴嶴嵐島嶺嶽崠巋嶨嶧峽嶢嶠崢巒嶗崍嶮嶄嶸嶔崳嶁脊巔鞏巰幣帥師幃帳簾幟帶幀幫幬幘幗冪襆幹並廣莊慶廬廡庫應廟龐廢廎廩開異棄張彌弳彎彈強歸當錄彠彥徹徑徠禦憶懺憂愾懷態慫憮慪悵愴憐總懟懌戀懇惡慟懨愷惻惱惲悅愨懸慳憫驚懼慘懲憊愜慚憚慣湣慍憤憒願懾憖怵懣懶懍戇戔戲戧戰戩戶紮撲扡執擴捫掃揚擾撫拋摶摳掄搶護報擔擬攏揀擁攔擰撥擇掛摯攣掗撾撻挾撓擋撟掙擠揮撏撈損撿換搗據撚擄摑擲撣摻摜摣攬撳攙擱摟攪攜攝攄擺搖擯攤攖撐攆擷擼攛擻攢敵斂數齋斕鬥斬斷無舊時曠暘曇晝曨顯晉曬曉曄暈暉暫曖劄術樸機殺雜權條來楊榪傑極構樅樞棗櫪梘棖槍楓梟櫃檸檉梔柵標棧櫛櫳棟櫨櫟欄樹棲樣欒棬椏橈楨檔榿橋樺檜槳樁夢檮棶檢欞槨櫝槧欏橢樓欖櫬櫚櫸檟檻檳櫧橫檣櫻櫫櫥櫓櫞簷檁歡歟歐殲歿殤殘殞殮殫殯毆毀轂畢斃氈毿氌氣氫氬氳彙漢汙湯洶遝溝沒灃漚瀝淪滄渢溈滬濔濘淚澩瀧瀘濼瀉潑澤涇潔灑窪浹淺漿澆湞溮濁測澮濟瀏滻渾滸濃潯濜塗湧濤澇淶漣潿渦溳渙滌潤澗漲澀澱淵淥漬瀆漸澠漁瀋滲溫遊灣濕潰濺漵漊潷滾滯灩灄滿瀅濾濫灤濱灘澦濫瀠瀟瀲濰潛瀦瀾瀨瀕灝滅燈靈災燦煬爐燉煒熗點煉熾爍爛烴燭煙煩燒燁燴燙燼熱煥燜燾煆糊溜愛爺牘犛牽犧犢強狀獷獁猶狽麅獮獰獨狹獅獪猙獄猻獫獵獼玀豬貓蝟獻獺璣璵瑒瑪瑋環現瑲璽瑉玨琺瓏璫琿璡璉瑣瓊瑤璦璿瓔瓚甕甌電畫暢佘疇癤療瘧癘瘍鬁瘡瘋皰屙癰痙癢瘂癆瘓癇癡癉瘮瘞瘺癟癱癮癭癩癬癲臒皚皺皸盞鹽監蓋盜盤瞘眥矓著睜睞瞼瞞矚矯磯礬礦碭碼磚硨硯碸礪礱礫礎硜矽碩硤磽磑礄確鹼礙磧磣堿镟滾禮禕禰禎禱禍稟祿禪離禿稈種積稱穢穠穭稅穌穩穡窮竊竅窯竄窩窺竇窶豎競篤筍筆筧箋籠籩築篳篩簹箏籌簽簡籙簀篋籜籮簞簫簣簍籃籬籪籟糴類秈糶糲粵糞糧糝餱緊縶糸糾紆紅紂纖紇約級紈纊紀紉緯紜紘純紕紗綱納紝縱綸紛紙紋紡紵紖紐紓線紺絏紱練組紳細織終縐絆紼絀紹繹經紿綁絨結絝繞絰絎繪給絢絳絡絕絞統綆綃絹繡綌綏絛繼綈績緒綾緓續綺緋綽緔緄繩維綿綬繃綢綯綹綣綜綻綰綠綴緇緙緗緘緬纜緹緲緝縕繢緦綞緞緶線緱縋緩締縷編緡緣縉縛縟縝縫縗縞纏縭縊縑繽縹縵縲纓縮繆繅纈繚繕繒韁繾繰繯繳纘罌網羅罰罷羆羈羥羨翹翽翬耮耬聳恥聶聾職聹聯聵聰肅腸膚膁腎腫脹脅膽勝朧腖臚脛膠脈膾髒臍腦膿臠腳脫腡臉臘醃膕齶膩靦膃騰臏臢輿艤艦艙艫艱豔艸藝節羋薌蕪蘆蓯葦藶莧萇蒼苧蘇檾蘋莖蘢蔦塋煢繭荊薦薘莢蕘蓽蕎薈薺蕩榮葷滎犖熒蕁藎蓀蔭蕒葒葤藥蒞蓧萊蓮蒔萵薟獲蕕瑩鶯蓴蘀蘿螢營縈蕭薩蔥蕆蕢蔣蔞藍薊蘺蕷鎣驀薔蘞藺藹蘄蘊藪槁蘚虜慮虛蟲虯蟣雖蝦蠆蝕蟻螞蠶蠔蜆蠱蠣蟶蠻蟄蛺蟯螄蠐蛻蝸蠟蠅蟈蟬蠍螻蠑螿蟎蠨釁銜補襯袞襖嫋褘襪襲襏裝襠褌褳襝褲襇褸襤繈襴見觀覎規覓視覘覽覺覬覡覿覥覦覯覲覷觴觸觶讋譽謄訁計訂訃認譏訐訌討讓訕訖訓議訊記訒講諱謳詎訝訥許訛論訩訟諷設訪訣證詁訶評詛識詗詐訴診詆謅詞詘詔詖譯詒誆誄試詿詩詰詼誠誅詵話誕詬詮詭詢詣諍該詳詫諢詡譸誡誣語誚誤誥誘誨誑說誦誒請諸諏諾讀諑誹課諉諛誰諗調諂諒諄誶談誼謀諶諜謊諫諧謔謁謂諤諭諼讒諮諳諺諦謎諞諝謨讜謖謝謠謗諡謙謐謹謾謫譾謬譚譖譙讕譜譎讞譴譫讖穀豶貝貞負貟貢財責賢敗賬貨質販貪貧貶購貯貫貳賤賁貰貼貴貺貸貿費賀貽賊贄賈賄貲賃賂贓資賅贐賕賑賚賒賦賭齎贖賞賜贔賙賡賠賧賴賵贅賻賺賽賾贗讚贇贈贍贏贛赬趙趕趨趲躉躍蹌蹠躒踐躂蹺蹕躚躋踴躊蹤躓躑躡蹣躕躥躪躦軀車軋軌軒軑軔轉軛輪軟轟軲軻轤軸軹軼軤軫轢軺輕軾載輊轎輈輇輅較輒輔輛輦輩輝輥輞輬輟輜輳輻輯轀輸轡轅轄輾轆轍轔辭辯辮邊遼達遷過邁運還這進遠違連遲邇逕跡適選遜遞邐邏遺遙鄧鄺鄔郵鄒鄴鄰鬱郤郟鄶鄭鄆酈鄖鄲醞醱醬釅釃釀釋裏钜鑒鑾鏨釓釔針釘釗釙釕釷釺釧釤鈒釩釣鍆釹鍚釵鈃鈣鈈鈦鈍鈔鍾鈉鋇鋼鈑鈐鑰欽鈞鎢鉤鈧鈁鈥鈄鈕鈀鈺錢鉦鉗鈷缽鈳鉕鈽鈸鉞鑽鉬鉭鉀鈿鈾鐵鉑鈴鑠鉛鉚鈰鉉鉈鉍鈹鐸鉶銬銠鉺銪鋏鋣鐃銍鐺銅鋁銱銦鎧鍘銖銑鋌銩銛鏵銓鉿銚鉻銘錚銫鉸銥鏟銃鐋銨銀銣鑄鐒鋪鋙錸鋱鏈鏗銷鎖鋰鋥鋤鍋鋯鋨鏽銼鋝鋒鋅鋶鐦鐧銳銻鋃鋟鋦錒錆鍺錯錨錡錁錕錩錫錮鑼錘錐錦鍁錈錇錟錠鍵鋸錳錙鍥鍈鍇鏘鍶鍔鍤鍬鍾鍛鎪鍠鍰鎄鍍鎂鏤鎡鏌鎮鎛鎘鑷鐫鎳鎿鎦鎬鎊鎰鎔鏢鏜鏍鏰鏞鏡鏑鏃鏇鏐鐔钁鐐鏷鑥鐓鑭鐠鑹鏹鐙鑊鐳鐶鐲鐮鐿鑔鑣鑞鑲長門閂閃閆閈閉問闖閏闈閑閎間閔閌悶閘鬧閨聞闥閩閭闓閥閣閡閫鬮閱閬闍閾閹閶鬩閿閽閻閼闡闌闃闠闊闋闔闐闒闕闞闤隊陽陰陣階際陸隴陳陘陝隉隕險隨隱隸雋難雛讎靂霧霽黴靄靚靜靨韃鞽韉韝韋韌韍韓韙韞韜韻頁頂頃頇項順須頊頑顧頓頎頒頌頏預顱領頗頸頡頰頲頜潁熲頦頤頻頮頹頷頴穎顆題顒顎顓顏額顳顢顛顙顥纇顫顬顰顴風颺颭颮颯颶颸颼颻飀飄飆飆飛饗饜飣饑飥餳飩餼飪飫飭飯飲餞飾飽飼飿飴餌饒餉餄餎餃餏餅餑餖餓餘餒餕餜餛餡館餷饋餶餿饞饁饃餺餾饈饉饅饊饌饢馬馭馱馴馳驅馹駁驢駔駛駟駙駒騶駐駝駑駕驛駘驍罵駰驕驊駱駭駢驫驪騁驗騂駸駿騏騎騍騅騌驌驂騙騭騤騷騖驁騮騫騸驃騾驄驏驟驥驦驤髏髖髕鬢魘魎魚魛魢魷魨魯魴魺鮁鮃鯰鱸鮋鮓鮒鮊鮑鱟鮍鮐鮭鮚鮳鮪鮞鮦鰂鮜鱠鱭鮫鮮鮺鯗鱘鯁鱺鰱鰹鯉鰣鰷鯀鯊鯇鮶鯽鯒鯖鯪鯕鯫鯡鯤鯧鯝鯢鯰鯛鯨鯵鯴鯔鱝鰈鰏鱨鯷鰮鰃鰓鱷鰍鰒鰉鰁鱂鯿鰠鼇鰭鰨鰥鰩鰟鰜鰳鰾鱈鱉鰻鰵鱅鰼鱖鱔鱗鱒鱯鱤鱧鱣鳥鳩雞鳶鳴鳲鷗鴉鶬鴇鴆鴣鶇鸕鴨鴞鴦鴒鴟鴝鴛鴬鴕鷥鷙鴯鴰鵂鴴鵃鴿鸞鴻鵐鵓鸝鵑鵠鵝鵒鷳鵜鵡鵲鶓鵪鶤鵯鵬鵮鶉鶊鵷鷫鶘鶡鶚鶻鶿鶥鶩鷊鷂鶲鶹鶺鷁鶼鶴鷖鸚鷓鷚鷯鷦鷲鷸鷺鸇鷹鸌鸏鸛鸘鹺麥麩黃黌黶黷黲黽黿鼂鼉鞀鼴齇齊齏齒齔齕齗齟齡齙齠齜齦齬齪齲齷龍龔龕龜誌製谘隻裡係範鬆冇嚐嘗鬨麵準鐘彆閒乾儘臟拚"; 16 | 17 | /** 18 | * utf8转换big5 19 | * 20 | * @param $str 21 | * @return string 22 | */ 23 | public static function fromutf8($str) 24 | { 25 | $str_t = ''; 26 | $len = strlen($str); 27 | $a = 0; 28 | while ($a < $len) { 29 | if (ord($str{$a}) >= 224 && ord($str{$a}) <= 239) { 30 | if (($temp = strpos(self::$utf8, $str{$a} . $str{$a + 1} . $str{$a + 2})) !== false) { 31 | $str_t .= self::$big5{$temp} . self::$big5{$temp + 1} . self::$big5{$temp + 2}; 32 | $a += 3; 33 | continue; 34 | } 35 | } 36 | $str_t .= $str{$a}; 37 | $a += 1; 38 | } 39 | return $str_t; 40 | } 41 | 42 | /** 43 | * big5转换utf8 44 | * 45 | * @param $str 46 | * @return string 47 | */ 48 | public static function toutf8($str) 49 | { 50 | $str_t = ''; 51 | $len = strlen($str); 52 | $a = 0; 53 | while ($a < $len) { 54 | if (ord($str{$a}) >= 224 && ord($str{$a}) <= 239) { 55 | if (($temp = strpos(self::$big5, $str{$a} . $str{$a + 1} . $str{$a + 2})) !== false) { 56 | $str_t .= self::$utf8{$temp} . self::$utf8{$temp + 1} . self::$utf8{$temp + 2}; 57 | $a += 3; 58 | continue; 59 | } 60 | } 61 | $str_t .= $str{$a}; 62 | $a += 1; 63 | } 64 | return $str_t; 65 | } 66 | } -------------------------------------------------------------------------------- /kuxin/helper/data/pinyin.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ptcms/RuleApi/9a493d8deb726e33cf702be183ca52871470a593/kuxin/helper/data/pinyin.dat -------------------------------------------------------------------------------- /kuxin/helper/emoji.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Http 15 | { 16 | 17 | 18 | /** 19 | * @param $url 20 | * @param array $params 21 | * @param string $method 22 | * @param array $header 23 | * @param array $option 24 | * @return bool|mixed 25 | */ 26 | public static function curl($url, $params = [], $method = 'GET', $header = [], $option = []) 27 | { 28 | $opts = [ 29 | CURLOPT_TIMEOUT => Config::get('http.timeout', 15), 30 | CURLOPT_CONNECTTIMEOUT => Config::get('http.timeout', 15), 31 | CURLOPT_RETURNTRANSFER => 1, 32 | CURLOPT_FOLLOWLOCATION => 1, 33 | CURLOPT_HEADER => 0, 34 | //CURLOPT_FILETIME => true, 35 | //CURLOPT_FRESH_CONNECT => false, 36 | //CURLOPT_MAXREDIRS => 5, 37 | CURLOPT_USERAGENT => Config::get('http.user_agent', 'PTCMS Framework Http Client'), 38 | CURLOPT_REFERER => isset($header['referer']) ? $header['referer'] : $url, 39 | CURLOPT_NOSIGNAL => 1, 40 | CURLOPT_ENCODING => 'gzip, deflate', 41 | CURLOPT_SSL_VERIFYPEER => false, 42 | CURLOPT_SSL_VERIFYHOST => false, 43 | ]; 44 | 45 | if (Config::get('http.proxy.power')) { 46 | $opts[CURLOPT_PROXY] = Config::get('http.proxy.host'); 47 | $opts[CURLOPT_PROXYPORT] = Config::get('http.proxy.port'); 48 | $opts[CURLOPT_PROXYTYPE] = Config::get('http.proxy.type'); 49 | if (!empty($user = Config::get('http.proxy.username')) && !empty($pwd = Config::get('http.proxy.password'))) { 50 | $opts[CURLOPT_PROXYUSERPWD] = "{$user}:{$pwd}"; 51 | } 52 | } 53 | 54 | if (isset($header['cookie'])) { 55 | $opts[CURLOPT_COOKIE] = $header['cookie']; 56 | unset($header['cookie']); 57 | } 58 | 59 | if (isset($header['useragent'])) { 60 | $opts[CURLOPT_USERAGENT] = $header['useragent']; 61 | unset($header['useragent']); 62 | } 63 | 64 | if (isset($header['showheader'])) { 65 | $opts[CURLOPT_HEADER] = true; 66 | unset($header['showheader']); 67 | } 68 | 69 | if (!empty($header)) { 70 | foreach ($header as $key => $item) { 71 | $opts[CURLOPT_HTTPHEADER][] = $key . ': ' . $item; 72 | } 73 | } 74 | //补充配置 75 | foreach ($option as $k => $v) { 76 | $opts[$k] = $v; 77 | } 78 | $opts[CURLOPT_URL] = $url; 79 | /* 根据请求类型设置特定参数 */ 80 | switch (strtoupper($method)) { 81 | case 'GET': 82 | if ($params) { 83 | if (is_array($params)) { 84 | $params = http_build_query($params); 85 | } 86 | if (strpos($url, '?')) { 87 | $url .= '&' . $params; 88 | } else { 89 | $url .= '?' . $params; 90 | } 91 | $opts[CURLOPT_URL] = $url; 92 | } 93 | break; 94 | case 'POST': 95 | //判断是否传输文件 96 | $opts[CURLOPT_POST] = 1; 97 | $opts[CURLOPT_POSTFIELDS] = $params; 98 | break; 99 | case 'PUT': 100 | $opts[CURLOPT_CUSTOMREQUEST] = 'PUT'; 101 | $opts[CURLOPT_POSTFIELDS] = $params; 102 | break; 103 | case 'HEAD': 104 | $opts[CURLOPT_CUSTOMREQUEST] = 'HEAD'; 105 | $opts[CURLOPT_NOBODY] = 1; 106 | break; 107 | case 'DELETE': 108 | $opts[CURLOPT_CUSTOMREQUEST] = 'DELETE'; 109 | $opts[CURLOPT_POSTFIELDS] = $params; 110 | break; 111 | default: 112 | exit('不支持的请求方式!'); 113 | } 114 | 115 | /* 初始化并执行curl请求 */ 116 | $ch = curl_init(); 117 | curl_setopt_array($ch, $opts); 118 | $data = curl_exec($ch); 119 | $error = curl_error($ch); 120 | $errno = curl_errno($ch); 121 | curl_close($ch); 122 | if ($error && $errno !== 28) { 123 | if (Config::get('app.debug')) { 124 | trigger_error('Curl获取远程内容错误!原因:' . $error . ' 地址:' . $url); 125 | } else { 126 | Log::record('Curl获取远程内容错误!原因:' . $error . ' 地址:' . $url); 127 | } 128 | return false; 129 | } 130 | return $data; 131 | } 132 | 133 | public static function get($url, $data = [], $header = [], $option = []) 134 | { 135 | return self::curl($url, $data, 'GET', $header, $option); 136 | } 137 | 138 | public static function post($url, $data = [], $header = [], $option = []) 139 | { 140 | return self::curl($url, $data, 'POST', $header, $option); 141 | } 142 | 143 | public static function getJson($url, $data = []) 144 | { 145 | return Json::decode(trim(self::curl($url, $data, 'GET'))); 146 | } 147 | 148 | public static function postJson($url, $data = [], $header = []) 149 | { 150 | return Json::decode(trim(self::curl($url, $data, 'POST', $header))); 151 | } 152 | 153 | /** 154 | * 触发url 155 | * 156 | * @param $url 157 | */ 158 | public static function trigger($url) 159 | { 160 | if (stripos($url, 'http') === 0) { 161 | if (defined('CURLOPT_TIMEOUT_MS')) { 162 | self::curl($url, [], 'GET', [], [ 163 | CURLOPT_TIMEOUT_MS => 300, 164 | CURLOPT_CONNECTTIMEOUT_MS => 300, 165 | ]); 166 | } elseif (function_exists('file_get_contents')) { 167 | $context = [ 168 | 'http' => [ 169 | 'timeout' => 0, 170 | ], 171 | ]; 172 | $stream_context = stream_context_create($context); 173 | file_get_contents($url, false, $stream_context); 174 | } else { 175 | stream_context_set_default( 176 | [ 177 | 'http' => [ 178 | 'method' => 'HEAD', 179 | ], 180 | ] 181 | ); 182 | get_headers($url); 183 | } 184 | } 185 | } 186 | 187 | /** 188 | * 解析头部内容 189 | * @param $response 190 | * @return array 191 | */ 192 | public static function parse_headers($response) 193 | { 194 | $result = []; 195 | $headers = explode("\r\n\r\n", $response, 2)[0]; 196 | $headers = explode("\n", $headers); 197 | array_shift($headers); 198 | foreach ($headers as $header) { 199 | list($key, $value) = explode(':', $header, 2); 200 | $result[strtolower($key)] = trim($value); 201 | } 202 | return $result; 203 | } 204 | 205 | /** 206 | * 解析内容 207 | * @param $response 208 | * @return array 209 | */ 210 | public static function parse_content($response) 211 | { 212 | return explode("\r\n\r\n", $response, 2)[1]; 213 | } 214 | } -------------------------------------------------------------------------------- /kuxin/helper/json.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Json 12 | { 13 | 14 | /** 15 | * @param $data 16 | * @param int $format 17 | * @return string 18 | */ 19 | public static function encode($data, $format = JSON_UNESCAPED_UNICODE) 20 | { 21 | return json_encode($data, $format); 22 | } 23 | 24 | /** 25 | * @param $data 26 | * @param bool $assoc 27 | * @return mixed 28 | */ 29 | public static function decode($data, $assoc = true) 30 | { 31 | return json_decode($data, $assoc); 32 | } 33 | } -------------------------------------------------------------------------------- /kuxin/helper/jsonp.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class Jsonp 15 | { 16 | 17 | /** 18 | * @param $data 19 | * @param int $format 20 | * @return string 21 | */ 22 | public static function encode($data, $format = JSON_UNESCAPED_UNICODE) 23 | { 24 | $callback = Input::get(Config::get('jsonp_callback'), 'en', 'ptcms_jsonp'); 25 | return $callback . '(' . json_encode($data, $format) . ');'; 26 | } 27 | 28 | /** 29 | * @param $data 30 | * @param bool $assoc 31 | * @return mixed|null 32 | */ 33 | public static function decode($data, $assoc = true) 34 | { 35 | if (strpos($data, '(')) { 36 | $data = explode('(', substr($data, 0, -2), 2)[1]; 37 | return json_decode($data, $assoc); 38 | } else { 39 | return null; 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /kuxin/helper/math.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Math 12 | { 13 | 14 | /** 15 | * 取子id 16 | * 17 | * @param $id 18 | * @return float 19 | */ 20 | public static function subid($id) 21 | { 22 | $id = (int)$id; 23 | return floor($id / 1000); 24 | } 25 | 26 | /** 27 | * 取子id 28 | * 29 | * @param $id 30 | * @return float 31 | */ 32 | public static function subIdPlus($id) 33 | { 34 | $id = (int)$id; 35 | return Ceil($id / 1000); 36 | } 37 | 38 | /** 39 | * 文件大小格式化 40 | * 41 | * @param integer $size 初始文件大小,单位为byte 42 | * @return string 格式化后的文件大小和单位数组,单位为byte、KB、MB、GB、TB 43 | */ 44 | public static function fileSizeFormat($size = 0, $dec = 2) 45 | { 46 | if(!$size){ 47 | return '未知'; 48 | } 49 | $unit = ["B", "KB", "MB", "GB", "TB", "PB"]; 50 | $pos = 0; 51 | while ($size >= 1024) { 52 | $size /= 1024; 53 | $pos++; 54 | } 55 | $result['size'] = round($size, $dec); 56 | $result['unit'] = $unit[$pos]; 57 | return $result['size'] . $result['unit']; 58 | } 59 | 60 | /** 61 | * @param mixed $num 科学计数法字符串 如 2.1E-5 62 | * @param int $double 小数点保留位数 默认5位 63 | * @return string 64 | */ 65 | public static function ScToNum($num, $double = 5) 66 | { 67 | if (false !== stripos($num, "e")) { 68 | $a = explode("e", strtolower($num)); 69 | return bcmul($a[0], bcpow(10, $a[1], $double), $double); 70 | } 71 | return $num; 72 | } 73 | } -------------------------------------------------------------------------------- /kuxin/helper/pinyin.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class Pinyin 14 | { 15 | 16 | protected static $data = null; 17 | 18 | /** 19 | * @return mixed 20 | */ 21 | protected static function getdata() 22 | { 23 | if (self::$data === null) { 24 | $fp = fopen(__DIR__ . '/data/pinyin.dat', 'r') or exit('读取字典失败'); 25 | while (!feof($fp)) { 26 | $line = trim(fgets($fp)); 27 | self::$data[$line[0] . $line[1]] = substr($line, 3, strlen($line) - 3); 28 | } 29 | fclose($fp); 30 | } 31 | return self::$data; 32 | } 33 | 34 | /** 35 | * 转换成拼音 36 | * 37 | * @param string $str 待转换的字符串 38 | * @param bool $isfirst 是否只需要首字符 39 | * @param string $default 匹配不到默认显示字符 40 | * @return string 41 | */ 42 | public static function change(string $str, $isfirst = null, $default = '_') 43 | { 44 | $str = iconv('UTF-8', 'GBK//ignore', $str); 45 | $isfirst = $isfirst === null ? (Config::get('pinyin.ucfirst', 1)) : $isfirst; 46 | $data = self::getdata(); 47 | $restr = ''; 48 | for ($i = 0, $j = strlen($str); $i < $j; $i++) { 49 | if (ord($str[$i]) > 0x80) { 50 | $c = $str[$i] . $str[$i + 1]; 51 | ++$i; 52 | if (isset($data[$c])) { 53 | $restr .= $isfirst ? ucfirst($data[$c]) : $data[$c]; 54 | } else { 55 | $restr .= $default; 56 | } 57 | } elseif (preg_match("/[\w\-]/i", $str[$i])) { 58 | $restr .= $str[$i]; 59 | } else { 60 | $restr .= $default; 61 | } 62 | } 63 | return $restr; 64 | } 65 | } -------------------------------------------------------------------------------- /kuxin/helper/serialize.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Serialize 12 | { 13 | 14 | /** 15 | * 序列化 16 | * 17 | * @param $data 18 | * @return string 19 | */ 20 | public static function encode($data) 21 | { 22 | if (function_exists('swoole_serialize')) { 23 | return swoole_serialize($data); 24 | } else { 25 | return serialize($data); 26 | } 27 | } 28 | 29 | /** 30 | * 反序列化 31 | * 32 | * @param $data 33 | * @return mixed 34 | */ 35 | public static function decode($data) 36 | { 37 | if(is_string($data) && $data){ 38 | if (function_exists('swoole_unserialize')) { 39 | return swoole_unserialize($data); 40 | } else { 41 | return unserialize($data); 42 | } 43 | }else{ 44 | return null; 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /kuxin/helper/str.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Str 12 | { 13 | 14 | /** 15 | * 字符串截取,支持中文和其他编码 16 | * 17 | * @param string $string 需要转换的字符串 18 | * @param string $length 截取长度 19 | * @param string $suffix 截断显示字符 20 | * @param int $start 开始位置 21 | * @return string 22 | */ 23 | public static function truncate($string, $length, $suffix = '', $start = 0) 24 | { 25 | if (empty($string) or empty($length) or strlen($string) < $length) return $string; 26 | if (function_exists('mb_substr')) { 27 | $slice = mb_substr($string, $start, $length, 'utf-8'); 28 | } elseif (function_exists('iconv_substr')) { 29 | $slice = iconv_substr($string, $start, $length, 'utf-8'); 30 | } else { 31 | preg_match_all('/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/', $string, $match); 32 | $slice = implode('', array_slice(reset($match), $start, $length)); 33 | } 34 | return $slice . $suffix; 35 | } 36 | } -------------------------------------------------------------------------------- /kuxin/helper/upload.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Upload 12 | { 13 | 14 | /** 15 | * @var \Kuxin\Storage 16 | */ 17 | public $storage = null; 18 | //$_FILES的文件信息 19 | public $fileinfo; 20 | //自定义文件名 21 | public $fileName; 22 | //自定义文件存放目录 23 | public $fileDir; 24 | //自定义文件存放完整路径 25 | public $filePath; 26 | //自定义允许文件后缀 27 | public $allowType = "jpg|png|gif|txt|bmp|ico|doc|xls|jpeg|zip|rar"; 28 | //自定义允许文件mime 29 | public $allowMime = []; 30 | //自定义文件大小 Kb 31 | public $allowMaxSize = 2048; 32 | 33 | public function __construct($storage) 34 | { 35 | $this->storage = $storage; 36 | } 37 | 38 | /** 39 | * 临时文件 40 | * 41 | * @param $fileinfo 42 | */ 43 | public function setFile($fileinfo) 44 | { 45 | $this->fileinfo = $fileinfo; 46 | } 47 | 48 | /** 49 | * 设置上传的文件名 50 | * 51 | * @param $filename 52 | */ 53 | public function setName($filename) 54 | { 55 | $this->fileName = $filename; 56 | } 57 | 58 | /** 59 | * 设置上传的文件路径 60 | * 61 | * @param $filedir 62 | */ 63 | public function setDir($filedir) 64 | { 65 | $this->fileDir = $filedir; 66 | } 67 | 68 | /** 69 | * 设置上传的文件后缀 70 | * 71 | * @param $filetype 72 | */ 73 | public function setType($filetype) 74 | { 75 | $this->allowType = $filetype; 76 | } 77 | 78 | /** 79 | * 设置上传的文件大小 80 | * 81 | * @param $filesize 82 | */ 83 | public function setSize($filesize) 84 | { 85 | $this->allowMaxSize = $filesize; 86 | } 87 | 88 | /** 89 | *检测文件大小 90 | */ 91 | private function checkSize() 92 | { 93 | return $this->fileinfo['size'] > 0 && ($this->fileinfo['size'] <= $this->allowMaxSize * 1024); 94 | } 95 | 96 | /** 97 | *检测文件后缀 98 | */ 99 | private function checkType() 100 | { 101 | return in_array($this->getType(), explode("|", strtolower($this->allowType))); 102 | } 103 | 104 | /** 105 | *获取文件后缀 106 | */ 107 | private function getType() 108 | { 109 | return strtolower(pathinfo($this->fileinfo['name'], PATHINFO_EXTENSION)); 110 | } 111 | 112 | 113 | /** 114 | *获取文件完整路径 115 | */ 116 | private function getPath() 117 | { 118 | if (empty($this->fileDir)) 119 | $this->fileDir = date('Ym') . '/' . date('d'); 120 | if (!$this->fileName) 121 | $this->fileName = md5($this->fileinfo['name'] . $this->fileinfo['size']); 122 | $this->filePath = $this->fileDir . '/' . $this->fileName . "." . $this->getType(); 123 | } 124 | 125 | /** 126 | * 检测mime类型 127 | * 128 | * @return bool 129 | */ 130 | protected function checkMime() 131 | { 132 | return !(!empty($this->allowMime) && !in_array($this->fileinfo['type'], $this->allowMime)); 133 | } 134 | 135 | /** 136 | * 错误返回 137 | * 138 | * @param $info 139 | * @return array 140 | */ 141 | private function error($info) 142 | { 143 | return ['status' => 0, 'info' => $info]; 144 | } 145 | 146 | /** 147 | *上传文件 148 | */ 149 | public function save() 150 | { 151 | if ($this->fileinfo['error'] !== 0) { 152 | $this->error($this->geterrorinfo($this->fileinfo['error'])); 153 | } 154 | //检测文件大小 155 | if (!$this->checkSize()) { 156 | return $this->error("上传附件不得超过" . $this->allowMaxSize . "KB"); 157 | } 158 | //校验mime信息 159 | if (!$this->checkMime()) { 160 | return $this->error("上传文件MIME类型不允许!"); 161 | } 162 | //不符则警告 163 | if (!$this->checkType()) { 164 | return $this->error("正确的扩展名必须为" . $this->allowType . "其中的一种!"); 165 | } 166 | //检查是否合法上传 167 | if (!is_uploaded_file($this->fileinfo['tmp_name'])) { 168 | return $this->error("非法上传文件!"); 169 | } 170 | // 获取上传文件的保存信息 171 | $this->getpath(); 172 | 173 | 174 | if ($this->write(file_get_contents($this->fileinfo['tmp_name']))) { 175 | $info['ext'] = $this->getType(); 176 | $info['fileurl'] = $this->storage->getUrl($this->filePath); 177 | $info['filepath'] = $this->filePath; 178 | $info['filename'] = $this->fileinfo['name']; 179 | $info['hash'] = md5_file($this->fileinfo['tmp_name']); 180 | $info['size'] = $this->fileinfo['size']; 181 | return ['status' => 1, 'info' => $info]; 182 | } else { 183 | return $this->error("上传失败!"); 184 | } 185 | } 186 | 187 | protected function getErrorInfo($num) 188 | { 189 | switch ($num) { 190 | case 1: 191 | return '上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值'; 192 | case 2: 193 | return '上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值'; 194 | case 3: 195 | return '文件只有部分被上传'; 196 | case 4: 197 | return '没有文件被上传'; 198 | case 6: 199 | return '找不到临时文件夹'; 200 | case 7: 201 | return '文件写入失败'; 202 | default: 203 | return '未知上传错误!'; 204 | } 205 | } 206 | 207 | public function write($content) 208 | { 209 | // 上传操作 210 | if (in_array($this->getType(), ['gif', 'jpg', 'jpeg', 'bmp', 'png'])) { 211 | //$imginfo = getimagesize($this->fileinfo['tmp_name']); 212 | $img = new image($this->fileinfo['tmp_name']); 213 | $content = $img->save(); 214 | } 215 | return $this->storage->write($this->filePath, $content); 216 | } 217 | 218 | public function saveFromUrl($url, $content = '') 219 | { 220 | $this->fileName = $this->filePath = ''; 221 | if ($content == '') { 222 | $content = http::get($url); 223 | } 224 | $this->fileinfo = [ 225 | 'name' => basename(parse_url($url, PHP_URL_PATH)), 226 | 'size' => strlen($content), 227 | 'tmp_name' => $url, 228 | ]; 229 | $this->getpath(); 230 | 231 | if ($this->write($content)) { 232 | $info['ext'] = $this->getType(); 233 | $info['fileurl'] = $this->storage->getUrl($this->filePath); 234 | $info['filepath'] = $this->filePath; 235 | $info['filename'] = $this->fileinfo['name']; 236 | $info['hash'] = md5($content); 237 | $info['size'] = $this->fileinfo['size']; 238 | return ['status' => 1, 'info' => $info]; 239 | } else { 240 | return $this->error("上传失败!"); 241 | } 242 | } 243 | } -------------------------------------------------------------------------------- /kuxin/helper/url.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class Url 17 | { 18 | 19 | /** 20 | * 获取微信用的当前URL 去掉#后面的内容 21 | * 22 | * @return string 23 | */ 24 | public static function weixin() 25 | { 26 | $url = self::current(); 27 | if (strpos($url, '#')) { 28 | $url = explode('#', $url)['0']; 29 | } 30 | return $url; 31 | } 32 | 33 | /** 34 | * 获取当前地址 35 | * 36 | * @return string 37 | */ 38 | public static function current() 39 | { 40 | if (PHP_SAPI == 'cli') { 41 | return 'cli'; 42 | } 43 | if (strpos($_SERVER['REQUEST_URI'], 'http://') === 0) { 44 | return $_SERVER['REQUEST_URI']; 45 | } 46 | $protocol = (!empty($_SERVER['HTTPS']) 47 | && $_SERVER['HTTPS'] !== 'off' 48 | || $_SERVER['SERVER_PORT'] === 443) ? 'https://' : 'http://'; 49 | 50 | $host = isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : $_SERVER['HTTP_HOST']; 51 | $uri = isset($_SERVER['HTTP_X_REAL_URI']) ? $_SERVER['HTTP_X_REAL_URI'] : $_SERVER['REQUEST_URI']; 52 | return $protocol . $host . $uri; 53 | } 54 | 55 | /** 56 | * 生成url 57 | * 58 | * @param string $method 59 | * @param array $args 60 | * @param string $type 61 | * @param array $ignores 62 | * @return mixed|string 63 | */ 64 | public static function build($method = '', $args = [], $type = 'html', $ignores = []) 65 | { 66 | if (!empty($_REQUEST['template'])){ 67 | $args['template'] = $_REQUEST['template']; 68 | } 69 | static $rules = null, $_method = [], $power = false, $default_data = [], $ignore_params = [], $auto_calc = []; 70 | if ($rules === null) { 71 | $rules = Config::get('rewrite.rules'); 72 | $power = Config::get('rewrite.power', false); 73 | $default_data = Config::get('url.default_data', []); 74 | $ignore_params = Config::get('url.ignore_param', []); 75 | $auto_calc = Config::get('url.auto_calc', []); 76 | } 77 | $ignores = array_merge($ignores, $ignore_params); 78 | 79 | foreach ($args as $oarg_k => $oarg_v) { 80 | if ((isset($default_data[$oarg_k]) && $default_data[$oarg_k] == $oarg_v)) { 81 | unset($args[$oarg_k]); 82 | } 83 | } 84 | if (empty($_method[$method])) { 85 | if ($method === '') { 86 | $_method[$method] = strtolower(str_replace('\\', '.', Router::$controller) . '.' . Router::$action); 87 | } elseif (substr_count($method, '.') == 0) { 88 | $_method[$method] = strtolower(str_replace('\\', '.', Router::$controller) . '.' . $method); 89 | } else { 90 | $_method[$method] = strtolower($method); 91 | } 92 | } 93 | $method = $_method[$method]; 94 | if ($power && isset($rules[$method])) { 95 | foreach ($auto_calc as $key => $var) { 96 | if (isset($args[$key])) { 97 | foreach ($var as $item) { 98 | $args[$item['name']] = $item['func']($args[$key]); 99 | } 100 | } 101 | } 102 | $keys = []; 103 | $rule = $rules[$method]; 104 | $oargs = $args; 105 | foreach ($args as $key => &$arg) { 106 | $keys[] = '{' . $key . '}'; 107 | $arg = rawurlencode(urldecode($arg)); 108 | if (strpos($rule, '{' . $key . '}')) { 109 | unset($oargs[$key]); 110 | } 111 | } 112 | $url = self::clearUrl(str_replace($keys, $args, $rule)); 113 | if (strpos($url, ']')) { 114 | $url = strtr($url, ['[' => '', ']' => '']); 115 | } 116 | if (strpos($url, '{')) { 117 | foreach ($default_data as $default_k => $default_v) { 118 | if (strpos($url, '{' . $default_k . '}')) { 119 | $url = str_replace('{' . $default_k . '}', $default_v, $url); 120 | } 121 | } 122 | } 123 | if (!empty($oargs) && $ignores) { 124 | foreach ($oargs as $oarg_k => $oarg_v) { 125 | if (in_array($oarg_k, $ignores)) { 126 | unset($oargs[$oarg_k]); 127 | } 128 | } 129 | } 130 | $url = (substr($url, 0, 1) == '/' ? '' : '/') . $url; 131 | if (empty($oargs)) { 132 | return $url; 133 | } else { 134 | return $url . (strpos($url, '?') ? '&' : '?') . http_build_query($oargs); 135 | } 136 | } else { 137 | $type = $type ? $type : Response::getType(); 138 | $url = '/' . strtr($method, '.', '/') . '.' . $type; 139 | if ($args) { 140 | $url .= '?' . http_build_query($args); 141 | } 142 | return $url; 143 | } 144 | } 145 | 146 | /** 147 | * 清除url中可选参数 148 | * 149 | * @param $url 150 | * @return mixed 151 | */ 152 | private static function clearUrl($url) 153 | { 154 | while (preg_match('#\[[^\[\]]*?\{\w+\}[^\[\]]*?\]#', $url, $match)) { 155 | $url = str_replace($match['0'], '', $url); 156 | } 157 | return $url; 158 | } 159 | } -------------------------------------------------------------------------------- /kuxin/helper/verify.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class Verify 14 | { 15 | 16 | /** 17 | * 产生随机字串,可用来自动生成密码 18 | * 默认长度6位 字母和数字混合 支持中文 19 | * 20 | * @param int $len 长度 21 | * @param int $type 字串类型 22 | * 0 字母 1 数字 其它 混合 23 | * @param string $addChars 额外字符 24 | * @return string 25 | */ 26 | static public function randString($len = 6, $type = 0, $addChars = '') 27 | { 28 | $str = ''; 29 | switch ($type) { 30 | case 0: 31 | $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' . $addChars; 32 | break; 33 | case 1: 34 | $chars = str_repeat('0123456789', $len); 35 | break; 36 | case 2: 37 | $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' . $addChars; 38 | break; 39 | case 3: 40 | $chars = 'abcdefghijklmnopqrstuvwxyz' . $addChars; 41 | break; 42 | case 4: 43 | $chars = "们以我到他会作时要动国产的一是工就年阶义发成部民可出能方进在了不和有大这主中人上为来分生对于学下级地个用同行面说种过命度革而多子后自社加小机也经力线本电高量长党得实家定深法表着水理化争现所二起政三好十战无农使性前等反体合斗路图把结第里正新开论之物从当两些还天资事队批点育重其思与间内去因件日利相由压员气业代全组数果期导平各基或月毛然如应形想制心样干都向变关问比展那它最及外没看治提五解系林者米群头意只明四道马认次文通但条较克又公孔领军流入接席位情运器并飞原油放立题质指建区验活众很教决特此常石强极土少已根共直团统式转别造切九你取西持总料连任志观调七么山程百报更见必真保热委手改管处己将修支识病象几先老光专什六型具示复安带每东增则完风回南广劳轮科北打积车计给节做务被整联步类集号列温装即毫知轴研单色坚据速防史拉世设达尔场织历花受求传口断况采精金界品判参层止边清至万确究书术状厂须离再目海交权且儿青才证低越际八试规斯近注办布门铁需走议县兵固除般引齿千胜细影济白格效置推空配刀叶率述今选养德话查差半敌始片施响收华觉备名红续均药标记难存测士身紧液派准斤角降维板许破述技消底床田势端感往神便贺村构照容非搞亚磨族火段算适讲按值美态黄易彪服早班麦削信排台声该击素张密害侯草何树肥继右属市严径螺检左页抗苏显苦英快称坏移约巴材省黑武培著河帝仅针怎植京助升王眼她抓含苗副杂普谈围食射源例致酸旧却充足短划剂宣环落首尺波承粉践府鱼随考刻靠够满夫失包住促枝局菌杆周护岩师举曲春元超负砂封换太模贫减阳扬江析亩木言球朝医校古呢稻宋听唯输滑站另卫字鼓刚写刘微略范供阿块某功套友限项余倒卷创律雨让骨远帮初皮播优占死毒圈伟季训控激找叫云互跟裂粮粒母练塞钢顶策双留误础吸阻故寸盾晚丝女散焊功株亲院冷彻弹错散商视艺灭版烈零室轻血倍缺厘泵察绝富城冲喷壤简否柱李望盘磁雄似困巩益洲脱投送奴侧润盖挥距触星松送获兴独官混纪依未突架宽冬章湿偏纹吃执阀矿寨责熟稳夺硬价努翻奇甲预职评读背协损棉侵灰虽矛厚罗泥辟告卵箱掌氧恩爱停曾溶营终纲孟钱待尽俄缩沙退陈讨奋械载胞幼哪剥迫旋征槽倒握担仍呀鲜吧卡粗介钻逐弱脚怕盐末阴丰雾冠丙街莱贝辐肠付吉渗瑞惊顿挤秒悬姆烂森糖圣凹陶词迟蚕亿矩康遵牧遭幅园腔订香肉弟屋敏恢忘编印蜂急拿扩伤飞露核缘游振操央伍域甚迅辉异序免纸夜乡久隶缸夹念兰映沟乙吗儒杀汽磷艰晶插埃燃欢铁补咱芽永瓦倾阵碳演威附牙芽永瓦斜灌欧献顺猪洋腐请透司危括脉宜笑若尾束壮暴企菜穗楚汉愈绿拖牛份染既秋遍锻玉夏疗尖殖井费州访吹荣铜沿替滚客召旱悟刺脑措贯藏敢令隙炉壳硫煤迎铸粘探临薄旬善福纵择礼愿伏残雷延烟句纯渐耕跑泽慢栽鲁赤繁境潮横掉锥希池败船假亮谓托伙哲怀割摆贡呈劲财仪沉炼麻罪祖息车穿货销齐鼠抽画饲龙库守筑房歌寒喜哥洗蚀废纳腹乎录镜妇恶脂庄擦险赞钟摇典柄辩竹谷卖乱虚桥奥伯赶垂途额壁网截野遗静谋弄挂课镇妄盛耐援扎虑键归符庆聚绕摩忙舞遇索顾胶羊湖钉仁音迹碎伸灯避泛亡答勇频皇柳哈揭甘诺概宪浓岛袭谁洪谢炮浇斑讯懂灵蛋闭孩释乳巨徒私银伊景坦累匀霉杜乐勒隔弯绩招绍胡呼痛峰零柴簧午跳居尚丁秦稍追梁折耗碱殊岗挖氏刃剧堆赫荷胸衡勤膜篇登驻案刊秧缓凸役剪川雪链渔啦脸户洛孢勃盟买杨宗焦赛旗滤硅炭股坐蒸凝竟陷枪黎救冒暗洞犯筒您宋弧爆谬涂味津臂障褐陆啊健尊豆拔莫抵桑坡缝警挑污冰柬嘴啥饭塑寄赵喊垫丹渡耳刨虎笔稀昆浪萨茶滴浅拥穴覆伦娘吨浸袖珠雌妈紫戏塔锤震岁貌洁剖牢锋疑霸闪埔猛诉刷狠忽灾闹乔唐漏闻沈熔氯荒茎男凡抢像浆旁玻亦忠唱蒙予纷捕锁尤乘乌智淡允叛畜俘摸锈扫毕璃宝芯爷鉴秘净蒋钙肩腾枯抛轨堂拌爸循诱祝励肯酒绳穷塘燥泡袋朗喂铝软渠颗惯贸粪综墙趋彼届墨碍启逆卸航衣孙龄岭骗休借" . $addChars; 44 | break; 45 | default : 46 | // 默认去掉了容易混淆的字符oOLl和数字01,要添加请使用addChars参数 47 | $chars = 'ABCDEFGHIJKMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789' . $addChars; 48 | break; 49 | } 50 | if ($len > 10) { //位数过长重复字符串一定次数 51 | $chars = $type == 1 ? str_repeat($chars, $len) : str_repeat($chars, 5); 52 | } 53 | if ($type != 4) { 54 | $chars = str_shuffle($chars); 55 | $str = substr($chars, 0, $len); 56 | } else { 57 | // 中文随机字 58 | for ($i = 0; $i < $len; $i++) { 59 | $str .= Str::truncate($chars, 1, '', floor(mt_rand(0, mb_strlen($chars, 'utf-8') - 1))); 60 | } 61 | } 62 | return $str; 63 | } 64 | 65 | /** 66 | * 生成图像验证码 67 | * 68 | * @static 69 | * @access public 70 | * @param int $length 位数 71 | * @param int $mode 类型 72 | * @param string $type 图像格式 73 | * @param int $width 宽度 74 | * @param int $height 高度 75 | * @param string $verifyName session名字 76 | * @return string 77 | */ 78 | static function buildImageVerify($length = 4, $mode = 1, $type = 'png', $width = 48, $height = 22, $verifyName = 'verify') 79 | { 80 | $randval = self::randString($length, $mode); 81 | Session::start(); 82 | Session::set($verifyName, strtolower($randval)); 83 | $width = ($length * 10 + 10) > $width ? $length * 10 + 10 : $width; 84 | if ($type != 'gif' && function_exists('imagecreatetruecolor')) { 85 | $im = imagecreatetruecolor($width, $height); 86 | } else { 87 | $im = imagecreate($width, $height); 88 | } 89 | $r = [225, 255, 255, 223]; 90 | $g = [225, 236, 237, 255]; 91 | $b = [225, 236, 166, 125]; 92 | $key = mt_rand(0, 3); 93 | 94 | $backColor = imagecolorallocate($im, $r[$key], $g[$key], $b[$key]); //背景色(随机) 95 | $borderColor = imagecolorallocate($im, 100, 100, 100); //边框色 96 | imagefilledrectangle($im, 0, 0, $width - 1, $height - 1, $backColor); 97 | imagerectangle($im, 0, 0, $width - 1, $height - 1, $borderColor); 98 | $stringColor = imagecolorallocate($im, mt_rand(0, 200), mt_rand(0, 120), mt_rand(0, 120)); 99 | // 干扰 100 | for ($i = 0; $i < 10; $i++) { 101 | imagearc($im, mt_rand(-10, $width), mt_rand(-10, $height), mt_rand(30, 300), mt_rand(20, 200), 55, 44, $stringColor); 102 | } 103 | for ($i = 0; $i < 25; $i++) { 104 | imagesetpixel($im, mt_rand(0, $width), mt_rand(0, $height), $stringColor); 105 | } 106 | for ($i = 0; $i < $length; $i++) { 107 | imagestring($im, rand(2,5), $i * 10 + 5, mt_rand(1, 8), $randval{$i}, $stringColor); 108 | } 109 | self::output($im, $type); 110 | exit; 111 | } 112 | 113 | /** 114 | * 输出图像 115 | * 116 | * @param resource $im 图像资源 117 | * @param string $type 图像类型 118 | * @param string $filename 保存为文件 119 | */ 120 | static function output($im, $type = 'png', $filename = '') 121 | { 122 | error_reporting(0); 123 | ob_end_clean(); 124 | header("Content-type: image/" . $type); 125 | $ImageFun = 'image' . $type; 126 | if (empty($filename)) { 127 | $ImageFun($im); 128 | } else { 129 | $ImageFun($im, $filename); 130 | } 131 | imagedestroy($im); 132 | exit; 133 | } 134 | 135 | } -------------------------------------------------------------------------------- /kuxin/helper/xml.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Xml 12 | { 13 | 14 | /** 15 | * @param $data 16 | * @param string $root 17 | * @param string $attr 18 | * @param string $encoding 19 | * @return mixed 20 | */ 21 | public static function encode($data, $root = 'xml', $attr = '', $encoding = 'utf-8') 22 | { 23 | if (is_array($attr)) { 24 | $_attr = []; 25 | foreach ($attr as $key => $value) { 26 | $_attr[] = "{$key}=\"{$value}\""; 27 | } 28 | $attr = implode(' ', $_attr); 29 | } 30 | $attr = trim($attr); 31 | $attr = empty($attr) ? '' : " {$attr}"; 32 | $xml = ""; 33 | $xml .= "<{$root}{$attr}>"; 34 | $xml .= self::dataToXml($data); 35 | $xml .= ""; 36 | return preg_replace('/[\x00-\x1f]/', '', $xml); 37 | } 38 | 39 | /** 40 | * 数据XML编码 41 | * 42 | * @param mixed $data 数据 43 | * @param string $parentkey 44 | * @return string 45 | */ 46 | protected static function dataToXml($data, $parentkey = '') 47 | { 48 | $xml = ''; 49 | foreach ($data as $key => $val) { 50 | if (is_numeric($key)) { 51 | $key = $parentkey; 52 | } elseif (substr($key, 0, 10) == '__string__') { 53 | $xml .= $val; 54 | continue; 55 | } 56 | $key = $key ? $key : 'xmldata'; 57 | $xml .= "<{$key}>"; 58 | if (is_array($val) || is_object($val)) { 59 | $len = strlen("<{$key}>"); 60 | $con = self::dataToXml($val, $key); 61 | if (strpos($con, "<{$key}>") === 0) { 62 | $con = substr($con, $len, -($len + 1)); 63 | } 64 | $xml .= $con; 65 | } elseif (strlen($val) > 150 || preg_match('{[<>&\'|"]+}', $val)) { 66 | $xml .= ''; 67 | } else { 68 | $xml .= $val; 69 | } 70 | $xml .= ""; 71 | } 72 | return $xml; 73 | } 74 | 75 | public static function decode($con) 76 | { 77 | if (!$con) { 78 | return []; 79 | } 80 | if ($con{0} == '<') { 81 | $con = simplexml_load_string($con, 'SimpleXMLElement', LIBXML_NOCDATA | LIBXML_NOBLANKS); 82 | } else { 83 | $con = simplexml_load_file($con, 'SimpleXMLElement', LIBXML_NOCDATA | LIBXML_NOBLANKS); 84 | } 85 | return Json::decode(Json::encode($con), true); 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /kuxin/input.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Input 12 | { 13 | 14 | /** 15 | * 获取$_GET 16 | * 17 | * @param $name 18 | * @param $type 19 | * @param mixed $default 20 | * @return array|float|int|mixed|null|string 21 | */ 22 | public static function get(string $name = '', $type = '', $default = null) 23 | { 24 | return self::param($name, $type, $default, $_GET); 25 | } 26 | 27 | /** 28 | * 获取$_POST 29 | * 30 | * @param $name 31 | * @param $type 32 | * @param mixed $default 33 | * @return array|float|int|mixed|null|string 34 | */ 35 | public static function post(string $name = '', $type = '', $default = null) 36 | { 37 | return self::param($name, $type, $default, $_POST); 38 | } 39 | 40 | /** 41 | * 获取$_REQUEST 42 | * 43 | * @param $name 44 | * @param $type 45 | * @param mixed $default 46 | * @return array|float|int|mixed|null|string 47 | */ 48 | public static function request(string $name = '', $type = '', $default = null) 49 | { 50 | return self::param($name, $type, $default, $_REQUEST); 51 | } 52 | 53 | /** 54 | * 获取put 55 | * 56 | * @param $name 57 | * @param $type 58 | * @param mixed $default 59 | * @return array|float|int|mixed|null|string 60 | */ 61 | public static function put(string $name = '', $type = '', $default = null) 62 | { 63 | parse_str(file_get_contents('php://input'), $input); 64 | return self::param($name, $type, $default, $input); 65 | } 66 | 67 | /** 68 | * 获取$_SERVER 69 | * 70 | * @param $name 71 | * @param $type 72 | * @param mixed $default 73 | * @return array|float|int|mixed|null|string 74 | */ 75 | public static function server(string $name = '', $type = '', $default = null) 76 | { 77 | return self::param($name, $type, $default, $_SERVER); 78 | } 79 | 80 | /** 81 | * 获取$GLOBALS 82 | * 83 | * @param $name 84 | * @param $type 85 | * @param mixed $default 86 | * @return array|float|int|mixed|null|string 87 | */ 88 | public static function globals(string $name = '', $type = '', $default = null) 89 | { 90 | return self::param($name, $type, $default, $GLOBALS); 91 | } 92 | 93 | /** 94 | * 获取$_FILES 95 | * 96 | * @param $name 97 | * @param $type 98 | * @param mixed $default 99 | * @return array|float|int|mixed|null|string 100 | */ 101 | public static function files(string $name = '', $type = '', $default = null) 102 | { 103 | return self::param($name, $type, $default, $_FILES); 104 | } 105 | 106 | /** 107 | * 判断$_REQUEST 是否有某个值 108 | * 109 | * @param $name 110 | * @param array $type 111 | * @return bool 112 | */ 113 | public static function has(string $name, array $type = null): bool 114 | { 115 | $type = $type ?: $_REQUEST; 116 | return isset($type[$name]); 117 | } 118 | 119 | /** 120 | * @param string $name 121 | * @param $filter 122 | * @param mixed $default 123 | * @param array $param 124 | * @return array|float|int|mixed|null|string 125 | */ 126 | public static function param(string $name, $filter = '', $default = null, array $param = []) 127 | { 128 | if ($name == '') { 129 | return $param; 130 | } 131 | if (!isset($param[$name])) { 132 | return is_callable($default) ? $default($name) : $default; 133 | } else { 134 | $value = $param[$name]; 135 | if($filter){ 136 | $value = Filter::check($value, $filter); 137 | } 138 | } 139 | 140 | return $value; 141 | } 142 | } -------------------------------------------------------------------------------- /kuxin/kuxin.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class Kuxin 17 | { 18 | 19 | /** 20 | * 21 | */ 22 | public static function init(): void 23 | { 24 | // 注册AUTOLOAD方法 25 | spl_autoload_register([Loader::class, 'autoload']); 26 | //程序关闭 27 | register_shutdown_function([__CLASS__, 'shutdown']); 28 | // 设定错误和异常处理 29 | set_error_handler([__CLASS__, 'error']); 30 | set_exception_handler([__CLASS__, 'exception']); 31 | // 注册配置 32 | Config::LoadDir(KX_ROOT . '/app/config'); 33 | // 时区 34 | date_default_timezone_set('PRC'); 35 | // 记录开始运行时间 36 | Registry::set('_startTime', microtime(true)); 37 | // 记录sql执行次数 38 | Registry::set('_sql', []); 39 | Registry::set('_sqlnum', 0); 40 | // 缓存读取次数 41 | Registry::set('_cacheRead', 0); 42 | Registry::set('_cacheHit', 0); 43 | // 缓存写入次数 44 | Registry::set('_cacheWrite', 0); 45 | // 记录内存初始使用 46 | Registry::set('_startUseMems', memory_get_usage()); 47 | // 记录网络请求 48 | Registry::set('_http', []); 49 | Registry::set('_httpnum', 0); 50 | // 常量定义 51 | define('KX_VERSION', '1.0.0'); 52 | 53 | 54 | if (Config::get('app.debug')) { 55 | ini_set('display_errors', 'on'); 56 | error_reporting(E_ALL); 57 | } else { 58 | ini_set('display_errors', 'off'); 59 | error_reporting(0); 60 | } 61 | 62 | // 加载助手文件 63 | Loader::import(KX_ROOT . '/app/helper.php'); 64 | // 加载版本文件 65 | Loader::import(KX_ROOT . '/app/version.php'); 66 | } 67 | 68 | /** 69 | * 70 | */ 71 | public static function start(): void 72 | { 73 | self::init(); 74 | Plugin::call('app_start'); 75 | if (PHP_SAPI == 'cli') { 76 | Router::cli(); 77 | global $argv; 78 | //系统console和用户的走不同的位置 79 | if (in_array(Router::$controller, Router::$selfConsoleClass)) { 80 | $controllerName = 'Kuxin\\Console\\' . Router::$controller; 81 | } else { 82 | $controllerName = 'App\\Console\\' . Router::$controller; 83 | } 84 | unset($argv[0], $argv[1]); 85 | $controller = Loader::instance($controllerName, $argv); 86 | if (!$controller) { 87 | trigger_error('控制器[' . $controllerName . ']不存在', E_USER_ERROR); 88 | } 89 | $actionName = Router::$action; 90 | $controller->init(); 91 | if (is_callable([$controller, $actionName])) { 92 | $controller->$actionName(); 93 | } else { 94 | trigger_error('控制器[' . $controllerName . ']对应的方法[' . $actionName . ']不存在', E_USER_ERROR); 95 | } 96 | } else { 97 | Router::dispatcher(); 98 | $controllerName = 'App\\Controller\\' . Router::$controller; 99 | /** @var \Kuxin\Controller $controller */ 100 | $controller = Loader::instance($controllerName); 101 | if (!$controller) { 102 | trigger_error('控制器[' . $controllerName . ']不存在', E_USER_ERROR); 103 | } 104 | $actionName = Router::$action; 105 | $return = $controller->middleware(); 106 | if ($return === null) { 107 | if (is_callable([$controller, $actionName])) { 108 | $controller->init(); 109 | if (in_array($actionName, $controller->disableActions)) { 110 | trigger_error('控制器[' . $controllerName . ']对应的方法[' . $actionName . ']不允许访问', E_USER_ERROR); 111 | } 112 | $return = $controller->$actionName(); 113 | } else { 114 | trigger_error('控制器[' . $controllerName . ']对应的方法[' . $actionName . ']不存在', E_USER_ERROR); 115 | } 116 | } 117 | if (Response::isAutoRender()) { 118 | switch (Response::getType()) { 119 | case 'html': 120 | $body = $return; 121 | break; 122 | case 'json': 123 | $body = Json::encode($return); 124 | break; 125 | case 'jsonp': 126 | $body = Jsonp::encode($return); 127 | break; 128 | case 'xml': 129 | $body = Xml::encode($return); 130 | break; 131 | default: 132 | if (Request::isAjax()) { 133 | Response::setType('json'); 134 | $body = Json::encode($return); 135 | } else { 136 | $body = $return; 137 | } 138 | } 139 | } else { 140 | $body = $return; 141 | } 142 | //设置输出内容 143 | Response::setBody($body); 144 | } 145 | 146 | } 147 | 148 | /** 149 | * 150 | */ 151 | public static function shutdown(): void 152 | { 153 | //如果开启日志 则记录日志 154 | if (Config::get('log.power')) { 155 | Log::build(); 156 | } 157 | } 158 | 159 | /** 160 | * @param \Exception $e 161 | */ 162 | public static function exception($e) 163 | { 164 | Response::error($e->getmessage(), $e->getFile(), $e->getLine()); 165 | } 166 | 167 | /** 168 | * @param $errno 169 | * @param $errstr 170 | * @param $errfile 171 | * @param $errline 172 | */ 173 | public static function error($errno, $errstr, $errfile, $errline) 174 | { 175 | switch ($errno) { 176 | case E_ERROR: 177 | case E_PARSE: 178 | case E_CORE_ERROR: 179 | case E_COMPILE_ERROR: 180 | case E_USER_ERROR: 181 | Response::error($errstr, $errfile, $errline); 182 | break; 183 | case E_STRICT: 184 | case E_USER_WARNING: 185 | case E_USER_NOTICE: 186 | default: 187 | break; 188 | } 189 | } 190 | } 191 | 192 | 193 | include __DIR__ . '/loader.php'; 194 | 195 | Kuxin::start(); 196 | -------------------------------------------------------------------------------- /kuxin/loader.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class Loader 14 | { 15 | 16 | /** 17 | * @var array 18 | */ 19 | static $_importFiles = []; 20 | 21 | /** 22 | * @var array 23 | */ 24 | static $_class = []; 25 | 26 | /** 27 | * 加载文件 28 | * 29 | * @param string $filename 30 | * @return mixed 31 | */ 32 | public static function import(string $filename) 33 | { 34 | if (!isset(self::$_importFiles[$filename])) { 35 | if (is_file($filename)) { 36 | self::$_importFiles[$filename] = require $filename; 37 | } else { 38 | return false; 39 | } 40 | } 41 | return self::$_importFiles[$filename]; 42 | } 43 | 44 | /** 45 | * 初始化类 46 | * @param string $class 47 | * @param array $args 48 | * @return mixed 49 | */ 50 | public static function instance(string $class, array $args = []) 51 | { 52 | $key = md5($class . '_' . Serialize::encode($args)); 53 | if (empty(self::$_class[$key])) { 54 | try { 55 | self::$_class[$key] = (new \ReflectionClass($class))->newInstanceArgs($args);; 56 | } catch (\Exception $e) { 57 | return false; 58 | } 59 | } 60 | return self::$_class[$key]; 61 | } 62 | 63 | 64 | /** 65 | * @param $classname 66 | */ 67 | public static function autoload($classname): void 68 | { 69 | $file = KX_ROOT . '/' . strtr(strtolower($classname), '\\', '/') . '.php'; 70 | Loader::import($file); 71 | } 72 | } -------------------------------------------------------------------------------- /kuxin/log.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Log 12 | { 13 | 14 | /** 15 | * 日志内容 16 | * 17 | * @var array 18 | */ 19 | protected static $log = []; 20 | 21 | /** 22 | * 获取日志信息 23 | * 24 | * @param string $type 信息类型 25 | * @return array 26 | */ 27 | public static function getLog($type = ''): array 28 | { 29 | return $type ? (self::$log[$type] ?? []) : self::$log; 30 | } 31 | 32 | /** 33 | * 记录日志 默认为pt 34 | * 35 | * @param mixed $msg 调试信息 36 | * @param string $type 信息类型 37 | * @return void 38 | */ 39 | public static function record($msg, $type = 'kx'):void 40 | { 41 | self::$log[$type][] = "[" . date('Y-m-d H:i:s') . "] " . $msg;; 42 | } 43 | 44 | /** 45 | * 清空日志信息 46 | * 47 | * @return void 48 | */ 49 | public static function clear():void 50 | { 51 | self::$log = []; 52 | } 53 | 54 | /** 55 | * 手动写入指定日志到文件 56 | * 57 | * @param string $content 58 | * @param string $type 59 | */ 60 | public static function write(string $content, string $type = 'kx', bool $withTime = true):void 61 | { 62 | DI::Storage('log')->append($type . '_' . date('Ymd') . '.txt', ($withTime ? ("[" . date('Y-m-d H:i:s') . "] ") : "") . $content . PHP_EOL); 63 | } 64 | 65 | /** 66 | * 自动写入指定类型日志 67 | */ 68 | public static function build():void 69 | { 70 | $logBuild = Config::get('log.buildtype', ['pt', 'debug', 'collect', 'collecterror', 'cron']); 71 | foreach ($logBuild as $type) { 72 | if (isset(self::$log[$type])) { 73 | self::write(implode(PHP_EOL, self::$log[$type]), $type, false); 74 | } 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /kuxin/oauth/oauth.php: -------------------------------------------------------------------------------- 1 | appid = $config['appid']; 96 | $this->appsecret = $config['appsecret']; 97 | $this->token = $token; 98 | Config::set('user_agent', ''); 99 | } 100 | 101 | /** 102 | * @param $type 103 | * @param null $token 104 | * @return static 105 | */ 106 | public static function getInstance($type, $token = null) 107 | { 108 | $config = Config::get("oauth.{$type}"); 109 | $classname = '\\Kuxin\\Oauth\\' . $type; 110 | return Loader::instance($classname, [ $config, $token ]); 111 | } 112 | 113 | /** 114 | * 前往认证页 115 | * 116 | * @param $url 117 | * @return string 118 | */ 119 | public function authorize($url) 120 | { 121 | $param = [ 122 | "response_type" => $this->responseType, 123 | "client_id" => $this->appid, 124 | "redirect_uri" => $url, 125 | "state" => time(), 126 | ]; 127 | $param = array_merge($param, $this->authorizeParam); 128 | if (strpos($this->getRequestCodeURL, '?') === false) { 129 | return $this->getRequestCodeURL . '?' . http_build_query($param); 130 | } else { 131 | return $this->getRequestCodeURL . '&' . http_build_query($param); 132 | } 133 | } 134 | 135 | /** 136 | * 获取access token 137 | * 138 | * @param $code 139 | * @param $url 140 | * @return string 141 | */ 142 | public function getAccessToken($code, $url) 143 | { 144 | $param = [ 145 | "grant_type" => $this->grantType, 146 | "client_id" => $this->appid, 147 | "redirect_uri" => $url, 148 | "client_secret" => $this->appsecret, 149 | "code" => $code, 150 | ]; 151 | $param = array_merge($param, $this->getTokenParam); 152 | $response = Http::post($this->getAccessTokenURL, http_build_query($param)); 153 | return $this->parseToken($response); 154 | } 155 | 156 | 157 | /** 158 | * @param string $api 接口名 159 | * @param string $param 参数 160 | * @param string $method 是否POST 161 | * @param bool $multi 是否上传文件 162 | * @return array 163 | */ 164 | abstract protected function call($api, $param = '', $method = 'GET', $multi = false); 165 | 166 | /** 167 | * 抽象方法 解析access_token方法请求后的返回值 168 | * 169 | * @param 待处理内容 170 | * @return string 171 | */ 172 | abstract protected function parseToken($result); 173 | 174 | /** 175 | * 抽象方法 获取当前授权用户的标识 176 | * 177 | * @return mixed 178 | */ 179 | abstract public function getOpenId(); 180 | 181 | /** 182 | * 获取用户信息 183 | * 184 | * @return mixed 185 | */ 186 | abstract public function getInfo(); 187 | } 188 | -------------------------------------------------------------------------------- /kuxin/oauth/qq.php: -------------------------------------------------------------------------------- 1 | 'get_user_info,add_topic,add_share,add_t', 30 | ); 31 | 32 | /** 33 | * 获取accesstoekn时候的附加参数 34 | * 35 | * @var array 36 | */ 37 | protected $getTokenParam = array(); 38 | 39 | 40 | /** 41 | * API根路径 42 | * 43 | * @var string 44 | */ 45 | protected $apiBase = 'https://graph.qq.com/'; 46 | 47 | /** 48 | * 组装接口调用参数 并调用接口 49 | * 50 | * @param string $api 微博API 51 | * @param array $param 调用API的额外参数 52 | * @param string $method HTTP请求方法 默认为GET 53 | * @param bool $multi 54 | * @return string json 55 | */ 56 | public function call($api, $param = array(), $method = 'GET', $multi = false) { 57 | /* 腾讯QQ调用公共参数 */ 58 | $params = array( 59 | 'oauth_consumer_key' => $this->appid, 60 | 'access_token' => $this->token, 61 | 'openid' => $this->getOpenId(), 62 | 'format' => 'json' 63 | ); 64 | $params = array_merge($params, $param); 65 | $data = Http::get($this->apiBase . $api, $params); 66 | return Json::decode($data, true); 67 | } 68 | 69 | /** 70 | * 解析access_token方法请求后的返回值 71 | * 72 | * @param $result 73 | * @return mixed 74 | * @throws \Exception 75 | */ 76 | protected function parseToken($result) { 77 | parse_str($result, $data); 78 | if (!empty($data['access_token'])) { 79 | $this->token = $data['access_token']; 80 | return array( 81 | 'openid' => $this->getOpenId(), 82 | 'token' => $data['access_token'], 83 | 'expires' => $data['expires_in'], 84 | 'refresh' => $data['refresh_token'], 85 | ); 86 | } else 87 | return "获取 ACCESS_TOKEN 出错:{$result}"; 88 | } 89 | 90 | /** 91 | * 获取openid 92 | * 93 | * @return mixed 94 | * @throws \Exception 95 | */ 96 | public function getOpenId() { 97 | if ($this->openid) return $this->openid; 98 | $data = http::get($this->apiBase . 'oauth2.0/me', array('access_token' => $this->token)); 99 | $data = Json::decode(trim(substr($data, 9), " );\n"), true); 100 | if (isset($data['openid'])) { 101 | $this->openid = $data['openid']; 102 | return $data['openid']; 103 | } else 104 | return false; 105 | } 106 | 107 | /** 108 | * 获取用户信息 109 | * 110 | * @return array 111 | */ 112 | public function getInfo() { 113 | $data = $this->call('user/get_user_info'); 114 | return array( 115 | 'id' => $this->openid, 116 | 'name' => $data['nickname'], 117 | 'gender' => $data['gender'], 118 | 'avatar' => $data['figureurl_2'], 119 | ); 120 | } 121 | } -------------------------------------------------------------------------------- /kuxin/oauth/weibo.php: -------------------------------------------------------------------------------- 1 | 'all' 29 | 'forcelogin' => 'true', 30 | ); 31 | 32 | /** 33 | * 获取accesstoekn时候的附加参数 34 | * 35 | * @var array 36 | */ 37 | protected $getTokenParam = array(); 38 | 39 | 40 | /** 41 | * API根路径 42 | * 43 | * @var string 44 | */ 45 | protected $apiBase = 'https://api.weibo.com/2/'; 46 | 47 | /** 48 | * 组装接口调用参数 并调用接口 49 | * 50 | * @param string $api 微博API 51 | * @param array $param 调用API的额外参数 52 | * @param string $method HTTP请求方法 默认为GET 53 | * @param bool $multi 54 | * @return string json 55 | */ 56 | public function call($api, $param = array(), $method = 'GET', $multi = false) { 57 | /* 腾讯QQ调用公共参数 */ 58 | $params = array( 59 | 'access_token' => $this->token, 60 | ); 61 | $params = array_merge($params, $param); 62 | $data = Http::get($this->apiBase . $api, $params); 63 | return Json::decode($data, true); 64 | } 65 | 66 | /** 67 | * 解析access_token方法请求后的返回值 68 | * 69 | * @param $result 70 | * @return mixed 71 | * @throws \Exception 72 | */ 73 | protected function parseToken($result) { 74 | $data = Json::decode($result, true); 75 | if (isset($data['access_token'])) { 76 | $this->token = $data['access_token']; 77 | $this->openid = $data['uid']; 78 | return array( 79 | 'openid' => $this->openid, 80 | 'token' => $data['access_token'], 81 | 'expires' => $data['expires_in'], 82 | 'refresh' => '', 83 | ); 84 | } else 85 | return "获取 ACCESS_TOKEN 出错:{$result}"; 86 | } 87 | 88 | /** 89 | * 获取openid 90 | * 91 | * @return mixed 92 | * @throws \Exception 93 | */ 94 | public function getOpenId() { 95 | if ($this->openid) return $this->openid; 96 | return false; 97 | } 98 | 99 | /** 100 | * 获取用户信息 101 | * 102 | * @return array 103 | */ 104 | public function getInfo() { 105 | $data = $this->call('users/show.json', array('uid' => $this->getOpenId())); 106 | return array( 107 | 'id' => $this->openid, 108 | 'name' => $data['screen_name'], 109 | 'gender' => ($data['gender'] == 'f') ? '女' : (($data['gender'] == 'm') ? '男' : '未知'), 110 | 'avatar' => $data['avatar_large'], 111 | ); 112 | } 113 | } -------------------------------------------------------------------------------- /kuxin/oauth/weixin.php: -------------------------------------------------------------------------------- 1 | 'all' 30 | //'forcelogin' => 'true', 31 | ]; 32 | 33 | /** 34 | * 获取accesstoekn时候的附加参数 35 | * 36 | * @var array 37 | */ 38 | protected $getTokenParam = [ 39 | ]; 40 | 41 | 42 | /** 43 | * API根路径 44 | * 45 | * @var string 46 | */ 47 | protected $apiBase = 'https://api.weixin.qq.com/'; 48 | 49 | /** 50 | * 构造函数 51 | * 52 | * @param array $config 53 | * @param null $token 54 | */ 55 | public function __construct(array $config, $token = null) 56 | { 57 | parent::__construct($config, $token); 58 | $this->getTokenParam = [ 59 | 'appid' => $this->appid, 60 | 'secret' => $this->appsecret, 61 | ]; 62 | } 63 | 64 | /** 65 | * 组装接口调用参数 并调用接口 66 | * 67 | * @param string $api 微博API 68 | * @param array $param 调用API的额外参数 69 | * @param string $method HTTP请求方法 默认为GET 70 | * @param bool $multi 71 | * @return string json 72 | */ 73 | public function call($api, $param = [], $method = 'GET', $multi = false) 74 | { 75 | /* 腾讯QQ调用公共参数 */ 76 | $params = [ 77 | 'access_token' => $this->token, 78 | ]; 79 | $params = array_merge($params, $param); 80 | $data = Http::get($this->apiBase . $api, $params); 81 | return Json::decode($data, true); 82 | } 83 | 84 | /** 85 | * 解析access_token方法请求后的返回值 86 | * 87 | * @param $result 88 | * @return mixed 89 | * @throws \Exception 90 | */ 91 | protected function parseToken($result) 92 | { 93 | $data = Json::decode($result, true); 94 | if (isset($data['access_token'])) { 95 | $this->token = $data['access_token']; 96 | $this->openid = $data['openid']; 97 | return [ 98 | 'openid' => $this->openid, 99 | 'token' => $this->token, 100 | 'expires' => $data['expires_in'], 101 | 'refresh' => $data['refresh_token'], 102 | ]; 103 | } else 104 | return "获取 ACCESS_TOKEN 出错:{$result}"; 105 | } 106 | 107 | /** 108 | * 获取openid 109 | * 110 | * @return mixed 111 | * @throws \Exception 112 | */ 113 | public function getOpenId() 114 | { 115 | if ($this->openid) return $this->openid; 116 | return false; 117 | } 118 | 119 | /** 120 | * 获取用户信息 121 | * 122 | * @return array 123 | */ 124 | public function getInfo() 125 | { 126 | $data = $this->call('sns/userinfo', ['openid' => $this->getOpenId()]); 127 | return [ 128 | 'id' => $this->openid, 129 | 'name' => $data['nickname'], 130 | 'gender' => $data['sex'], 131 | 'avatar' => $data['headimgurl'], 132 | ]; 133 | } 134 | } -------------------------------------------------------------------------------- /kuxin/plugin.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Plugin 12 | { 13 | 14 | /** 15 | * 调用插件 16 | * 17 | * @param $tag 18 | * @param mixed $param 19 | * @return mixed 20 | */ 21 | public static function call(string $tag, $param = null) 22 | { 23 | $methods = Config::get('plugin.' . $tag); 24 | if ($methods && is_array($methods)) { 25 | foreach ($methods as $method) { 26 | $class = Loader::instance($method); 27 | $param = $class->handle($param); 28 | } 29 | } 30 | return $param; 31 | } 32 | } -------------------------------------------------------------------------------- /kuxin/registry.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class Registry 19 | { 20 | 21 | protected static $_data; 22 | 23 | /** 24 | * 获取 25 | * 26 | * @param $key 27 | * @param $default 28 | * @return mixed 29 | */ 30 | public static function get(string $key, $default = null) 31 | { 32 | return isset(self::$_data[$key]) ? self::$_data[$key] : (is_callable($default) ? $default($key) : $default); 33 | } 34 | 35 | /** 36 | * 设置 37 | * 38 | * @param string $key 39 | * @param mixed $value 40 | */ 41 | public static function set(string $key, $value): void 42 | { 43 | if (is_array($key)) { 44 | self::$_data = Arr::merge(self::$_data, $key); 45 | } else { 46 | self::$_data[$key] = $value; 47 | } 48 | } 49 | 50 | /** 51 | * 合并信息 52 | * 53 | * @param $key 54 | * @param $value 55 | */ 56 | public static function merge($key, $value) 57 | { 58 | $data = (array)self::get($key); 59 | $data[] = $value; 60 | self::set($key, $data); 61 | } 62 | 63 | /** 64 | * 移除 65 | * 66 | * @param $key 67 | */ 68 | public static function remove(string $key): void 69 | { 70 | if (isset(self::$_data[$key])) { 71 | unset(self::$_data[$key]); 72 | } 73 | } 74 | 75 | /** 76 | * 对值增加 77 | * 78 | * @param $key 79 | * @param int $num 80 | */ 81 | public static function setInc(string $key, int $num = 1): void 82 | { 83 | if (isset(self::$_data[$key])) { 84 | self::$_data[$key] += $num; 85 | } else { 86 | self::$_data[$key] = $num; 87 | } 88 | } 89 | 90 | /** 91 | * 对值减少 92 | * 93 | * @param $key 94 | * @param $num 95 | */ 96 | public static function setDec(string $key, int $num = 1): void 97 | { 98 | if (isset(self::$_data[$key])) { 99 | self::$_data[$key] -= $num; 100 | } else { 101 | self::$_data[$key] = $num; 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /kuxin/request.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Request 12 | { 13 | 14 | /** 15 | * @return bool 16 | */ 17 | public static function isGet(): bool 18 | { 19 | return $_SERVER['REQUEST_METHOD'] === 'GET' ? true : false; 20 | 21 | } 22 | 23 | /** 24 | * @return bool 25 | */ 26 | public static function isPost(): bool 27 | { 28 | return $_SERVER['REQUEST_METHOD'] === 'POST' ? true : false; 29 | } 30 | 31 | /** 32 | * @return bool 33 | */ 34 | public static function isAjax(): bool 35 | { 36 | return ((isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') || isset($_POST['isajax']) || isset($_GET['isajax'])) ? true : false; 37 | } 38 | 39 | /** 40 | * @return bool 41 | */ 42 | public static function isMobile(): bool 43 | { 44 | // 如果有HTTP_X_WAP_PROFILE则一定是移动设备 45 | if (isset ($_SERVER['HTTP_X_WAP_PROFILE'])) { 46 | return true; 47 | } 48 | // 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息 49 | if (isset ($_SERVER['HTTP_VIA'])) { 50 | // 找不到为flase,否则为true 51 | if (stristr($_SERVER['HTTP_VIA'], "wap")) { 52 | return true; 53 | } 54 | } 55 | // 脑残法,判断手机发送的客户端标志,兼容性有待提高 56 | if (isset ($_SERVER['HTTP_USER_AGENT'])) { 57 | $clientkeywords = [ 58 | 'nokia', 59 | 'sony', 60 | 'ericsson', 61 | 'mot', 62 | 'samsung', 63 | 'htc', 64 | 'sgh', 65 | 'lg', 66 | 'sharp', 67 | 'sie-', 68 | 'philips', 69 | 'panasonic', 70 | 'alcatel', 71 | 'lenovo', 72 | 'iphone', 73 | 'ipod', 74 | 'blackberry', 75 | 'meizu', 76 | 'android', 77 | 'netfront', 78 | 'symbian', 79 | 'ucweb', 80 | 'windowsce', 81 | 'palm', 82 | 'operamini', 83 | 'operamobi', 84 | 'openwave', 85 | 'nexusone', 86 | 'cldc', 87 | 'midp', 88 | 'wap', 89 | 'mobile', 90 | 'UCBrowser', 91 | ]; 92 | // 从HTTP_USER_AGENT中查找手机浏览器的关键字 93 | if (preg_match("/(" . implode('|', $clientkeywords) . ")/i", strtolower($_SERVER['HTTP_USER_AGENT']))) { 94 | return true; 95 | } 96 | } 97 | // 协议法,因为有可能不准确,放到最后判断 98 | if (isset ($_SERVER['HTTP_ACCEPT'])) { 99 | // 如果只支持wml并且不支持html那一定是移动设备 100 | // 如果支持wml和html但是wml在html之前则是移动设备 101 | if ((strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html')))) { 102 | return true; 103 | } 104 | } 105 | return false; 106 | } 107 | 108 | /** 109 | * @param string $ua 110 | * @return bool 111 | */ 112 | public static function isSpider($ua = ''): bool 113 | { 114 | empty($ua) && $ua = $_SERVER['HTTP_USER_AGENT']; 115 | $ua = strtolower($ua); 116 | $spiders = [ 'bot', 'crawl', 'spider', 'slurp', 'sohu-search', 'lycos', 'robozilla' ]; 117 | foreach ($spiders as $spider) { 118 | if (false !== strpos($ua, $spider)) 119 | return true; 120 | } 121 | return false; 122 | } 123 | 124 | /** 125 | * @param string $defaultIp 126 | * @return string 127 | */ 128 | public static function getIp(string $defaultIp = '0.0.0.0'): string 129 | { 130 | $ip = $_SERVER['REMOTE_ADDR']; 131 | $i = explode('.', $ip); 132 | if ($i[0] == 10 || ($i[0] == 172 && $i[1] > 15 && $i[1] < 32) || ($i[0] == 192 && $i[1] == 168)) { 133 | //如果是内网ip重新获取 134 | $keys = [ 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_REAL_IP' ]; 135 | foreach ($keys as $key) { 136 | if (empty($_SERVER[$key])) 137 | continue; 138 | $ips = explode(',', $_SERVER[$key], 1); 139 | $ip = $ips[0]; 140 | break; 141 | } 142 | } 143 | $l = ip2long($ip); 144 | if ((false !== $l) && ($ip === long2ip($l))) { 145 | return $ip; 146 | } 147 | return $defaultIp; 148 | } 149 | 150 | } -------------------------------------------------------------------------------- /kuxin/rewrite.php: -------------------------------------------------------------------------------- 1 | '(\d+)', 9 | '{chapterid}' => '(\d+)', 10 | '{siteid}' => '(\d+)', 11 | '{key}' => '([a-zA-Z0-9_\-]+)', 12 | '{searchkey}' => '([^\?\/]+?)', 13 | '{novelid}' => '(\d+)', 14 | '{author}' => '([^\?\/]+?)', 15 | '{authorid}' => '(\d+)', 16 | '{pinyin}' => '([\w\-]+)', 17 | '{type}' => '([\w\-]+)', 18 | '{booklistid}' => '(\d+)', 19 | '{specialid}' => '(\d+)', 20 | 21 | ]; 22 | 23 | public static function format($param) 24 | { 25 | foreach ($param as &$v) { 26 | $v = '/' . ltrim($v, '/'); 27 | } 28 | return $param; 29 | } 30 | 31 | public static function createRouter($param) 32 | { 33 | $data = []; 34 | foreach ($param as $k => $v) { 35 | $v = ltrim($v, '/'); 36 | if ($v != '') { 37 | $rules = self::parseRule($v); 38 | if (strpos($v, '{') === false) { 39 | $position = 99; 40 | } else { 41 | $position = substr_count(explode('{', $v, 2)[0], '/') + 1; 42 | } 43 | foreach ($rules as $rule) { 44 | preg_match_all('/\{([a-z]+)\}/', $rule, $match); 45 | foreach ($match['0'] as $p) { 46 | $preg = empty(self::$rule[$p]) ? '([a-zA-Z0-9]+)' : self::$rule[$p]; 47 | $rule = str_replace($p, $preg, $rule); 48 | } 49 | 50 | $rule = rtrim($rule, '/'); 51 | $data[$position]["^{$rule}(\?(.*))*$"] = str_replace('.', '/', $k) . '?' . implode('&', $match[1]); 52 | } 53 | } 54 | } 55 | krsort($data); 56 | $result = []; 57 | foreach ($data as $value) { 58 | $result = array_merge($result, $value); 59 | } 60 | return $result; 61 | } 62 | 63 | public static function parseRule($rule) 64 | { 65 | preg_match_all('#\[[^\[\]]*?\{\w+\}[^\[\]]*?\]#', $rule, $match); 66 | $rules = [ $rule ]; 67 | if ($match['0']) { 68 | if (count($match['0']) == 1) { 69 | $rules[] = str_replace($match['0']['0'], '', $rule); 70 | } elseif (count($match['0'] == 2)) { 71 | $rules[] = str_replace($match['0']['0'], '', $rule); 72 | $rules[] = str_replace($match['0']['1'], '', $rule); 73 | $rules[] = str_replace([ $match['0']['0'], $match['0']['1'] ], '', $rule); 74 | } elseif (count($match['0'] == 3)) { 75 | $rules[] = str_replace($match['0']['0'], '', $rule); 76 | $rules[] = str_replace($match['0']['1'], '', $rule); 77 | $rules[] = str_replace($match['0']['2'], '', $rule); 78 | $rules[] = str_replace([ $match['0']['0'], $match['0']['1'] ], '', $rule); 79 | $rules[] = str_replace([ $match['0']['0'], $match['0']['2'] ], '', $rule); 80 | $rules[] = str_replace([ $match['0']['1'], $match['0']['2'] ], '', $rule); 81 | $rules[] = str_replace([ $match['0']['0'], $match['0']['1'], $match['0']['2'] ], '', $rule); 82 | } 83 | foreach ($rules as &$v) { 84 | $v = str_replace([ '[', ']' ], '', $v); 85 | } 86 | } 87 | 88 | return $rules; 89 | } 90 | 91 | public static function iisRule($param) 92 | { 93 | $data = "[ISAPI_Rewrite]\r\n\r\nCacheClockRate 3600\r\nRepeatLimit 32\r\n"; 94 | foreach ($param as $k => $v) { 95 | $v = ltrim($v, '/'); 96 | if ($v != '') { 97 | $param = []; 98 | preg_match_all('/\{([a-z]+)\}/', $v, $match); 99 | $n = 1; 100 | foreach ($match['0'] as $kk => $vv) { 101 | $preg = empty(self::$rule[$vv]) ? '([a-zA-Z0-9]+)' : self::$rule[$vv]; 102 | $v = str_replace($vv, $preg, $v); 103 | ++$n; 104 | $param[] = "{$match['1'][$kk]}=$" . $n; 105 | } 106 | $data .= "RewriteRule ^(.*)/{$v}(\?(.*))*$ $1/index.php\?s=" . str_replace([ 107 | '.', 108 | '_', 109 | ], '/', $k) . '&' . implode('&', $param) . '&$' . ($n + 2) . " [I]\r\n"; 110 | } 111 | } 112 | return trim($data); 113 | } 114 | } -------------------------------------------------------------------------------- /kuxin/router.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Router 12 | { 13 | 14 | static $controller = 'index'; 15 | static $action = 'index'; 16 | 17 | static $selfConsoleClass = [ 18 | 'migrate', 19 | ]; 20 | 21 | /** 22 | * 解析controller和action 23 | */ 24 | public static function dispatcher(): void 25 | { 26 | //解析s变量 27 | if (isset($_GET['s'])) { 28 | $superVar = rtrim($_GET['s'], '/'); 29 | //判断是否需要进行rewrite转换 30 | if (Config::get('rewrite.power')) { 31 | $superVar = self::rewrite($superVar); 32 | } 33 | if (strpos($superVar, '/')) { 34 | if (strpos($superVar, '.')) { 35 | $param = explode('.', $superVar, 2); 36 | if (!Request::isAjax()) { 37 | Response::setType($param['1']); 38 | } 39 | $param = explode('/', $param['0']); 40 | } else { 41 | $param = explode('/', $superVar); 42 | } 43 | self::$action = strtolower(array_pop($param)); 44 | self::$controller = strtolower(implode('\\', $param)); 45 | } else { 46 | self::$controller = strtolower($superVar); 47 | self::$action = 'index'; 48 | } 49 | unset($_GET['s']); 50 | unset($_REQUEST['s']); 51 | } 52 | } 53 | 54 | /** 55 | * 正则模式解析 56 | */ 57 | public static function rewrite($superVar) 58 | { 59 | if ($router = Config::get('rewrite.router')) { 60 | foreach ($router as $rule => $url) { 61 | if (preg_match('{' . $rule . '}isU', $superVar, $match)) { 62 | unset($match['0']); 63 | if (strpos($url, '?')) { 64 | list($url, $query) = explode('?', $url); 65 | } 66 | $superVar = rtrim($url, '/'); 67 | if ($match && !empty($query)) {//组合后面的参数 68 | $param = explode('&', $query); 69 | if (count($param) == count($match) && $var = array_combine($param, $match)) { 70 | $_GET = array_merge($_GET, $var); 71 | $_REQUEST = array_merge($_REQUEST, $var); 72 | } 73 | } 74 | break; 75 | } 76 | } 77 | } 78 | return $superVar; 79 | } 80 | 81 | /** 82 | * 命令行解析 83 | */ 84 | public static function cli() 85 | { 86 | global $argv; 87 | if (strpos($argv['1'], ':')) { 88 | $param = explode(':', $argv['1']); 89 | self::$action = array_pop($param); 90 | self::$controller = implode('\\', $param); 91 | } else { 92 | self::$action = 'run'; 93 | self::$controller = $argv['1']; 94 | } 95 | $data = ['argv' => $argv]; 96 | if (!empty($argv['2'])) { 97 | $param = explode('/', $argv['2']); 98 | $num = count($param); 99 | for ($i = 0; $i < $num; $i += 2) { 100 | $data[$param[$i]] = $param[$i + 1] ?? ""; 101 | } 102 | } 103 | Registry::set('cli_params', $data); 104 | } 105 | } -------------------------------------------------------------------------------- /kuxin/session.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Session 12 | { 13 | 14 | public static $start = false; 15 | 16 | /** 17 | * 启动session 18 | * 19 | * @param array $config 20 | */ 21 | public static function start(array $config = []) 22 | { 23 | if (self::$start || session_status() === PHP_SESSION_ACTIVE) { 24 | return; 25 | } 26 | if (!$config) { 27 | $config = Config::get('session', []); 28 | } 29 | if (isset($config['hanlder'])) { 30 | ini_set("session.save_handler", $config['hanlder']); 31 | ini_set("session.save_path", $config['path']); 32 | //ini_set("session.save_path", "tcp://127.0.0.1:11211"); 33 | } 34 | session_start(); 35 | self::$start = true; 36 | } 37 | 38 | /** 39 | * @param string $name 40 | * @param mixed $default 41 | * @return mixed 42 | */ 43 | public static function get(string $name = '', $default = null) 44 | { 45 | self::start(); 46 | if ($name == '') 47 | return $_SESSION; 48 | //数组模式 找到返回 49 | if (strpos($name, '.')) { 50 | //数组模式 找到返回 51 | $c = $_SESSION; 52 | $fields = explode('.', $name); 53 | foreach ($fields as $field) { 54 | if (!isset($c[$field])) 55 | return (is_callable($default) ? $default($name) : $default); 56 | $c = $c[$field]; 57 | } 58 | return $c; 59 | } elseif (isset($_SESSION[$name])) { 60 | return $_SESSION[$name]; 61 | } else { 62 | return (is_callable($default) ? $default($name) : $default); 63 | } 64 | } 65 | 66 | /** 67 | * 是否存在 68 | * @param string $key 69 | * @return bool 70 | */ 71 | public static function has(string $key) 72 | { 73 | self::start(); 74 | return isset($_SESSION[$key]); 75 | } 76 | 77 | /** 78 | * @param $key 79 | * @param mixed $value 80 | * @return bool 81 | */ 82 | public static function set(string $key, $value) 83 | { 84 | $_SESSION[$key] = $value; 85 | return true; 86 | } 87 | 88 | /** 89 | * @param $key 90 | * @return bool 91 | */ 92 | public static function remove(string $key): bool 93 | { 94 | self::start(); 95 | if (!isset($_SESSION[$key])) { 96 | return false; 97 | } 98 | 99 | unset($_SESSION[$key]); 100 | 101 | return true; 102 | } 103 | 104 | /** 105 | * 清空session值 106 | * 107 | * @access public 108 | * @return void 109 | */ 110 | public static function clear() 111 | { 112 | $_SESSION = []; 113 | } 114 | 115 | /** 116 | * 注销session 117 | * 118 | * @access public 119 | * @return void 120 | */ 121 | public static function destory() 122 | { 123 | if (session_id()) { 124 | unset($_SESSION); 125 | session_destroy(); 126 | } 127 | } 128 | 129 | /** 130 | * 当浏览器关闭时,session将停止写入 131 | * 132 | * @access public 133 | * @return void 134 | */ 135 | public static function close() 136 | { 137 | if (session_status() === PHP_SESSION_ACTIVE) { 138 | session_write_close(); 139 | } 140 | } 141 | } -------------------------------------------------------------------------------- /kuxin/storage.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Storage 12 | { 13 | /** 14 | * @var array 15 | */ 16 | protected $config; 17 | 18 | /** 19 | * @var \Kuxin\Storage\File 20 | */ 21 | protected $handler; 22 | 23 | public function __construct(array $config) 24 | { 25 | $this->config = $config; 26 | $class = 'Kuxin\\storage\\' . $config['driver']; 27 | return $this->handler = Loader::instance($class, [$config['option']]); 28 | } 29 | 30 | /** 31 | * @param string $file 32 | * @return bool 33 | */ 34 | public function exist(string $file) 35 | { 36 | return $this->handler->exist($file); 37 | } 38 | 39 | /** 40 | * @param $file 41 | * @return bool|int 42 | */ 43 | public function mtime(string $file) 44 | { 45 | return $this->handler->mtime($file); 46 | } 47 | 48 | /** 49 | * @param string $file 50 | * @param string $content 51 | * @return bool|int 52 | */ 53 | public function write(string $file, string $content) 54 | { 55 | return $this->handler->write($file, $content); 56 | } 57 | 58 | /** 59 | * @param $file 60 | * @return bool|string 61 | */ 62 | public function read(string $file) 63 | { 64 | return $this->handler->read($file); 65 | } 66 | 67 | /** 68 | * @param $file 69 | * @param $content 70 | * @return bool|int 71 | */ 72 | public function append(string $file, string $content) 73 | { 74 | return $this->handler->append($file, $content); 75 | } 76 | 77 | /** 78 | * @param $file 79 | * @return bool 80 | */ 81 | public function remove(string $file) 82 | { 83 | return $this->handler->remove($file); 84 | } 85 | 86 | /** 87 | * @param $file 88 | * @return string 89 | */ 90 | public function getUrl(string $file) 91 | { 92 | return $this->handler->getUrl($file); 93 | } 94 | 95 | /** 96 | * @param $file 97 | * @return string 98 | */ 99 | public function getPath(string $file) 100 | { 101 | return $this->handler->getPath($file); 102 | } 103 | 104 | /** 105 | * @return string 106 | */ 107 | public function error() 108 | { 109 | return $this->handler->error(); 110 | } 111 | 112 | /** 113 | * @return string 114 | */ 115 | public function flush() 116 | { 117 | return $this->handler->flush(); 118 | } 119 | } -------------------------------------------------------------------------------- /kuxin/storage/cos.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class Cos 14 | { 15 | 16 | public function __construct($config) 17 | { 18 | if (empty($config['key'])) { 19 | trigger_error('access key 未设置'); 20 | } 21 | if (empty($config['secret'])) { 22 | trigger_error('access secret 未设置'); 23 | } 24 | if (empty($config['bucket'])) { 25 | trigger_error('bucket 未设置'); 26 | } 27 | if (empty($config['endpoint'])) { 28 | trigger_error('endpoint 未设置'); 29 | } 30 | if (empty($config['url'])) { 31 | trigger_error('访问域名 未设置'); 32 | } 33 | 34 | $this->accessKeyId = $config['key']; 35 | $this->accessKeySecret = $config['secret']; 36 | $this->bucket = $config['bucket']; 37 | $this->host = $config['bucket'] . '.cos.' . $config['endpoint']; 38 | $this->api = 'http://' . $this->host; 39 | $this->preUrl = $config['url']; 40 | $this->path = '/' . ltrim('/' . $config['path'] ?? '', '/'); 41 | } 42 | 43 | public function getPath($object) 44 | { 45 | return $object; 46 | } 47 | 48 | public function exist($object) 49 | { 50 | $path = $this->path . '/' . $object; 51 | $sign = $this->sign('HEAD', $path, $this->host); 52 | $res = Http::curl($this->api . $path, [], 'HEAD', ['Authorization' => $sign], [CURLOPT_HEADER => 1]); 53 | return strpos($res, ' 200 OK'); 54 | } 55 | 56 | public function mtime($object) 57 | { 58 | $path = $this->path . '/' . $object; 59 | $sign = $this->sign('HEAD', $path, $this->host); 60 | $res = Http::curl($this->api . $path, [], 'HEAD', ['Authorization' => $sign], [CURLOPT_HEADER => 1]); 61 | $data = Http::parse_headers($res); 62 | return isset($data['last-modified']) ? strtotime($data['last-modified']) : 0; 63 | } 64 | 65 | 66 | public function read($object) 67 | { 68 | $path = $this->path . '/' . $object; 69 | $sign = $this->sign('GET', $path, $this->host); 70 | return Http::curl($this->api . $path, [], 'GET', ['Authorization' => $sign]); 71 | } 72 | 73 | public function write($object, $content) 74 | { 75 | $path = $this->path . '/' . $object; 76 | $sign = $this->sign('PUT', $path, $this->host); 77 | $res = Http::curl($this->api . $path, $content, 'PUT', ['Authorization' => $sign]); 78 | return $res === ""; 79 | } 80 | 81 | public function append($object, $content) 82 | { 83 | if ($this->exist($object)) { 84 | $content = $this->read($object) . $content; 85 | } 86 | return $this->write($object, $content); 87 | } 88 | 89 | public function remove($object) 90 | { 91 | $path = $this->path . '/' . $object; 92 | $sign = $this->sign('DELETE', $path, $this->host); 93 | $res = Http::curl($this->api . $path, [], 'DELETE', ['Authorization' => $sign]); 94 | return $res === ''; 95 | } 96 | 97 | public function getUrl($object) 98 | { 99 | return $this->preUrl . $this->path . '/' . $object; 100 | } 101 | 102 | public function error() 103 | { 104 | return ''; 105 | } 106 | 107 | 108 | private function sign($method, $path, $host) 109 | { 110 | $signTime = (string)(time() - 60) . ';' . (string)(time() + 3600); 111 | $httpString = strtolower($method) . "\n" . urldecode($path) . 112 | "\n\nhost=" . $host . "\n"; 113 | $sha1edHttpString = sha1($httpString); 114 | $stringToSign = "sha1\n$signTime\n$sha1edHttpString\n"; 115 | $signKey = hash_hmac('sha1', $signTime, $this->accessKeySecret); 116 | $signature = hash_hmac('sha1', $stringToSign, $signKey); 117 | $authorization = 'q-sign-algorithm=sha1&q-ak=' . $this->accessKeyId . 118 | "&q-sign-time=$signTime&q-key-time=$signTime&q-header-list=host&q-url-param-list=&" . 119 | "q-signature=$signature"; 120 | return $authorization; 121 | } 122 | } -------------------------------------------------------------------------------- /kuxin/storage/file.php: -------------------------------------------------------------------------------- 1 | path = $config['path']; 17 | $this->url = $config['url'] ?? ""; 18 | } 19 | 20 | public function exist($file) 21 | { 22 | return is_file($this->getPath($file)); 23 | } 24 | 25 | public function mtime($file) 26 | { 27 | return filemtime($this->getPath($file)); 28 | } 29 | 30 | public function write($file, $content) 31 | { 32 | $fullfile = $this->getPath($file); 33 | if (!is_dir(dirname($fullfile))) { 34 | mkdir(dirname($fullfile), 0755, true); 35 | } 36 | return file_put_contents($fullfile, (string)$content); 37 | } 38 | 39 | public function read($file) 40 | { 41 | $fullfile = $this->getPath($file); 42 | if (is_file($fullfile)) { 43 | return file_get_contents($fullfile); 44 | } else { 45 | return false; 46 | } 47 | } 48 | 49 | public function append($file, $content) 50 | { 51 | $fullfile = $this->getPath($file); 52 | if (!is_dir(dirname($fullfile))) { 53 | mkdir(dirname($fullfile), 0755, true); 54 | } 55 | return file_put_contents($fullfile, (string)$content, FILE_APPEND); 56 | } 57 | 58 | public function remove($file) 59 | { 60 | $file = $this->getPath($file); 61 | if (is_file($file)) { 62 | //删除文件 63 | return unlink($file); 64 | } elseif (is_dir($file)) { 65 | //删除目录 66 | $handle = opendir($file); 67 | while (($filename = readdir($handle)) !== false) { 68 | if ($filename !== '.' && $filename !== '..') { 69 | $this->remove($file . '/' . $filename); 70 | } 71 | } 72 | closedir($handle); 73 | return rmdir($file); 74 | } 75 | } 76 | 77 | public function getUrl($file) 78 | { 79 | $file = ($file{0} === '/' || $file{1} == ':') ? str_replace($this->path, '', $file) : $file; 80 | return rtrim($this->url, '/') . '/' . $file; 81 | } 82 | 83 | public function getPath($file) 84 | { 85 | return ($file{0} === '/' || $file{1} == ':') ? $file : $this->path . '/' . ltrim($file, '/'); 86 | } 87 | 88 | public function error() 89 | { 90 | return ''; 91 | } 92 | 93 | public function flush() 94 | { 95 | return $this->remove(''); 96 | } 97 | } -------------------------------------------------------------------------------- /kuxin/storage/oss.php: -------------------------------------------------------------------------------- 1 | accessKeyId = $config['key']; 29 | $this->accessKeySecret = $config['secret']; 30 | $this->bucket = $config['bucket']; 31 | $this->host = $config['bucket'] . '.' . $config['endpoint']; 32 | $this->api = 'http://' . $this->host . '/'; 33 | $this->preUrl = $config['url']; 34 | $this->path = '/' . ltrim($config['path'], '/'); 35 | } 36 | 37 | public function getPath($object) 38 | { 39 | return $object; 40 | } 41 | 42 | public function exist($object) 43 | { 44 | $data = $this->getObjectMeta($object); 45 | 46 | return !empty($data['last-modified']); 47 | } 48 | 49 | public function mtime($object) 50 | { 51 | $data = $this->getObjectMeta($object); 52 | 53 | return $data['x-oss-meta-mtime'] ?? (isset($data['last-modified']) ? strtotime($data['last-modified']) : 0); 54 | } 55 | 56 | 57 | public function read($object) 58 | { 59 | $path = $this->path . '/' . $object; 60 | $method = 'GET'; 61 | $options[CURLOPT_HEADER] = 1; 62 | 63 | $headers = $this->genHeaders($object); 64 | $sign = $this->sign($method, $headers, '/' . $this->bucket . $path); 65 | $headers['Authorization'] = "OSS {$this->accessKeyId}:{$sign}"; 66 | $url = $this->api . $path; 67 | $res = Http::curl($url, [], $method, $headers, $options); 68 | if (strpos($res, 'Content-MD5')) { 69 | return Http::parse_content($res); 70 | } else { 71 | return false; 72 | } 73 | } 74 | 75 | public function write($object, $content) 76 | { 77 | $path = $this->path . '/' . $object; 78 | $method = 'PUT'; 79 | $options[CURLOPT_HEADER] = 1; 80 | $headers = $this->genHeaders($object); 81 | $headers['Content-MD5'] = base64_encode(md5($content, true)); 82 | $sign = $this->sign($method, $headers, '/' . $this->bucket . $path); 83 | $headers['Authorization'] = "OSS {$this->accessKeyId}:{$sign}"; 84 | $url = $this->api . $path; 85 | $res = Http::curl($url, $content, $method, $headers, $options); 86 | 87 | return strpos($res, '200 OK'); 88 | } 89 | 90 | public function append($object, $content) 91 | { 92 | $data = $this->getObjectMeta($object); 93 | $path = $this->path . '/' . $object . '?append&position=0' . $data['content-length']; 94 | $method = 'POST'; 95 | $options[CURLOPT_HEADER] = 1; 96 | $headers = $this->genHeaders($object); 97 | $headers['Content-MD5'] = base64_encode(md5($content, true)); 98 | $headers['Accept-Encoding'] = ''; 99 | $sign = $this->sign($method, $headers, '/' . $this->bucket . $path); 100 | $headers['Authorization'] = "OSS {$this->accessKeyId}:{$sign}"; 101 | $url = $this->api . $path; 102 | $res = Http::curl($url, $content, $method, $headers, $options); 103 | 104 | return strpos($res, '200 OK'); 105 | } 106 | 107 | public function remove($object) 108 | { 109 | $path = $this->path . '/' . $object; 110 | $method = 'DELETE'; 111 | $options[CURLOPT_HEADER] = 1; 112 | $headers = $this->genHeaders($object); 113 | $sign = $this->sign($method, $headers, '/' . $this->bucket . $path); 114 | $headers['Authorization'] = "OSS {$this->accessKeyId}:{$sign}"; 115 | $url = $this->api . $path; 116 | $res = Http::curl($url, [], $method, $headers, $options); 117 | $headers = Http::parse_headers($res); 118 | if ($headers['x-oss-meta-mtime']) { 119 | return Http::parse_content($res); 120 | } else { 121 | return false; 122 | } 123 | } 124 | 125 | public function getUrl($object) 126 | { 127 | return $this->preUrl . $this->path . '/' . $object; 128 | } 129 | 130 | public function error() 131 | { 132 | return ''; 133 | } 134 | 135 | private function getObjectMeta($object) 136 | { 137 | $path = $this->path . '/' . $object . '?objectMeta'; 138 | $method = 'HEAD'; 139 | $options[CURLOPT_HEADER] = 1; 140 | 141 | $headers = $this->genHeaders($object); 142 | $sign = $this->sign($method, $headers, '/' . $this->bucket . $path); 143 | $headers['Authorization'] = "OSS {$this->accessKeyId}:{$sign}"; 144 | $url = $this->api . $path; 145 | $res = Http::curl($url, [], $method, $headers, $options); 146 | 147 | return Http::parse_headers($res); 148 | } 149 | 150 | private function sign($method, $headers, $object) 151 | { 152 | $param = []; 153 | $param[] = $method; 154 | $param[] = $headers['Content-MD5'] ?? ""; 155 | $param[] = $headers['Content-Type'] ?? ""; 156 | $param[] = $headers['Date']; 157 | //$param[] = $headers['Host']; 158 | $param[] = $object; 159 | 160 | return base64_encode(hash_hmac('sha1', join("\n", $param), $this->accessKeySecret, true)); 161 | } 162 | 163 | private function genHeaders($object) 164 | { 165 | return [ 166 | 'Date' => gmdate('D, d M Y H:i:s \G\M\T'), 167 | 'Host' => $this->host, 168 | 'Content-Type' => File::getMimeByName($object), 169 | ]; 170 | } 171 | } -------------------------------------------------------------------------------- /kuxin/tpl/error.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | KXcms Framework - 系统发生错误 6 | 141 | 142 | 143 | 0 ? $errorLine - $halfLines : 0; 161 | $endLine = $errorLine + $halfLines < $lineCount ? $errorLine + $halfLines : $lineCount - 1; 162 | $lineNumberWidth = strlen($endLine + 1); 163 | $output = ''; 164 | for ($i = $beginLine; $i <= $endLine; ++$i) { 165 | $isErrorLine = $i === $errorLine; 166 | $code = sprintf("%0{$lineNumberWidth}d %s", $i + 1, htmlspecialchars(str_replace("\t", ' ', $lines[$i]))); 167 | if (!$isErrorLine) 168 | $output .= $code; 169 | else 170 | $output .= '' . $code . ''; 171 | } 172 | return ' 173 |

174 |
' . $output . '
175 |
176 | '; 177 | } 178 | 179 | ?> 180 |
181 | Hi,出错了! 182 |

183 |
184 | 185 |
186 |
187 | 错误位置 188 |
189 |
190 |

 LINE:

191 |
192 |
193 | 199 |
200 |
TRACE
201 |
202 |
203 |
204 |
205 |
206 |
文件加载
207 |
208 |
209 |
210 |
211 |
212 |
213 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /kuxin/weixin/js.php: -------------------------------------------------------------------------------- 1 | getSignaturePackage($debug); 36 | $base = array( 37 | 'debug' => $debug, 38 | ); 39 | $config = array_merge($base, $signPackage, array('jsApiList' => $APIs)); 40 | 41 | return $json ? json_encode($config) : $config; 42 | } 43 | 44 | /** 45 | * 获取数组形式的配置. 46 | * 47 | * @param array $APIs 48 | * @param bool $debug 49 | * 50 | * @return array 51 | */ 52 | public function getConfigArray(array $APIs, $debug = false) 53 | { 54 | return $this->config($APIs, $debug, false); 55 | } 56 | 57 | /** 58 | * 获取jsticket. 59 | * 60 | * @return string 61 | */ 62 | public function getTicket() 63 | { 64 | $key = 'kuxin.weixin.jsapi_ticket.' . $this->appId; 65 | 66 | $data = DI::Cache()->get($key); 67 | if (!$data || empty($data['ticket'])) { 68 | $token = $this->getToken(); 69 | $data = $this->parseJSON(Http::get(self::API_TICKET, ['access_token' => $token])); 70 | if ($data && !empty($data['ticket'])) { 71 | DI::Cache()->set($key, $data, $data['expires_in']/10); 72 | } else { 73 | $data['ticket'] = null; 74 | } 75 | 76 | } 77 | return $data['ticket']; 78 | } 79 | 80 | /** 81 | * 签名. 82 | * 83 | * @param bool $debug 84 | * @param string $url 85 | * @param string $nonce 86 | * @param int $timestamp 87 | * 88 | * @return array 89 | */ 90 | public function getSignaturePackage($debug=false,$url = null, $nonce = null, $timestamp = null) 91 | { 92 | $url = $url ? $url : $this->getUrl(); 93 | $nonce = $nonce ? $nonce : $this->getNonce(); 94 | $timestamp = $timestamp ? $timestamp : time(); 95 | $ticket = $this->getTicket(); 96 | $sign = [ 97 | 'appId' => $this->appId, 98 | 'nonceStr' => $nonce, 99 | 'timestamp' => $timestamp, 100 | 'signature' => $this->getSignature($ticket, $nonce, $timestamp, $url), 101 | ]; 102 | if($debug){ 103 | $sign['url']=$url; 104 | $sign['ticket']=$ticket; 105 | } 106 | return $sign; 107 | } 108 | 109 | /** 110 | * 生成签名. 111 | * 112 | * @param string $ticket 113 | * @param string $nonce 114 | * @param int $timestamp 115 | * @param string $url 116 | * 117 | * @return string 118 | */ 119 | public function getSignature($ticket, $nonce, $timestamp, $url) 120 | { 121 | return sha1("jsapi_ticket={$ticket}&noncestr={$nonce}×tamp={$timestamp}&url={$url}"); 122 | } 123 | 124 | /** 125 | * 设置当前URL. 126 | * 127 | * @param string $url 128 | * 129 | * @return Js 130 | */ 131 | public function setUrl($url) 132 | { 133 | $this->url = $url; 134 | 135 | return $this; 136 | } 137 | 138 | /** 139 | * 获取当前URL. 140 | * 141 | * @return string 142 | */ 143 | public function getUrl() 144 | { 145 | if ($this->url) { 146 | return $this->url; 147 | } 148 | 149 | return Url::weixin(); 150 | } 151 | 152 | /** 153 | * 获取随机字符串. 154 | * 155 | * @return string 156 | */ 157 | public function getNonce() 158 | { 159 | return uniqid('kxcms_'); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /kuxin/weixin/oauth.php: -------------------------------------------------------------------------------- 1 | $this->appId, 47 | 'redirect_uri' => $to, 48 | 'response_type' => 'code', 49 | 'scope' => $scope, 50 | 'state' => $state, 51 | ); 52 | return self::API_URL . '?' . http_build_query($params) . '#wechat_redirect'; 53 | } 54 | 55 | /** 56 | * 直接跳转 57 | * 58 | * @param string $to 59 | * @param string $scope 60 | * @param string $state 61 | */ 62 | public function redirect($to = null, $scope = 'snsapi_userinfo', $state = 'STATE') 63 | { 64 | header('Location:' . $this->url($to, $scope, $state)); 65 | exit; 66 | } 67 | 68 | 69 | /** 70 | * 获取已授权用户 71 | * 72 | * @return mixed 73 | */ 74 | public function user() 75 | { 76 | if ($this->authorizedUser 77 | || !Input::has('state', $_GET) 78 | || (!$code = Input::get('code','str')) && !Input::has('state', $_GET) 79 | ) { 80 | return $this->authorizedUser; 81 | } 82 | $permission = $this->getAccessPermission($code); 83 | 84 | if ($permission['scope'] !== 'snsapi_userinfo') { 85 | $user = ['openid' => $permission['openid']]; 86 | } else { 87 | $user = $this->getUser($permission['openid'], $permission['access_token']); 88 | } 89 | 90 | return $this->authorizedUser = $user; 91 | } 92 | 93 | /** 94 | * 通过授权获取用户 95 | * 96 | * @param string $to 97 | * @param string $state 98 | * @param string $scope 99 | * 100 | * @return null 101 | */ 102 | public function authorize($to = null, $scope = 'snsapi_userinfo', $state = 'STATE') 103 | { 104 | if (!Input::has('state', $_GET) && !Input::has('code', $_GET)) { 105 | $this->redirect($to, $scope, $state); 106 | } 107 | return $this->user(); 108 | } 109 | 110 | /** 111 | * 检查 Access Token 是否有效 112 | * 113 | * @param string $accessToken 114 | * @param string $openId 115 | * 116 | * @return boolean 117 | */ 118 | public function accessTokenIsValid($accessToken, $openId) 119 | { 120 | $params = array( 121 | 'openid' => $openId, 122 | 'access_token' => $accessToken, 123 | ); 124 | try { 125 | Http::get(self::API_TOKEN_VALIDATE, $params); 126 | return true; 127 | } catch (\Exception $e) { 128 | return false; 129 | } 130 | } 131 | 132 | /** 133 | * 刷新 access_token 134 | * 135 | * @param string $refreshToken 136 | * 137 | * @return mixed 138 | */ 139 | public function refresh($refreshToken) 140 | { 141 | $params = [ 142 | 'appid' => $this->appId, 143 | 'grant_type' => 'refresh_token', 144 | 'refresh_token' => $refreshToken, 145 | ]; 146 | $permission = $this->parseJSON(Http::get(self::API_TOKEN_REFRESH, $params)); 147 | $this->lastPermission = array_merge($this->lastPermission, $permission); 148 | return $permission; 149 | } 150 | 151 | /** 152 | * 获取用户信息 153 | * 154 | * @param string $openId 155 | * @param string $accessToken 156 | * 157 | * @return array 158 | */ 159 | public function getUser($openId, $accessToken) 160 | { 161 | $queries = array( 162 | 'access_token' => $accessToken, 163 | 'openid' => $openId, 164 | 'lang' => 'zh_CN', 165 | ); 166 | return $this->parseJSON(Http::get(self::API_USER,$queries)); 167 | } 168 | 169 | /** 170 | * 获取access token 171 | * 172 | * @param string $code 173 | * 174 | * @return string 175 | */ 176 | public function getAccessPermission($code) 177 | { 178 | $params = array( 179 | 'appid' => $this->appId, 180 | 'secret' => $this->appSecret, 181 | 'code' => $code, 182 | 'grant_type' => 'authorization_code', 183 | ); 184 | $res=$this->parseJSON(Http::post(self::API_TOKEN_GET.'?'.http_build_query($params))); 185 | if(isset($res['errorcode'])){ 186 | trigger_error('get access_token error: '.$res['errormsg'], E_USER_ERROR); 187 | } 188 | return $this->lastPermission = $res; 189 | } 190 | } -------------------------------------------------------------------------------- /kuxin/weixin/pay.php: -------------------------------------------------------------------------------- 1 | $this->appId, 20 | 'mch_id' => Config::get('weixin.mchid'), 21 | 'nonce_str' => $this->getNonceStr(), 22 | 'notify_url' => Config::get('weixin.notifyurl'), 23 | 'spbill_create_ip' => Request::getIp(), 24 | 'trade_type' => 'JSAPI', 25 | ], $data); 26 | $data['sign'] = $this->makeSign($data); 27 | 28 | $xml = Xml::encode($data); 29 | $res = Http::post(self::API_UNIFIED_ORDER, $xml); 30 | if($res){ 31 | $res = Xml::decode($res); 32 | if ($res['return_code'] == 'SUCCESS') { 33 | return $res['prepay_id']; 34 | } else { 35 | return $res['return_msg']; 36 | } 37 | } 38 | 39 | } 40 | 41 | public function notify($data = null) 42 | { 43 | if (!$data) { 44 | $data = file_get_contents('php://input'); 45 | } 46 | $data = Xml::decode($data); 47 | if (isset($data['return_code']) && $data['return_code'] == 'SUCCESS') { 48 | if ($data['sign'] == $this->makeSign($data)) { 49 | return [ 50 | 'id' => $data['out_trade_no'], 51 | 'is_subscribe' => $data['is_subscribe'], 52 | 'openid' => $data['openid'], 53 | 'attach' => isset($data['attach']) ? $data['attach'] : "", 54 | 'time_end' => strtotime($data['time_end']), 55 | 'transaction_id' => $data['transaction_id'], 56 | 'money' => $data['total_fee'] / 100, 57 | ]; 58 | } else { 59 | return '签名验证失败'; 60 | } 61 | } else { 62 | return '解析数据失败'; 63 | } 64 | } 65 | 66 | public function jsPayConfig($prepay_id, $json = true) 67 | { 68 | $data = [ 69 | 'appId' => $this->appId, 70 | 'timeStamp' => (string)$_SERVER['REQUEST_TIME'], 71 | 'nonceStr' => $this->getNonceStr(), 72 | 'package' => 'prepay_id=' . $prepay_id, 73 | 'signType' => 'MD5', 74 | ]; 75 | $data['paySign'] = $this->makeSign($data); 76 | return $json ? Json::encode($data) : $data; 77 | } 78 | 79 | /** 80 | * 格式化参数格式化成url参数 81 | */ 82 | public function toUrlParams($data) 83 | { 84 | $buff = ""; 85 | foreach ($data as $k => $v) { 86 | if ($k != "sign" && $v != "" && !is_array($v)) { 87 | $buff .= $k . "=" . $v . "&"; 88 | } 89 | } 90 | 91 | $buff = trim($buff, "&"); 92 | return $buff; 93 | } 94 | 95 | /** 96 | * 生成签名 97 | * 98 | * @return string 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值 99 | */ 100 | public function makeSign($data) 101 | { 102 | //签名步骤一:按字典序排序参数 103 | ksort($data); 104 | $string = $this->toUrlParams($data); 105 | //签名步骤二:在string后加入KEY 106 | $string = $string . "&key=" . Config::get('weixin.paykey'); 107 | //签名步骤三:MD5加密 108 | $string = md5($string); 109 | //签名步骤四:所有字符转为大写 110 | $result = strtoupper($string); 111 | return $result; 112 | } 113 | 114 | } -------------------------------------------------------------------------------- /kuxin/weixin/resource.php: -------------------------------------------------------------------------------- 1 | $this->getToken(), 15 | 'media_id'=>$media_id, 16 | ]; 17 | return Http::get(self::API_MEDIA_GET,http_build_query($params)); 18 | } 19 | } -------------------------------------------------------------------------------- /kuxin/weixin/weixin.php: -------------------------------------------------------------------------------- 1 | appId = $appId ?: Config::get('weixin.appid'); 42 | $this->appSecret = $appSecret ?: Config::get('weixin.appsecret'); 43 | } 44 | 45 | /** 46 | * @param $appId 47 | * @param $appSecret 48 | * @return static 49 | */ 50 | public static function I($appId = null, $appSecret = null) 51 | { 52 | $class = static::class; 53 | return Loader::instance($class, [$appId, $appSecret]); 54 | } 55 | 56 | 57 | public function getToken($forceRefresh = 0) 58 | { 59 | $cacheKey = 'kuxin.weixin.accesstoken_' . $this->appId; 60 | $data = DI::Cache()->get($cacheKey); 61 | if ($forceRefresh || empty($data)) { 62 | $token = $this->_getTokenFromServer(); 63 | // XXX: T_T... 7200 - 1500 64 | DI::Cache()->set($cacheKey, $token['access_token'], $token['expires_in'] - 1500); 65 | return $token['access_token']; 66 | } 67 | return $data; 68 | } 69 | 70 | protected function _getTokenFromServer() 71 | { 72 | $params = [ 73 | 'appid' => $this->appId, 74 | 'secret' => $this->appSecret, 75 | 'grant_type' => 'client_credential', 76 | ]; 77 | $token = $this->parseJSON(Http::get(self::API_TOKEN_GET, $params)); 78 | if (empty($token['access_token'])) { 79 | trigger_error('Request AccessToken fail. response: ' . json_encode($token, JSON_UNESCAPED_UNICODE), E_USER_ERROR); 80 | } 81 | return $token; 82 | } 83 | 84 | protected function parseJSON($data) 85 | { 86 | if ($data{0} == '{') { 87 | return Json::decode($data, true); 88 | } else { 89 | return null; 90 | } 91 | } 92 | 93 | public static function getNonceStr($length = 32) 94 | { 95 | $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; 96 | $str = ""; 97 | for ($i = 0; $i < $length; $i++) { 98 | $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); 99 | } 100 | return $str; 101 | } 102 | } -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine on 3 | RewriteRule ^.git - [F,L] 4 | RewriteRule ^.svn - [F,L] 5 | RewriteCond %{REQUEST_FILENAME} !-d 6 | RewriteCond %{REQUEST_FILENAME} !-f 7 | RewriteRule (.*) index.php?s=$1 [QSA,PT,L] 8 | 9 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ptcms/RuleApi/9a493d8deb726e33cf702be183ca52871470a593/public/favicon.ico -------------------------------------------------------------------------------- /public/githook.php: -------------------------------------------------------------------------------- 1 | &1'; 12 | $result=shell_exec($cmd); 13 | echo '
'.$result.'
'; 14 | addlog('shell_exec output:' . PHP_EOL . $result); 15 | if (function_exists('opcache_reset')) { 16 | opcache_reset(); 17 | } 18 | } catch (Exception $e) { 19 | addlog("Caught exception: " . $e->getMessage()); 20 | } 21 | addlog("Git End"); -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 |