76 changed files with 31964 additions and 0 deletions
@ -0,0 +1 @@ |
|||
|
|||
@ -0,0 +1 @@ |
|||
open_basedir=/www/wwwroot/why_xingtongworld_com/:/tmp/ |
|||
Binary file not shown.
@ -0,0 +1,26 @@ |
|||
<!doctype html> |
|||
<html> |
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> |
|||
<title>404</title> |
|||
<style> |
|||
body{ |
|||
background-color:#444; |
|||
font-size:14px; |
|||
} |
|||
h3{ |
|||
font-size:60px; |
|||
color:#eee; |
|||
text-align:center; |
|||
padding-top:30px; |
|||
font-weight:normal; |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
<h3>404,您请求的文件不存在!</h3> |
|||
</body> |
|||
</html> |
|||
@ -0,0 +1,14 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
// [ 应用入口文件 ] |
|||
|
|||
header("Location: ./admin"); |
|||
exit; |
|||
@ -0,0 +1,5 @@ |
|||
#!/bin/bash |
|||
|
|||
eval "curl http://127.0.0.1/api/fdd_tool/task" > /home/wwwroot/default/history.log |
|||
eval "curl http://127.0.0.1/api/sms/ymcallback" > /home/wwwroot/default/history.log |
|||
echo "hello world" > /home/wwwroot/default/history.log |
|||
@ -0,0 +1,6 @@ |
|||
#!/bin/bash |
|||
|
|||
eval "curl http://127.0.0.1/api/Sms/send" > /home/wwwroot/default/history.log |
|||
eval "curl http://127.0.0.1/api/Sms/ymcallback" > /home/wwwroot/default/history.log |
|||
echo "hello world2" > /home/wwwroot/default/history.log |
|||
#eval "curl http://127.0.0.1/api/Sms/reminder" > /home/wwwroot/default/history.log |
|||
|
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
@ -0,0 +1,39 @@ |
|||
<!doctype html> |
|||
<html> |
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<title>恭喜,站点创建成功!</title> |
|||
<style> |
|||
.container { |
|||
width: 60%; |
|||
margin: 10% auto 0; |
|||
background-color: #f0f0f0; |
|||
padding: 2% 5%; |
|||
border-radius: 10px |
|||
} |
|||
|
|||
ul { |
|||
padding-left: 20px; |
|||
} |
|||
|
|||
ul li { |
|||
line-height: 2.3 |
|||
} |
|||
|
|||
a { |
|||
color: #20a53a |
|||
} |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div class="container"> |
|||
<h1>恭喜, 站点创建成功!</h1> |
|||
<h3>这是默认index.html,本页面由系统自动生成</h3> |
|||
<ul> |
|||
<li>本页面在FTP根目录下的index.html</li> |
|||
<li>您可以修改、删除或覆盖本页面</li> |
|||
<li>FTP相关信息,请到“面板系统后台 > FTP” 查看</li> |
|||
</ul> |
|||
</div> |
|||
</body> |
|||
</html> |
|||
@ -0,0 +1,19 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
// [ 应用入口文件 ] |
|||
// 定义应用目录 |
|||
define('APP_PATH', __DIR__ . '/app/'); |
|||
define("RUNTIME_PATH", __DIR__ .'/data/runtime/'); |
|||
define('SITE_PATH', 'https://www.szcaee.cn/'); |
|||
// 加载框架引导文件 |
|||
require __DIR__ . '/thinkphp/start.php'; |
|||
|
|||
|
After Width: | Height: | Size: 40 KiB |
@ -0,0 +1,4 @@ |
|||
/composer.lock |
|||
/vendor |
|||
.idea |
|||
.DS_Store |
|||
@ -0,0 +1 @@ |
|||
deny from all |
|||
@ -0,0 +1,47 @@ |
|||
sudo: false |
|||
|
|||
language: php |
|||
|
|||
services: |
|||
- memcached |
|||
- mongodb |
|||
- mysql |
|||
- postgresql |
|||
- redis-server |
|||
|
|||
matrix: |
|||
fast_finish: true |
|||
include: |
|||
- php: 5.4 |
|||
- php: 5.5 |
|||
- php: 5.6 |
|||
- php: 7.0 |
|||
- php: hhvm |
|||
allow_failures: |
|||
- php: hhvm |
|||
|
|||
cache: |
|||
directories: |
|||
- $HOME/.composer/cache |
|||
|
|||
before_install: |
|||
- composer self-update |
|||
- mysql -e "create database IF NOT EXISTS test;" -uroot |
|||
- psql -c 'DROP DATABASE IF EXISTS test;' -U postgres |
|||
- psql -c 'create database test;' -U postgres |
|||
|
|||
install: |
|||
- ./tests/script/install.sh |
|||
|
|||
script: |
|||
## LINT |
|||
- find . -path ./vendor -prune -o -type f -name \*.php -exec php -l {} \; |
|||
## PHP Copy/Paste Detector |
|||
- vendor/bin/phpcpd --verbose --exclude vendor ./ || true |
|||
## PHPLOC |
|||
- vendor/bin/phploc --exclude vendor ./ |
|||
## PHPUNIT |
|||
- vendor/bin/phpunit --coverage-clover=coverage.xml --configuration=phpunit.xml |
|||
|
|||
after_success: |
|||
- bash <(curl -s https://codecov.io/bash) |
|||
@ -0,0 +1,119 @@ |
|||
如何贡献我的源代码 |
|||
=== |
|||
|
|||
此文档介绍了 ThinkPHP 团队的组成以及运转机制,您提交的代码将给 ThinkPHP 项目带来什么好处,以及如何才能加入我们的行列。 |
|||
|
|||
## 通过 Github 贡献代码 |
|||
|
|||
ThinkPHP 目前使用 Git 来控制程序版本,如果你想为 ThinkPHP 贡献源代码,请先大致了解 Git 的使用方法。我们目前把项目托管在 GitHub 上,任何 GitHub 用户都可以向我们贡献代码。 |
|||
|
|||
参与的方式很简单,`fork`一份 ThinkPHP 的代码到你的仓库中,修改后提交,并向我们发起`pull request`申请,我们会及时对代码进行审查并处理你的申请并。审查通过后,你的代码将被`merge`进我们的仓库中,这样你就会自动出现在贡献者名单里了,非常方便。 |
|||
|
|||
我们希望你贡献的代码符合: |
|||
|
|||
* ThinkPHP 的编码规范 |
|||
* 适当的注释,能让其他人读懂 |
|||
* 遵循 Apache2 开源协议 |
|||
|
|||
**如果想要了解更多细节或有任何疑问,请继续阅读下面的内容** |
|||
|
|||
### 注意事项 |
|||
|
|||
* 本项目代码格式化标准选用 [**PSR-2**](http://www.kancloud.cn/thinkphp/php-fig-psr/3141); |
|||
* 类名和类文件名遵循 [**PSR-4**](http://www.kancloud.cn/thinkphp/php-fig-psr/3144); |
|||
* 对于 Issues 的处理,请使用诸如 `fix #xxx(Issue ID)` 的 commit title 直接关闭 issue。 |
|||
* 系统会自动在 PHP 5.4 5.5 5.6 7.0 和 HHVM 上测试修改,其中 HHVM 下的测试容许报错,请确保你的修改符合 PHP 5.4 ~ 5.6 和 PHP 7.0 的语法规范; |
|||
* 管理员不会合并造成 CI faild 的修改,若出现 CI faild 请检查自己的源代码或修改相应的[单元测试文件](tests); |
|||
|
|||
## GitHub Issue |
|||
|
|||
GitHub 提供了 Issue 功能,该功能可以用于: |
|||
|
|||
* 提出 bug |
|||
* 提出功能改进 |
|||
* 反馈使用体验 |
|||
|
|||
该功能不应该用于: |
|||
|
|||
* 提出修改意见(涉及代码署名和修订追溯问题) |
|||
* 不友善的言论 |
|||
|
|||
## 快速修改 |
|||
|
|||
**GitHub 提供了快速编辑文件的功能** |
|||
|
|||
1. 登录 GitHub 帐号; |
|||
2. 浏览项目文件,找到要进行修改的文件; |
|||
3. 点击右上角铅笔图标进行修改; |
|||
4. 填写 `Commit changes` 相关内容(Title 必填); |
|||
5. 提交修改,等待 CI 验证和管理员合并。 |
|||
|
|||
**若您需要一次提交大量修改,请继续阅读下面的内容** |
|||
|
|||
## 完整流程 |
|||
|
|||
1. `fork`本项目; |
|||
2. 克隆(`clone`)你 `fork` 的项目到本地; |
|||
3. 新建分支(`branch`)并检出(`checkout`)新分支; |
|||
4. 添加本项目到你的本地 git 仓库作为上游(`upstream`); |
|||
5. 进行修改,若你的修改包含方法或函数的增减,请记得修改[单元测试文件](tests); |
|||
6. 变基(衍合 `rebase`)你的分支到上游 master 分支; |
|||
7. `push` 你的本地仓库到 GitHub; |
|||
8. 提交 `pull request`; |
|||
9. 等待 CI 验证(若不通过则重复 5~7,GitHub 会自动更新你的 `pull request`); |
|||
10. 等待管理员处理,并及时 `rebase` 你的分支到上游 master 分支(若上游 master 分支有修改)。 |
|||
|
|||
*若有必要,可以 `git push -f` 强行推送 rebase 后的分支到自己的 `fork`* |
|||
|
|||
*绝对不可以使用 `git push -f` 强行推送修改到上游* |
|||
|
|||
### 注意事项 |
|||
|
|||
* 若对上述流程有任何不清楚的地方,请查阅 GIT 教程,如 [这个](http://backlogtool.com/git-guide/cn/); |
|||
* 对于代码**不同方面**的修改,请在自己 `fork` 的项目中**创建不同的分支**(原因参见`完整流程`第9条备注部分); |
|||
* 变基及交互式变基操作参见 [Git 交互式变基](http://pakchoi.me/2015/03/17/git-interactive-rebase/) |
|||
|
|||
## 推荐资源 |
|||
|
|||
### 开发环境 |
|||
|
|||
* XAMPP for Windows 5.5.x |
|||
* WampServer (for Windows) |
|||
* upupw Apache PHP5.4 ( for Windows) |
|||
|
|||
或自行安装 |
|||
|
|||
- Apache / Nginx |
|||
- PHP 5.4 ~ 5.6 |
|||
- MySQL / MariaDB |
|||
|
|||
*Windows 用户推荐添加 PHP bin 目录到 PATH,方便使用 composer* |
|||
|
|||
*Linux 用户自行配置环境, Mac 用户推荐使用内置 Apache 配合 Homebrew 安装 PHP 和 MariaDB* |
|||
|
|||
### 编辑器 |
|||
|
|||
Sublime Text 3 + phpfmt 插件 |
|||
|
|||
phpfmt 插件参数 |
|||
|
|||
```json |
|||
{ |
|||
"autocomplete": true, |
|||
"enable_auto_align": true, |
|||
"format_on_save": true, |
|||
"indent_with_space": true, |
|||
"psr1_naming": false, |
|||
"psr2": true, |
|||
"version": 4 |
|||
} |
|||
``` |
|||
|
|||
或其他 编辑器 / IDE 配合 PSR2 自动格式化工具 |
|||
|
|||
### Git GUI |
|||
|
|||
* SourceTree |
|||
* GitHub Desktop |
|||
|
|||
或其他 Git 图形界面客户端 |
|||
@ -0,0 +1,32 @@ |
|||
|
|||
ThinkPHP遵循Apache2开源协议发布,并提供免费使用。 |
|||
版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn) |
|||
All rights reserved。 |
|||
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。 |
|||
|
|||
Apache Licence是著名的非盈利开源组织Apache采用的协议。 |
|||
该协议和BSD类似,鼓励代码共享和尊重原作者的著作权, |
|||
允许代码修改,再作为开源或商业软件发布。需要满足 |
|||
的条件: |
|||
1. 需要给代码的用户一份Apache Licence ; |
|||
2. 如果你修改了代码,需要在被修改的文件中说明; |
|||
3. 在延伸的代码中(修改和有源代码衍生的代码中)需要 |
|||
带有原来代码中的协议,商标,专利声明和其他原来作者规 |
|||
定需要包含的说明; |
|||
4. 如果再发布的产品中包含一个Notice文件,则在Notice文 |
|||
件中需要带有本协议内容。你可以在Notice中增加自己的 |
|||
许可,但不可以表现为对Apache Licence构成更改。 |
|||
具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|||
POSSIBILITY OF SUCH DAMAGE. |
|||
@ -0,0 +1,114 @@ |
|||
ThinkPHP 5.0 |
|||
=============== |
|||
|
|||
[](https://styleci.io/repos/48530411) |
|||
[](https://travis-ci.org/top-think/framework) |
|||
[](http://codecov.io/github/github/top-think/framework?branch=master) |
|||
[](https://packagist.org/packages/topthink/framework) |
|||
[](https://packagist.org/packages/topthink/framework) |
|||
[](https://packagist.org/packages/topthink/framework) |
|||
[](https://packagist.org/packages/topthink/framework) |
|||
|
|||
ThinkPHP5在保持快速开发和大道至简的核心理念不变的同时,PHP版本要求提升到5.4,优化核心,减少依赖,基于全新的架构思想和命名空间实现,是ThinkPHP突破原有框架思路的颠覆之作,其主要特性包括: |
|||
|
|||
+ 基于命名空间和众多PHP新特性 |
|||
+ 核心功能组件化 |
|||
+ 强化路由功能 |
|||
+ 更灵活的控制器 |
|||
+ 重构的模型和数据库类 |
|||
+ 配置文件可分离 |
|||
+ 重写的自动验证和完成 |
|||
+ 简化扩展机制 |
|||
+ API支持完善 |
|||
+ 改进的Log类 |
|||
+ 命令行访问支持 |
|||
+ REST支持 |
|||
+ 引导文件支持 |
|||
+ 方便的自动生成定义 |
|||
+ 真正惰性加载 |
|||
+ 分布式环境支持 |
|||
+ 支持Composer |
|||
+ 支持MongoDb |
|||
|
|||
> ThinkPHP5的运行环境要求PHP5.4以上。 |
|||
|
|||
详细开发文档参考 [ThinkPHP5完全开发手册](http://www.kancloud.cn/manual/thinkphp5) 以及[ThinkPHP5入门系列教程](http://www.kancloud.cn/special/thinkphp5_quickstart) |
|||
|
|||
## 目录结构 |
|||
|
|||
初始的目录结构如下: |
|||
|
|||
~~~ |
|||
www WEB部署目录(或者子目录) |
|||
├─application 应用目录 |
|||
│ ├─common 公共模块目录(可以更改) |
|||
│ ├─module_name 模块目录 |
|||
│ │ ├─config.php 模块配置文件 |
|||
│ │ ├─common.php 模块函数文件 |
|||
│ │ ├─controller 控制器目录 |
|||
│ │ ├─model 模型目录 |
|||
│ │ ├─view 视图目录 |
|||
│ │ └─ ... 更多类库目录 |
|||
│ │ |
|||
│ ├─command.php 命令行工具配置文件 |
|||
│ ├─common.php 公共函数文件 |
|||
│ ├─config.php 公共配置文件 |
|||
│ ├─route.php 路由配置文件 |
|||
│ ├─tags.php 应用行为扩展定义文件 |
|||
│ └─database.php 数据库配置文件 |
|||
│ |
|||
├─public WEB目录(对外访问目录) |
|||
│ ├─index.php 入口文件 |
|||
│ ├─router.php 快速测试文件 |
|||
│ └─.htaccess 用于apache的重写 |
|||
│ |
|||
├─thinkphp 框架系统目录 |
|||
│ ├─lang 语言文件目录 |
|||
│ ├─library 框架类库目录 |
|||
│ │ ├─think Think类库包目录 |
|||
│ │ └─traits 系统Trait目录 |
|||
│ │ |
|||
│ ├─tpl 系统模板目录 |
|||
│ ├─base.php 基础定义文件 |
|||
│ ├─console.php 控制台入口文件 |
|||
│ ├─convention.php 框架惯例配置文件 |
|||
│ ├─helper.php 助手函数文件 |
|||
│ ├─phpunit.xml phpunit配置文件 |
|||
│ └─start.php 框架入口文件 |
|||
│ |
|||
├─extend 扩展类库目录 |
|||
├─runtime 应用的运行时目录(可写,可定制) |
|||
├─vendor 第三方类库目录(Composer依赖库) |
|||
├─build.php 自动生成定义文件(参考) |
|||
├─composer.json composer 定义文件 |
|||
├─LICENSE.txt 授权说明文件 |
|||
├─README.md README 文件 |
|||
├─think 命令行入口文件 |
|||
~~~ |
|||
|
|||
> router.php用于php自带webserver支持,可用于快速测试 |
|||
> 切换到public目录后,启动命令:php -S localhost:8888 router.php |
|||
> 上面的目录结构和名称是可以改变的,这取决于你的入口文件和配置参数。 |
|||
|
|||
## 命名规范 |
|||
|
|||
ThinkPHP5的命名规范遵循PSR-2规范以及PSR-4自动加载规范。 |
|||
|
|||
## 参与开发 |
|||
注册并登录 Github 帐号, fork 本项目并进行改动。 |
|||
|
|||
更多细节参阅 [CONTRIBUTING.md](CONTRIBUTING.md) |
|||
|
|||
## 版权信息 |
|||
|
|||
ThinkPHP遵循Apache2开源协议发布,并提供免费使用。 |
|||
|
|||
本项目包含的第三方源码和二进制文件之版权信息另行标注。 |
|||
|
|||
版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn) |
|||
|
|||
All rights reserved。 |
|||
|
|||
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。 |
|||
|
|||
更多细节参阅 [LICENSE.txt](LICENSE.txt) |
|||
@ -0,0 +1,63 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
define('THINK_VERSION', '5.0.7'); |
|||
define('THINK_START_TIME', microtime(true)); |
|||
define('THINK_START_MEM', memory_get_usage()); |
|||
define('EXT', '.php'); |
|||
define('DS', DIRECTORY_SEPARATOR); |
|||
defined('THINK_PATH') or define('THINK_PATH', __DIR__ . DS); |
|||
define('LIB_PATH', THINK_PATH . 'library' . DS); |
|||
define('CORE_PATH', LIB_PATH . 'think' . DS); |
|||
define('TRAIT_PATH', LIB_PATH . 'traits' . DS); |
|||
defined('APP_PATH') or define('APP_PATH', dirname($_SERVER['SCRIPT_FILENAME']) . DS); |
|||
defined('ROOT_PATH') or define('ROOT_PATH', dirname(realpath(APP_PATH)) . DS); |
|||
defined('EXTEND_PATH') or define('EXTEND_PATH', ROOT_PATH . 'extend' . DS); |
|||
defined('VENDOR_PATH') or define('VENDOR_PATH', ROOT_PATH . 'vendor' . DS); |
|||
defined('RUNTIME_PATH') or define('RUNTIME_PATH', ROOT_PATH . 'runtime' . DS); |
|||
defined('LOG_PATH') or define('LOG_PATH', RUNTIME_PATH . 'log' . DS); |
|||
defined('CACHE_PATH') or define('CACHE_PATH', RUNTIME_PATH . 'cache' . DS); |
|||
defined('TEMP_PATH') or define('TEMP_PATH', RUNTIME_PATH . 'temp' . DS); |
|||
defined('CONF_PATH') or define('CONF_PATH', APP_PATH); // 配置文件目录 |
|||
defined('CONF_EXT') or define('CONF_EXT', EXT); // 配置文件后缀 |
|||
defined('ENV_PREFIX') or define('ENV_PREFIX', 'PHP_'); // 环境变量的配置前缀 |
|||
|
|||
// 环境常量 |
|||
define('IS_CLI', PHP_SAPI == 'cli' ? true : false); |
|||
define('IS_WIN', strpos(PHP_OS, 'WIN') !== false); |
|||
|
|||
// 载入Loader类 |
|||
require CORE_PATH . 'Loader.php'; |
|||
|
|||
// 加载环境变量配置文件 |
|||
if (is_file(ROOT_PATH . '.env')) { |
|||
$env = parse_ini_file(ROOT_PATH . '.env', true); |
|||
foreach ($env as $key => $val) { |
|||
$name = ENV_PREFIX . strtoupper($key); |
|||
if (is_array($val)) { |
|||
foreach ($val as $k => $v) { |
|||
$item = $name . '_' . strtoupper($k); |
|||
putenv("$item=$v"); |
|||
} |
|||
} else { |
|||
putenv("$name=$val"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 注册自动加载 |
|||
\think\Loader::register(); |
|||
|
|||
// 注册错误和异常处理机制 |
|||
\think\Error::register(); |
|||
|
|||
// 加载惯例配置文件 |
|||
\think\Config::set(include THINK_PATH . 'convention' . EXT); |
|||
@ -0,0 +1,12 @@ |
|||
comment: |
|||
layout: header, changes, diff |
|||
coverage: |
|||
ignore: |
|||
- base.php |
|||
- helper.php |
|||
- convention.php |
|||
- lang/zh-cn.php |
|||
- start.php |
|||
- console.php |
|||
status: |
|||
patch: false |
|||
@ -0,0 +1,35 @@ |
|||
{ |
|||
"name": "topthink/framework", |
|||
"description": "the new thinkphp framework", |
|||
"type": "think-framework", |
|||
"keywords": [ |
|||
"framework", |
|||
"thinkphp", |
|||
"ORM" |
|||
], |
|||
"homepage": "http://thinkphp.cn/", |
|||
"license": "Apache-2.0", |
|||
"authors": [ |
|||
{ |
|||
"name": "liu21st", |
|||
"email": "liu21st@gmail.com" |
|||
} |
|||
], |
|||
"require": { |
|||
"php": ">=5.4.0", |
|||
"topthink/think-installer": "~1.0" |
|||
}, |
|||
"require-dev": { |
|||
"phpunit/phpunit": "4.8.*", |
|||
"johnkary/phpunit-speedtrap": "^1.0", |
|||
"mikey179/vfsStream": "~1.6", |
|||
"phploc/phploc": "2.*", |
|||
"sebastian/phpcpd": "2.*", |
|||
"phpdocumentor/reflection-docblock": "^2.0" |
|||
}, |
|||
"autoload": { |
|||
"psr-4": { |
|||
"think\\": "library/think" |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: yunwuxin <448901948@qq.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
// ThinkPHP 引导文件 |
|||
// 加载基础文件 |
|||
require __DIR__ . '/base.php'; |
|||
|
|||
// 执行应用 |
|||
App::initCommon(); |
|||
Console::init(); |
|||
@ -0,0 +1,286 @@ |
|||
<?php |
|||
|
|||
return [ |
|||
// +---------------------------------------------------------------------- |
|||
// | 应用设置 |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
// 应用命名空间 |
|||
'app_namespace' => 'app', |
|||
// 应用调试模式 |
|||
'app_debug' => true, |
|||
// 应用Trace |
|||
'app_trace' => false, |
|||
// 应用模式状态 |
|||
'app_status' => '', |
|||
// 是否支持多模块 |
|||
'app_multi_module' => true, |
|||
// 入口自动绑定模块 |
|||
'auto_bind_module' => false, |
|||
// 注册的根命名空间 |
|||
'root_namespace' => [], |
|||
// 扩展函数文件 |
|||
'extra_file_list' => [THINK_PATH . 'helper' . EXT], |
|||
// 默认输出类型 |
|||
'default_return_type' => 'html', |
|||
// 默认AJAX 数据返回格式,可选json xml ... |
|||
'default_ajax_return' => 'json', |
|||
// 默认JSONP格式返回的处理方法 |
|||
'default_jsonp_handler' => 'jsonpReturn', |
|||
// 默认JSONP处理方法 |
|||
'var_jsonp_handler' => 'callback', |
|||
// 默认时区 |
|||
'default_timezone' => 'PRC', |
|||
// 是否开启多语言 |
|||
'lang_switch_on' => false, |
|||
// 默认全局过滤方法 用逗号分隔多个 |
|||
'default_filter' => '', |
|||
// 默认语言 |
|||
'default_lang' => 'zh-cn', |
|||
// 应用类库后缀 |
|||
'class_suffix' => false, |
|||
// 控制器类后缀 |
|||
'controller_suffix' => false, |
|||
|
|||
// +---------------------------------------------------------------------- |
|||
// | 模块设置 |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
// 默认模块名 |
|||
'default_module' => 'index', |
|||
// 禁止访问模块 |
|||
'deny_module_list' => ['common'], |
|||
// 默认控制器名 |
|||
'default_controller' => 'Index', |
|||
// 默认操作名 |
|||
'default_action' => 'index', |
|||
// 默认验证器 |
|||
'default_validate' => '', |
|||
// 默认的空控制器名 |
|||
'empty_controller' => 'Error', |
|||
// 操作方法前缀 |
|||
'use_action_prefix' => false, |
|||
// 操作方法后缀 |
|||
'action_suffix' => '', |
|||
// 自动搜索控制器 |
|||
'controller_auto_search' => false, |
|||
|
|||
// +---------------------------------------------------------------------- |
|||
// | URL设置 |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
// PATHINFO变量名 用于兼容模式 |
|||
'var_pathinfo' => 's', |
|||
// 兼容PATH_INFO获取 |
|||
'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'], |
|||
// pathinfo分隔符 |
|||
'pathinfo_depr' => '/', |
|||
// URL伪静态后缀 |
|||
'url_html_suffix' => 'html', |
|||
// URL普通方式参数 用于自动生成 |
|||
'url_common_param' => false, |
|||
// URL参数方式 0 按名称成对解析 1 按顺序解析 |
|||
'url_param_type' => 0, |
|||
// 是否开启路由 |
|||
'url_route_on' => true, |
|||
// 路由配置文件(支持配置多个) |
|||
'route_config_file' => ['route'], |
|||
// 路由使用完整匹配 |
|||
'route_complete_match' => false, |
|||
// 是否强制使用路由 |
|||
'url_route_must' => false, |
|||
// 域名部署 |
|||
'url_domain_deploy' => false, |
|||
// 域名根,如thinkphp.cn |
|||
'url_domain_root' => '', |
|||
// 是否自动转换URL中的控制器和操作名 |
|||
'url_convert' => true, |
|||
// 默认的访问控制器层 |
|||
'url_controller_layer' => 'controller', |
|||
// 表单请求类型伪装变量 |
|||
'var_method' => '_method', |
|||
// 表单ajax伪装变量 |
|||
'var_ajax' => '_ajax', |
|||
// 表单pjax伪装变量 |
|||
'var_pjax' => '_pjax', |
|||
// 是否开启请求缓存 true自动缓存 支持设置请求缓存规则 |
|||
'request_cache' => false, |
|||
// 请求缓存有效期 |
|||
'request_cache_expire' => null, |
|||
// 全局请求缓存排除规则 |
|||
'request_cache_except' => [], |
|||
|
|||
// +---------------------------------------------------------------------- |
|||
// | 模板设置 |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
'template' => [ |
|||
// 模板引擎类型 支持 php think 支持扩展 |
|||
'type' => 'Think', |
|||
// 视图基础目录,配置目录为所有模块的视图起始目录 |
|||
'view_base' => '', |
|||
// 当前模板的视图目录 留空为自动获取 |
|||
'view_path' => '', |
|||
// 模板后缀 |
|||
'view_suffix' => 'html', |
|||
// 模板文件名分隔符 |
|||
'view_depr' => DS, |
|||
// 模板引擎普通标签开始标记 |
|||
'tpl_begin' => '{', |
|||
// 模板引擎普通标签结束标记 |
|||
'tpl_end' => '}', |
|||
// 标签库标签开始标记 |
|||
'taglib_begin' => '{', |
|||
// 标签库标签结束标记 |
|||
'taglib_end' => '}', |
|||
], |
|||
|
|||
// 视图输出字符串内容替换 |
|||
'view_replace_str' => [], |
|||
// 默认跳转页面对应的模板文件 |
|||
'dispatch_success_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl', |
|||
'dispatch_error_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl', |
|||
|
|||
// +---------------------------------------------------------------------- |
|||
// | 异常及错误设置 |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
// 异常页面的模板文件 |
|||
'exception_tmpl' => THINK_PATH . 'tpl' . DS . 'think_exception.tpl', |
|||
|
|||
// 错误显示信息,非调试模式有效 |
|||
'error_message' => '页面错误!请稍后再试~', |
|||
// 显示错误信息 |
|||
'show_error_msg' => false, |
|||
// 异常处理handle类 留空使用 \think\exception\Handle |
|||
'exception_handle' => '', |
|||
|
|||
// +---------------------------------------------------------------------- |
|||
// | 日志设置 |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
'log' => [ |
|||
// 日志记录方式,内置 file socket 支持扩展 |
|||
'type' => 'File', |
|||
// 日志保存目录 |
|||
'path' => LOG_PATH, |
|||
// 日志记录级别 |
|||
'level' => [], |
|||
], |
|||
|
|||
// +---------------------------------------------------------------------- |
|||
// | Trace设置 开启 app_trace 后 有效 |
|||
// +---------------------------------------------------------------------- |
|||
'trace' => [ |
|||
// 内置Html Console 支持扩展 |
|||
'type' => 'Html', |
|||
], |
|||
|
|||
// +---------------------------------------------------------------------- |
|||
// | 缓存设置 |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
'cache' => [ |
|||
// 驱动方式 |
|||
'type' => 'File', |
|||
// 缓存保存目录 |
|||
'path' => CACHE_PATH, |
|||
// 缓存前缀 |
|||
'prefix' => '', |
|||
// 缓存有效期 0表示永久缓存 |
|||
'expire' => 0, |
|||
], |
|||
|
|||
// +---------------------------------------------------------------------- |
|||
// | 会话设置 |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
'session' => [ |
|||
'id' => '', |
|||
// SESSION_ID的提交变量,解决flash上传跨域 |
|||
'var_session_id' => '', |
|||
// SESSION 前缀 |
|||
'prefix' => 'think', |
|||
// 驱动方式 支持redis memcache memcached |
|||
'type' => '', |
|||
// 是否自动开启 SESSION |
|||
'auto_start' => true, |
|||
'httponly' => true, |
|||
'secure' => false, |
|||
], |
|||
|
|||
// +---------------------------------------------------------------------- |
|||
// | Cookie设置 |
|||
// +---------------------------------------------------------------------- |
|||
'cookie' => [ |
|||
// cookie 名称前缀 |
|||
'prefix' => '', |
|||
// cookie 保存时间 |
|||
'expire' => 0, |
|||
// cookie 保存路径 |
|||
'path' => '/', |
|||
// cookie 有效域名 |
|||
'domain' => '', |
|||
// cookie 启用安全传输 |
|||
'secure' => false, |
|||
// httponly设置 |
|||
'httponly' => '', |
|||
// 是否使用 setcookie |
|||
'setcookie' => true, |
|||
], |
|||
|
|||
// +---------------------------------------------------------------------- |
|||
// | 数据库设置 |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
'database' => [ |
|||
// 数据库类型 |
|||
'type' => 'mysql', |
|||
// 数据库连接DSN配置 |
|||
'dsn' => '', |
|||
// 服务器地址 |
|||
'hostname' => '127.0.0.1', |
|||
// 数据库名 |
|||
'database' => '', |
|||
// 数据库用户名 |
|||
'username' => 'root', |
|||
// 数据库密码 |
|||
'password' => '', |
|||
// 数据库连接端口 |
|||
'hostport' => '', |
|||
// 数据库连接参数 |
|||
'params' => [], |
|||
// 数据库编码默认采用utf8 |
|||
'charset' => 'utf8', |
|||
// 数据库表前缀 |
|||
'prefix' => '', |
|||
// 数据库调试模式 |
|||
'debug' => false, |
|||
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) |
|||
'deploy' => 0, |
|||
// 数据库读写是否分离 主从式有效 |
|||
'rw_separate' => false, |
|||
// 读写分离后 主服务器数量 |
|||
'master_num' => 1, |
|||
// 指定从服务器序号 |
|||
'slave_no' => '', |
|||
// 是否严格检查字段是否存在 |
|||
'fields_strict' => true, |
|||
// 数据集返回类型 |
|||
'resultset_type' => 'array', |
|||
// 自动写入时间戳字段 |
|||
'auto_timestamp' => false, |
|||
// 时间字段取出后的默认时间格式 |
|||
'datetime_format' => 'Y-m-d H:i:s', |
|||
// 是否需要进行SQL性能分析 |
|||
'sql_explain' => false, |
|||
], |
|||
|
|||
//分页配置 |
|||
'paginate' => [ |
|||
'type' => 'bootstrap', |
|||
'var_page' => 'page', |
|||
'list_rows' => 15, |
|||
], |
|||
|
|||
]; |
|||
@ -0,0 +1,586 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
//------------------------ |
|||
// ThinkPHP 助手函数 |
|||
//------------------------- |
|||
|
|||
use think\Cache; |
|||
use think\Config; |
|||
use think\Cookie; |
|||
use think\Db; |
|||
use think\Debug; |
|||
use think\exception\HttpException; |
|||
use think\exception\HttpResponseException; |
|||
use think\Lang; |
|||
use think\Loader; |
|||
use think\Log; |
|||
use think\Model; |
|||
use think\Request; |
|||
use think\Response; |
|||
use think\Session; |
|||
use think\Url; |
|||
use think\View; |
|||
|
|||
if (!function_exists('load_trait')) { |
|||
/** |
|||
* 快速导入Traits PHP5.5以上无需调用 |
|||
* @param string $class trait库 |
|||
* @param string $ext 类库后缀 |
|||
* @return boolean |
|||
*/ |
|||
function load_trait($class, $ext = EXT) |
|||
{ |
|||
return Loader::import($class, TRAIT_PATH, $ext); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('exception')) { |
|||
/** |
|||
* 抛出异常处理 |
|||
* |
|||
* @param string $msg 异常消息 |
|||
* @param integer $code 异常代码 默认为0 |
|||
* @param string $exception 异常类 |
|||
* |
|||
* @throws Exception |
|||
*/ |
|||
function exception($msg, $code = 0, $exception = '') |
|||
{ |
|||
$e = $exception ?: '\think\Exception'; |
|||
throw new $e($msg, $code); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('debug')) { |
|||
/** |
|||
* 记录时间(微秒)和内存使用情况 |
|||
* @param string $start 开始标签 |
|||
* @param string $end 结束标签 |
|||
* @param integer|string $dec 小数位 如果是m 表示统计内存占用 |
|||
* @return mixed |
|||
*/ |
|||
function debug($start, $end = '', $dec = 6) |
|||
{ |
|||
if ('' == $end) { |
|||
Debug::remark($start); |
|||
} else { |
|||
return 'm' == $dec ? Debug::getRangeMem($start, $end) : Debug::getRangeTime($start, $end, $dec); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('lang')) { |
|||
/** |
|||
* 获取语言变量值 |
|||
* @param string $name 语言变量名 |
|||
* @param array $vars 动态变量值 |
|||
* @param string $lang 语言 |
|||
* @return mixed |
|||
*/ |
|||
function lang($name, $vars = [], $lang = '') |
|||
{ |
|||
return Lang::get($name, $vars, $lang); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('config')) { |
|||
/** |
|||
* 获取和设置配置参数 |
|||
* @param string|array $name 参数名 |
|||
* @param mixed $value 参数值 |
|||
* @param string $range 作用域 |
|||
* @return mixed |
|||
*/ |
|||
function config($name = '', $value = null, $range = '') |
|||
{ |
|||
if (is_null($value) && is_string($name)) { |
|||
return 0 === strpos($name, '?') ? Config::has(substr($name, 1), $range) : Config::get($name, $range); |
|||
} else { |
|||
return Config::set($name, $value, $range); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('input')) { |
|||
/** |
|||
* 获取输入数据 支持默认值和过滤 |
|||
* @param string $key 获取的变量名 |
|||
* @param mixed $default 默认值 |
|||
* @param string $filter 过滤方法 |
|||
* @return mixed |
|||
*/ |
|||
function input($key = '', $default = null, $filter = '') |
|||
{ |
|||
if (0 === strpos($key, '?')) { |
|||
$key = substr($key, 1); |
|||
$has = true; |
|||
} |
|||
if ($pos = strpos($key, '.')) { |
|||
// 指定参数来源 |
|||
list($method, $key) = explode('.', $key, 2); |
|||
if (!in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) { |
|||
$key = $method . '.' . $key; |
|||
$method = 'param'; |
|||
} |
|||
} else { |
|||
// 默认为自动判断 |
|||
$method = 'param'; |
|||
} |
|||
if (isset($has)) { |
|||
return request()->has($key, $method, $default); |
|||
} else { |
|||
return request()->$method($key, $default, $filter); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('widget')) { |
|||
/** |
|||
* 渲染输出Widget |
|||
* @param string $name Widget名称 |
|||
* @param array $data 传入的参数 |
|||
* @return mixed |
|||
*/ |
|||
function widget($name, $data = []) |
|||
{ |
|||
return Loader::action($name, $data, 'widget'); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('model')) { |
|||
/** |
|||
* 实例化Model |
|||
* @param string $name Model名称 |
|||
* @param string $layer 业务层名称 |
|||
* @param bool $appendSuffix 是否添加类名后缀 |
|||
* @return \think\Model |
|||
*/ |
|||
function model($name = '', $layer = 'model', $appendSuffix = false) |
|||
{ |
|||
return Loader::model($name, $layer, $appendSuffix); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('validate')) { |
|||
/** |
|||
* 实例化验证器 |
|||
* @param string $name 验证器名称 |
|||
* @param string $layer 业务层名称 |
|||
* @param bool $appendSuffix 是否添加类名后缀 |
|||
* @return \think\Validate |
|||
*/ |
|||
function validate($name = '', $layer = 'validate', $appendSuffix = false) |
|||
{ |
|||
return Loader::validate($name, $layer, $appendSuffix); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('db')) { |
|||
/** |
|||
* 实例化数据库类 |
|||
* @param string $name 操作的数据表名称(不含前缀) |
|||
* @param array|string $config 数据库配置参数 |
|||
* @param bool $force 是否强制重新连接 |
|||
* @return \think\db\Query |
|||
*/ |
|||
function db($name = '', $config = [], $force = true) |
|||
{ |
|||
return Db::connect($config, $force)->name($name); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('controller')) { |
|||
/** |
|||
* 实例化控制器 格式:[模块/]控制器 |
|||
* @param string $name 资源地址 |
|||
* @param string $layer 控制层名称 |
|||
* @param bool $appendSuffix 是否添加类名后缀 |
|||
* @return \think\Controller |
|||
*/ |
|||
function controller($name, $layer = 'controller', $appendSuffix = false) |
|||
{ |
|||
return Loader::controller($name, $layer, $appendSuffix); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('action')) { |
|||
/** |
|||
* 调用模块的操作方法 参数格式 [模块/控制器/]操作 |
|||
* @param string $url 调用地址 |
|||
* @param string|array $vars 调用参数 支持字符串和数组 |
|||
* @param string $layer 要调用的控制层名称 |
|||
* @param bool $appendSuffix 是否添加类名后缀 |
|||
* @return mixed |
|||
*/ |
|||
function action($url, $vars = [], $layer = 'controller', $appendSuffix = false) |
|||
{ |
|||
return Loader::action($url, $vars, $layer, $appendSuffix); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('import')) { |
|||
/** |
|||
* 导入所需的类库 同java的Import 本函数有缓存功能 |
|||
* @param string $class 类库命名空间字符串 |
|||
* @param string $baseUrl 起始路径 |
|||
* @param string $ext 导入的文件扩展名 |
|||
* @return boolean |
|||
*/ |
|||
function import($class, $baseUrl = '', $ext = EXT) |
|||
{ |
|||
return Loader::import($class, $baseUrl, $ext); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('vendor')) { |
|||
/** |
|||
* 快速导入第三方框架类库 所有第三方框架的类库文件统一放到 系统的Vendor目录下面 |
|||
* @param string $class 类库 |
|||
* @param string $ext 类库后缀 |
|||
* @return boolean |
|||
*/ |
|||
function vendor($class, $ext = EXT) |
|||
{ |
|||
return Loader::import($class, VENDOR_PATH, $ext); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('dump')) { |
|||
/** |
|||
* 浏览器友好的变量输出 |
|||
* @param mixed $var 变量 |
|||
* @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串 |
|||
* @param string $label 标签 默认为空 |
|||
* @return void|string |
|||
*/ |
|||
function dump($var, $echo = true, $label = null) |
|||
{ |
|||
return Debug::dump($var, $echo, $label); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('url')) { |
|||
/** |
|||
* Url生成 |
|||
* @param string $url 路由地址 |
|||
* @param string|array $vars 变量 |
|||
* @param bool|string $suffix 生成的URL后缀 |
|||
* @param bool|string $domain 域名 |
|||
* @return string |
|||
*/ |
|||
function url($url = '', $vars = '', $suffix = true, $domain = false){ |
|||
|
|||
return Url::build($url, $vars, $suffix, $domain); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('session')) { |
|||
/** |
|||
* Session管理 |
|||
* @param string|array $name session名称,如果为数组表示进行session设置 |
|||
* @param mixed $value session值 |
|||
* @param string $prefix 前缀 |
|||
* @return mixed |
|||
*/ |
|||
function session($name, $value = '', $prefix = null) |
|||
{ |
|||
if (is_array($name)) { |
|||
// 初始化 |
|||
Session::init($name); |
|||
} elseif (is_null($name)) { |
|||
// 清除 |
|||
Session::clear('' === $value ? null : $value); |
|||
} elseif ('' === $value) { |
|||
// 判断或获取 |
|||
return 0 === strpos($name, '?') ? Session::has(substr($name, 1), $prefix) : Session::get($name, $prefix); |
|||
} elseif (is_null($value)) { |
|||
// 删除 |
|||
return Session::delete($name, $prefix); |
|||
} else { |
|||
// 设置 |
|||
return Session::set($name, $value, $prefix); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('cookie')) { |
|||
/** |
|||
* Cookie管理 |
|||
* @param string|array $name cookie名称,如果为数组表示进行cookie设置 |
|||
* @param mixed $value cookie值 |
|||
* @param mixed $option 参数 |
|||
* @return mixed |
|||
*/ |
|||
function cookie($name, $value = '', $option = null) |
|||
{ |
|||
if (is_array($name)) { |
|||
// 初始化 |
|||
Cookie::init($name); |
|||
} elseif (is_null($name)) { |
|||
// 清除 |
|||
Cookie::clear($value); |
|||
} elseif ('' === $value) { |
|||
// 获取 |
|||
return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1), $option) : Cookie::get($name, $option); |
|||
} elseif (is_null($value)) { |
|||
// 删除 |
|||
return Cookie::delete($name); |
|||
} else { |
|||
// 设置 |
|||
return Cookie::set($name, $value, $option); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('cache')) { |
|||
/** |
|||
* 缓存管理 |
|||
* @param mixed $name 缓存名称,如果为数组表示进行缓存设置 |
|||
* @param mixed $value 缓存值 |
|||
* @param mixed $options 缓存参数 |
|||
* @param string $tag 缓存标签 |
|||
* @return mixed |
|||
*/ |
|||
function cache($name, $value = '', $options = null, $tag = null) |
|||
{ |
|||
if (is_array($options)) { |
|||
// 缓存操作的同时初始化 |
|||
Cache::connect($options); |
|||
} elseif (is_array($name)) { |
|||
// 缓存初始化 |
|||
return Cache::connect($name); |
|||
} |
|||
if (is_null($name)) { |
|||
return Cache::clear($value); |
|||
} elseif ('' === $value) { |
|||
// 获取缓存 |
|||
return 0 === strpos($name, '?') ? Cache::has(substr($name, 1)) : Cache::get($name); |
|||
} elseif (is_null($value)) { |
|||
// 删除缓存 |
|||
return Cache::rm($name); |
|||
} elseif (0 === strpos($name, '?') && '' !== $value) { |
|||
$expire = is_numeric($options) ? $options : null; |
|||
return Cache::remember(substr($name, 1), $value, $expire); |
|||
} else { |
|||
// 缓存数据 |
|||
if (is_array($options)) { |
|||
$expire = isset($options['expire']) ? $options['expire'] : null; //修复查询缓存无法设置过期时间 |
|||
} else { |
|||
$expire = is_numeric($options) ? $options : null; //默认快捷缓存设置过期时间 |
|||
} |
|||
if (is_null($tag)) { |
|||
return Cache::set($name, $value, $expire); |
|||
} else { |
|||
return Cache::tag($tag)->set($name, $value, $expire); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('trace')) { |
|||
/** |
|||
* 记录日志信息 |
|||
* @param mixed $log log信息 支持字符串和数组 |
|||
* @param string $level 日志级别 |
|||
* @return void|array |
|||
*/ |
|||
function trace($log = '[think]', $level = 'log') |
|||
{ |
|||
if ('[think]' === $log) { |
|||
return Log::getLog(); |
|||
} else { |
|||
Log::record($log, $level); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('request')) { |
|||
/** |
|||
* 获取当前Request对象实例 |
|||
* @return Request |
|||
*/ |
|||
function request() |
|||
{ |
|||
return Request::instance(); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('response')) { |
|||
/** |
|||
* 创建普通 Response 对象实例 |
|||
* @param mixed $data 输出数据 |
|||
* @param int|string $code 状态码 |
|||
* @param array $header 头信息 |
|||
* @param string $type |
|||
* @return Response |
|||
*/ |
|||
function response($data = [], $code = 200, $header = [], $type = 'html') |
|||
{ |
|||
return Response::create($data, $type, $code, $header); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('view')) { |
|||
/** |
|||
* 渲染模板输出 |
|||
* @param string $template 模板文件 |
|||
* @param array $vars 模板变量 |
|||
* @param array $replace 模板替换 |
|||
* @param integer $code 状态码 |
|||
* @return \think\response\View |
|||
*/ |
|||
function view($template = '', $vars = [], $replace = [], $code = 200) |
|||
{ |
|||
return Response::create($template, 'view', $code)->replace($replace)->assign($vars); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('json')) { |
|||
/** |
|||
* 获取\think\response\Json对象实例 |
|||
* @param mixed $data 返回的数据 |
|||
* @param integer $code 状态码 |
|||
* @param array $header 头部 |
|||
* @param array $options 参数 |
|||
* @return \think\response\Json |
|||
*/ |
|||
function json($data = [], $code = 200, $header = [], $options = []) |
|||
{ |
|||
return Response::create($data, 'json', $code, $header, $options); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('jsonp')) { |
|||
/** |
|||
* 获取\think\response\Jsonp对象实例 |
|||
* @param mixed $data 返回的数据 |
|||
* @param integer $code 状态码 |
|||
* @param array $header 头部 |
|||
* @param array $options 参数 |
|||
* @return \think\response\Jsonp |
|||
*/ |
|||
function jsonp($data = [], $code = 200, $header = [], $options = []) |
|||
{ |
|||
return Response::create($data, 'jsonp', $code, $header, $options); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('xml')) { |
|||
/** |
|||
* 获取\think\response\Xml对象实例 |
|||
* @param mixed $data 返回的数据 |
|||
* @param integer $code 状态码 |
|||
* @param array $header 头部 |
|||
* @param array $options 参数 |
|||
* @return \think\response\Xml |
|||
*/ |
|||
function xml($data = [], $code = 200, $header = [], $options = []) |
|||
{ |
|||
return Response::create($data, 'xml', $code, $header, $options); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('redirect')) { |
|||
/** |
|||
* 获取\think\response\Redirect对象实例 |
|||
* @param mixed $url 重定向地址 支持Url::build方法的地址 |
|||
* @param array|integer $params 额外参数 |
|||
* @param integer $code 状态码 |
|||
* @param array $with 隐式传参 |
|||
* @return \think\response\Redirect |
|||
*/ |
|||
function redirect($url = [], $params = [], $code = 302, $with = []) |
|||
{ |
|||
if (is_integer($params)) { |
|||
$code = $params; |
|||
$params = []; |
|||
} |
|||
return Response::create($url, 'redirect', $code)->params($params)->with($with); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('abort')) { |
|||
/** |
|||
* 抛出HTTP异常 |
|||
* @param integer|Response $code 状态码 或者 Response对象实例 |
|||
* @param string $message 错误信息 |
|||
* @param array $header 参数 |
|||
*/ |
|||
function abort($code, $message = null, $header = []) |
|||
{ |
|||
if ($code instanceof Response) { |
|||
throw new HttpResponseException($code); |
|||
} else { |
|||
throw new HttpException($code, $message, null, $header); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('halt')) { |
|||
/** |
|||
* 调试变量并且中断输出 |
|||
* @param mixed $var 调试变量或者信息 |
|||
*/ |
|||
function halt($var) |
|||
{ |
|||
dump($var); |
|||
throw new HttpResponseException(new Response); |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('token')) { |
|||
/** |
|||
* 生成表单令牌 |
|||
* @param string $name 令牌名称 |
|||
* @param mixed $type 令牌生成方法 |
|||
* @return string |
|||
*/ |
|||
function token($name = '__token__', $type = 'md5') |
|||
{ |
|||
$token = Request::instance()->token($name, $type); |
|||
return '<input type="hidden" name="' . $name . '" value="' . $token . '" />'; |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('load_relation')) { |
|||
/** |
|||
* 延迟预载入关联查询 |
|||
* @param mixed $resultSet 数据集 |
|||
* @param mixed $relation 关联 |
|||
* @return array |
|||
*/ |
|||
function load_relation($resultSet, $relation) |
|||
{ |
|||
$item = current($resultSet); |
|||
if ($item instanceof Model) { |
|||
$item->eagerlyResultSet($resultSet, $relation); |
|||
} |
|||
return $resultSet; |
|||
} |
|||
} |
|||
|
|||
if (!function_exists('collection')) { |
|||
/** |
|||
* 数组转换为数据集对象 |
|||
* @param array $resultSet 数据集数组 |
|||
* @return \think\model\Collection|\think\Collection |
|||
*/ |
|||
function collection($resultSet) |
|||
{ |
|||
$item = current($resultSet); |
|||
if ($item instanceof Model) { |
|||
return \think\model\Collection::make($resultSet); |
|||
} else { |
|||
return \think\Collection::make($resultSet); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
// 核心中文语言包 |
|||
return [ |
|||
// 系统错误提示 |
|||
'Undefined variable' => '未定义变量', |
|||
'Undefined index' => '未定义数组索引', |
|||
'Undefined offset' => '未定义数组下标', |
|||
'Parse error' => '语法解析错误', |
|||
'Type error' => '类型错误', |
|||
'Fatal error' => '致命错误', |
|||
'syntax error' => '语法错误', |
|||
|
|||
// 框架核心错误提示 |
|||
'dispatch type not support' => '不支持的调度类型', |
|||
'method param miss' => '方法参数错误', |
|||
'method not exists' => '方法不存在', |
|||
'module not exists' => '模块不存在', |
|||
'controller not exists' => '控制器不存在', |
|||
'class not exists' => '类不存在', |
|||
'property not exists' => '类的属性不存在', |
|||
'template not exists' => '模板文件不存在', |
|||
'illegal controller name' => '非法的控制器名称', |
|||
'illegal action name' => '非法的操作名称', |
|||
'url suffix deny' => '禁止的URL后缀访问', |
|||
'Route Not Found' => '当前访问路由未定义', |
|||
'Underfined db type' => '未定义数据库类型', |
|||
'variable type error' => '变量类型错误', |
|||
'PSR-4 error' => 'PSR-4 规范错误', |
|||
'not support total' => '简洁模式下不能获取数据总数', |
|||
'not support last' => '简洁模式下不能获取最后一页', |
|||
'error session handler' => '错误的SESSION处理器类', |
|||
'not allow php tag' => '模板不允许使用PHP语法', |
|||
'not support' => '不支持', |
|||
'redisd master' => 'Redisd 主服务器错误', |
|||
'redisd slave' => 'Redisd 从服务器错误', |
|||
'must run at sae' => '必须在SAE运行', |
|||
'memcache init error' => '未开通Memcache服务,请在SAE管理平台初始化Memcache服务', |
|||
'KVDB init error' => '没有初始化KVDB,请在SAE管理平台初始化KVDB服务', |
|||
'fields not exists' => '数据表字段不存在', |
|||
'where express error' => '查询表达式错误', |
|||
'no data to update' => '没有任何数据需要更新', |
|||
'miss data to insert' => '缺少需要写入的数据', |
|||
'miss complex primary data' => '缺少复合主键数据', |
|||
'miss update condition' => '缺少更新条件', |
|||
'model data Not Found' => '模型数据不存在', |
|||
'table data not Found' => '表数据不存在', |
|||
'delete without condition' => '没有条件不会执行删除操作', |
|||
'miss relation data' => '缺少关联表数据', |
|||
'tag attr must' => '模板标签属性必须', |
|||
'tag error' => '模板标签错误', |
|||
'cache write error' => '缓存写入失败', |
|||
'sae mc write error' => 'SAE mc 写入错误', |
|||
'route name not exists' => '路由标识不存在(或参数不够)', |
|||
'invalid request' => '非法请求', |
|||
'bind attr has exists' => '模型的属性已经存在', |
|||
'relation data not exists' => '关联数据不存在', |
|||
'relation not support' => '关联不支持', |
|||
]; |
|||
@ -0,0 +1,569 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
use think\exception\HttpException; |
|||
use think\exception\HttpResponseException; |
|||
use think\exception\RouteNotFoundException; |
|||
|
|||
/** |
|||
* App 应用管理 |
|||
* @author liu21st <liu21st@gmail.com> |
|||
*/ |
|||
class App |
|||
{ |
|||
/** |
|||
* @var bool 是否初始化过 |
|||
*/ |
|||
protected static $init = false; |
|||
|
|||
/** |
|||
* @var string 当前模块路径 |
|||
*/ |
|||
public static $modulePath; |
|||
|
|||
/** |
|||
* @var bool 应用调试模式 |
|||
*/ |
|||
public static $debug = true; |
|||
|
|||
/** |
|||
* @var string 应用类库命名空间 |
|||
*/ |
|||
public static $namespace = 'app'; |
|||
|
|||
/** |
|||
* @var bool 应用类库后缀 |
|||
*/ |
|||
public static $suffix = false; |
|||
|
|||
/** |
|||
* @var bool 应用路由检测 |
|||
*/ |
|||
protected static $routeCheck; |
|||
|
|||
/** |
|||
* @var bool 严格路由检测 |
|||
*/ |
|||
protected static $routeMust; |
|||
|
|||
protected static $dispatch; |
|||
protected static $file = []; |
|||
|
|||
/** |
|||
* 执行应用程序 |
|||
* @access public |
|||
* @param Request $request Request对象 |
|||
* @return Response |
|||
* @throws Exception |
|||
*/ |
|||
public static function run(Request $request = null) |
|||
{ |
|||
is_null($request) && $request = Request::instance(); |
|||
|
|||
try { |
|||
$config = self::initCommon(); |
|||
if (defined('BIND_MODULE')) { |
|||
// 模块/控制器绑定 |
|||
BIND_MODULE && Route::bind(BIND_MODULE); |
|||
} elseif ($config['auto_bind_module']) { |
|||
// 入口自动绑定 |
|||
$name = pathinfo($request->baseFile(), PATHINFO_FILENAME); |
|||
if ($name && 'index' != $name && is_dir(APP_PATH . $name)) { |
|||
Route::bind($name); |
|||
} |
|||
} |
|||
|
|||
$request->filter($config['default_filter']); |
|||
|
|||
if ($config['lang_switch_on']) { |
|||
// 开启多语言机制 检测当前语言 |
|||
Lang::detect(); |
|||
} else { |
|||
// 读取默认语言 |
|||
Lang::range($config['default_lang']); |
|||
} |
|||
$request->langset(Lang::range()); |
|||
// 加载系统语言包 |
|||
Lang::load([ |
|||
THINK_PATH . 'lang' . DS . $request->langset() . EXT, |
|||
APP_PATH . 'lang' . DS . $request->langset() . EXT, |
|||
]); |
|||
|
|||
// 获取应用调度信息 |
|||
$dispatch = self::$dispatch; |
|||
if (empty($dispatch)) { |
|||
// 进行URL路由检测 |
|||
$dispatch = self::routeCheck($request, $config); |
|||
} |
|||
// 记录当前调度信息 |
|||
$request->dispatch($dispatch); |
|||
|
|||
// 记录路由和请求信息 |
|||
if (self::$debug) { |
|||
Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info'); |
|||
Log::record('[ HEADER ] ' . var_export($request->header(), true), 'info'); |
|||
Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info'); |
|||
} |
|||
|
|||
// 监听app_begin |
|||
Hook::listen('app_begin', $dispatch); |
|||
// 请求缓存检查 |
|||
$request->cache($config['request_cache'], $config['request_cache_expire'], $config['request_cache_except']); |
|||
|
|||
switch ($dispatch['type']) { |
|||
case 'redirect': |
|||
// 执行重定向跳转 |
|||
$data = Response::create($dispatch['url'], 'redirect')->code($dispatch['status']); |
|||
break; |
|||
case 'module': |
|||
// 模块/控制器/操作 |
|||
$data = self::module($dispatch['module'], $config, isset($dispatch['convert']) ? $dispatch['convert'] : null); |
|||
break; |
|||
case 'controller': |
|||
// 执行控制器操作 |
|||
$vars = array_merge(Request::instance()->param(), $dispatch['var']); |
|||
$data = Loader::action($dispatch['controller'], $vars, $config['url_controller_layer'], $config['controller_suffix']); |
|||
break; |
|||
case 'method': |
|||
// 执行回调方法 |
|||
$vars = array_merge(Request::instance()->param(), $dispatch['var']); |
|||
$data = self::invokeMethod($dispatch['method'], $vars); |
|||
break; |
|||
case 'function': |
|||
// 执行闭包 |
|||
$data = self::invokeFunction($dispatch['function']); |
|||
break; |
|||
case 'response': |
|||
$data = $dispatch['response']; |
|||
break; |
|||
default: |
|||
throw new \InvalidArgumentException('dispatch type not support'); |
|||
} |
|||
} catch (HttpResponseException $exception) { |
|||
$data = $exception->getResponse(); |
|||
} |
|||
|
|||
// 清空类的实例化 |
|||
Loader::clearInstance(); |
|||
|
|||
// 输出数据到客户端 |
|||
if ($data instanceof Response) { |
|||
$response = $data; |
|||
} elseif (!is_null($data)) { |
|||
// 默认自动识别响应输出类型 |
|||
$isAjax = $request->isAjax(); |
|||
$type = $isAjax ? Config::get('default_ajax_return') : Config::get('default_return_type'); |
|||
$response = Response::create($data, $type); |
|||
} else { |
|||
$response = Response::create(); |
|||
} |
|||
|
|||
// 监听app_end |
|||
Hook::listen('app_end', $response); |
|||
|
|||
return $response; |
|||
} |
|||
|
|||
/** |
|||
* 设置当前请求的调度信息 |
|||
* @access public |
|||
* @param array|string $dispatch 调度信息 |
|||
* @param string $type 调度类型 |
|||
* @return void |
|||
*/ |
|||
public static function dispatch($dispatch, $type = 'module') |
|||
{ |
|||
self::$dispatch = ['type' => $type, $type => $dispatch]; |
|||
} |
|||
|
|||
/** |
|||
* 执行函数或者闭包方法 支持参数调用 |
|||
* @access public |
|||
* @param string|array|\Closure $function 函数或者闭包 |
|||
* @param array $vars 变量 |
|||
* @return mixed |
|||
*/ |
|||
public static function invokeFunction($function, $vars = []) |
|||
{ |
|||
$reflect = new \ReflectionFunction($function); |
|||
$args = self::bindParams($reflect, $vars); |
|||
// 记录执行信息 |
|||
self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info'); |
|||
return $reflect->invokeArgs($args); |
|||
} |
|||
|
|||
/** |
|||
* 调用反射执行类的方法 支持参数绑定 |
|||
* @access public |
|||
* @param string|array $method 方法 |
|||
* @param array $vars 变量 |
|||
* @return mixed |
|||
*/ |
|||
public static function invokeMethod($method, $vars = []) |
|||
{ |
|||
if (is_array($method)) { |
|||
$class = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]); |
|||
$reflect = new \ReflectionMethod($class, $method[1]); |
|||
} else { |
|||
// 静态方法 |
|||
$reflect = new \ReflectionMethod($method); |
|||
} |
|||
$args = self::bindParams($reflect, $vars); |
|||
|
|||
self::$debug && Log::record('[ RUN ] ' . $reflect->class . '->' . $reflect->name . '[ ' . $reflect->getFileName() . ' ]', 'info'); |
|||
return $reflect->invokeArgs(isset($class) ? $class : null, $args); |
|||
} |
|||
|
|||
/** |
|||
* 调用反射执行类的实例化 支持依赖注入 |
|||
* @access public |
|||
* @param string $class 类名 |
|||
* @param array $vars 变量 |
|||
* @return mixed |
|||
*/ |
|||
public static function invokeClass($class, $vars = []) |
|||
{ |
|||
$reflect = new \ReflectionClass($class); |
|||
$constructor = $reflect->getConstructor(); |
|||
if ($constructor) { |
|||
$args = self::bindParams($constructor, $vars); |
|||
} else { |
|||
$args = []; |
|||
} |
|||
return $reflect->newInstanceArgs($args); |
|||
} |
|||
|
|||
/** |
|||
* 绑定参数 |
|||
* @access public |
|||
* @param \ReflectionMethod|\ReflectionFunction $reflect 反射类 |
|||
* @param array $vars 变量 |
|||
* @return array |
|||
*/ |
|||
private static function bindParams($reflect, $vars = []) |
|||
{ |
|||
if (empty($vars)) { |
|||
// 自动获取请求变量 |
|||
if (Config::get('url_param_type')) { |
|||
$vars = Request::instance()->route(); |
|||
} else { |
|||
$vars = Request::instance()->param(); |
|||
} |
|||
} |
|||
$args = []; |
|||
// 判断数组类型 数字数组时按顺序绑定参数 |
|||
reset($vars); |
|||
$type = key($vars) === 0 ? 1 : 0; |
|||
if ($reflect->getNumberOfParameters() > 0) { |
|||
$params = $reflect->getParameters(); |
|||
foreach ($params as $param) { |
|||
$name = $param->getName(); |
|||
$class = $param->getClass(); |
|||
if ($class) { |
|||
$className = $class->getName(); |
|||
$bind = Request::instance()->$name; |
|||
if ($bind instanceof $className) { |
|||
$args[] = $bind; |
|||
} else { |
|||
if (method_exists($className, 'invoke')) { |
|||
$method = new \ReflectionMethod($className, 'invoke'); |
|||
if ($method->isPublic() && $method->isStatic()) { |
|||
$args[] = $className::invoke(Request::instance()); |
|||
continue; |
|||
} |
|||
} |
|||
$args[] = method_exists($className, 'instance') ? $className::instance() : new $className; |
|||
} |
|||
} elseif (1 == $type && !empty($vars)) { |
|||
$args[] = array_shift($vars); |
|||
} elseif (0 == $type && isset($vars[$name])) { |
|||
$args[] = $vars[$name]; |
|||
} elseif ($param->isDefaultValueAvailable()) { |
|||
$args[] = $param->getDefaultValue(); |
|||
} else { |
|||
throw new \InvalidArgumentException('method param miss:' . $name); |
|||
} |
|||
} |
|||
} |
|||
return $args; |
|||
} |
|||
|
|||
/** |
|||
* 执行模块 |
|||
* @access public |
|||
* @param array $result 模块/控制器/操作 |
|||
* @param array $config 配置参数 |
|||
* @param bool $convert 是否自动转换控制器和操作名 |
|||
* @return mixed |
|||
*/ |
|||
public static function module($result, $config, $convert = null) |
|||
{ |
|||
if (is_string($result)) { |
|||
$result = explode('/', $result); |
|||
} |
|||
$request = Request::instance(); |
|||
if ($config['app_multi_module']) { |
|||
// 多模块部署 |
|||
$module = strip_tags(strtolower($result[0] ?: $config['default_module'])); |
|||
$bind = Route::getBind('module'); |
|||
$available = false; |
|||
if ($bind) { |
|||
// 绑定模块 |
|||
list($bindModule) = explode('/', $bind); |
|||
if (empty($result[0])) { |
|||
$module = $bindModule; |
|||
$available = true; |
|||
} elseif ($module == $bindModule) { |
|||
$available = true; |
|||
} |
|||
} elseif (!in_array($module, $config['deny_module_list']) && is_dir(APP_PATH . $module)) { |
|||
$available = true; |
|||
} |
|||
|
|||
// 模块初始化 |
|||
if ($module && $available) { |
|||
// 初始化模块 |
|||
$request->module($module); |
|||
$config = self::init($module); |
|||
// 模块请求缓存检查 |
|||
$request->cache($config['request_cache'], $config['request_cache_expire'], $config['request_cache_except']); |
|||
} else { |
|||
throw new HttpException(404, 'module not exists:' . $module); |
|||
} |
|||
} else { |
|||
// 单一模块部署 |
|||
$module = ''; |
|||
$request->module($module); |
|||
} |
|||
// 当前模块路径 |
|||
App::$modulePath = APP_PATH . ($module ? $module . DS : ''); |
|||
|
|||
// 是否自动转换控制器和操作名 |
|||
$convert = is_bool($convert) ? $convert : $config['url_convert']; |
|||
// 获取控制器名 |
|||
$controller = strip_tags($result[1] ?: $config['default_controller']); |
|||
$controller = $convert ? strtolower($controller) : $controller; |
|||
if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) { |
|||
throw new HttpException(404, 'controller not exists:' . $controller); |
|||
} |
|||
// 获取操作名 |
|||
$actionName = strip_tags($result[2] ?: $config['default_action']); |
|||
$actionName = $convert ? strtolower($actionName) : $actionName; |
|||
|
|||
// 设置当前请求的控制器、操作 |
|||
$request->controller(Loader::parseName($controller, 1))->action($actionName); |
|||
|
|||
// 监听module_init |
|||
Hook::listen('module_init', $request); |
|||
|
|||
$instance = Loader::controller($controller, $config['url_controller_layer'], $config['controller_suffix'], $config['empty_controller']); |
|||
if (is_null($instance)) { |
|||
throw new HttpException(404, 'controller not exists:' . Loader::parseName($controller, 1)); |
|||
} |
|||
// 获取当前操作名 |
|||
$action = $actionName . $config['action_suffix']; |
|||
|
|||
$vars = []; |
|||
if (is_callable([$instance, $action])) { |
|||
// 执行操作方法 |
|||
$call = [$instance, $action]; |
|||
} elseif (is_callable([$instance, '_empty'])) { |
|||
// 空操作 |
|||
$call = [$instance, '_empty']; |
|||
$vars = [$actionName]; |
|||
} else { |
|||
// 操作不存在 |
|||
throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()'); |
|||
} |
|||
|
|||
Hook::listen('action_begin', $call); |
|||
|
|||
return self::invokeMethod($call, $vars); |
|||
} |
|||
|
|||
/** |
|||
* 初始化应用 |
|||
*/ |
|||
public static function initCommon() |
|||
{ |
|||
if (empty(self::$init)) { |
|||
// 初始化应用 |
|||
$config = self::init(); |
|||
self::$suffix = $config['class_suffix']; |
|||
|
|||
// 应用调试模式 |
|||
self::$debug = Env::get('app_debug', Config::get('app_debug')); |
|||
if (!self::$debug) { |
|||
ini_set('display_errors', 'Off'); |
|||
} elseif (!IS_CLI) { |
|||
//重新申请一块比较大的buffer |
|||
if (ob_get_level() > 0) { |
|||
$output = ob_get_clean(); |
|||
} |
|||
ob_start(); |
|||
if (!empty($output)) { |
|||
echo $output; |
|||
} |
|||
} |
|||
|
|||
// 注册应用命名空间 |
|||
self::$namespace = $config['app_namespace']; |
|||
Loader::addNamespace($config['app_namespace'], APP_PATH); |
|||
if (!empty($config['root_namespace'])) { |
|||
Loader::addNamespace($config['root_namespace']); |
|||
} |
|||
|
|||
// 加载额外文件 |
|||
if (!empty($config['extra_file_list'])) { |
|||
foreach ($config['extra_file_list'] as $file) { |
|||
$file = strpos($file, '.') ? $file : APP_PATH . $file . EXT; |
|||
if (is_file($file) && !isset(self::$file[$file])) { |
|||
include $file; |
|||
self::$file[$file] = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 设置系统时区 |
|||
date_default_timezone_set($config['default_timezone']); |
|||
|
|||
// 监听app_init |
|||
Hook::listen('app_init'); |
|||
|
|||
self::$init = true; |
|||
} |
|||
return Config::get(); |
|||
} |
|||
|
|||
/** |
|||
* 初始化应用或模块 |
|||
* @access public |
|||
* @param string $module 模块名 |
|||
* @return array |
|||
*/ |
|||
private static function init($module = '') |
|||
{ |
|||
// 定位模块目录 |
|||
$module = $module ? $module . DS : ''; |
|||
|
|||
// 加载初始化文件 |
|||
if (is_file(APP_PATH . $module . 'init' . EXT)) { |
|||
include APP_PATH . $module . 'init' . EXT; |
|||
} elseif (is_file(RUNTIME_PATH . $module . 'init' . EXT)) { |
|||
include RUNTIME_PATH . $module . 'init' . EXT; |
|||
} else { |
|||
$path = APP_PATH . $module; |
|||
// 加载模块配置 |
|||
$config = Config::load(CONF_PATH . $module . 'config' . CONF_EXT); |
|||
// 读取数据库配置文件 |
|||
$filename = CONF_PATH . $module . 'database' . CONF_EXT; |
|||
Config::load($filename, 'database'); |
|||
// 读取扩展配置文件 |
|||
if (is_dir(CONF_PATH . $module . 'extra')) { |
|||
$dir = CONF_PATH . $module . 'extra'; |
|||
$files = scandir($dir); |
|||
foreach ($files as $file) { |
|||
if (strpos($file, CONF_EXT)) { |
|||
$filename = $dir . DS . $file; |
|||
Config::load($filename, pathinfo($file, PATHINFO_FILENAME)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 加载应用状态配置 |
|||
if ($config['app_status']) { |
|||
$config = Config::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT); |
|||
} |
|||
|
|||
// 加载行为扩展文件 |
|||
if (is_file(CONF_PATH . $module . 'tags' . EXT)) { |
|||
Hook::import(include CONF_PATH . $module . 'tags' . EXT); |
|||
} |
|||
|
|||
// 加载公共文件 |
|||
if (is_file($path . 'common' . EXT)) { |
|||
include $path . 'common' . EXT; |
|||
} |
|||
|
|||
// 加载当前模块语言包 |
|||
if ($module) { |
|||
Lang::load($path . 'lang' . DS . Request::instance()->langset() . EXT); |
|||
} |
|||
} |
|||
return Config::get(); |
|||
} |
|||
|
|||
/** |
|||
* URL路由检测(根据PATH_INFO) |
|||
* @access public |
|||
* @param \think\Request $request |
|||
* @param array $config |
|||
* @return array |
|||
* @throws \think\Exception |
|||
*/ |
|||
public static function routeCheck($request, array $config) |
|||
{ |
|||
$path = $request->path(); |
|||
$depr = $config['pathinfo_depr']; |
|||
$result = false; |
|||
// 路由检测 |
|||
$check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on']; |
|||
if ($check) { |
|||
// 开启路由 |
|||
if (is_file(RUNTIME_PATH . 'route.php')) { |
|||
// 读取路由缓存 |
|||
$rules = include RUNTIME_PATH . 'route.php'; |
|||
if (is_array($rules)) { |
|||
Route::rules($rules); |
|||
} |
|||
} else { |
|||
$files = $config['route_config_file']; |
|||
foreach ($files as $file) { |
|||
if (is_file(CONF_PATH . $file . CONF_EXT)) { |
|||
// 导入路由配置 |
|||
$rules = include CONF_PATH . $file . CONF_EXT; |
|||
if (is_array($rules)) { |
|||
Route::import($rules); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 路由检测(根据路由定义返回不同的URL调度) |
|||
$result = Route::check($request, $path, $depr, $config['url_domain_deploy']); |
|||
$must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must']; |
|||
if ($must && false === $result) { |
|||
// 路由无效 |
|||
throw new RouteNotFoundException(); |
|||
} |
|||
} |
|||
if (false === $result) { |
|||
// 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索 |
|||
$result = Route::parseUrl($path, $depr, $config['controller_auto_search']); |
|||
} |
|||
return $result; |
|||
} |
|||
|
|||
/** |
|||
* 设置应用的路由检测机制 |
|||
* @access public |
|||
* @param bool $route 是否需要检测路由 |
|||
* @param bool $must 是否强制检测路由 |
|||
* @return void |
|||
*/ |
|||
public static function route($route, $must = false) |
|||
{ |
|||
self::$routeCheck = $route; |
|||
self::$routeMust = $must; |
|||
} |
|||
} |
|||
@ -0,0 +1,204 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
class Build |
|||
{ |
|||
/** |
|||
* 根据传入的build资料创建目录和文件 |
|||
* @access protected |
|||
* @param array $build build列表 |
|||
* @param string $namespace 应用类库命名空间 |
|||
* @param bool $suffix 类库后缀 |
|||
* @return void |
|||
*/ |
|||
public static function run(array $build = [], $namespace = 'app', $suffix = false) |
|||
{ |
|||
// 锁定 |
|||
$lockfile = APP_PATH . 'build.lock'; |
|||
if (is_writable($lockfile)) { |
|||
return; |
|||
} elseif (!touch($lockfile)) { |
|||
throw new Exception('应用目录[' . APP_PATH . ']不可写,目录无法自动生成!<BR>请手动生成项目目录~', 10006); |
|||
} |
|||
foreach ($build as $module => $list) { |
|||
if ('__dir__' == $module) { |
|||
// 创建目录列表 |
|||
self::buildDir($list); |
|||
} elseif ('__file__' == $module) { |
|||
// 创建文件列表 |
|||
self::buildFile($list); |
|||
} else { |
|||
// 创建模块 |
|||
self::module($module, $list, $namespace, $suffix); |
|||
} |
|||
} |
|||
// 解除锁定 |
|||
unlink($lockfile); |
|||
} |
|||
|
|||
/** |
|||
* 创建目录 |
|||
* @access protected |
|||
* @param array $list 目录列表 |
|||
* @return void |
|||
*/ |
|||
protected static function buildDir($list) |
|||
{ |
|||
foreach ($list as $dir) { |
|||
if (!is_dir(APP_PATH . $dir)) { |
|||
// 创建目录 |
|||
mkdir(APP_PATH . $dir, 0755, true); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 创建文件 |
|||
* @access protected |
|||
* @param array $list 文件列表 |
|||
* @return void |
|||
*/ |
|||
protected static function buildFile($list) |
|||
{ |
|||
foreach ($list as $file) { |
|||
if (!is_dir(APP_PATH . dirname($file))) { |
|||
// 创建目录 |
|||
mkdir(APP_PATH . dirname($file), 0755, true); |
|||
} |
|||
if (!is_file(APP_PATH . $file)) { |
|||
file_put_contents(APP_PATH . $file, 'php' == pathinfo($file, PATHINFO_EXTENSION) ? "<?php\n" : '');
|
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 创建模块 |
|||
* @access public |
|||
* @param string $module 模块名 |
|||
* @param array $list build列表 |
|||
* @param string $namespace 应用类库命名空间 |
|||
* @param bool $suffix 类库后缀 |
|||
* @return void |
|||
*/ |
|||
public static function module($module = '', $list = [], $namespace = 'app', $suffix = false) |
|||
{ |
|||
$module = $module ? $module : ''; |
|||
if (!is_dir(APP_PATH . $module)) { |
|||
// 创建模块目录 |
|||
mkdir(APP_PATH . $module); |
|||
} |
|||
if (basename(RUNTIME_PATH) != $module) { |
|||
// 创建配置文件和公共文件 |
|||
self::buildCommon($module); |
|||
// 创建模块的默认页面 |
|||
self::buildHello($module, $namespace, $suffix); |
|||
} |
|||
if (empty($list)) { |
|||
// 创建默认的模块目录和文件 |
|||
$list = [ |
|||
'__file__' => ['config.php', 'common.php'], |
|||
'__dir__' => ['controller', 'model', 'view'], |
|||
]; |
|||
} |
|||
// 创建子目录和文件 |
|||
foreach ($list as $path => $file) { |
|||
$modulePath = APP_PATH . $module . DS; |
|||
if ('__dir__' == $path) { |
|||
// 生成子目录 |
|||
foreach ($file as $dir) { |
|||
if (!is_dir($modulePath . $dir)) { |
|||
// 创建目录 |
|||
mkdir($modulePath . $dir, 0755, true); |
|||
} |
|||
} |
|||
} elseif ('__file__' == $path) { |
|||
// 生成(空白)文件 |
|||
foreach ($file as $name) { |
|||
if (!is_file($modulePath . $name)) { |
|||
file_put_contents($modulePath . $name, 'php' == pathinfo($name, PATHINFO_EXTENSION) ? "<?php\n" : '');
|
|||
} |
|||
} |
|||
} else { |
|||
// 生成相关MVC文件 |
|||
foreach ($file as $val) { |
|||
$val = trim($val); |
|||
$filename = $modulePath . $path . DS . $val . ($suffix ? ucfirst($path) : '') . EXT; |
|||
$space = $namespace . '\\' . ($module ? $module . '\\' : '') . $path; |
|||
$class = $val . ($suffix ? ucfirst($path) : ''); |
|||
switch ($path) { |
|||
case 'controller': // 控制器 |
|||
$content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
|
|||
break; |
|||
case 'model': // 模型 |
|||
$content = "<?php\nnamespace {$space};\n\nuse think\Model;\n\nclass {$class} extends Model\n{\n\n}";
|
|||
break; |
|||
case 'view': // 视图 |
|||
$filename = $modulePath . $path . DS . $val . '.html'; |
|||
if (!is_dir(dirname($filename))) { |
|||
// 创建目录 |
|||
mkdir(dirname($filename), 0755, true); |
|||
} |
|||
$content = ''; |
|||
break; |
|||
default: |
|||
// 其他文件 |
|||
$content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
|
|||
} |
|||
|
|||
if (!is_file($filename)) { |
|||
file_put_contents($filename, $content); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 创建模块的欢迎页面 |
|||
* @access public |
|||
* @param string $module 模块名 |
|||
* @param string $namespace 应用类库命名空间 |
|||
* @param bool $suffix 类库后缀 |
|||
* @return void |
|||
*/ |
|||
protected static function buildHello($module, $namespace, $suffix = false) |
|||
{ |
|||
$filename = APP_PATH . ($module ? $module . DS : '') . 'controller' . DS . 'Index' . ($suffix ? 'Controller' : '') . EXT; |
|||
if (!is_file($filename)) { |
|||
$content = file_get_contents(THINK_PATH . 'tpl' . DS . 'default_index.tpl'); |
|||
$content = str_replace(['{$app}', '{$module}', '{layer}', '{$suffix}'], [$namespace, $module ? $module . '\\' : '', 'controller', $suffix ? 'Controller' : ''], $content); |
|||
if (!is_dir(dirname($filename))) { |
|||
mkdir(dirname($filename), 0755, true); |
|||
} |
|||
file_put_contents($filename, $content); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 创建模块的公共文件 |
|||
* @access public |
|||
* @param string $module 模块名 |
|||
* @return void |
|||
*/ |
|||
protected static function buildCommon($module) |
|||
{ |
|||
$filename = CONF_PATH . ($module ? $module . DS : '') . 'config.php'; |
|||
if (!is_file($filename)) { |
|||
file_put_contents($filename, "<?php\n//配置文件\nreturn [\n\n];");
|
|||
} |
|||
$filename = APP_PATH . ($module ? $module . DS : '') . 'common.php'; |
|||
if (!is_file($filename)) { |
|||
file_put_contents($filename, "<?php\n");
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,231 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
use think\cache\Driver; |
|||
|
|||
class Cache |
|||
{ |
|||
protected static $instance = []; |
|||
public static $readTimes = 0; |
|||
public static $writeTimes = 0; |
|||
|
|||
/** |
|||
* 操作句柄 |
|||
* @var object |
|||
* @access protected |
|||
*/ |
|||
protected static $handler; |
|||
|
|||
/** |
|||
* 连接缓存 |
|||
* @access public |
|||
* @param array $options 配置数组 |
|||
* @param bool|string $name 缓存连接标识 true 强制重新连接 |
|||
* @return Driver |
|||
*/ |
|||
public static function connect(array $options = [], $name = false) |
|||
{ |
|||
$type = !empty($options['type']) ? $options['type'] : 'File'; |
|||
if (false === $name) { |
|||
$name = md5(serialize($options)); |
|||
} |
|||
|
|||
if (true === $name || !isset(self::$instance[$name])) { |
|||
$class = false !== strpos($type, '\\') ? $type : '\\think\\cache\\driver\\' . ucwords($type); |
|||
|
|||
// 记录初始化信息 |
|||
App::$debug && Log::record('[ CACHE ] INIT ' . $type, 'info'); |
|||
if (true === $name) { |
|||
return new $class($options); |
|||
} else { |
|||
self::$instance[$name] = new $class($options); |
|||
} |
|||
} |
|||
self::$handler = self::$instance[$name]; |
|||
return self::$handler; |
|||
} |
|||
|
|||
/** |
|||
* 自动初始化缓存 |
|||
* @access public |
|||
* @param array $options 配置数组 |
|||
* @return void |
|||
*/ |
|||
public static function init(array $options = []) |
|||
{ |
|||
if (is_null(self::$handler)) { |
|||
// 自动初始化缓存 |
|||
if (!empty($options)) { |
|||
self::connect($options); |
|||
} elseif ('complex' == Config::get('cache.type')) { |
|||
self::connect(Config::get('cache.default')); |
|||
} else { |
|||
self::connect(Config::get('cache')); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 切换缓存类型 需要配置 cache.type 为 complex |
|||
* @access public |
|||
* @param string $name 缓存标识 |
|||
* @return Driver |
|||
*/ |
|||
public static function store($name = '') |
|||
{ |
|||
if ('' !== $name && 'complex' == Config::get('cache.type')) { |
|||
self::connect(Config::get('cache.' . $name), strtolower($name)); |
|||
} |
|||
return self::$handler; |
|||
} |
|||
|
|||
/** |
|||
* 判断缓存是否存在 |
|||
* @access public |
|||
* @param string $name 缓存变量名 |
|||
* @return bool |
|||
*/ |
|||
public static function has($name) |
|||
{ |
|||
self::init(); |
|||
self::$readTimes++; |
|||
return self::$handler->has($name); |
|||
} |
|||
|
|||
/** |
|||
* 读取缓存 |
|||
* @access public |
|||
* @param string $name 缓存标识 |
|||
* @param mixed $default 默认值 |
|||
* @return mixed |
|||
*/ |
|||
public static function get($name, $default = false) |
|||
{ |
|||
self::init(); |
|||
self::$readTimes++; |
|||
return self::$handler->get($name, $default); |
|||
} |
|||
|
|||
/** |
|||
* 写入缓存 |
|||
* @access public |
|||
* @param string $name 缓存标识 |
|||
* @param mixed $value 存储数据 |
|||
* @param int|null $expire 有效时间 0为永久 |
|||
* @return boolean |
|||
*/ |
|||
public static function set($name, $value, $expire = null) |
|||
{ |
|||
self::init(); |
|||
self::$writeTimes++; |
|||
return self::$handler->set($name, $value, $expire); |
|||
} |
|||
|
|||
/** |
|||
* 自增缓存(针对数值缓存) |
|||
* @access public |
|||
* @param string $name 缓存变量名 |
|||
* @param int $step 步长 |
|||
* @return false|int |
|||
*/ |
|||
public static function inc($name, $step = 1) |
|||
{ |
|||
self::init(); |
|||
self::$writeTimes++; |
|||
return self::$handler->inc($name, $step); |
|||
} |
|||
|
|||
/** |
|||
* 自减缓存(针对数值缓存) |
|||
* @access public |
|||
* @param string $name 缓存变量名 |
|||
* @param int $step 步长 |
|||
* @return false|int |
|||
*/ |
|||
public static function dec($name, $step = 1) |
|||
{ |
|||
self::init(); |
|||
self::$writeTimes++; |
|||
return self::$handler->dec($name, $step); |
|||
} |
|||
|
|||
/** |
|||
* 删除缓存 |
|||
* @access public |
|||
* @param string $name 缓存标识 |
|||
* @return boolean |
|||
*/ |
|||
public static function rm($name) |
|||
{ |
|||
self::init(); |
|||
self::$writeTimes++; |
|||
return self::$handler->rm($name); |
|||
} |
|||
|
|||
/** |
|||
* 清除缓存 |
|||
* @access public |
|||
* @param string $tag 标签名 |
|||
* @return boolean |
|||
*/ |
|||
public static function clear($tag = null) |
|||
{ |
|||
self::init(); |
|||
self::$writeTimes++; |
|||
return self::$handler->clear($tag); |
|||
} |
|||
|
|||
/** |
|||
* 读取缓存并删除 |
|||
* @access public |
|||
* @param string $name 缓存变量名 |
|||
* @return mixed |
|||
*/ |
|||
public static function pull($name) |
|||
{ |
|||
self::init(); |
|||
self::$readTimes++; |
|||
self::$writeTimes++; |
|||
return self::$handler->pull($name); |
|||
} |
|||
|
|||
/** |
|||
* 如果不存在则写入缓存 |
|||
* @access public |
|||
* @param string $name 缓存变量名 |
|||
* @param mixed $value 存储数据 |
|||
* @param int $expire 有效时间 0为永久 |
|||
* @return mixed |
|||
*/ |
|||
public static function remember($name, $value, $expire = null) |
|||
{ |
|||
self::init(); |
|||
self::$readTimes++; |
|||
return self::$handler->remember($name, $value, $expire); |
|||
} |
|||
|
|||
/** |
|||
* 缓存标签 |
|||
* @access public |
|||
* @param string $name 标签名 |
|||
* @param string|array $keys 缓存标识 |
|||
* @param bool $overlay 是否覆盖 |
|||
* @return Driver |
|||
*/ |
|||
public static function tag($name, $keys = null, $overlay = false) |
|||
{ |
|||
self::init(); |
|||
return self::$handler->tag($name, $keys, $overlay); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,373 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: zhangyajun <448901948@qq.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
use ArrayAccess; |
|||
use ArrayIterator; |
|||
use Countable; |
|||
use IteratorAggregate; |
|||
use JsonSerializable; |
|||
|
|||
class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable |
|||
{ |
|||
protected $items = []; |
|||
|
|||
public function __construct($items = []) |
|||
{ |
|||
$this->items = $this->convertToArray($items); |
|||
} |
|||
|
|||
public static function make($items = []) |
|||
{ |
|||
return new static($items); |
|||
} |
|||
|
|||
/** |
|||
* 是否为空 |
|||
* @return bool |
|||
*/ |
|||
public function isEmpty() |
|||
{ |
|||
return empty($this->items); |
|||
} |
|||
|
|||
public function toArray() |
|||
{ |
|||
return array_map(function ($value) { |
|||
return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value; |
|||
}, $this->items); |
|||
} |
|||
|
|||
public function all() |
|||
{ |
|||
return $this->items; |
|||
} |
|||
|
|||
/** |
|||
* 合并数组 |
|||
* |
|||
* @param mixed $items |
|||
* @return static |
|||
*/ |
|||
public function merge($items) |
|||
{ |
|||
return new static(array_merge($this->items, $this->convertToArray($items))); |
|||
} |
|||
|
|||
/** |
|||
* 比较数组,返回差集 |
|||
* |
|||
* @param mixed $items |
|||
* @return static |
|||
*/ |
|||
public function diff($items) |
|||
{ |
|||
return new static(array_diff($this->items, $this->convertToArray($items))); |
|||
} |
|||
|
|||
/** |
|||
* 交换数组中的键和值 |
|||
* |
|||
* @return static |
|||
*/ |
|||
public function flip() |
|||
{ |
|||
return new static(array_flip($this->items)); |
|||
} |
|||
|
|||
/** |
|||
* 比较数组,返回交集 |
|||
* |
|||
* @param mixed $items |
|||
* @return static |
|||
*/ |
|||
public function intersect($items) |
|||
{ |
|||
return new static(array_intersect($this->items, $this->convertToArray($items))); |
|||
} |
|||
|
|||
/** |
|||
* 返回数组中所有的键名 |
|||
* |
|||
* @return static |
|||
*/ |
|||
public function keys() |
|||
{ |
|||
return new static(array_keys($this->items)); |
|||
} |
|||
|
|||
/** |
|||
* 删除数组的最后一个元素(出栈) |
|||
* |
|||
* @return mixed |
|||
*/ |
|||
public function pop() |
|||
{ |
|||
return array_pop($this->items); |
|||
} |
|||
|
|||
/** |
|||
* 通过使用用户自定义函数,以字符串返回数组 |
|||
* |
|||
* @param callable $callback |
|||
* @param mixed $initial |
|||
* @return mixed |
|||
*/ |
|||
public function reduce(callable $callback, $initial = null) |
|||
{ |
|||
return array_reduce($this->items, $callback, $initial); |
|||
} |
|||
|
|||
/** |
|||
* 以相反的顺序返回数组。 |
|||
* |
|||
* @return static |
|||
*/ |
|||
public function reverse() |
|||
{ |
|||
return new static(array_reverse($this->items)); |
|||
} |
|||
|
|||
/** |
|||
* 删除数组中首个元素,并返回被删除元素的值 |
|||
* |
|||
* @return mixed |
|||
*/ |
|||
public function shift() |
|||
{ |
|||
return array_shift($this->items); |
|||
} |
|||
|
|||
/** |
|||
* 把一个数组分割为新的数组块. |
|||
* |
|||
* @param int $size |
|||
* @param bool $preserveKeys |
|||
* @return static |
|||
*/ |
|||
public function chunk($size, $preserveKeys = false) |
|||
{ |
|||
$chunks = []; |
|||
|
|||
foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) { |
|||
$chunks[] = new static($chunk); |
|||
} |
|||
|
|||
return new static($chunks); |
|||
} |
|||
|
|||
/** |
|||
* 在数组开头插入一个元素 |
|||
* @param mixed $value |
|||
* @param null $key |
|||
* @return int |
|||
*/ |
|||
public function unshift($value, $key = null) |
|||
{ |
|||
if (is_null($key)) { |
|||
array_unshift($this->items, $value); |
|||
} else { |
|||
$this->items = [$key => $value] + $this->items; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 给每个元素执行个回调 |
|||
* |
|||
* @param callable $callback |
|||
* @return $this |
|||
*/ |
|||
public function each(callable $callback) |
|||
{ |
|||
foreach ($this->items as $key => $item) { |
|||
if ($callback($item, $key) === false) { |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 用回调函数过滤数组中的元素 |
|||
* @param callable|null $callback |
|||
* @return static |
|||
*/ |
|||
public function filter(callable $callback = null) |
|||
{ |
|||
if ($callback) { |
|||
return new static(array_filter($this->items, $callback)); |
|||
} |
|||
|
|||
return new static(array_filter($this->items)); |
|||
} |
|||
|
|||
/** |
|||
* 返回数组中指定的一列 |
|||
* @param $column_key |
|||
* @param null $index_key |
|||
* @return array |
|||
*/ |
|||
public function column($column_key, $index_key = null) |
|||
{ |
|||
if (function_exists('array_column')) { |
|||
return array_column($this->items, $column_key, $index_key); |
|||
} |
|||
|
|||
$result = []; |
|||
foreach ($this->items as $row) { |
|||
$key = $value = null; |
|||
$keySet = $valueSet = false; |
|||
if (null !== $index_key && array_key_exists($index_key, $row)) { |
|||
$keySet = true; |
|||
$key = (string) $row[$index_key]; |
|||
} |
|||
if (null === $column_key) { |
|||
$valueSet = true; |
|||
$value = $row; |
|||
} elseif (is_array($row) && array_key_exists($column_key, $row)) { |
|||
$valueSet = true; |
|||
$value = $row[$column_key]; |
|||
} |
|||
if ($valueSet) { |
|||
if ($keySet) { |
|||
$result[$key] = $value; |
|||
} else { |
|||
$result[] = $value; |
|||
} |
|||
} |
|||
} |
|||
return $result; |
|||
} |
|||
|
|||
/** |
|||
* 对数组排序 |
|||
* |
|||
* @param callable|null $callback |
|||
* @return static |
|||
*/ |
|||
public function sort(callable $callback = null) |
|||
{ |
|||
$items = $this->items; |
|||
|
|||
$callback ? uasort($items, $callback) : uasort($items, function ($a, $b) { |
|||
|
|||
if ($a == $b) { |
|||
return 0; |
|||
} |
|||
|
|||
return ($a < $b) ? -1 : 1; |
|||
}); |
|||
|
|||
return new static($items); |
|||
} |
|||
|
|||
/** |
|||
* 将数组打乱 |
|||
* |
|||
* @return static |
|||
*/ |
|||
public function shuffle() |
|||
{ |
|||
$items = $this->items; |
|||
|
|||
shuffle($items); |
|||
|
|||
return new static($items); |
|||
} |
|||
|
|||
/** |
|||
* 截取数组 |
|||
* |
|||
* @param int $offset |
|||
* @param int $length |
|||
* @param bool $preserveKeys |
|||
* @return static |
|||
*/ |
|||
public function slice($offset, $length = null, $preserveKeys = false) |
|||
{ |
|||
return new static(array_slice($this->items, $offset, $length, $preserveKeys)); |
|||
} |
|||
|
|||
// ArrayAccess |
|||
public function offsetExists($offset) |
|||
{ |
|||
return array_key_exists($offset, $this->items); |
|||
} |
|||
|
|||
public function offsetGet($offset) |
|||
{ |
|||
return $this->items[$offset]; |
|||
} |
|||
|
|||
public function offsetSet($offset, $value) |
|||
{ |
|||
if (is_null($offset)) { |
|||
$this->items[] = $value; |
|||
} else { |
|||
$this->items[$offset] = $value; |
|||
} |
|||
} |
|||
|
|||
public function offsetUnset($offset) |
|||
{ |
|||
unset($this->items[$offset]); |
|||
} |
|||
|
|||
//Countable |
|||
public function count() |
|||
{ |
|||
return count($this->items); |
|||
} |
|||
|
|||
//IteratorAggregate |
|||
public function getIterator() |
|||
{ |
|||
return new ArrayIterator($this->items); |
|||
} |
|||
|
|||
//JsonSerializable |
|||
public function jsonSerialize() |
|||
{ |
|||
return $this->toArray(); |
|||
} |
|||
|
|||
/** |
|||
* 转换当前数据集为JSON字符串 |
|||
* @access public |
|||
* @param integer $options json参数 |
|||
* @return string |
|||
*/ |
|||
public function toJson($options = JSON_UNESCAPED_UNICODE) |
|||
{ |
|||
return json_encode($this->toArray(), $options); |
|||
} |
|||
|
|||
public function __toString() |
|||
{ |
|||
return $this->toJson(); |
|||
} |
|||
|
|||
/** |
|||
* 转换成数组 |
|||
* |
|||
* @param mixed $items |
|||
* @return array |
|||
*/ |
|||
protected function convertToArray($items) |
|||
{ |
|||
if ($items instanceof self) { |
|||
return $items->all(); |
|||
} |
|||
return (array) $items; |
|||
} |
|||
} |
|||
@ -0,0 +1,170 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
class Config |
|||
{ |
|||
// 配置参数 |
|||
private static $config = []; |
|||
// 参数作用域 |
|||
private static $range = '_sys_'; |
|||
|
|||
// 设定配置参数的作用域 |
|||
public static function range($range) |
|||
{ |
|||
self::$range = $range; |
|||
if (!isset(self::$config[$range])) { |
|||
self::$config[$range] = []; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 解析配置文件或内容 |
|||
* @param string $config 配置文件路径或内容 |
|||
* @param string $type 配置解析类型 |
|||
* @param string $name 配置名(如设置即表示二级配置) |
|||
* @param string $range 作用域 |
|||
* @return mixed |
|||
*/ |
|||
public static function parse($config, $type = '', $name = '', $range = '') |
|||
{ |
|||
$range = $range ?: self::$range; |
|||
if (empty($type)) { |
|||
$type = pathinfo($config, PATHINFO_EXTENSION); |
|||
} |
|||
$class = false !== strpos($type, '\\') ? $type : '\\think\\config\\driver\\' . ucwords($type); |
|||
return self::set((new $class())->parse($config), $name, $range); |
|||
} |
|||
|
|||
/** |
|||
* 加载配置文件(PHP格式) |
|||
* @param string $file 配置文件名 |
|||
* @param string $name 配置名(如设置即表示二级配置) |
|||
* @param string $range 作用域 |
|||
* @return mixed |
|||
*/ |
|||
public static function load($file, $name = '', $range = '') |
|||
{ |
|||
$range = $range ?: self::$range; |
|||
if (!isset(self::$config[$range])) { |
|||
self::$config[$range] = []; |
|||
} |
|||
if (is_file($file)) { |
|||
$name = strtolower($name); |
|||
$type = pathinfo($file, PATHINFO_EXTENSION); |
|||
if ('php' == $type) { |
|||
return self::set(include $file, $name, $range); |
|||
} elseif ('yaml' == $type && function_exists('yaml_parse_file')) { |
|||
return self::set(yaml_parse_file($file), $name, $range); |
|||
} else { |
|||
return self::parse($file, $type, $name, $range); |
|||
} |
|||
} else { |
|||
return self::$config[$range]; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 检测配置是否存在 |
|||
* @param string $name 配置参数名(支持二级配置 .号分割) |
|||
* @param string $range 作用域 |
|||
* @return bool |
|||
*/ |
|||
public static function has($name, $range = '') |
|||
{ |
|||
$range = $range ?: self::$range; |
|||
|
|||
if (!strpos($name, '.')) { |
|||
return isset(self::$config[$range][strtolower($name)]); |
|||
} else { |
|||
// 二维数组设置和获取支持 |
|||
$name = explode('.', $name, 2); |
|||
return isset(self::$config[$range][strtolower($name[0])][$name[1]]); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取配置参数 为空则获取所有配置 |
|||
* @param string $name 配置参数名(支持二级配置 .号分割) |
|||
* @param string $range 作用域 |
|||
* @return mixed |
|||
*/ |
|||
public static function get($name = null, $range = '') |
|||
{ |
|||
$range = $range ?: self::$range; |
|||
// 无参数时获取所有 |
|||
if (empty($name) && isset(self::$config[$range])) { |
|||
return self::$config[$range]; |
|||
} |
|||
|
|||
if (!strpos($name, '.')) { |
|||
$name = strtolower($name); |
|||
return isset(self::$config[$range][$name]) ? self::$config[$range][$name] : null; |
|||
} else { |
|||
// 二维数组设置和获取支持 |
|||
$name = explode('.', $name, 2); |
|||
$name[0] = strtolower($name[0]); |
|||
return isset(self::$config[$range][$name[0]][$name[1]]) ? self::$config[$range][$name[0]][$name[1]] : null; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 设置配置参数 name为数组则为批量设置 |
|||
* @param string|array $name 配置参数名(支持二级配置 .号分割) |
|||
* @param mixed $value 配置值 |
|||
* @param string $range 作用域 |
|||
* @return mixed |
|||
*/ |
|||
public static function set($name, $value = null, $range = '') |
|||
{ |
|||
$range = $range ?: self::$range; |
|||
if (!isset(self::$config[$range])) { |
|||
self::$config[$range] = []; |
|||
} |
|||
if (is_string($name)) { |
|||
if (!strpos($name, '.')) { |
|||
self::$config[$range][strtolower($name)] = $value; |
|||
} else { |
|||
// 二维数组设置和获取支持 |
|||
$name = explode('.', $name, 2); |
|||
self::$config[$range][strtolower($name[0])][$name[1]] = $value; |
|||
} |
|||
return; |
|||
} elseif (is_array($name)) { |
|||
// 批量设置 |
|||
if (!empty($value)) { |
|||
self::$config[$range][$value] = isset(self::$config[$range][$value]) ? |
|||
array_merge(self::$config[$range][$value], $name) : |
|||
self::$config[$range][$value] = $name; |
|||
return self::$config[$range][$value]; |
|||
} else { |
|||
return self::$config[$range] = array_merge(self::$config[$range], array_change_key_case($name)); |
|||
} |
|||
} else { |
|||
// 为空直接返回 已有配置 |
|||
return self::$config[$range]; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 重置配置参数 |
|||
*/ |
|||
public static function reset($range = '') |
|||
{ |
|||
$range = $range ?: self::$range; |
|||
if (true === $range) { |
|||
self::$config = []; |
|||
} else { |
|||
self::$config[$range] = []; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,719 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | TopThink [ WE CAN DO IT JUST THINK IT ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2015 http://www.topthink.com All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: zhangyajun <448901948@qq.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
use think\console\Command; |
|||
use think\console\command\Help as HelpCommand; |
|||
use think\console\Input; |
|||
use think\console\input\Argument as InputArgument; |
|||
use think\console\input\Definition as InputDefinition; |
|||
use think\console\input\Option as InputOption; |
|||
use think\console\Output; |
|||
use think\console\output\driver\Buffer; |
|||
|
|||
class Console |
|||
{ |
|||
|
|||
private $name; |
|||
private $version; |
|||
|
|||
/** @var Command[] */ |
|||
private $commands = []; |
|||
|
|||
private $wantHelps = false; |
|||
|
|||
private $catchExceptions = true; |
|||
private $autoExit = true; |
|||
private $definition; |
|||
private $defaultCommand; |
|||
|
|||
private static $defaultCommands = [ |
|||
"think\\console\\command\\Help", |
|||
"think\\console\\command\\Lists", |
|||
"think\\console\\command\\Build", |
|||
"think\\console\\command\\Clear", |
|||
"think\\console\\command\\make\\Controller", |
|||
"think\\console\\command\\make\\Model", |
|||
"think\\console\\command\\optimize\\Autoload", |
|||
"think\\console\\command\\optimize\\Config", |
|||
"think\\console\\command\\optimize\\Route", |
|||
"think\\console\\command\\optimize\\Schema", |
|||
]; |
|||
|
|||
public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') |
|||
{ |
|||
$this->name = $name; |
|||
$this->version = $version; |
|||
|
|||
$this->defaultCommand = 'list'; |
|||
$this->definition = $this->getDefaultInputDefinition(); |
|||
|
|||
foreach ($this->getDefaultCommands() as $command) { |
|||
$this->add($command); |
|||
} |
|||
} |
|||
|
|||
public static function init($run = true) |
|||
{ |
|||
static $console; |
|||
if (!$console) { |
|||
// 实例化console |
|||
$console = new self('Think Console', '0.1'); |
|||
// 读取指令集 |
|||
if (is_file(CONF_PATH . 'command' . EXT)) { |
|||
$commands = include CONF_PATH . 'command' . EXT; |
|||
if (is_array($commands)) { |
|||
foreach ($commands as $command) { |
|||
if (class_exists($command) && is_subclass_of($command, "\\think\\console\\Command")) { |
|||
// 注册指令 |
|||
$console->add(new $command()); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
if ($run) { |
|||
// 运行 |
|||
return $console->run(); |
|||
} else { |
|||
return $console; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @param $command |
|||
* @param array $parameters |
|||
* @param string $driver |
|||
* @return Output|Buffer |
|||
*/ |
|||
public static function call($command, array $parameters = [], $driver = 'buffer') |
|||
{ |
|||
$console = self::init(false); |
|||
|
|||
array_unshift($parameters, $command); |
|||
|
|||
$input = new Input($parameters); |
|||
$output = new Output($driver); |
|||
|
|||
$console->setCatchExceptions(false); |
|||
$console->find($command)->run($input, $output); |
|||
|
|||
return $output; |
|||
} |
|||
|
|||
/** |
|||
* 执行当前的指令 |
|||
* @return int |
|||
* @throws \Exception |
|||
* @api |
|||
*/ |
|||
public function run() |
|||
{ |
|||
$input = new Input(); |
|||
$output = new Output(); |
|||
|
|||
$this->configureIO($input, $output); |
|||
|
|||
try { |
|||
$exitCode = $this->doRun($input, $output); |
|||
} catch (\Exception $e) { |
|||
if (!$this->catchExceptions) { |
|||
throw $e; |
|||
} |
|||
|
|||
$output->renderException($e); |
|||
|
|||
$exitCode = $e->getCode(); |
|||
if (is_numeric($exitCode)) { |
|||
$exitCode = (int) $exitCode; |
|||
if (0 === $exitCode) { |
|||
$exitCode = 1; |
|||
} |
|||
} else { |
|||
$exitCode = 1; |
|||
} |
|||
} |
|||
|
|||
if ($this->autoExit) { |
|||
if ($exitCode > 255) { |
|||
$exitCode = 255; |
|||
} |
|||
|
|||
exit($exitCode); |
|||
} |
|||
|
|||
return $exitCode; |
|||
} |
|||
|
|||
/** |
|||
* 执行指令 |
|||
* @param Input $input |
|||
* @param Output $output |
|||
* @return int |
|||
*/ |
|||
public function doRun(Input $input, Output $output) |
|||
{ |
|||
if (true === $input->hasParameterOption(['--version', '-V'])) { |
|||
$output->writeln($this->getLongVersion()); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
$name = $this->getCommandName($input); |
|||
|
|||
if (true === $input->hasParameterOption(['--help', '-h'])) { |
|||
if (!$name) { |
|||
$name = 'help'; |
|||
$input = new Input(['help']); |
|||
} else { |
|||
$this->wantHelps = true; |
|||
} |
|||
} |
|||
|
|||
if (!$name) { |
|||
$name = $this->defaultCommand; |
|||
$input = new Input([$this->defaultCommand]); |
|||
} |
|||
|
|||
$command = $this->find($name); |
|||
|
|||
$exitCode = $this->doRunCommand($command, $input, $output); |
|||
|
|||
return $exitCode; |
|||
} |
|||
|
|||
/** |
|||
* 设置输入参数定义 |
|||
* @param InputDefinition $definition |
|||
*/ |
|||
public function setDefinition(InputDefinition $definition) |
|||
{ |
|||
$this->definition = $definition; |
|||
} |
|||
|
|||
/** |
|||
* 获取输入参数定义 |
|||
* @return InputDefinition The InputDefinition instance |
|||
*/ |
|||
public function getDefinition() |
|||
{ |
|||
return $this->definition; |
|||
} |
|||
|
|||
/** |
|||
* Gets the help message. |
|||
* @return string A help message. |
|||
*/ |
|||
public function getHelp() |
|||
{ |
|||
return $this->getLongVersion(); |
|||
} |
|||
|
|||
/** |
|||
* 是否捕获异常 |
|||
* @param bool $boolean |
|||
* @api |
|||
*/ |
|||
public function setCatchExceptions($boolean) |
|||
{ |
|||
$this->catchExceptions = (bool) $boolean; |
|||
} |
|||
|
|||
/** |
|||
* 是否自动退出 |
|||
* @param bool $boolean |
|||
* @api |
|||
*/ |
|||
public function setAutoExit($boolean) |
|||
{ |
|||
$this->autoExit = (bool) $boolean; |
|||
} |
|||
|
|||
/** |
|||
* 获取名称 |
|||
* @return string |
|||
*/ |
|||
public function getName() |
|||
{ |
|||
return $this->name; |
|||
} |
|||
|
|||
/** |
|||
* 设置名称 |
|||
* @param string $name |
|||
*/ |
|||
public function setName($name) |
|||
{ |
|||
$this->name = $name; |
|||
} |
|||
|
|||
/** |
|||
* 获取版本 |
|||
* @return string |
|||
* @api |
|||
*/ |
|||
public function getVersion() |
|||
{ |
|||
return $this->version; |
|||
} |
|||
|
|||
/** |
|||
* 设置版本 |
|||
* @param string $version |
|||
*/ |
|||
public function setVersion($version) |
|||
{ |
|||
$this->version = $version; |
|||
} |
|||
|
|||
/** |
|||
* 获取完整的版本号 |
|||
* @return string |
|||
*/ |
|||
public function getLongVersion() |
|||
{ |
|||
if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) { |
|||
return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion()); |
|||
} |
|||
|
|||
return '<info>Console Tool</info>'; |
|||
} |
|||
|
|||
/** |
|||
* 注册一个指令 |
|||
* @param string $name |
|||
* @return Command |
|||
*/ |
|||
public function register($name) |
|||
{ |
|||
return $this->add(new Command($name)); |
|||
} |
|||
|
|||
/** |
|||
* 添加指令 |
|||
* @param Command[] $commands |
|||
*/ |
|||
public function addCommands(array $commands) |
|||
{ |
|||
foreach ($commands as $command) { |
|||
$this->add($command); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 添加一个指令 |
|||
* @param Command $command |
|||
* @return Command |
|||
*/ |
|||
public function add(Command $command) |
|||
{ |
|||
$command->setConsole($this); |
|||
|
|||
if (!$command->isEnabled()) { |
|||
$command->setConsole(null); |
|||
return; |
|||
} |
|||
|
|||
if (null === $command->getDefinition()) { |
|||
throw new \LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command))); |
|||
} |
|||
|
|||
$this->commands[$command->getName()] = $command; |
|||
|
|||
foreach ($command->getAliases() as $alias) { |
|||
$this->commands[$alias] = $command; |
|||
} |
|||
|
|||
return $command; |
|||
} |
|||
|
|||
/** |
|||
* 获取指令 |
|||
* @param string $name 指令名称 |
|||
* @return Command |
|||
* @throws \InvalidArgumentException |
|||
*/ |
|||
public function get($name) |
|||
{ |
|||
if (!isset($this->commands[$name])) { |
|||
throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name)); |
|||
} |
|||
|
|||
$command = $this->commands[$name]; |
|||
|
|||
if ($this->wantHelps) { |
|||
$this->wantHelps = false; |
|||
|
|||
/** @var HelpCommand $helpCommand */ |
|||
$helpCommand = $this->get('help'); |
|||
$helpCommand->setCommand($command); |
|||
|
|||
return $helpCommand; |
|||
} |
|||
|
|||
return $command; |
|||
} |
|||
|
|||
/** |
|||
* 某个指令是否存在 |
|||
* @param string $name 指令名称 |
|||
* @return bool |
|||
*/ |
|||
public function has($name) |
|||
{ |
|||
return isset($this->commands[$name]); |
|||
} |
|||
|
|||
/** |
|||
* 获取所有的命名空间 |
|||
* @return array |
|||
*/ |
|||
public function getNamespaces() |
|||
{ |
|||
$namespaces = []; |
|||
foreach ($this->commands as $command) { |
|||
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName())); |
|||
|
|||
foreach ($command->getAliases() as $alias) { |
|||
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias)); |
|||
} |
|||
} |
|||
|
|||
return array_values(array_unique(array_filter($namespaces))); |
|||
} |
|||
|
|||
/** |
|||
* 查找注册命名空间中的名称或缩写。 |
|||
* @param string $namespace |
|||
* @return string |
|||
* @throws \InvalidArgumentException |
|||
*/ |
|||
public function findNamespace($namespace) |
|||
{ |
|||
$allNamespaces = $this->getNamespaces(); |
|||
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) { |
|||
return preg_quote($matches[1]) . '[^:]*'; |
|||
}, $namespace); |
|||
$namespaces = preg_grep('{^' . $expr . '}', $allNamespaces); |
|||
|
|||
if (empty($namespaces)) { |
|||
$message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); |
|||
|
|||
if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) { |
|||
if (1 == count($alternatives)) { |
|||
$message .= "\n\nDid you mean this?\n "; |
|||
} else { |
|||
$message .= "\n\nDid you mean one of these?\n "; |
|||
} |
|||
|
|||
$message .= implode("\n ", $alternatives); |
|||
} |
|||
|
|||
throw new \InvalidArgumentException($message); |
|||
} |
|||
|
|||
$exact = in_array($namespace, $namespaces, true); |
|||
if (count($namespaces) > 1 && !$exact) { |
|||
throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces)))); |
|||
} |
|||
|
|||
return $exact ? $namespace : reset($namespaces); |
|||
} |
|||
|
|||
/** |
|||
* 查找指令 |
|||
* @param string $name 名称或者别名 |
|||
* @return Command |
|||
* @throws \InvalidArgumentException |
|||
*/ |
|||
public function find($name) |
|||
{ |
|||
$allCommands = array_keys($this->commands); |
|||
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) { |
|||
return preg_quote($matches[1]) . '[^:]*'; |
|||
}, $name); |
|||
$commands = preg_grep('{^' . $expr . '}', $allCommands); |
|||
|
|||
if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) { |
|||
if (false !== $pos = strrpos($name, ':')) { |
|||
$this->findNamespace(substr($name, 0, $pos)); |
|||
} |
|||
|
|||
$message = sprintf('Command "%s" is not defined.', $name); |
|||
|
|||
if ($alternatives = $this->findAlternatives($name, $allCommands)) { |
|||
if (1 == count($alternatives)) { |
|||
$message .= "\n\nDid you mean this?\n "; |
|||
} else { |
|||
$message .= "\n\nDid you mean one of these?\n "; |
|||
} |
|||
$message .= implode("\n ", $alternatives); |
|||
} |
|||
|
|||
throw new \InvalidArgumentException($message); |
|||
} |
|||
|
|||
if (count($commands) > 1) { |
|||
$commandList = $this->commands; |
|||
$commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) { |
|||
$commandName = $commandList[$nameOrAlias]->getName(); |
|||
|
|||
return $commandName === $nameOrAlias || !in_array($commandName, $commands); |
|||
}); |
|||
} |
|||
|
|||
$exact = in_array($name, $commands, true); |
|||
if (count($commands) > 1 && !$exact) { |
|||
$suggestions = $this->getAbbreviationSuggestions(array_values($commands)); |
|||
|
|||
throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions)); |
|||
} |
|||
|
|||
return $this->get($exact ? $name : reset($commands)); |
|||
} |
|||
|
|||
/** |
|||
* 获取所有的指令 |
|||
* @param string $namespace 命名空间 |
|||
* @return Command[] |
|||
* @api |
|||
*/ |
|||
public function all($namespace = null) |
|||
{ |
|||
if (null === $namespace) { |
|||
return $this->commands; |
|||
} |
|||
|
|||
$commands = []; |
|||
foreach ($this->commands as $name => $command) { |
|||
if ($this->extractNamespace($name, substr_count($namespace, ':') + 1) === $namespace) { |
|||
$commands[$name] = $command; |
|||
} |
|||
} |
|||
|
|||
return $commands; |
|||
} |
|||
|
|||
/** |
|||
* 获取可能的指令名 |
|||
* @param array $names |
|||
* @return array |
|||
*/ |
|||
public static function getAbbreviations($names) |
|||
{ |
|||
$abbrevs = []; |
|||
foreach ($names as $name) { |
|||
for ($len = strlen($name); $len > 0; --$len) { |
|||
$abbrev = substr($name, 0, $len); |
|||
$abbrevs[$abbrev][] = $name; |
|||
} |
|||
} |
|||
|
|||
return $abbrevs; |
|||
} |
|||
|
|||
/** |
|||
* 配置基于用户的参数和选项的输入和输出实例。 |
|||
* @param Input $input 输入实例 |
|||
* @param Output $output 输出实例 |
|||
*/ |
|||
protected function configureIO(Input $input, Output $output) |
|||
{ |
|||
if (true === $input->hasParameterOption(['--ansi'])) { |
|||
$output->setDecorated(true); |
|||
} elseif (true === $input->hasParameterOption(['--no-ansi'])) { |
|||
$output->setDecorated(false); |
|||
} |
|||
|
|||
if (true === $input->hasParameterOption(['--no-interaction', '-n'])) { |
|||
$input->setInteractive(false); |
|||
} |
|||
|
|||
if (true === $input->hasParameterOption(['--quiet', '-q'])) { |
|||
$output->setVerbosity(Output::VERBOSITY_QUIET); |
|||
} else { |
|||
if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) { |
|||
$output->setVerbosity(Output::VERBOSITY_DEBUG); |
|||
} elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) { |
|||
$output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE); |
|||
} elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) { |
|||
$output->setVerbosity(Output::VERBOSITY_VERBOSE); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 执行指令 |
|||
* @param Command $command 指令实例 |
|||
* @param Input $input 输入实例 |
|||
* @param Output $output 输出实例 |
|||
* @return int |
|||
* @throws \Exception |
|||
*/ |
|||
protected function doRunCommand(Command $command, Input $input, Output $output) |
|||
{ |
|||
return $command->run($input, $output); |
|||
} |
|||
|
|||
/** |
|||
* 获取指令的基础名称 |
|||
* @param Input $input |
|||
* @return string |
|||
*/ |
|||
protected function getCommandName(Input $input) |
|||
{ |
|||
return $input->getFirstArgument(); |
|||
} |
|||
|
|||
/** |
|||
* 获取默认输入定义 |
|||
* @return InputDefinition |
|||
*/ |
|||
protected function getDefaultInputDefinition() |
|||
{ |
|||
return new InputDefinition([ |
|||
new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), |
|||
new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'), |
|||
new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this console version'), |
|||
new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'), |
|||
new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'), |
|||
new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'), |
|||
new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'), |
|||
new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'), |
|||
]); |
|||
} |
|||
|
|||
/** |
|||
* 设置默认命令 |
|||
* @return Command[] An array of default Command instances |
|||
*/ |
|||
protected function getDefaultCommands() |
|||
{ |
|||
$defaultCommands = []; |
|||
|
|||
foreach (self::$defaultCommands as $classname) { |
|||
if (class_exists($classname) && is_subclass_of($classname, "think\\console\\Command")) { |
|||
$defaultCommands[] = new $classname(); |
|||
} |
|||
} |
|||
|
|||
return $defaultCommands; |
|||
} |
|||
|
|||
public static function addDefaultCommands(array $classnames) |
|||
{ |
|||
self::$defaultCommands = array_merge(self::$defaultCommands, $classnames); |
|||
} |
|||
|
|||
/** |
|||
* 获取可能的建议 |
|||
* @param array $abbrevs |
|||
* @return string |
|||
*/ |
|||
private function getAbbreviationSuggestions($abbrevs) |
|||
{ |
|||
return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''); |
|||
} |
|||
|
|||
/** |
|||
* 返回命名空间部分 |
|||
* @param string $name 指令 |
|||
* @param string $limit 部分的命名空间的最大数量 |
|||
* @return string |
|||
*/ |
|||
public function extractNamespace($name, $limit = null) |
|||
{ |
|||
$parts = explode(':', $name); |
|||
array_pop($parts); |
|||
|
|||
return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit)); |
|||
} |
|||
|
|||
/** |
|||
* 查找可替代的建议 |
|||
* @param string $name |
|||
* @param array|\Traversable $collection |
|||
* @return array |
|||
*/ |
|||
private function findAlternatives($name, $collection) |
|||
{ |
|||
$threshold = 1e3; |
|||
$alternatives = []; |
|||
|
|||
$collectionParts = []; |
|||
foreach ($collection as $item) { |
|||
$collectionParts[$item] = explode(':', $item); |
|||
} |
|||
|
|||
foreach (explode(':', $name) as $i => $subname) { |
|||
foreach ($collectionParts as $collectionName => $parts) { |
|||
$exists = isset($alternatives[$collectionName]); |
|||
if (!isset($parts[$i]) && $exists) { |
|||
$alternatives[$collectionName] += $threshold; |
|||
continue; |
|||
} elseif (!isset($parts[$i])) { |
|||
continue; |
|||
} |
|||
|
|||
$lev = levenshtein($subname, $parts[$i]); |
|||
if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) { |
|||
$alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev; |
|||
} elseif ($exists) { |
|||
$alternatives[$collectionName] += $threshold; |
|||
} |
|||
} |
|||
} |
|||
|
|||
foreach ($collection as $item) { |
|||
$lev = levenshtein($name, $item); |
|||
if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { |
|||
$alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; |
|||
} |
|||
} |
|||
|
|||
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) { |
|||
return $lev < 2 * $threshold; |
|||
}); |
|||
asort($alternatives); |
|||
|
|||
return array_keys($alternatives); |
|||
} |
|||
|
|||
/** |
|||
* 设置默认的指令 |
|||
* @param string $commandName The Command name |
|||
*/ |
|||
public function setDefaultCommand($commandName) |
|||
{ |
|||
$this->defaultCommand = $commandName; |
|||
} |
|||
|
|||
/** |
|||
* 返回所有的命名空间 |
|||
* @param string $name |
|||
* @return array |
|||
*/ |
|||
private function extractAllNamespaces($name) |
|||
{ |
|||
$parts = explode(':', $name, -1); |
|||
$namespaces = []; |
|||
|
|||
foreach ($parts as $part) { |
|||
if (count($namespaces)) { |
|||
$namespaces[] = end($namespaces) . ':' . $part; |
|||
} else { |
|||
$namespaces[] = $part; |
|||
} |
|||
} |
|||
|
|||
return $namespaces; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,212 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
\think\Loader::import('controller/Jump', TRAIT_PATH, EXT); |
|||
|
|||
use think\exception\ValidateException; |
|||
|
|||
class Controller |
|||
{ |
|||
use \traits\controller\Jump; |
|||
|
|||
/** |
|||
* @var \think\View 视图类实例 |
|||
*/ |
|||
protected $view; |
|||
/** |
|||
* @var \think\Request Request实例 |
|||
*/ |
|||
protected $request; |
|||
// 验证失败是否抛出异常 |
|||
protected $failException = false; |
|||
// 是否批量验证 |
|||
protected $batchValidate = false; |
|||
|
|||
/** |
|||
* 前置操作方法列表 |
|||
* @var array $beforeActionList |
|||
* @access protected |
|||
*/ |
|||
protected $beforeActionList = []; |
|||
|
|||
/** |
|||
* 构造方法 |
|||
* @param Request $request Request对象 |
|||
* @access public |
|||
*/ |
|||
public function __construct(Request $request = null) |
|||
{ |
|||
if (is_null($request)) { |
|||
$request = Request::instance(); |
|||
} |
|||
$this->view = View::instance(Config::get('template'), Config::get('view_replace_str')); |
|||
$this->request = $request; |
|||
|
|||
// 控制器初始化 |
|||
$this->_initialize(); |
|||
|
|||
// 前置操作方法 |
|||
if ($this->beforeActionList) { |
|||
foreach ($this->beforeActionList as $method => $options) { |
|||
is_numeric($method) ? |
|||
$this->beforeAction($options) : |
|||
$this->beforeAction($method, $options); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 初始化 |
|||
protected function _initialize() |
|||
{ |
|||
} |
|||
|
|||
/** |
|||
* 前置操作 |
|||
* @access protected |
|||
* @param string $method 前置操作方法名 |
|||
* @param array $options 调用参数 ['only'=>[...]] 或者['except'=>[...]] |
|||
*/ |
|||
protected function beforeAction($method, $options = []) |
|||
{ |
|||
if (isset($options['only'])) { |
|||
if (is_string($options['only'])) { |
|||
$options['only'] = explode(',', $options['only']); |
|||
} |
|||
if (!in_array($this->request->action(), $options['only'])) { |
|||
return; |
|||
} |
|||
} elseif (isset($options['except'])) { |
|||
if (is_string($options['except'])) { |
|||
$options['except'] = explode(',', $options['except']); |
|||
} |
|||
if (in_array($this->request->action(), $options['except'])) { |
|||
return; |
|||
} |
|||
} |
|||
|
|||
call_user_func([$this, $method]); |
|||
} |
|||
|
|||
/** |
|||
* 加载模板输出 |
|||
* @access protected |
|||
* @param string $template 模板文件名 |
|||
* @param array $vars 模板输出变量 |
|||
* @param array $replace 模板替换 |
|||
* @param array $config 模板参数 |
|||
* @return mixed |
|||
*/ |
|||
protected function fetch($template = '', $vars = [], $replace = [], $config = []) |
|||
{ |
|||
return $this->view->fetch($template, $vars, $replace, $config); |
|||
} |
|||
|
|||
/** |
|||
* 渲染内容输出 |
|||
* @access protected |
|||
* @param string $content 模板内容 |
|||
* @param array $vars 模板输出变量 |
|||
* @param array $replace 替换内容 |
|||
* @param array $config 模板参数 |
|||
* @return mixed |
|||
*/ |
|||
protected function display($content = '', $vars = [], $replace = [], $config = []) |
|||
{ |
|||
return $this->view->display($content, $vars, $replace, $config); |
|||
} |
|||
|
|||
/** |
|||
* 模板变量赋值 |
|||
* @access protected |
|||
* @param mixed $name 要显示的模板变量 |
|||
* @param mixed $value 变量的值 |
|||
* @return void |
|||
*/ |
|||
protected function assign($name, $value = '') |
|||
{ |
|||
$this->view->assign($name, $value); |
|||
} |
|||
|
|||
/** |
|||
* 初始化模板引擎 |
|||
* @access protected |
|||
* @param array|string $engine 引擎参数 |
|||
* @return void |
|||
*/ |
|||
protected function engine($engine) |
|||
{ |
|||
$this->view->engine($engine); |
|||
} |
|||
|
|||
/** |
|||
* 设置验证失败后是否抛出异常 |
|||
* @access protected |
|||
* @param bool $fail 是否抛出异常 |
|||
* @return $this |
|||
*/ |
|||
protected function validateFailException($fail = true) |
|||
{ |
|||
$this->failException = $fail; |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 验证数据 |
|||
* @access protected |
|||
* @param array $data 数据 |
|||
* @param string|array $validate 验证器名或者验证规则数组 |
|||
* @param array $message 提示信息 |
|||
* @param bool $batch 是否批量验证 |
|||
* @param mixed $callback 回调方法(闭包) |
|||
* @return array|string|true |
|||
* @throws ValidateException |
|||
*/ |
|||
protected function validate($data, $validate, $message = [], $batch = false, $callback = null) |
|||
{ |
|||
if (is_array($validate)) { |
|||
$v = Loader::validate(); |
|||
$v->rule($validate); |
|||
} else { |
|||
if (strpos($validate, '.')) { |
|||
// 支持场景 |
|||
list($validate, $scene) = explode('.', $validate); |
|||
} |
|||
$v = Loader::validate($validate); |
|||
if (!empty($scene)) { |
|||
$v->scene($scene); |
|||
} |
|||
} |
|||
// 是否批量验证 |
|||
if ($batch || $this->batchValidate) { |
|||
$v->batch(true); |
|||
} |
|||
|
|||
if (is_array($message)) { |
|||
$v->message($message); |
|||
} |
|||
|
|||
if ($callback && is_callable($callback)) { |
|||
call_user_func_array($callback, [$v, &$data]); |
|||
} |
|||
|
|||
if (!$v->check($data)) { |
|||
if ($this->failException) { |
|||
throw new ValidateException($v->getError()); |
|||
} else { |
|||
return $v->getError(); |
|||
} |
|||
} else { |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,211 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
class Cookie |
|||
{ |
|||
protected static $config = [ |
|||
// cookie 名称前缀 |
|||
'prefix' => '', |
|||
// cookie 保存时间 |
|||
'expire' => 0, |
|||
// cookie 保存路径 |
|||
'path' => '/', |
|||
// cookie 有效域名 |
|||
'domain' => '', |
|||
// cookie 启用安全传输 |
|||
'secure' => false, |
|||
// httponly设置 |
|||
'httponly' => '', |
|||
// 是否使用 setcookie |
|||
'setcookie' => true, |
|||
]; |
|||
|
|||
protected static $init; |
|||
|
|||
/** |
|||
* Cookie初始化 |
|||
* @param array $config |
|||
* @return void |
|||
*/ |
|||
public static function init(array $config = []) |
|||
{ |
|||
if (empty($config)) { |
|||
$config = Config::get('cookie'); |
|||
} |
|||
self::$config = array_merge(self::$config, array_change_key_case($config)); |
|||
if (!empty(self::$config['httponly'])) { |
|||
ini_set('session.cookie_httponly', 1); |
|||
} |
|||
self::$init = true; |
|||
} |
|||
|
|||
/** |
|||
* 设置或者获取cookie作用域(前缀) |
|||
* @param string $prefix |
|||
* @return string|void |
|||
*/ |
|||
public static function prefix($prefix = '') |
|||
{ |
|||
if (empty($prefix)) { |
|||
return self::$config['prefix']; |
|||
} |
|||
self::$config['prefix'] = $prefix; |
|||
} |
|||
|
|||
/** |
|||
* Cookie 设置、获取、删除 |
|||
* |
|||
* @param string $name cookie名称 |
|||
* @param mixed $value cookie值 |
|||
* @param mixed $option 可选参数 可能会是 null|integer|string |
|||
* |
|||
* @return mixed |
|||
* @internal param mixed $options cookie参数 |
|||
*/ |
|||
public static function set($name, $value = '', $option = null) |
|||
{ |
|||
!isset(self::$init) && self::init(); |
|||
// 参数设置(会覆盖黙认设置) |
|||
if (!is_null($option)) { |
|||
if (is_numeric($option)) { |
|||
$option = ['expire' => $option]; |
|||
} elseif (is_string($option)) { |
|||
parse_str($option, $option); |
|||
} |
|||
$config = array_merge(self::$config, array_change_key_case($option)); |
|||
} else { |
|||
$config = self::$config; |
|||
} |
|||
$name = $config['prefix'] . $name; |
|||
// 设置cookie |
|||
if (is_array($value)) { |
|||
array_walk_recursive($value, 'self::jsonFormatProtect', 'encode'); |
|||
$value = 'think:' . json_encode($value); |
|||
} |
|||
$expire = !empty($config['expire']) ? $_SERVER['REQUEST_TIME'] + intval($config['expire']) : 0; |
|||
if ($config['setcookie']) { |
|||
setcookie($name, $value, $expire, $config['path'], $config['domain'], $config['secure'], $config['httponly']); |
|||
} |
|||
$_COOKIE[$name] = $value; |
|||
} |
|||
|
|||
/** |
|||
* 永久保存Cookie数据 |
|||
* @param string $name cookie名称 |
|||
* @param mixed $value cookie值 |
|||
* @param mixed $option 可选参数 可能会是 null|integer|string |
|||
* @return void |
|||
*/ |
|||
public static function forever($name, $value = '', $option = null) |
|||
{ |
|||
if (is_null($option) || is_numeric($option)) { |
|||
$option = []; |
|||
} |
|||
$option['expire'] = 315360000; |
|||
self::set($name, $value, $option); |
|||
} |
|||
|
|||
/** |
|||
* 判断Cookie数据 |
|||
* @param string $name cookie名称 |
|||
* @param string|null $prefix cookie前缀 |
|||
* @return bool |
|||
*/ |
|||
public static function has($name, $prefix = null) |
|||
{ |
|||
!isset(self::$init) && self::init(); |
|||
$prefix = !is_null($prefix) ? $prefix : self::$config['prefix']; |
|||
$name = $prefix . $name; |
|||
return isset($_COOKIE[$name]); |
|||
} |
|||
|
|||
/** |
|||
* Cookie获取 |
|||
* @param string $name cookie名称 |
|||
* @param string|null $prefix cookie前缀 |
|||
* @return mixed |
|||
*/ |
|||
public static function get($name, $prefix = null) |
|||
{ |
|||
!isset(self::$init) && self::init(); |
|||
$prefix = !is_null($prefix) ? $prefix : self::$config['prefix']; |
|||
$name = $prefix . $name; |
|||
if (isset($_COOKIE[$name])) { |
|||
$value = $_COOKIE[$name]; |
|||
if (0 === strpos($value, 'think:')) { |
|||
$value = substr($value, 6); |
|||
$value = json_decode($value, true); |
|||
array_walk_recursive($value, 'self::jsonFormatProtect', 'decode'); |
|||
} |
|||
return $value; |
|||
} else { |
|||
return; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Cookie删除 |
|||
* @param string $name cookie名称 |
|||
* @param string|null $prefix cookie前缀 |
|||
* @return mixed |
|||
*/ |
|||
public static function delete($name, $prefix = null) |
|||
{ |
|||
!isset(self::$init) && self::init(); |
|||
$config = self::$config; |
|||
$prefix = !is_null($prefix) ? $prefix : $config['prefix']; |
|||
$name = $prefix . $name; |
|||
if ($config['setcookie']) { |
|||
setcookie($name, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'], $config['domain'], $config['secure'], $config['httponly']); |
|||
} |
|||
// 删除指定cookie |
|||
unset($_COOKIE[$name]); |
|||
} |
|||
|
|||
/** |
|||
* Cookie清空 |
|||
* @param string|null $prefix cookie前缀 |
|||
* @return mixed |
|||
*/ |
|||
public static function clear($prefix = null) |
|||
{ |
|||
// 清除指定前缀的所有cookie |
|||
if (empty($_COOKIE)) { |
|||
return; |
|||
} |
|||
!isset(self::$init) && self::init(); |
|||
// 要删除的cookie前缀,不指定则删除config设置的指定前缀 |
|||
$config = self::$config; |
|||
$prefix = !is_null($prefix) ? $prefix : $config['prefix']; |
|||
if ($prefix) { |
|||
// 如果前缀为空字符串将不作处理直接返回 |
|||
foreach ($_COOKIE as $key => $val) { |
|||
if (0 === strpos($key, $prefix)) { |
|||
if ($config['setcookie']) { |
|||
setcookie($key, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'], $config['domain'], $config['secure'], $config['httponly']); |
|||
} |
|||
unset($_COOKIE[$key]); |
|||
} |
|||
} |
|||
} |
|||
return; |
|||
} |
|||
|
|||
private static function jsonFormatProtect(&$val, $key, $type = 'encode') |
|||
{ |
|||
if (!empty($val) && true !== $val) { |
|||
$val = 'decode' == $type ? urldecode($val) : urlencode($val); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,151 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
use think\db\Connection; |
|||
use think\db\Query; |
|||
|
|||
/** |
|||
* Class Db |
|||
* @package think |
|||
* @method Query table(string $table) static 指定数据表(含前缀) |
|||
* @method Query name(string $name) static 指定数据表(不含前缀) |
|||
* @method Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件 |
|||
* @method Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询 |
|||
* @method Query union(mixed $union, boolean $all = false) static UNION查询 |
|||
* @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT |
|||
* @method Query order(mixed $field, string $order = null) static 查询ORDER |
|||
* @method Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存 |
|||
* @method mixed value(string $field) static 获取某个字段的值 |
|||
* @method array column(string $field, string $key = '') static 获取某个列的值 |
|||
* @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询 |
|||
* @method mixed find(mixed $data = null) static 查询单个记录 |
|||
* @method mixed select(mixed $data = null) static 查询多个记录 |
|||
* @method integer insert(array $data, boolean $replace = false, boolean $getLastInsID = false, string $sequence = null) static 插入一条记录 |
|||
* @method integer insertGetId(array $data, boolean $replace = false, string $sequence = null) static 插入一条记录并返回自增ID |
|||
* @method integer insertAll(array $dataSet) static 插入多条记录 |
|||
* @method integer update(array $data) static 更新记录 |
|||
* @method integer delete(mixed $data = null) static 删除记录 |
|||
* @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据 |
|||
* @method mixed query(string $sql, array $bind = [], boolean $fetch = false, boolean $master = false, mixed $class = null) static SQL查询 |
|||
* @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行 |
|||
* @method Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询 |
|||
* @method mixed transaction(callable $callback) static 执行数据库事务 |
|||
* @method void startTrans() static 启动事务 |
|||
* @method void commit() static 用于非自动提交状态下面的查询提交 |
|||
* @method void rollback() static 事务回滚 |
|||
* @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句 |
|||
*/ |
|||
class Db |
|||
{ |
|||
// 数据库连接实例 |
|||
private static $instance = []; |
|||
// 查询次数 |
|||
public static $queryTimes = 0; |
|||
// 执行次数 |
|||
public static $executeTimes = 0; |
|||
|
|||
/** |
|||
* 数据库初始化 并取得数据库类实例 |
|||
* @static |
|||
* @access public |
|||
* @param mixed $config 连接配置 |
|||
* @param bool|string $name 连接标识 true 强制重新连接 |
|||
* @return Connection |
|||
* @throws Exception |
|||
*/ |
|||
public static function connect($config = [], $name = false) |
|||
{ |
|||
if (false === $name) { |
|||
$name = md5(serialize($config)); |
|||
} |
|||
if (true === $name || !isset(self::$instance[$name])) { |
|||
// 解析连接参数 支持数组和字符串 |
|||
$options = self::parseConfig($config); |
|||
if (empty($options['type'])) { |
|||
throw new \InvalidArgumentException('Underfined db type'); |
|||
} |
|||
$class = false !== strpos($options['type'], '\\') ? $options['type'] : '\\think\\db\\connector\\' . ucwords($options['type']); |
|||
// 记录初始化信息 |
|||
if (App::$debug) { |
|||
Log::record('[ DB ] INIT ' . $options['type'], 'info'); |
|||
} |
|||
if (true === $name) { |
|||
return new $class($options); |
|||
} else { |
|||
self::$instance[$name] = new $class($options); |
|||
} |
|||
} |
|||
return self::$instance[$name]; |
|||
} |
|||
|
|||
/** |
|||
* 数据库连接参数解析 |
|||
* @static |
|||
* @access private |
|||
* @param mixed $config |
|||
* @return array |
|||
*/ |
|||
private static function parseConfig($config) |
|||
{ |
|||
if (empty($config)) { |
|||
$config = Config::get('database'); |
|||
} elseif (is_string($config) && false === strpos($config, '/')) { |
|||
// 支持读取配置参数 |
|||
$config = Config::get($config); |
|||
} |
|||
if (is_string($config)) { |
|||
return self::parseDsn($config); |
|||
} else { |
|||
return $config; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* DSN解析 |
|||
* 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1¶m2=val2#utf8 |
|||
* @static |
|||
* @access private |
|||
* @param string $dsnStr |
|||
* @return array |
|||
*/ |
|||
private static function parseDsn($dsnStr) |
|||
{ |
|||
$info = parse_url($dsnStr); |
|||
if (!$info) { |
|||
return []; |
|||
} |
|||
$dsn = [ |
|||
'type' => $info['scheme'], |
|||
'username' => isset($info['user']) ? $info['user'] : '', |
|||
'password' => isset($info['pass']) ? $info['pass'] : '', |
|||
'hostname' => isset($info['host']) ? $info['host'] : '', |
|||
'hostport' => isset($info['port']) ? $info['port'] : '', |
|||
'database' => !empty($info['path']) ? ltrim($info['path'], '/') : '', |
|||
'charset' => isset($info['fragment']) ? $info['fragment'] : 'utf8', |
|||
]; |
|||
|
|||
if (isset($info['query'])) { |
|||
parse_str($info['query'], $dsn['params']); |
|||
} else { |
|||
$dsn['params'] = []; |
|||
} |
|||
return $dsn; |
|||
} |
|||
|
|||
// 调用驱动类的方法 |
|||
public static function __callStatic($method, $params) |
|||
{ |
|||
// 自动初始化数据库 |
|||
return call_user_func_array([self::connect(), $method], $params); |
|||
} |
|||
} |
|||
@ -0,0 +1,212 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
use think\exception\ClassNotFoundException; |
|||
use think\response\Redirect; |
|||
|
|||
class Debug |
|||
{ |
|||
// 区间时间信息 |
|||
protected static $info = []; |
|||
// 区间内存信息 |
|||
protected static $mem = []; |
|||
|
|||
/** |
|||
* 记录时间(微秒)和内存使用情况 |
|||
* @param string $name 标记位置 |
|||
* @param mixed $value 标记值 留空则取当前 time 表示仅记录时间 否则同时记录时间和内存 |
|||
* @return mixed |
|||
*/ |
|||
public static function remark($name, $value = '') |
|||
{ |
|||
// 记录时间和内存使用 |
|||
self::$info[$name] = is_float($value) ? $value : microtime(true); |
|||
if ('time' != $value) { |
|||
self::$mem['mem'][$name] = is_float($value) ? $value : memory_get_usage(); |
|||
self::$mem['peak'][$name] = memory_get_peak_usage(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 统计某个区间的时间(微秒)使用情况 |
|||
* @param string $start 开始标签 |
|||
* @param string $end 结束标签 |
|||
* @param integer|string $dec 小数位 |
|||
* @return integer |
|||
*/ |
|||
public static function getRangeTime($start, $end, $dec = 6) |
|||
{ |
|||
if (!isset(self::$info[$end])) { |
|||
self::$info[$end] = microtime(true); |
|||
} |
|||
return number_format((self::$info[$end] - self::$info[$start]), $dec); |
|||
} |
|||
|
|||
/** |
|||
* 统计从开始到统计时的时间(微秒)使用情况 |
|||
* @param integer|string $dec 小数位 |
|||
* @return integer |
|||
*/ |
|||
public static function getUseTime($dec = 6) |
|||
{ |
|||
return number_format((microtime(true) - THINK_START_TIME), $dec); |
|||
} |
|||
|
|||
/** |
|||
* 获取当前访问的吞吐率情况 |
|||
* @return string |
|||
*/ |
|||
public static function getThroughputRate() |
|||
{ |
|||
return number_format(1 / self::getUseTime(), 2) . 'req/s'; |
|||
} |
|||
|
|||
/** |
|||
* 记录区间的内存使用情况 |
|||
* @param string $start 开始标签 |
|||
* @param string $end 结束标签 |
|||
* @param integer|string $dec 小数位 |
|||
* @return string |
|||
*/ |
|||
public static function getRangeMem($start, $end, $dec = 2) |
|||
{ |
|||
if (!isset(self::$mem['mem'][$end])) { |
|||
self::$mem['mem'][$end] = memory_get_usage(); |
|||
} |
|||
$size = self::$mem['mem'][$end] - self::$mem['mem'][$start]; |
|||
$a = ['B', 'KB', 'MB', 'GB', 'TB']; |
|||
$pos = 0; |
|||
while ($size >= 1024) { |
|||
$size /= 1024; |
|||
$pos++; |
|||
} |
|||
return round($size, $dec) . " " . $a[$pos]; |
|||
} |
|||
|
|||
/** |
|||
* 统计从开始到统计时的内存使用情况 |
|||
* @param integer|string $dec 小数位 |
|||
* @return string |
|||
*/ |
|||
public static function getUseMem($dec = 2) |
|||
{ |
|||
$size = memory_get_usage() - THINK_START_MEM; |
|||
$a = ['B', 'KB', 'MB', 'GB', 'TB']; |
|||
$pos = 0; |
|||
while ($size >= 1024) { |
|||
$size /= 1024; |
|||
$pos++; |
|||
} |
|||
return round($size, $dec) . " " . $a[$pos]; |
|||
} |
|||
|
|||
/** |
|||
* 统计区间的内存峰值情况 |
|||
* @param string $start 开始标签 |
|||
* @param string $end 结束标签 |
|||
* @param integer|string $dec 小数位 |
|||
* @return mixed |
|||
*/ |
|||
public static function getMemPeak($start, $end, $dec = 2) |
|||
{ |
|||
if (!isset(self::$mem['peak'][$end])) { |
|||
self::$mem['peak'][$end] = memory_get_peak_usage(); |
|||
} |
|||
$size = self::$mem['peak'][$end] - self::$mem['peak'][$start]; |
|||
$a = ['B', 'KB', 'MB', 'GB', 'TB']; |
|||
$pos = 0; |
|||
while ($size >= 1024) { |
|||
$size /= 1024; |
|||
$pos++; |
|||
} |
|||
return round($size, $dec) . " " . $a[$pos]; |
|||
} |
|||
|
|||
/** |
|||
* 获取文件加载信息 |
|||
* @param bool $detail 是否显示详细 |
|||
* @return integer|array |
|||
*/ |
|||
public static function getFile($detail = false) |
|||
{ |
|||
if ($detail) { |
|||
$files = get_included_files(); |
|||
$info = []; |
|||
foreach ($files as $key => $file) { |
|||
$info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )'; |
|||
} |
|||
return $info; |
|||
} |
|||
return count(get_included_files()); |
|||
} |
|||
|
|||
/** |
|||
* 浏览器友好的变量输出 |
|||
* @param mixed $var 变量 |
|||
* @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串 |
|||
* @param string $label 标签 默认为空 |
|||
* @param integer $flags htmlspecialchars flags |
|||
* @return void|string |
|||
*/ |
|||
public static function dump($var, $echo = true, $label = null, $flags = ENT_SUBSTITUTE) |
|||
{ |
|||
$label = (null === $label) ? '' : rtrim($label) . ':'; |
|||
ob_start(); |
|||
var_dump($var); |
|||
$output = ob_get_clean(); |
|||
$output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output); |
|||
if (IS_CLI) { |
|||
$output = PHP_EOL . $label . $output . PHP_EOL; |
|||
} else { |
|||
if (!extension_loaded('xdebug')) { |
|||
$output = htmlspecialchars($output, $flags); |
|||
} |
|||
$output = '<pre>' . $label . $output . '</pre>'; |
|||
} |
|||
if ($echo) { |
|||
echo($output); |
|||
return; |
|||
} else { |
|||
return $output; |
|||
} |
|||
} |
|||
|
|||
public static function inject(Response $response, &$content) |
|||
{ |
|||
$config = Config::get('trace'); |
|||
$type = isset($config['type']) ? $config['type'] : 'Html'; |
|||
$request = Request::instance(); |
|||
$class = false !== strpos($type, '\\') ? $type : '\\think\\debug\\' . ucwords($type); |
|||
unset($config['type']); |
|||
if (class_exists($class)) { |
|||
$trace = new $class($config); |
|||
} else { |
|||
throw new ClassNotFoundException('class not exists:' . $class, $class); |
|||
} |
|||
|
|||
if ($response instanceof Redirect) { |
|||
//TODO 记录 |
|||
} else { |
|||
$output = $trace->output($response, Log::getLog()); |
|||
if (is_string($output)) { |
|||
// trace调试信息注入 |
|||
$pos = strripos($content, '</body>'); |
|||
if (false !== $pos) { |
|||
$content = substr($content, 0, $pos) . $output . substr($content, $pos); |
|||
} else { |
|||
$content = $content . $output; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
class Env |
|||
{ |
|||
/** |
|||
* 获取环境变量值 |
|||
* @param string $name 环境变量名(支持二级 .号分割) |
|||
* @param string $default 默认值 |
|||
* @return mixed |
|||
*/ |
|||
public static function get($name, $default = null) |
|||
{ |
|||
$result = getenv(ENV_PREFIX . strtoupper(str_replace('.', '_', $name))); |
|||
if (false !== $result) { |
|||
return $result; |
|||
} else { |
|||
return $default; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,117 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
use think\console\Output as ConsoleOutput; |
|||
use think\exception\ErrorException; |
|||
use think\exception\Handle; |
|||
use think\exception\ThrowableError; |
|||
|
|||
class Error |
|||
{ |
|||
/** |
|||
* 注册异常处理 |
|||
* @return void |
|||
*/ |
|||
public static function register() |
|||
{ |
|||
error_reporting(E_ALL); |
|||
set_error_handler([__CLASS__, 'appError']); |
|||
set_exception_handler([__CLASS__, 'appException']); |
|||
register_shutdown_function([__CLASS__, 'appShutdown']); |
|||
} |
|||
|
|||
/** |
|||
* Exception Handler |
|||
* @param \Exception|\Throwable $e |
|||
*/ |
|||
public static function appException($e) |
|||
{ |
|||
if (!$e instanceof \Exception) { |
|||
$e = new ThrowableError($e); |
|||
} |
|||
|
|||
self::getExceptionHandler()->report($e); |
|||
if (IS_CLI) { |
|||
self::getExceptionHandler()->renderForConsole(new ConsoleOutput, $e); |
|||
} else { |
|||
self::getExceptionHandler()->render($e)->send(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Error Handler |
|||
* @param integer $errno 错误编号 |
|||
* @param integer $errstr 详细错误信息 |
|||
* @param string $errfile 出错的文件 |
|||
* @param integer $errline 出错行号 |
|||
* @param array $errcontext |
|||
* @throws ErrorException |
|||
*/ |
|||
public static function appError($errno, $errstr, $errfile = '', $errline = 0, $errcontext = []) |
|||
{ |
|||
$exception = new ErrorException($errno, $errstr, $errfile, $errline, $errcontext); |
|||
if (error_reporting() & $errno) { |
|||
// 将错误信息托管至 think\exception\ErrorException |
|||
throw $exception; |
|||
} else { |
|||
self::getExceptionHandler()->report($exception); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Shutdown Handler |
|||
*/ |
|||
public static function appShutdown() |
|||
{ |
|||
if (!is_null($error = error_get_last()) && self::isFatal($error['type'])) { |
|||
// 将错误信息托管至think\ErrorException |
|||
$exception = new ErrorException($error['type'], $error['message'], $error['file'], $error['line']); |
|||
|
|||
self::appException($exception); |
|||
} |
|||
|
|||
// 写入日志 |
|||
Log::save(); |
|||
} |
|||
|
|||
/** |
|||
* 确定错误类型是否致命 |
|||
* |
|||
* @param int $type |
|||
* @return bool |
|||
*/ |
|||
protected static function isFatal($type) |
|||
{ |
|||
return in_array($type, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]); |
|||
} |
|||
|
|||
/** |
|||
* Get an instance of the exception handler. |
|||
* |
|||
* @return Handle |
|||
*/ |
|||
public static function getExceptionHandler() |
|||
{ |
|||
static $handle; |
|||
if (!$handle) { |
|||
// 异常处理handle |
|||
$class = Config::get('exception_handle'); |
|||
if ($class && class_exists($class) && is_subclass_of($class, "\\think\\exception\\Handle")) { |
|||
$handle = new $class; |
|||
} else { |
|||
$handle = new Handle; |
|||
} |
|||
} |
|||
return $handle; |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
class Exception extends \Exception |
|||
{ |
|||
|
|||
/** |
|||
* 保存异常页面显示的额外Debug数据 |
|||
* @var array |
|||
*/ |
|||
protected $data = []; |
|||
|
|||
/** |
|||
* 设置异常额外的Debug数据 |
|||
* 数据将会显示为下面的格式 |
|||
* |
|||
* Exception Data |
|||
* -------------------------------------------------- |
|||
* Label 1 |
|||
* key1 value1 |
|||
* key2 value2 |
|||
* Label 2 |
|||
* key1 value1 |
|||
* key2 value2 |
|||
* |
|||
* @param string $label 数据分类,用于异常页面显示 |
|||
* @param array $data 需要显示的数据,必须为关联数组 |
|||
*/ |
|||
final protected function setData($label, array $data) |
|||
{ |
|||
$this->data[$label] = $data; |
|||
} |
|||
|
|||
/** |
|||
* 获取异常额外Debug数据 |
|||
* 主要用于输出到异常页面便于调试 |
|||
* @return array 由setData设置的Debug数据 |
|||
*/ |
|||
final public function getData() |
|||
{ |
|||
return $this->data; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,422 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
use SplFileInfo; |
|||
use SplFileObject; |
|||
|
|||
class File extends SplFileObject |
|||
{ |
|||
/** |
|||
* 错误信息 |
|||
* @var string |
|||
*/ |
|||
private $error = ''; |
|||
// 当前完整文件名 |
|||
protected $filename; |
|||
// 上传文件名 |
|||
protected $saveName; |
|||
// 文件上传命名规则 |
|||
protected $rule = 'date'; |
|||
// 文件上传验证规则 |
|||
protected $validate = []; |
|||
// 单元测试 |
|||
protected $isTest; |
|||
// 上传文件信息 |
|||
protected $info; |
|||
// 文件hash信息 |
|||
protected $hash = []; |
|||
|
|||
public function __construct($filename, $mode = 'r') |
|||
{ |
|||
parent::__construct($filename, $mode); |
|||
$this->filename = $this->getRealPath() ?: $this->getPathname(); |
|||
} |
|||
|
|||
/** |
|||
* 是否测试 |
|||
* @param bool $test 是否测试 |
|||
* @return $this |
|||
*/ |
|||
public function isTest($test = false) |
|||
{ |
|||
$this->isTest = $test; |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 设置上传信息 |
|||
* @param array $info 上传文件信息 |
|||
* @return $this |
|||
*/ |
|||
public function setUploadInfo($info) |
|||
{ |
|||
$this->info = $info; |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 获取上传文件的信息 |
|||
* @param string $name |
|||
* @return array|string |
|||
*/ |
|||
public function getInfo($name = '') |
|||
{ |
|||
return isset($this->info[$name]) ? $this->info[$name] : $this->info; |
|||
} |
|||
|
|||
/** |
|||
* 获取上传文件的文件名 |
|||
* @return string |
|||
*/ |
|||
public function getSaveName() |
|||
{ |
|||
return $this->saveName; |
|||
} |
|||
|
|||
/** |
|||
* 设置上传文件的保存文件名 |
|||
* @param string $saveName |
|||
* @return $this |
|||
*/ |
|||
public function setSaveName($saveName) |
|||
{ |
|||
$this->saveName = $saveName; |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 获取文件的哈希散列值 |
|||
* @return $string |
|||
*/ |
|||
public function hash($type = 'sha1') |
|||
{ |
|||
if (!isset($this->hash[$type])) { |
|||
$this->hash[$type] = hash_file($type, $this->filename); |
|||
} |
|||
return $this->hash[$type]; |
|||
} |
|||
|
|||
/** |
|||
* 检查目录是否可写 |
|||
* @param string $path 目录 |
|||
* @return boolean |
|||
*/ |
|||
protected function checkPath($path) |
|||
{ |
|||
if (is_dir($path)) { |
|||
return true; |
|||
} |
|||
|
|||
if (mkdir($path, 0755, true)) { |
|||
return true; |
|||
} else { |
|||
$this->error = "目录 {$path} 创建失败!"; |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取文件类型信息 |
|||
* @return string |
|||
*/ |
|||
public function getMime() |
|||
{ |
|||
$finfo = finfo_open(FILEINFO_MIME_TYPE); |
|||
return finfo_file($finfo, $this->filename); |
|||
} |
|||
|
|||
/** |
|||
* 设置文件的命名规则 |
|||
* @param string $rule 文件命名规则 |
|||
* @return $this |
|||
*/ |
|||
public function rule($rule) |
|||
{ |
|||
$this->rule = $rule; |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 设置上传文件的验证规则 |
|||
* @param array $rule 验证规则 |
|||
* @return $this |
|||
*/ |
|||
public function validate($rule = []) |
|||
{ |
|||
$this->validate = $rule; |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 检测是否合法的上传文件 |
|||
* @return bool |
|||
*/ |
|||
public function isValid() |
|||
{ |
|||
if ($this->isTest) { |
|||
return is_file($this->filename); |
|||
} |
|||
return is_uploaded_file($this->filename); |
|||
} |
|||
|
|||
/** |
|||
* 检测上传文件 |
|||
* @param array $rule 验证规则 |
|||
* @return bool |
|||
*/ |
|||
public function check($rule = []) |
|||
{ |
|||
$rule = $rule ?: $this->validate; |
|||
|
|||
/* 检查文件大小 */ |
|||
if (isset($rule['size']) && !$this->checkSize($rule['size'])) { |
|||
$this->error = '上传文件大小不符!'; |
|||
return false; |
|||
} |
|||
|
|||
/* 检查文件Mime类型 */ |
|||
if (isset($rule['type']) && !$this->checkMime($rule['type'])) { |
|||
$this->error = '上传文件MIME类型不允许!'; |
|||
return false; |
|||
} |
|||
|
|||
/* 检查文件后缀 */ |
|||
if (isset($rule['ext']) && !$this->checkExt($rule['ext'])) { |
|||
$this->error = '上传文件后缀不允许'; |
|||
return false; |
|||
} |
|||
|
|||
/* 检查图像文件 */ |
|||
if (!$this->checkImg()) { |
|||
$this->error = '非法图像文件!'; |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/** |
|||
* 检测上传文件后缀 |
|||
* @param array|string $ext 允许后缀 |
|||
* @return bool |
|||
*/ |
|||
public function checkExt($ext) |
|||
{ |
|||
if (is_string($ext)) { |
|||
$ext = explode(',', $ext); |
|||
} |
|||
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION)); |
|||
if (!in_array($extension, $ext)) { |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
/** |
|||
* 检测图像文件 |
|||
* @return bool |
|||
*/ |
|||
public function checkImg() |
|||
{ |
|||
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION)); |
|||
/* 对图像文件进行严格检测 */ |
|||
if (in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) && !in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6])) { |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
// 判断图像类型 |
|||
protected function getImageType($image) |
|||
{ |
|||
if (function_exists('exif_imagetype')) { |
|||
return exif_imagetype($image); |
|||
} else { |
|||
$info = getimagesize($image); |
|||
return $info[2]; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 检测上传文件大小 |
|||
* @param integer $size 最大大小 |
|||
* @return bool |
|||
*/ |
|||
public function checkSize($size) |
|||
{ |
|||
if ($this->getSize() > $size) { |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
/** |
|||
* 检测上传文件类型 |
|||
* @param array|string $mime 允许类型 |
|||
* @return bool |
|||
*/ |
|||
public function checkMime($mime) |
|||
{ |
|||
if (is_string($mime)) { |
|||
$mime = explode(',', $mime); |
|||
} |
|||
if (!in_array(strtolower($this->getMime()), $mime)) { |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
/** |
|||
* 移动文件 |
|||
* @param string $path 保存路径 |
|||
* @param string|bool $savename 保存的文件名 默认自动生成 |
|||
* @param boolean $replace 同名文件是否覆盖 |
|||
* @return false|SplFileInfo false-失败 否则返回SplFileInfo实例 |
|||
*/ |
|||
public function move($path, $savename = true, $replace = true) |
|||
{ |
|||
// 文件上传失败,捕获错误代码 |
|||
if (!empty($this->info['error'])) { |
|||
$this->error($this->info['error']); |
|||
return false; |
|||
} |
|||
|
|||
// 检测合法性 |
|||
if (!$this->isValid()) { |
|||
$this->error = '非法上传文件'; |
|||
return false; |
|||
} |
|||
|
|||
// 验证上传 |
|||
if (!$this->check()) { |
|||
return false; |
|||
} |
|||
$path = rtrim($path, DS) . DS; |
|||
// 文件保存命名规则 |
|||
$saveName = $this->buildSaveName($savename); |
|||
$filename = $path . $saveName; |
|||
|
|||
// 检测目录 |
|||
if (false === $this->checkPath(dirname($filename))) { |
|||
return false; |
|||
} |
|||
|
|||
/* 不覆盖同名文件 */ |
|||
if (!$replace && is_file($filename)) { |
|||
$this->error = '存在同名文件' . $filename; |
|||
return false; |
|||
} |
|||
|
|||
/* 移动文件 */ |
|||
if ($this->isTest) { |
|||
rename($this->filename, $filename); |
|||
} elseif (!move_uploaded_file($this->filename, $filename)) { |
|||
$this->error = '文件上传保存错误!'; |
|||
return false; |
|||
} |
|||
// 返回 File对象实例 |
|||
$file = new self($filename); |
|||
$file->setSaveName($saveName); |
|||
$file->setUploadInfo($this->info); |
|||
return $file; |
|||
} |
|||
|
|||
function getGuid() { |
|||
$charid = strtoupper(md5(uniqid(mt_rand(), true))); |
|||
$uuid = substr($charid, 0, 8).substr($charid, 8, 4).substr($charid,12, 4).substr($charid,16, 4).substr($charid,20,12); |
|||
$uuid = str_shuffle($uuid); |
|||
return $uuid; |
|||
} |
|||
|
|||
/** |
|||
* 获取保存文件名 |
|||
* @param string|bool $savename 保存的文件名 默认自动生成 |
|||
* @return string |
|||
*/ |
|||
protected function buildSaveName($savename) |
|||
{ |
|||
if (true === $savename) { |
|||
// 自动生成文件名 |
|||
if ($this->rule instanceof \Closure) { |
|||
$savename = call_user_func_array($this->rule, [$this]); |
|||
} else { |
|||
|
|||
switch ($this->rule) { |
|||
case 'date': |
|||
$savename = date('Ymd') . DS . md5(microtime(true)); |
|||
break; |
|||
case 'md5': |
|||
$savename = md5($this->getGuid()); |
|||
break; |
|||
default: |
|||
if (in_array($this->rule, hash_algos())) { |
|||
$hash = $this->hash($this->rule); |
|||
$savename = substr($hash, 0, 2) . DS . substr($hash, 2); |
|||
} elseif (is_callable($this->rule)) { |
|||
$savename = call_user_func($this->rule); |
|||
} else { |
|||
$savename = date('Ymd') . DS . md5(microtime(true)); |
|||
} |
|||
} |
|||
} |
|||
} elseif ('' === $savename) { |
|||
$savename = $this->getInfo('name'); |
|||
} |
|||
if (!strpos($savename, '.')) { |
|||
$savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION); |
|||
} |
|||
return $savename; |
|||
} |
|||
|
|||
/** |
|||
* 获取错误代码信息 |
|||
* @param int $errorNo 错误号 |
|||
*/ |
|||
private function error($errorNo) |
|||
{ |
|||
switch ($errorNo) { |
|||
case 1: |
|||
case 2: |
|||
$this->error = '上传文件大小超过了最大值!'; |
|||
break; |
|||
case 3: |
|||
$this->error = '文件只有部分被上传!'; |
|||
break; |
|||
case 4: |
|||
$this->error = '没有文件被上传!'; |
|||
break; |
|||
case 6: |
|||
$this->error = '找不到临时文件夹!'; |
|||
break; |
|||
case 7: |
|||
$this->error = '文件写入失败!'; |
|||
break; |
|||
default: |
|||
$this->error = '未知上传错误!'; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取错误信息 |
|||
* @return mixed |
|||
*/ |
|||
public function getError() |
|||
{ |
|||
return $this->error; |
|||
} |
|||
|
|||
public function __call($method, $args) |
|||
{ |
|||
return $this->hash($method); |
|||
} |
|||
} |
|||
@ -0,0 +1,136 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
class Hook |
|||
{ |
|||
|
|||
private static $tags = []; |
|||
|
|||
/** |
|||
* 动态添加行为扩展到某个标签 |
|||
* @param string $tag 标签名称 |
|||
* @param mixed $behavior 行为名称 |
|||
* @param bool $first 是否放到开头执行 |
|||
* @return void |
|||
*/ |
|||
public static function add($tag, $behavior, $first = false) |
|||
{ |
|||
isset(self::$tags[$tag]) || self::$tags[$tag] = []; |
|||
if (is_array($behavior) && !is_callable($behavior)) { |
|||
if (!array_key_exists('_overlay', $behavior) || !$behavior['_overlay']) { |
|||
unset($behavior['_overlay']); |
|||
self::$tags[$tag] = array_merge(self::$tags[$tag], $behavior); |
|||
} else { |
|||
unset($behavior['_overlay']); |
|||
self::$tags[$tag] = $behavior; |
|||
} |
|||
} elseif ($first) { |
|||
array_unshift(self::$tags[$tag], $behavior); |
|||
} else { |
|||
self::$tags[$tag][] = $behavior; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 批量导入插件 |
|||
* @param array $tags 插件信息 |
|||
* @param boolean $recursive 是否递归合并 |
|||
*/ |
|||
public static function import(array $tags, $recursive = true) |
|||
{ |
|||
if ($recursive) { |
|||
foreach ($tags as $tag => $behavior) { |
|||
self::add($tag, $behavior); |
|||
} |
|||
} else { |
|||
self::$tags = $tags + self::$tags; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取插件信息 |
|||
* @param string $tag 插件位置 留空获取全部 |
|||
* @return array |
|||
*/ |
|||
public static function get($tag = '') |
|||
{ |
|||
if (empty($tag)) { |
|||
//获取全部的插件信息 |
|||
return self::$tags; |
|||
} else { |
|||
return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : []; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 监听标签的行为 |
|||
* @param string $tag 标签名称 |
|||
* @param mixed $params 传入参数 |
|||
* @param mixed $extra 额外参数 |
|||
* @param bool $once 只获取一个有效返回值 |
|||
* @return mixed |
|||
*/ |
|||
public static function listen($tag, &$params = null, $extra = null, $once = false) |
|||
{ |
|||
$results = []; |
|||
$tags = static::get($tag); |
|||
foreach ($tags as $key => $name) { |
|||
$results[$key] = self::exec($name, $tag, $params, $extra); |
|||
if (false === $results[$key]) { |
|||
// 如果返回false 则中断行为执行 |
|||
break; |
|||
} elseif (!is_null($results[$key]) && $once) { |
|||
break; |
|||
} |
|||
} |
|||
return $once ? end($results) : $results; |
|||
} |
|||
|
|||
/** |
|||
* 执行某个行为 |
|||
* @param mixed $class 要执行的行为 |
|||
* @param string $tag 方法名(标签名) |
|||
* @param Mixed $params 传人的参数 |
|||
* @param mixed $extra 额外参数 |
|||
* @return mixed |
|||
*/ |
|||
public static function exec($class, $tag = '', &$params = null, $extra = null) |
|||
{ |
|||
App::$debug && Debug::remark('behavior_start', 'time'); |
|||
$method = Loader::parseName($tag, 1, false); |
|||
if ($class instanceof \Closure) { |
|||
$result = call_user_func_array($class, [ & $params, $extra]); |
|||
$class = 'Closure'; |
|||
} elseif (is_array($class)) { |
|||
list($class, $method) = $class; |
|||
|
|||
$result = (new $class())->$method($params, $extra); |
|||
$class = $class . '->' . $method; |
|||
} elseif (is_object($class)) { |
|||
$result = $class->$method($params, $extra); |
|||
$class = get_class($class); |
|||
} elseif (strpos($class, '::')) { |
|||
$result = call_user_func_array($class, [ & $params, $extra]); |
|||
} else { |
|||
$obj = new $class(); |
|||
$method = ($tag && is_callable([$obj, $method])) ? $method : 'run'; |
|||
$result = $obj->$method($params, $extra); |
|||
} |
|||
if (App::$debug) { |
|||
Debug::remark('behavior_end', 'time'); |
|||
Log::record('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info'); |
|||
} |
|||
return $result; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,217 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
class Lang |
|||
{ |
|||
// 语言数据 |
|||
private static $lang = []; |
|||
// 语言作用域 |
|||
private static $range = 'zh-cn'; |
|||
// 语言自动侦测的变量 |
|||
protected static $langDetectVar = 'lang'; |
|||
// 语言Cookie变量 |
|||
protected static $langCookieVar = 'think_var'; |
|||
// 语言Cookie的过期时间 |
|||
protected static $langCookieExpire = 3600; |
|||
// 允许语言列表 |
|||
protected static $allowLangList = []; |
|||
|
|||
// 设定当前的语言 |
|||
public static function range($range = '') |
|||
{ |
|||
if ('' == $range) { |
|||
return self::$range; |
|||
} else { |
|||
self::$range = $range; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 设置语言定义(不区分大小写) |
|||
* @param string|array $name 语言变量 |
|||
* @param string $value 语言值 |
|||
* @param string $range 语言作用域 |
|||
* @return mixed |
|||
*/ |
|||
public static function set($name, $value = null, $range = '') |
|||
{ |
|||
$range = $range ?: self::$range; |
|||
// 批量定义 |
|||
if (!isset(self::$lang[$range])) { |
|||
self::$lang[$range] = []; |
|||
} |
|||
if (is_array($name)) { |
|||
return self::$lang[$range] = array_change_key_case($name) + self::$lang[$range]; |
|||
} else { |
|||
return self::$lang[$range][strtolower($name)] = $value; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 加载语言定义(不区分大小写) |
|||
* @param string $file 语言文件 |
|||
* @param string $range 语言作用域 |
|||
* @return mixed |
|||
*/ |
|||
public static function load($file, $range = '') |
|||
{ |
|||
$range = $range ?: self::$range; |
|||
if (!isset(self::$lang[$range])) { |
|||
self::$lang[$range] = []; |
|||
} |
|||
// 批量定义 |
|||
if (is_string($file)) { |
|||
$file = [$file]; |
|||
} |
|||
$lang = []; |
|||
foreach ($file as $_file) { |
|||
if (is_file($_file)) { |
|||
// 记录加载信息 |
|||
App::$debug && Log::record('[ LANG ] ' . $_file, 'info'); |
|||
$_lang = include $_file; |
|||
if (is_array($_lang)) { |
|||
$lang = array_change_key_case($_lang) + $lang; |
|||
} |
|||
} |
|||
} |
|||
if (!empty($lang)) { |
|||
self::$lang[$range] = $lang + self::$lang[$range]; |
|||
} |
|||
return self::$lang[$range]; |
|||
} |
|||
|
|||
/** |
|||
* 获取语言定义(不区分大小写) |
|||
* @param string|null $name 语言变量 |
|||
* @param array $vars 变量替换 |
|||
* @param string $range 语言作用域 |
|||
* @return mixed |
|||
*/ |
|||
public static function has($name, $range = '') |
|||
{ |
|||
$range = $range ?: self::$range; |
|||
return isset(self::$lang[$range][strtolower($name)]); |
|||
} |
|||
|
|||
/** |
|||
* 获取语言定义(不区分大小写) |
|||
* @param string|null $name 语言变量 |
|||
* @param array $vars 变量替换 |
|||
* @param string $range 语言作用域 |
|||
* @return mixed |
|||
*/ |
|||
public static function get($name = null, $vars = [], $range = '') |
|||
{ |
|||
$range = $range ?: self::$range; |
|||
// 空参数返回所有定义 |
|||
if (empty($name)) { |
|||
return self::$lang[$range]; |
|||
} |
|||
$key = strtolower($name); |
|||
$value = isset(self::$lang[$range][$key]) ? self::$lang[$range][$key] : $name; |
|||
|
|||
// 变量解析 |
|||
if (!empty($vars) && is_array($vars)) { |
|||
/** |
|||
* Notes: |
|||
* 为了检测的方便,数字索引的判断仅仅是参数数组的第一个元素的key为数字0 |
|||
* 数字索引采用的是系统的 sprintf 函数替换,用法请参考 sprintf 函数 |
|||
*/ |
|||
if (key($vars) === 0) { |
|||
// 数字索引解析 |
|||
array_unshift($vars, $value); |
|||
$value = call_user_func_array('sprintf', $vars); |
|||
} else { |
|||
// 关联索引解析 |
|||
$replace = array_keys($vars); |
|||
foreach ($replace as &$v) { |
|||
$v = "{:{$v}}"; |
|||
} |
|||
$value = str_replace($replace, $vars, $value); |
|||
} |
|||
|
|||
} |
|||
return $value; |
|||
} |
|||
|
|||
/** |
|||
* 自动侦测设置获取语言选择 |
|||
* @return string |
|||
*/ |
|||
public static function detect() |
|||
{ |
|||
// 自动侦测设置获取语言选择 |
|||
$langSet = ''; |
|||
if (isset($_GET[self::$langDetectVar])) { |
|||
// url中设置了语言变量 |
|||
$langSet = strtolower($_GET[self::$langDetectVar]); |
|||
Cookie::set(self::$langCookieVar, $langSet, self::$langCookieExpire); |
|||
} elseif (Cookie::get(self::$langCookieVar)) { |
|||
// 获取上次用户的选择 |
|||
$langSet = strtolower(Cookie::get(self::$langCookieVar)); |
|||
} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { |
|||
// 自动侦测浏览器语言 |
|||
preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches); |
|||
$langSet = strtolower($matches[1]); |
|||
Cookie::set(self::$langCookieVar, $langSet, self::$langCookieExpire); |
|||
} |
|||
if (empty(self::$allowLangList) || in_array($langSet, self::$allowLangList)) { |
|||
// 合法的语言 |
|||
self::$range = $langSet ?: self::$range; |
|||
} |
|||
if ('zh-hans-cn' == self::$range) { |
|||
self::$range = 'zh-cn'; |
|||
} |
|||
return self::$range; |
|||
} |
|||
|
|||
/** |
|||
* 设置语言自动侦测的变量 |
|||
* @param string $var 变量名称 |
|||
* @return void |
|||
*/ |
|||
public static function setLangDetectVar($var) |
|||
{ |
|||
self::$langDetectVar = $var; |
|||
} |
|||
|
|||
/** |
|||
* 设置语言的cookie保存变量 |
|||
* @param string $var 变量名称 |
|||
* @return void |
|||
*/ |
|||
public static function setLangCookieVar($var) |
|||
{ |
|||
self::$langCookieVar = $var; |
|||
} |
|||
|
|||
/** |
|||
* 设置语言的cookie的过期时间 |
|||
* @param string $expire 过期时间 |
|||
* @return void |
|||
*/ |
|||
public static function setLangCookieExpire($expire) |
|||
{ |
|||
self::$langCookieExpire = $expire; |
|||
} |
|||
|
|||
/** |
|||
* 设置允许的语言列表 |
|||
* @param array $list 语言列表 |
|||
* @return void |
|||
*/ |
|||
public static function setAllowLangList($list) |
|||
{ |
|||
self::$allowLangList = $list; |
|||
} |
|||
} |
|||
@ -0,0 +1,568 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
use think\exception\ClassNotFoundException; |
|||
|
|||
class Loader |
|||
{ |
|||
protected static $instance = []; |
|||
// 类名映射 |
|||
protected static $map = []; |
|||
|
|||
// 命名空间别名 |
|||
protected static $namespaceAlias = []; |
|||
|
|||
// PSR-4 |
|||
private static $prefixLengthsPsr4 = []; |
|||
private static $prefixDirsPsr4 = []; |
|||
private static $fallbackDirsPsr4 = []; |
|||
|
|||
// PSR-0 |
|||
private static $prefixesPsr0 = []; |
|||
private static $fallbackDirsPsr0 = []; |
|||
|
|||
// 自动加载的文件 |
|||
private static $autoloadFiles = []; |
|||
|
|||
// 自动加载 |
|||
public static function autoload($class) |
|||
{ |
|||
// 检测命名空间别名 |
|||
if (!empty(self::$namespaceAlias)) { |
|||
$namespace = dirname($class); |
|||
if (isset(self::$namespaceAlias[$namespace])) { |
|||
$original = self::$namespaceAlias[$namespace] . '\\' . basename($class); |
|||
if (class_exists($original)) { |
|||
return class_alias($original, $class, false); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if ($file = self::findFile($class)) { |
|||
|
|||
// Win环境严格区分大小写 |
|||
if (IS_WIN && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) { |
|||
return false; |
|||
} |
|||
|
|||
__include_file($file); |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 查找文件 |
|||
* @param $class |
|||
* @return bool |
|||
*/ |
|||
private static function findFile($class) |
|||
{ |
|||
if (!empty(self::$map[$class])) { |
|||
// 类库映射 |
|||
return self::$map[$class]; |
|||
} |
|||
|
|||
// 查找 PSR-4 |
|||
$logicalPathPsr4 = strtr($class, '\\', DS) . EXT; |
|||
|
|||
$first = $class[0]; |
|||
if (isset(self::$prefixLengthsPsr4[$first])) { |
|||
foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) { |
|||
if (0 === strpos($class, $prefix)) { |
|||
foreach (self::$prefixDirsPsr4[$prefix] as $dir) { |
|||
if (is_file($file = $dir . DS . substr($logicalPathPsr4, $length))) { |
|||
return $file; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 查找 PSR-4 fallback dirs |
|||
foreach (self::$fallbackDirsPsr4 as $dir) { |
|||
if (is_file($file = $dir . DS . $logicalPathPsr4)) { |
|||
return $file; |
|||
} |
|||
} |
|||
|
|||
// 查找 PSR-0 |
|||
if (false !== $pos = strrpos($class, '\\')) { |
|||
// namespaced class name |
|||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) |
|||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DS); |
|||
} else { |
|||
// PEAR-like class name |
|||
$logicalPathPsr0 = strtr($class, '_', DS) . EXT; |
|||
} |
|||
|
|||
if (isset(self::$prefixesPsr0[$first])) { |
|||
foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) { |
|||
if (0 === strpos($class, $prefix)) { |
|||
foreach ($dirs as $dir) { |
|||
if (is_file($file = $dir . DS . $logicalPathPsr0)) { |
|||
return $file; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 查找 PSR-0 fallback dirs |
|||
foreach (self::$fallbackDirsPsr0 as $dir) { |
|||
if (is_file($file = $dir . DS . $logicalPathPsr0)) { |
|||
return $file; |
|||
} |
|||
} |
|||
|
|||
return self::$map[$class] = false; |
|||
} |
|||
|
|||
// 注册classmap |
|||
public static function addClassMap($class, $map = '') |
|||
{ |
|||
if (is_array($class)) { |
|||
self::$map = array_merge(self::$map, $class); |
|||
} else { |
|||
self::$map[$class] = $map; |
|||
} |
|||
} |
|||
|
|||
// 注册命名空间 |
|||
public static function addNamespace($namespace, $path = '') |
|||
{ |
|||
if (is_array($namespace)) { |
|||
foreach ($namespace as $prefix => $paths) { |
|||
self::addPsr4($prefix . '\\', rtrim($paths, DS), true); |
|||
} |
|||
} else { |
|||
self::addPsr4($namespace . '\\', rtrim($path, DS), true); |
|||
} |
|||
} |
|||
|
|||
// 添加Ps0空间 |
|||
private static function addPsr0($prefix, $paths, $prepend = false) |
|||
{ |
|||
if (!$prefix) { |
|||
if ($prepend) { |
|||
self::$fallbackDirsPsr0 = array_merge( |
|||
(array) $paths, |
|||
self::$fallbackDirsPsr0 |
|||
); |
|||
} else { |
|||
self::$fallbackDirsPsr0 = array_merge( |
|||
self::$fallbackDirsPsr0, |
|||
(array) $paths |
|||
); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
$first = $prefix[0]; |
|||
if (!isset(self::$prefixesPsr0[$first][$prefix])) { |
|||
self::$prefixesPsr0[$first][$prefix] = (array) $paths; |
|||
|
|||
return; |
|||
} |
|||
if ($prepend) { |
|||
self::$prefixesPsr0[$first][$prefix] = array_merge( |
|||
(array) $paths, |
|||
self::$prefixesPsr0[$first][$prefix] |
|||
); |
|||
} else { |
|||
self::$prefixesPsr0[$first][$prefix] = array_merge( |
|||
self::$prefixesPsr0[$first][$prefix], |
|||
(array) $paths |
|||
); |
|||
} |
|||
} |
|||
|
|||
// 添加Psr4空间 |
|||
private static function addPsr4($prefix, $paths, $prepend = false) |
|||
{ |
|||
if (!$prefix) { |
|||
// Register directories for the root namespace. |
|||
if ($prepend) { |
|||
self::$fallbackDirsPsr4 = array_merge( |
|||
(array) $paths, |
|||
self::$fallbackDirsPsr4 |
|||
); |
|||
} else { |
|||
self::$fallbackDirsPsr4 = array_merge( |
|||
self::$fallbackDirsPsr4, |
|||
(array) $paths |
|||
); |
|||
} |
|||
} elseif (!isset(self::$prefixDirsPsr4[$prefix])) { |
|||
// Register directories for a new namespace. |
|||
$length = strlen($prefix); |
|||
if ('\\' !== $prefix[$length - 1]) { |
|||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); |
|||
} |
|||
self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length; |
|||
self::$prefixDirsPsr4[$prefix] = (array) $paths; |
|||
} elseif ($prepend) { |
|||
// Prepend directories for an already registered namespace. |
|||
self::$prefixDirsPsr4[$prefix] = array_merge( |
|||
(array) $paths, |
|||
self::$prefixDirsPsr4[$prefix] |
|||
); |
|||
} else { |
|||
// Append directories for an already registered namespace. |
|||
self::$prefixDirsPsr4[$prefix] = array_merge( |
|||
self::$prefixDirsPsr4[$prefix], |
|||
(array) $paths |
|||
); |
|||
} |
|||
} |
|||
|
|||
// 注册命名空间别名 |
|||
public static function addNamespaceAlias($namespace, $original = '') |
|||
{ |
|||
if (is_array($namespace)) { |
|||
self::$namespaceAlias = array_merge(self::$namespaceAlias, $namespace); |
|||
} else { |
|||
self::$namespaceAlias[$namespace] = $original; |
|||
} |
|||
} |
|||
|
|||
// 注册自动加载机制 |
|||
public static function register($autoload = '') |
|||
{ |
|||
// 注册系统自动加载 |
|||
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true); |
|||
// 注册命名空间定义 |
|||
self::addNamespace([ |
|||
'think' => LIB_PATH . 'think' . DS, |
|||
'behavior' => LIB_PATH . 'behavior' . DS, |
|||
'traits' => LIB_PATH . 'traits' . DS, |
|||
]); |
|||
// 加载类库映射文件 |
|||
if (is_file(RUNTIME_PATH . 'classmap' . EXT)) { |
|||
self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT)); |
|||
} |
|||
|
|||
// Composer自动加载支持 |
|||
if (is_dir(VENDOR_PATH . 'composer')) { |
|||
self::registerComposerLoader(); |
|||
} |
|||
|
|||
// 自动加载extend目录 |
|||
self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS); |
|||
} |
|||
|
|||
// 注册composer自动加载 |
|||
private static function registerComposerLoader() |
|||
{ |
|||
if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) { |
|||
$map = require VENDOR_PATH . 'composer/autoload_namespaces.php'; |
|||
foreach ($map as $namespace => $path) { |
|||
self::addPsr0($namespace, $path); |
|||
} |
|||
} |
|||
|
|||
if (is_file(VENDOR_PATH . 'composer/autoload_psr4.php')) { |
|||
$map = require VENDOR_PATH . 'composer/autoload_psr4.php'; |
|||
foreach ($map as $namespace => $path) { |
|||
self::addPsr4($namespace, $path); |
|||
} |
|||
} |
|||
|
|||
if (is_file(VENDOR_PATH . 'composer/autoload_classmap.php')) { |
|||
$classMap = require VENDOR_PATH . 'composer/autoload_classmap.php'; |
|||
if ($classMap) { |
|||
self::addClassMap($classMap); |
|||
} |
|||
} |
|||
|
|||
if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) { |
|||
$includeFiles = require VENDOR_PATH . 'composer/autoload_files.php'; |
|||
foreach ($includeFiles as $fileIdentifier => $file) { |
|||
if (empty(self::$autoloadFiles[$fileIdentifier])) { |
|||
__require_file($file); |
|||
self::$autoloadFiles[$fileIdentifier] = true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 导入所需的类库 同java的Import 本函数有缓存功能 |
|||
* @param string $class 类库命名空间字符串 |
|||
* @param string $baseUrl 起始路径 |
|||
* @param string $ext 导入的文件扩展名 |
|||
* @return boolean |
|||
*/ |
|||
public static function import($class, $baseUrl = '', $ext = EXT) |
|||
{ |
|||
static $_file = []; |
|||
$key = $class . $baseUrl; |
|||
$class = str_replace(['.', '#'], [DS, '.'], $class); |
|||
if (isset($_file[$key])) { |
|||
return true; |
|||
} |
|||
|
|||
if (empty($baseUrl)) { |
|||
list($name, $class) = explode(DS, $class, 2); |
|||
|
|||
if (isset(self::$prefixDirsPsr4[$name . '\\'])) { |
|||
// 注册的命名空间 |
|||
$baseUrl = self::$prefixDirsPsr4[$name . '\\']; |
|||
} elseif ('@' == $name) { |
|||
//加载当前模块应用类库 |
|||
$baseUrl = App::$modulePath; |
|||
} elseif (is_dir(EXTEND_PATH . $name)) { |
|||
$baseUrl = EXTEND_PATH . $name . DS; |
|||
} else { |
|||
// 加载其它模块的类库 |
|||
$baseUrl = APP_PATH . $name . DS; |
|||
} |
|||
} elseif (substr($baseUrl, -1) != DS) { |
|||
$baseUrl .= DS; |
|||
} |
|||
// 如果类存在 则导入类库文件 |
|||
if (is_array($baseUrl)) { |
|||
foreach ($baseUrl as $path) { |
|||
$filename = $path . DS . $class . $ext; |
|||
if (is_file($filename)) { |
|||
break; |
|||
} |
|||
} |
|||
} else { |
|||
$filename = $baseUrl . $class . $ext; |
|||
} |
|||
|
|||
if (!empty($filename) && is_file($filename)) { |
|||
// 开启调试模式Win环境严格区分大小写 |
|||
if (IS_WIN && pathinfo($filename, PATHINFO_FILENAME) != pathinfo(realpath($filename), PATHINFO_FILENAME)) { |
|||
return false; |
|||
} |
|||
__include_file($filename); |
|||
$_file[$key] = true; |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 实例化(分层)模型 |
|||
* @param string $name Model名称 |
|||
* @param string $layer 业务层名称 |
|||
* @param bool $appendSuffix 是否添加类名后缀 |
|||
* @param string $common 公共模块名 |
|||
* @return Object |
|||
* @throws ClassNotFoundException |
|||
*/ |
|||
public static function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common') |
|||
{ |
|||
$guid = $name . $layer; |
|||
if (isset(self::$instance[$guid])) { |
|||
return self::$instance[$guid]; |
|||
} |
|||
if (false !== strpos($name, '\\')) { |
|||
$class = $name; |
|||
$module = Request::instance()->module(); |
|||
} else { |
|||
if (strpos($name, '/')) { |
|||
list($module, $name) = explode('/', $name, 2); |
|||
} else { |
|||
$module = Request::instance()->module(); |
|||
} |
|||
$class = self::parseClass($module, $layer, $name, $appendSuffix); |
|||
} |
|||
if (class_exists($class)) { |
|||
$model = new $class(); |
|||
} else { |
|||
$class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class); |
|||
if (class_exists($class)) { |
|||
$model = new $class(); |
|||
} else { |
|||
throw new ClassNotFoundException('class not exists:' . $class, $class); |
|||
} |
|||
} |
|||
self::$instance[$guid] = $model; |
|||
return $model; |
|||
} |
|||
|
|||
/** |
|||
* 实例化(分层)控制器 格式:[模块名/]控制器名 |
|||
* @param string $name 资源地址 |
|||
* @param string $layer 控制层名称 |
|||
* @param bool $appendSuffix 是否添加类名后缀 |
|||
* @param string $empty 空控制器名称 |
|||
* @return Object|false |
|||
* @throws ClassNotFoundException |
|||
*/ |
|||
public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '') |
|||
{ |
|||
if (false !== strpos($name, '\\')) { |
|||
$class = $name; |
|||
$module = Request::instance()->module(); |
|||
} else { |
|||
if (strpos($name, '/')) { |
|||
list($module, $name) = explode('/', $name); |
|||
} else { |
|||
$module = Request::instance()->module(); |
|||
} |
|||
$class = self::parseClass($module, $layer, $name, $appendSuffix); |
|||
} |
|||
if (class_exists($class)) { |
|||
return App::invokeClass($class); |
|||
} elseif ($empty && class_exists($emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix))) { |
|||
return new $emptyClass(Request::instance()); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 实例化验证类 格式:[模块名/]验证器名 |
|||
* @param string $name 资源地址 |
|||
* @param string $layer 验证层名称 |
|||
* @param bool $appendSuffix 是否添加类名后缀 |
|||
* @param string $common 公共模块名 |
|||
* @return Object|false |
|||
* @throws ClassNotFoundException |
|||
*/ |
|||
public static function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common') |
|||
{ |
|||
$name = $name ?: Config::get('default_validate'); |
|||
if (empty($name)) { |
|||
return new Validate; |
|||
} |
|||
$guid = $name . $layer; |
|||
if (isset(self::$instance[$guid])) { |
|||
return self::$instance[$guid]; |
|||
} |
|||
if (false !== strpos($name, '\\')) { |
|||
$class = $name; |
|||
$module = Request::instance()->module(); |
|||
} else { |
|||
if (strpos($name, '/')) { |
|||
list($module, $name) = explode('/', $name); |
|||
} else { |
|||
$module = Request::instance()->module(); |
|||
} |
|||
$class = self::parseClass($module, $layer, $name, $appendSuffix); |
|||
} |
|||
if (class_exists($class)) { |
|||
$validate = new $class; |
|||
} else { |
|||
$class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class); |
|||
if (class_exists($class)) { |
|||
$validate = new $class; |
|||
} else { |
|||
throw new ClassNotFoundException('class not exists:' . $class, $class); |
|||
} |
|||
} |
|||
self::$instance[$guid] = $validate; |
|||
return $validate; |
|||
} |
|||
|
|||
/** |
|||
* 数据库初始化 并取得数据库类实例 |
|||
* @param mixed $config 数据库配置 |
|||
* @param bool|string $name 连接标识 true 强制重新连接 |
|||
* @return \think\db\Connection |
|||
*/ |
|||
public static function db($config = [], $name = false) |
|||
{ |
|||
return Db::connect($config, $name); |
|||
} |
|||
|
|||
/** |
|||
* 远程调用模块的操作方法 参数格式 [模块/控制器/]操作 |
|||
* @param string $url 调用地址 |
|||
* @param string|array $vars 调用参数 支持字符串和数组 |
|||
* @param string $layer 要调用的控制层名称 |
|||
* @param bool $appendSuffix 是否添加类名后缀 |
|||
* @return mixed |
|||
*/ |
|||
public static function action($url, $vars = [], $layer = 'controller', $appendSuffix = false) |
|||
{ |
|||
$info = pathinfo($url); |
|||
$action = $info['basename']; |
|||
$module = '.' != $info['dirname'] ? $info['dirname'] : Request::instance()->controller(); |
|||
$class = self::controller($module, $layer, $appendSuffix); |
|||
if ($class) { |
|||
if (is_scalar($vars)) { |
|||
if (strpos($vars, '=')) { |
|||
parse_str($vars, $vars); |
|||
} else { |
|||
$vars = [$vars]; |
|||
} |
|||
} |
|||
return App::invokeMethod([$class, $action . Config::get('action_suffix')], $vars); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 字符串命名风格转换 |
|||
* type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格 |
|||
* @param string $name 字符串 |
|||
* @param integer $type 转换类型 |
|||
* @param bool $ucfirst 首字母是否大写(驼峰规则) |
|||
* @return string |
|||
*/ |
|||
public static function parseName($name, $type = 0, $ucfirst = true) |
|||
{ |
|||
if ($type) { |
|||
$name = preg_replace_callback('/_([a-zA-Z])/', function ($match) { |
|||
return strtoupper($match[1]); |
|||
}, $name); |
|||
return $ucfirst ? ucfirst($name) : lcfirst($name); |
|||
} else { |
|||
return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 解析应用类的类名 |
|||
* @param string $module 模块名 |
|||
* @param string $layer 层名 controller model ... |
|||
* @param string $name 类名 |
|||
* @param bool $appendSuffix |
|||
* @return string |
|||
*/ |
|||
public static function parseClass($module, $layer, $name, $appendSuffix = false) |
|||
{ |
|||
$name = str_replace(['/', '.'], '\\', $name); |
|||
$array = explode('\\', $name); |
|||
$class = self::parseName(array_pop($array), 1) . (App::$suffix || $appendSuffix ? ucfirst($layer) : ''); |
|||
$path = $array ? implode('\\', $array) . '\\' : ''; |
|||
return App::$namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $path . $class; |
|||
} |
|||
|
|||
/** |
|||
* 初始化类的实例 |
|||
* @return void |
|||
*/ |
|||
public static function clearInstance() |
|||
{ |
|||
self::$instance = []; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 作用范围隔离 |
|||
* |
|||
* @param $file |
|||
* @return mixed |
|||
*/ |
|||
function __include_file($file) |
|||
{ |
|||
return include $file; |
|||
} |
|||
|
|||
function __require_file($file) |
|||
{ |
|||
return require $file; |
|||
} |
|||
@ -0,0 +1,208 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
use think\exception\ClassNotFoundException; |
|||
|
|||
/** |
|||
* Class Log |
|||
* @package think |
|||
* |
|||
* @method void log($msg) static |
|||
* @method void error($msg) static |
|||
* @method void info($msg) static |
|||
* @method void sql($msg) static |
|||
* @method void notice($msg) static |
|||
* @method void alert($msg) static |
|||
*/ |
|||
class Log |
|||
{ |
|||
const LOG = 'log'; |
|||
const ERROR = 'error'; |
|||
const INFO = 'info'; |
|||
const SQL = 'sql'; |
|||
const NOTICE = 'notice'; |
|||
const ALERT = 'alert'; |
|||
const DEBUG = 'debug'; |
|||
|
|||
// 日志信息 |
|||
protected static $log = []; |
|||
// 配置参数 |
|||
protected static $config = []; |
|||
// 日志类型 |
|||
protected static $type = ['log', 'error', 'info', 'sql', 'notice', 'alert', 'debug']; |
|||
// 日志写入驱动 |
|||
protected static $driver; |
|||
|
|||
// 当前日志授权key |
|||
protected static $key; |
|||
|
|||
/** |
|||
* 日志初始化 |
|||
* @param array $config |
|||
*/ |
|||
public static function init($config = []) |
|||
{ |
|||
$type = isset($config['type']) ? $config['type'] : 'File'; |
|||
$class = false !== strpos($type, '\\') ? $type : '\\think\\log\\driver\\' . ucwords($type); |
|||
self::$config = $config; |
|||
unset($config['type']); |
|||
if (class_exists($class)) { |
|||
self::$driver = new $class($config); |
|||
} else { |
|||
throw new ClassNotFoundException('class not exists:' . $class, $class); |
|||
} |
|||
// 记录初始化信息 |
|||
App::$debug && Log::record('[ LOG ] INIT ' . $type, 'info'); |
|||
} |
|||
|
|||
/** |
|||
* 获取日志信息 |
|||
* @param string $type 信息类型 |
|||
* @return array |
|||
*/ |
|||
public static function getLog($type = '') |
|||
{ |
|||
return $type ? self::$log[$type] : self::$log; |
|||
} |
|||
|
|||
/** |
|||
* 记录调试信息 |
|||
* @param mixed $msg 调试信息 |
|||
* @param string $type 信息类型 |
|||
* @return void |
|||
*/ |
|||
public static function record($msg, $type = 'log') |
|||
{ |
|||
self::$log[$type][] = $msg; |
|||
if (IS_CLI && count(self::$log[$type]) > 100) { |
|||
// 命令行下面日志写入改进 |
|||
self::save(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 清空日志信息 |
|||
* @return void |
|||
*/ |
|||
public static function clear() |
|||
{ |
|||
self::$log = []; |
|||
} |
|||
|
|||
/** |
|||
* 当前日志记录的授权key |
|||
* @param string $key 授权key |
|||
* @return void |
|||
*/ |
|||
public static function key($key) |
|||
{ |
|||
self::$key = $key; |
|||
} |
|||
|
|||
/** |
|||
* 检查日志写入权限 |
|||
* @param array $config 当前日志配置参数 |
|||
* @return bool |
|||
*/ |
|||
public static function check($config) |
|||
{ |
|||
if (self::$key && !empty($config['allow_key']) && !in_array(self::$key, $config['allow_key'])) { |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
/** |
|||
* 保存调试信息 |
|||
* @return bool |
|||
*/ |
|||
public static function save() |
|||
{ |
|||
if (!empty(self::$log)) { |
|||
if (is_null(self::$driver)) { |
|||
self::init(Config::get('log')); |
|||
} |
|||
|
|||
if (!self::check(self::$config)) { |
|||
// 检测日志写入权限 |
|||
return false; |
|||
} |
|||
|
|||
if (empty(self::$config['level'])) { |
|||
// 获取全部日志 |
|||
$log = self::$log; |
|||
if (!App::$debug && isset($log['debug'])) { |
|||
unset($log['debug']); |
|||
} |
|||
} else { |
|||
// 记录允许级别 |
|||
$log = []; |
|||
foreach (self::$config['level'] as $level) { |
|||
if (isset(self::$log[$level])) { |
|||
$log[$level] = self::$log[$level]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
$result = self::$driver->save($log); |
|||
if ($result) { |
|||
self::$log = []; |
|||
} |
|||
|
|||
return $result; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
/** |
|||
* 实时写入日志信息 并支持行为 |
|||
* @param mixed $msg 调试信息 |
|||
* @param string $type 信息类型 |
|||
* @param bool $force 是否强制写入 |
|||
* @return bool |
|||
*/ |
|||
public static function write($msg, $type = 'log', $force = false) |
|||
{ |
|||
// 封装日志信息 |
|||
if (true === $force || empty(self::$config['level'])) { |
|||
$log[$type][] = $msg; |
|||
} elseif (in_array($type, self::$config['level'])) { |
|||
$log[$type][] = $msg; |
|||
} else { |
|||
return false; |
|||
} |
|||
|
|||
// 监听log_write |
|||
Hook::listen('log_write', $log); |
|||
if (is_null(self::$driver)) { |
|||
self::init(Config::get('log')); |
|||
} |
|||
// 写入日志 |
|||
return self::$driver->save($log, false); |
|||
} |
|||
|
|||
/** |
|||
* 静态调用 |
|||
* @param $method |
|||
* @param $args |
|||
* @return mixed |
|||
*/ |
|||
public static function __callStatic($method, $args) |
|||
{ |
|||
if (in_array($method, self::$type)) { |
|||
array_push($args, $method); |
|||
return call_user_func_array('\\think\\Log::record', $args); |
|||
} |
|||
} |
|||
|
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,369 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: zhangyajun <448901948@qq.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
use ArrayAccess; |
|||
use ArrayIterator; |
|||
use Countable; |
|||
use IteratorAggregate; |
|||
use JsonSerializable; |
|||
use Traversable; |
|||
|
|||
abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable |
|||
{ |
|||
/** @var bool 是否为简洁模式 */ |
|||
protected $simple = false; |
|||
|
|||
/** @var Collection 数据集 */ |
|||
protected $items; |
|||
|
|||
/** @var integer 当前页 */ |
|||
protected $currentPage; |
|||
|
|||
/** @var integer 最后一页 */ |
|||
protected $lastPage; |
|||
|
|||
/** @var integer|null 数据总数 */ |
|||
protected $total; |
|||
|
|||
/** @var integer 每页的数量 */ |
|||
protected $listRows; |
|||
|
|||
/** @var bool 是否有下一页 */ |
|||
protected $hasMore; |
|||
|
|||
/** @var array 一些配置 */ |
|||
protected $options = [ |
|||
'var_page' => 'page', |
|||
'path' => '/', |
|||
'query' => [], |
|||
'fragment' => '', |
|||
]; |
|||
|
|||
public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = []) |
|||
{ |
|||
$this->options = array_merge($this->options, $options); |
|||
|
|||
$this->options['path'] = '/' != $this->options['path'] ? rtrim($this->options['path'], '/') : $this->options['path']; |
|||
|
|||
$this->simple = $simple; |
|||
$this->listRows = $listRows; |
|||
|
|||
if (!$items instanceof Collection) { |
|||
$items = Collection::make($items); |
|||
} |
|||
|
|||
if ($simple) { |
|||
$this->currentPage = $this->setCurrentPage($currentPage); |
|||
$this->hasMore = count($items) > ($this->listRows); |
|||
$items = $items->slice(0, $this->listRows); |
|||
} else { |
|||
$this->total = $total; |
|||
$this->lastPage = (int) ceil($total / $listRows); |
|||
$this->currentPage = $this->setCurrentPage($currentPage); |
|||
$this->hasMore = $this->currentPage < $this->lastPage; |
|||
} |
|||
$this->items = $items; |
|||
} |
|||
|
|||
/** |
|||
* @param $items |
|||
* @param $listRows |
|||
* @param null $currentPage |
|||
* @param bool $simple |
|||
* @param null $total |
|||
* @param array $options |
|||
* @return Paginator |
|||
*/ |
|||
public static function make($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = []) |
|||
{ |
|||
return new static($items, $listRows, $currentPage, $total, $simple, $options); |
|||
} |
|||
|
|||
protected function setCurrentPage($currentPage) |
|||
{ |
|||
if (!$this->simple && $currentPage > $this->lastPage) { |
|||
return $this->lastPage > 0 ? $this->lastPage : 1; |
|||
} |
|||
|
|||
return $currentPage; |
|||
} |
|||
|
|||
/** |
|||
* 获取页码对应的链接 |
|||
* |
|||
* @param $page |
|||
* @return string |
|||
*/ |
|||
protected function url($page) |
|||
{ |
|||
if ($page <= 0) { |
|||
$page = 1; |
|||
} |
|||
|
|||
if (strpos($this->options['path'], '[PAGE]') === false) { |
|||
$parameters = [$this->options['var_page'] => $page]; |
|||
$path = $this->options['path']; |
|||
} else { |
|||
$parameters = []; |
|||
$path = str_replace('[PAGE]', $page, $this->options['path']); |
|||
} |
|||
if (count($this->options['query']) > 0) { |
|||
$parameters = array_merge($this->options['query'], $parameters); |
|||
} |
|||
$url = $path; |
|||
if (!empty($parameters)) { |
|||
$url .= '?' . urldecode(http_build_query($parameters, null, '&')); |
|||
} |
|||
return $url . $this->buildFragment(); |
|||
} |
|||
|
|||
/** |
|||
* 自动获取当前页码 |
|||
* @param string $varPage |
|||
* @param int $default |
|||
* @return int |
|||
*/ |
|||
public static function getCurrentPage($varPage = 'page', $default = 1) |
|||
{ |
|||
$page = Request::instance()->request($varPage); |
|||
|
|||
if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) { |
|||
return $page; |
|||
} |
|||
|
|||
return $default; |
|||
} |
|||
|
|||
/** |
|||
* 自动获取当前的path |
|||
* @return string |
|||
*/ |
|||
public static function getCurrentPath() |
|||
{ |
|||
return Request::instance()->baseUrl(); |
|||
} |
|||
|
|||
public function total() |
|||
{ |
|||
if ($this->simple) { |
|||
throw new \DomainException('not support total'); |
|||
} |
|||
return $this->total; |
|||
} |
|||
|
|||
public function listRows() |
|||
{ |
|||
return $this->listRows; |
|||
} |
|||
|
|||
public function currentPage() |
|||
{ |
|||
return $this->currentPage; |
|||
} |
|||
|
|||
public function lastPage() |
|||
{ |
|||
if ($this->simple) { |
|||
throw new \DomainException('not support last'); |
|||
} |
|||
return $this->lastPage; |
|||
} |
|||
|
|||
/** |
|||
* 数据是否足够分页 |
|||
* @return boolean |
|||
*/ |
|||
public function hasPages() |
|||
{ |
|||
return !(1 == $this->currentPage && !$this->hasMore); |
|||
} |
|||
|
|||
/** |
|||
* 创建一组分页链接 |
|||
* |
|||
* @param int $start |
|||
* @param int $end |
|||
* @return array |
|||
*/ |
|||
public function getUrlRange($start, $end) |
|||
{ |
|||
$urls = []; |
|||
|
|||
for ($page = $start; $page <= $end; $page++) { |
|||
$urls[$page] = $this->url($page); |
|||
} |
|||
|
|||
return $urls; |
|||
} |
|||
|
|||
/** |
|||
* 设置URL锚点 |
|||
* |
|||
* @param string|null $fragment |
|||
* @return $this |
|||
*/ |
|||
public function fragment($fragment) |
|||
{ |
|||
$this->options['fragment'] = $fragment; |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 添加URL参数 |
|||
* |
|||
* @param array|string $key |
|||
* @param string|null $value |
|||
* @return $this |
|||
*/ |
|||
public function appends($key, $value = null) |
|||
{ |
|||
if (!is_array($key)) { |
|||
$queries = [$key => $value]; |
|||
} else { |
|||
$queries = $key; |
|||
} |
|||
|
|||
foreach ($queries as $k => $v) { |
|||
if ($k !== $this->options['var_page']) { |
|||
$this->options['query'][$k] = $v; |
|||
} |
|||
} |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 构造锚点字符串 |
|||
* |
|||
* @return string |
|||
*/ |
|||
protected function buildFragment() |
|||
{ |
|||
return $this->options['fragment'] ? '#' . $this->options['fragment'] : ''; |
|||
} |
|||
|
|||
/** |
|||
* 渲染分页html |
|||
* @return mixed |
|||
*/ |
|||
abstract public function render(); |
|||
|
|||
public function items() |
|||
{ |
|||
return $this->items->all(); |
|||
} |
|||
|
|||
public function getCollection() |
|||
{ |
|||
return $this->items; |
|||
} |
|||
|
|||
public function isEmpty() |
|||
{ |
|||
return $this->items->isEmpty(); |
|||
} |
|||
|
|||
/** |
|||
* Retrieve an external iterator |
|||
* @return Traversable An instance of an object implementing <b>Iterator</b> or |
|||
* <b>Traversable</b> |
|||
*/ |
|||
public function getIterator() |
|||
{ |
|||
return new ArrayIterator($this->items->all()); |
|||
} |
|||
|
|||
/** |
|||
* Whether a offset exists |
|||
* @param mixed $offset |
|||
* @return bool |
|||
*/ |
|||
public function offsetExists($offset) |
|||
{ |
|||
return $this->items->offsetExists($offset); |
|||
} |
|||
|
|||
/** |
|||
* Offset to retrieve |
|||
* @param mixed $offset |
|||
* @return mixed |
|||
*/ |
|||
public function offsetGet($offset) |
|||
{ |
|||
return $this->items->offsetGet($offset); |
|||
} |
|||
|
|||
/** |
|||
* Offset to set |
|||
* @param mixed $offset |
|||
* @param mixed $value |
|||
*/ |
|||
public function offsetSet($offset, $value) |
|||
{ |
|||
$this->items->offsetSet($offset, $value); |
|||
} |
|||
|
|||
/** |
|||
* Offset to unset |
|||
* @param mixed $offset |
|||
* @return void |
|||
* @since 5.0.0 |
|||
*/ |
|||
public function offsetUnset($offset) |
|||
{ |
|||
$this->items->offsetUnset($offset); |
|||
} |
|||
|
|||
/** |
|||
* Count elements of an object |
|||
*/ |
|||
public function count() |
|||
{ |
|||
return $this->items->count(); |
|||
} |
|||
|
|||
public function __toString() |
|||
{ |
|||
return (string) $this->render(); |
|||
} |
|||
|
|||
public function toArray() |
|||
{ |
|||
try { |
|||
$total = $this->total(); |
|||
} catch (\DomainException $e) { |
|||
$total = null; |
|||
} |
|||
|
|||
return [ |
|||
'total' => $total, |
|||
'per_page' => $this->listRows(), |
|||
'current_page' => $this->currentPage(), |
|||
'data' => $this->items->toArray(), |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* Specify data which should be serialized to JSON |
|||
*/ |
|||
public function jsonSerialize() |
|||
{ |
|||
return $this->toArray(); |
|||
} |
|||
|
|||
public function __call($name, $arguments) |
|||
{ |
|||
return call_user_func_array([$this->getCollection(), $name], $arguments); |
|||
} |
|||
|
|||
} |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,328 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
use think\response\Json as JsonResponse; |
|||
use think\response\Jsonp as JsonpResponse; |
|||
use think\response\Redirect as RedirectResponse; |
|||
use think\response\View as ViewResponse; |
|||
use think\response\Xml as XmlResponse; |
|||
|
|||
class Response |
|||
{ |
|||
|
|||
// 原始数据 |
|||
protected $data; |
|||
|
|||
// 当前的contentType |
|||
protected $contentType = 'text/html'; |
|||
|
|||
// 字符集 |
|||
protected $charset = 'utf-8'; |
|||
|
|||
//状态 |
|||
protected $code = 200; |
|||
|
|||
// 输出参数 |
|||
protected $options = []; |
|||
// header参数 |
|||
protected $header = []; |
|||
|
|||
protected $content = null; |
|||
|
|||
/** |
|||
* 构造函数 |
|||
* @access public |
|||
* @param mixed $data 输出数据 |
|||
* @param int $code |
|||
* @param array $header |
|||
* @param array $options 输出参数 |
|||
*/ |
|||
public function __construct($data = '', $code = 200, array $header = [], $options = []) |
|||
{ |
|||
$this->data($data); |
|||
$this->header = $header; |
|||
$this->code = $code; |
|||
if (!empty($options)) { |
|||
$this->options = array_merge($this->options, $options); |
|||
} |
|||
$this->contentType($this->contentType, $this->charset); |
|||
} |
|||
|
|||
/** |
|||
* 创建Response对象 |
|||
* @access public |
|||
* @param mixed $data 输出数据 |
|||
* @param string $type 输出类型 |
|||
* @param int $code |
|||
* @param array $header |
|||
* @param array $options 输出参数 |
|||
* @return Response|JsonResponse|ViewResponse|XmlResponse|RedirectResponse|JsonpResponse |
|||
*/ |
|||
public static function create($data = '', $type = '', $code = 200, array $header = [], $options = []) |
|||
{ |
|||
$type = empty($type) ? 'null' : strtolower($type); |
|||
|
|||
$class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst($type); |
|||
if (class_exists($class)) { |
|||
$response = new $class($data, $code, $header, $options); |
|||
} else { |
|||
$response = new static($data, $code, $header, $options); |
|||
} |
|||
|
|||
return $response; |
|||
} |
|||
|
|||
/** |
|||
* 发送数据到客户端 |
|||
* @access public |
|||
* @return mixed |
|||
* @throws \InvalidArgumentException |
|||
*/ |
|||
public function send() |
|||
{ |
|||
// 处理输出数据 |
|||
$data = $this->getContent(); |
|||
|
|||
// Trace调试注入 |
|||
if (Env::get('app_trace', Config::get('app_trace'))) { |
|||
Debug::inject($this, $data); |
|||
} |
|||
|
|||
if (200 == $this->code) { |
|||
$cache = Request::instance()->getCache(); |
|||
if ($cache) { |
|||
$this->header['Cache-Control'] = 'max-age=' . $cache[1] . ',must-revalidate'; |
|||
$this->header['Last-Modified'] = gmdate('D, d M Y H:i:s') . ' GMT'; |
|||
$this->header['Expires'] = gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME'] + $cache[1]) . ' GMT'; |
|||
Cache::set($cache[0], [$data, $this->header], $cache[1]); |
|||
} |
|||
} |
|||
|
|||
if (!headers_sent() && !empty($this->header)) { |
|||
// 发送状态码 |
|||
http_response_code($this->code); |
|||
// 发送头部信息 |
|||
foreach ($this->header as $name => $val) { |
|||
header($name . ':' . $val); |
|||
} |
|||
} |
|||
|
|||
echo $data; |
|||
|
|||
if (function_exists('fastcgi_finish_request')) { |
|||
// 提高页面响应 |
|||
fastcgi_finish_request(); |
|||
} |
|||
|
|||
// 监听response_end |
|||
Hook::listen('response_end', $this); |
|||
|
|||
// 清空当次请求有效的数据 |
|||
if (!($this instanceof RedirectResponse)) { |
|||
Session::flush(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 处理数据 |
|||
* @access protected |
|||
* @param mixed $data 要处理的数据 |
|||
* @return mixed |
|||
*/ |
|||
protected function output($data) |
|||
{ |
|||
return $data; |
|||
} |
|||
|
|||
/** |
|||
* 输出的参数 |
|||
* @access public |
|||
* @param mixed $options 输出参数 |
|||
* @return $this |
|||
*/ |
|||
public function options($options = []) |
|||
{ |
|||
$this->options = array_merge($this->options, $options); |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 输出数据设置 |
|||
* @access public |
|||
* @param mixed $data 输出数据 |
|||
* @return $this |
|||
*/ |
|||
public function data($data) |
|||
{ |
|||
$this->data = $data; |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 设置响应头 |
|||
* @access public |
|||
* @param string|array $name 参数名 |
|||
* @param string $value 参数值 |
|||
* @return $this |
|||
*/ |
|||
public function header($name, $value = null) |
|||
{ |
|||
if (is_array($name)) { |
|||
$this->header = array_merge($this->header, $name); |
|||
} else { |
|||
$this->header[$name] = $value; |
|||
} |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 设置页面输出内容 |
|||
* @param $content |
|||
* @return $this |
|||
*/ |
|||
public function content($content) |
|||
{ |
|||
if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([ |
|||
$content, |
|||
'__toString', |
|||
]) |
|||
) { |
|||
throw new \InvalidArgumentException(sprintf('variable type error: %s', gettype($content))); |
|||
} |
|||
|
|||
$this->content = (string) $content; |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 发送HTTP状态 |
|||
* @param integer $code 状态码 |
|||
* @return $this |
|||
*/ |
|||
public function code($code) |
|||
{ |
|||
$this->code = $code; |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* LastModified |
|||
* @param string $time |
|||
* @return $this |
|||
*/ |
|||
public function lastModified($time) |
|||
{ |
|||
$this->header['Last-Modified'] = $time; |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Expires |
|||
* @param string $time |
|||
* @return $this |
|||
*/ |
|||
public function expires($time) |
|||
{ |
|||
$this->header['Expires'] = $time; |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* ETag |
|||
* @param string $eTag |
|||
* @return $this |
|||
*/ |
|||
public function eTag($eTag) |
|||
{ |
|||
$this->header['ETag'] = $eTag; |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 页面缓存控制 |
|||
* @param string $cache 状态码 |
|||
* @return $this |
|||
*/ |
|||
public function cacheControl($cache) |
|||
{ |
|||
$this->header['Cache-control'] = $cache; |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 页面输出类型 |
|||
* @param string $contentType 输出类型 |
|||
* @param string $charset 输出编码 |
|||
* @return $this |
|||
*/ |
|||
public function contentType($contentType, $charset = 'utf-8') |
|||
{ |
|||
$this->header['Content-Type'] = $contentType . '; charset=' . $charset; |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 获取头部信息 |
|||
* @param string $name 头部名称 |
|||
* @return mixed |
|||
*/ |
|||
public function getHeader($name = '') |
|||
{ |
|||
if (!empty($name)) { |
|||
return isset($this->header[$name]) ? $this->header[$name] : null; |
|||
} else { |
|||
return $this->header; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取原始数据 |
|||
* @return mixed |
|||
*/ |
|||
public function getData() |
|||
{ |
|||
return $this->data; |
|||
} |
|||
|
|||
/** |
|||
* 获取输出数据 |
|||
* @return mixed |
|||
*/ |
|||
public function getContent() |
|||
{ |
|||
if (null == $this->content) { |
|||
$content = $this->output($this->data); |
|||
|
|||
if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([ |
|||
$content, |
|||
'__toString', |
|||
]) |
|||
) { |
|||
throw new \InvalidArgumentException(sprintf('variable type error: %s', gettype($content))); |
|||
} |
|||
|
|||
$this->content = (string) $content; |
|||
} |
|||
return $this->content; |
|||
} |
|||
|
|||
/** |
|||
* 获取状态码 |
|||
* @return integer |
|||
*/ |
|||
public function getCode() |
|||
{ |
|||
return $this->code; |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,365 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
use think\exception\ClassNotFoundException; |
|||
|
|||
class Session |
|||
{ |
|||
protected static $prefix = ''; |
|||
protected static $init = null; |
|||
|
|||
/** |
|||
* 设置或者获取session作用域(前缀) |
|||
* @param string $prefix |
|||
* @return string|void |
|||
*/ |
|||
public static function prefix($prefix = '') |
|||
{ |
|||
if (empty($prefix) && null !== $prefix) { |
|||
return self::$prefix; |
|||
} else { |
|||
self::$prefix = $prefix; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* session初始化 |
|||
* @param array $config |
|||
* @return void |
|||
* @throws \think\Exception |
|||
*/ |
|||
public static function init(array $config = []) |
|||
{ |
|||
if (empty($config)) { |
|||
$config = Config::get('session'); |
|||
} |
|||
// 记录初始化信息 |
|||
App::$debug && Log::record('[ SESSION ] INIT ' . var_export($config, true), 'info'); |
|||
$isDoStart = false; |
|||
if (isset($config['use_trans_sid'])) { |
|||
ini_set('session.use_trans_sid', $config['use_trans_sid'] ? 1 : 0); |
|||
} |
|||
|
|||
// 启动session |
|||
if (!empty($config['auto_start']) && PHP_SESSION_ACTIVE != session_status()) { |
|||
ini_set('session.auto_start', 0); |
|||
$isDoStart = true; |
|||
} |
|||
|
|||
if (isset($config['prefix'])) { |
|||
self::$prefix = $config['prefix']; |
|||
} |
|||
if (isset($config['var_session_id']) && isset($_REQUEST[$config['var_session_id']])) { |
|||
session_id($_REQUEST[$config['var_session_id']]); |
|||
} elseif (isset($config['id']) && !empty($config['id'])) { |
|||
session_id($config['id']); |
|||
} |
|||
if (isset($config['name'])) { |
|||
session_name($config['name']); |
|||
} |
|||
if (isset($config['path'])) { |
|||
session_save_path($config['path']); |
|||
} |
|||
if (isset($config['domain'])) { |
|||
ini_set('session.cookie_domain', $config['domain']); |
|||
} |
|||
if (isset($config['expire'])) { |
|||
ini_set('session.gc_maxlifetime', $config['expire']); |
|||
ini_set('session.cookie_lifetime', $config['expire']); |
|||
} |
|||
if (isset($config['secure'])) { |
|||
ini_set('session.cookie_secure', $config['secure']); |
|||
} |
|||
if (isset($config['httponly'])) { |
|||
ini_set('session.cookie_httponly', $config['httponly']); |
|||
} |
|||
if (isset($config['use_cookies'])) { |
|||
ini_set('session.use_cookies', $config['use_cookies'] ? 1 : 0); |
|||
} |
|||
if (isset($config['cache_limiter'])) { |
|||
session_cache_limiter($config['cache_limiter']); |
|||
} |
|||
if (isset($config['cache_expire'])) { |
|||
session_cache_expire($config['cache_expire']); |
|||
} |
|||
if (!empty($config['type'])) { |
|||
// 读取session驱动 |
|||
$class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\session\\driver\\' . ucwords($config['type']); |
|||
|
|||
// 检查驱动类 |
|||
if (!class_exists($class) || !session_set_save_handler(new $class($config))) { |
|||
throw new ClassNotFoundException('error session handler:' . $class, $class); |
|||
} |
|||
} |
|||
if ($isDoStart) { |
|||
session_start(); |
|||
self::$init = true; |
|||
} else { |
|||
self::$init = false; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* session自动启动或者初始化 |
|||
* @return void |
|||
*/ |
|||
public static function boot() |
|||
{ |
|||
if (is_null(self::$init)) { |
|||
self::init(); |
|||
} elseif (false === self::$init) { |
|||
if (PHP_SESSION_ACTIVE != session_status()) { |
|||
session_start(); |
|||
} |
|||
self::$init = true; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* session设置 |
|||
* @param string $name session名称 |
|||
* @param mixed $value session值 |
|||
* @param string|null $prefix 作用域(前缀) |
|||
* @return void |
|||
*/ |
|||
public static function set($name, $value = '', $prefix = null) |
|||
{ |
|||
empty(self::$init) && self::boot(); |
|||
|
|||
$prefix = !is_null($prefix) ? $prefix : self::$prefix; |
|||
if (strpos($name, '.')) { |
|||
// 二维数组赋值 |
|||
list($name1, $name2) = explode('.', $name); |
|||
if ($prefix) { |
|||
$_SESSION[$prefix][$name1][$name2] = $value; |
|||
} else { |
|||
$_SESSION[$name1][$name2] = $value; |
|||
} |
|||
} elseif ($prefix) { |
|||
$_SESSION[$prefix][$name] = $value; |
|||
} else { |
|||
$_SESSION[$name] = $value; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* session获取 |
|||
* @param string $name session名称 |
|||
* @param string|null $prefix 作用域(前缀) |
|||
* @return mixed |
|||
*/ |
|||
public static function get($name = '', $prefix = null) |
|||
{ |
|||
empty(self::$init) && self::boot(); |
|||
$prefix = !is_null($prefix) ? $prefix : self::$prefix; |
|||
if ('' == $name) { |
|||
// 获取全部的session |
|||
$value = $prefix ? (!empty($_SESSION[$prefix]) ? $_SESSION[$prefix] : []) : $_SESSION; |
|||
} elseif ($prefix) { |
|||
// 获取session |
|||
if (strpos($name, '.')) { |
|||
list($name1, $name2) = explode('.', $name); |
|||
$value = isset($_SESSION[$prefix][$name1][$name2]) ? $_SESSION[$prefix][$name1][$name2] : null; |
|||
} else { |
|||
$value = isset($_SESSION[$prefix][$name]) ? $_SESSION[$prefix][$name] : null; |
|||
} |
|||
} else { |
|||
if (strpos($name, '.')) { |
|||
list($name1, $name2) = explode('.', $name); |
|||
$value = isset($_SESSION[$name1][$name2]) ? $_SESSION[$name1][$name2] : null; |
|||
} else { |
|||
$value = isset($_SESSION[$name]) ? $_SESSION[$name] : null; |
|||
} |
|||
} |
|||
return $value; |
|||
} |
|||
|
|||
/** |
|||
* session获取并删除 |
|||
* @param string $name session名称 |
|||
* @param string|null $prefix 作用域(前缀) |
|||
* @return mixed |
|||
*/ |
|||
public static function pull($name, $prefix = null) |
|||
{ |
|||
$result = self::get($name, $prefix); |
|||
if ($result) { |
|||
self::delete($name, $prefix); |
|||
return $result; |
|||
} else { |
|||
return; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* session设置 下一次请求有效 |
|||
* @param string $name session名称 |
|||
* @param mixed $value session值 |
|||
* @param string|null $prefix 作用域(前缀) |
|||
* @return void |
|||
*/ |
|||
public static function flash($name, $value) |
|||
{ |
|||
self::set($name, $value); |
|||
if (!self::has('__flash__.__time__')) { |
|||
self::set('__flash__.__time__', $_SERVER['REQUEST_TIME_FLOAT']); |
|||
} |
|||
self::push('__flash__', $name); |
|||
} |
|||
|
|||
/** |
|||
* 清空当前请求的session数据 |
|||
* @return void |
|||
*/ |
|||
public static function flush() |
|||
{ |
|||
if (self::$init) { |
|||
$item = self::get('__flash__'); |
|||
|
|||
if (!empty($item)) { |
|||
$time = $item['__time__']; |
|||
if ($_SERVER['REQUEST_TIME_FLOAT'] > $time) { |
|||
unset($item['__time__']); |
|||
self::delete($item); |
|||
self::set('__flash__', []); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 删除session数据 |
|||
* @param string|array $name session名称 |
|||
* @param string|null $prefix 作用域(前缀) |
|||
* @return void |
|||
*/ |
|||
public static function delete($name, $prefix = null) |
|||
{ |
|||
empty(self::$init) && self::boot(); |
|||
$prefix = !is_null($prefix) ? $prefix : self::$prefix; |
|||
if (is_array($name)) { |
|||
foreach ($name as $key) { |
|||
self::delete($key, $prefix); |
|||
} |
|||
} elseif (strpos($name, '.')) { |
|||
list($name1, $name2) = explode('.', $name); |
|||
if ($prefix) { |
|||
unset($_SESSION[$prefix][$name1][$name2]); |
|||
} else { |
|||
unset($_SESSION[$name1][$name2]); |
|||
} |
|||
} else { |
|||
if ($prefix) { |
|||
unset($_SESSION[$prefix][$name]); |
|||
} else { |
|||
unset($_SESSION[$name]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 清空session数据 |
|||
* @param string|null $prefix 作用域(前缀) |
|||
* @return void |
|||
*/ |
|||
public static function clear($prefix = null) |
|||
{ |
|||
empty(self::$init) && self::boot(); |
|||
$prefix = !is_null($prefix) ? $prefix : self::$prefix; |
|||
if ($prefix) { |
|||
unset($_SESSION[$prefix]); |
|||
} else { |
|||
$_SESSION = []; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 判断session数据 |
|||
* @param string $name session名称 |
|||
* @param string|null $prefix |
|||
* @return bool |
|||
*/ |
|||
public static function has($name, $prefix = null) |
|||
{ |
|||
empty(self::$init) && self::boot(); |
|||
$prefix = !is_null($prefix) ? $prefix : self::$prefix; |
|||
if (strpos($name, '.')) { |
|||
// 支持数组 |
|||
list($name1, $name2) = explode('.', $name); |
|||
return $prefix ? isset($_SESSION[$prefix][$name1][$name2]) : isset($_SESSION[$name1][$name2]); |
|||
} else { |
|||
return $prefix ? isset($_SESSION[$prefix][$name]) : isset($_SESSION[$name]); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 添加数据到一个session数组 |
|||
* @param string $key |
|||
* @param mixed $value |
|||
* @return void |
|||
*/ |
|||
public static function push($key, $value) |
|||
{ |
|||
$array = self::get($key); |
|||
if (is_null($array)) { |
|||
$array = []; |
|||
} |
|||
$array[] = $value; |
|||
self::set($key, $array); |
|||
} |
|||
|
|||
/** |
|||
* 启动session |
|||
* @return void |
|||
*/ |
|||
public static function start() |
|||
{ |
|||
session_start(); |
|||
self::$init = true; |
|||
} |
|||
|
|||
/** |
|||
* 销毁session |
|||
* @return void |
|||
*/ |
|||
public static function destroy() |
|||
{ |
|||
if (!empty($_SESSION)) { |
|||
$_SESSION = []; |
|||
} |
|||
session_unset(); |
|||
session_destroy(); |
|||
self::$init = null; |
|||
} |
|||
|
|||
/** |
|||
* 重新生成session_id |
|||
* @param bool $delete 是否删除关联会话文件 |
|||
* @return void |
|||
*/ |
|||
private static function regenerate($delete = false) |
|||
{ |
|||
session_regenerate_id($delete); |
|||
} |
|||
|
|||
/** |
|||
* 暂停session |
|||
* @return void |
|||
*/ |
|||
public static function pause() |
|||
{ |
|||
// 暂停session |
|||
session_write_close(); |
|||
self::$init = false; |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,329 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
use think\exception\HttpException; |
|||
|
|||
class Url |
|||
{ |
|||
// 生成URL地址的root |
|||
protected static $root; |
|||
protected static $bindCheck; |
|||
|
|||
/** |
|||
* URL生成 支持路由反射 |
|||
* @param string $url 路由地址 |
|||
* @param string|array $vars 参数(支持数组和字符串)a=val&b=val2... ['a'=>'val1', 'b'=>'val2'] |
|||
* @param string|bool $suffix 伪静态后缀,默认为true表示获取配置值 |
|||
* @param boolean|string $domain 是否显示域名 或者直接传入域名 |
|||
* @return string |
|||
*/ |
|||
public static function build($url = '', $vars = '', $suffix = true, $domain = false) |
|||
{ |
|||
if (false === $domain && Route::rules('domain')) { |
|||
$domain = true; |
|||
} |
|||
// 解析URL |
|||
if (0 === strpos($url, '[') && $pos = strpos($url, ']')) { |
|||
// [name] 表示使用路由命名标识生成URL |
|||
$name = substr($url, 1, $pos - 1); |
|||
$url = 'name' . substr($url, $pos + 1); |
|||
} |
|||
if (false === strpos($url, '://') && 0 !== strpos($url, '/')) { |
|||
$info = parse_url($url); |
|||
$url = !empty($info['path']) ? $info['path'] : ''; |
|||
if (isset($info['fragment'])) { |
|||
// 解析锚点 |
|||
$anchor = $info['fragment']; |
|||
if (false !== strpos($anchor, '?')) { |
|||
// 解析参数 |
|||
list($anchor, $info['query']) = explode('?', $anchor, 2); |
|||
} |
|||
if (false !== strpos($anchor, '@')) { |
|||
// 解析域名 |
|||
list($anchor, $domain) = explode('@', $anchor, 2); |
|||
} |
|||
} elseif (strpos($url, '@') && false === strpos($url, '\\')) { |
|||
// 解析域名 |
|||
list($url, $domain) = explode('@', $url, 2); |
|||
} |
|||
} |
|||
|
|||
// 解析参数 |
|||
if (is_string($vars)) { |
|||
// aaa=1&bbb=2 转换成数组 |
|||
parse_str($vars, $vars); |
|||
} |
|||
|
|||
if ($url) { |
|||
$rule = Route::name(isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : '')); |
|||
if (is_null($rule) && isset($info['query'])) { |
|||
$rule = Route::name($url); |
|||
// 解析地址里面参数 合并到vars |
|||
parse_str($info['query'], $params); |
|||
$vars = array_merge($params, $vars); |
|||
unset($info['query']); |
|||
} |
|||
} |
|||
if (!empty($rule) && $match = self::getRuleUrl($rule, $vars)) { |
|||
// 匹配路由命名标识 |
|||
$url = $match[0]; |
|||
// 替换可选分隔符 |
|||
$url = preg_replace(['/(\W)\?$/', '/(\W)\?/'], ['', '\1'], $url); |
|||
if (!empty($match[1])) { |
|||
$domain = $match[1]; |
|||
} |
|||
if (!is_null($match[2])) { |
|||
$suffix = $match[2]; |
|||
} |
|||
} elseif (!empty($rule) && isset($name)) { |
|||
throw new \InvalidArgumentException('route name not exists:' . $name); |
|||
} else { |
|||
// 检查别名路由 |
|||
$alias = Route::rules('alias'); |
|||
$matchAlias = false; |
|||
if ($alias) { |
|||
// 别名路由解析 |
|||
foreach ($alias as $key => $val) { |
|||
if (is_array($val)) { |
|||
$val = $val[0]; |
|||
} |
|||
if (0 === strpos($url, $val)) { |
|||
$url = $key . substr($url, strlen($val)); |
|||
$matchAlias = true; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
if (!$matchAlias) { |
|||
// 路由标识不存在 直接解析 |
|||
$url = self::parseUrl($url, $domain); |
|||
} |
|||
if (isset($info['query'])) { |
|||
// 解析地址里面参数 合并到vars |
|||
parse_str($info['query'], $params); |
|||
$vars = array_merge($params, $vars); |
|||
} |
|||
} |
|||
|
|||
// 检测URL绑定 |
|||
if (!self::$bindCheck) { |
|||
$type = Route::getBind('type'); |
|||
if ($type) { |
|||
$bind = Route::getBind($type); |
|||
if (0 === strpos($url, $bind)) { |
|||
$url = substr($url, strlen($bind) + 1); |
|||
} |
|||
} |
|||
} |
|||
// 还原URL分隔符 |
|||
$depr = Config::get('pathinfo_depr'); |
|||
$url = str_replace('/', $depr, $url); |
|||
|
|||
// URL后缀 |
|||
$suffix = in_array($url, ['/', '']) ? '' : self::parseSuffix($suffix); |
|||
// 锚点 |
|||
$anchor = !empty($anchor) ? '#' . $anchor : ''; |
|||
// 参数组装 |
|||
if (!empty($vars)) { |
|||
// 添加参数 |
|||
if (Config::get('url_common_param')) { |
|||
$vars = urldecode(http_build_query($vars)); |
|||
$url .= $suffix . '?' . $vars . $anchor; |
|||
} else { |
|||
$paramType = Config::get('url_param_type'); |
|||
foreach ($vars as $var => $val) { |
|||
if ('' !== trim($val)) { |
|||
if ($paramType) { |
|||
$url .= $depr . urlencode($val); |
|||
} else { |
|||
$url .= $depr . $var . $depr . urlencode($val); |
|||
} |
|||
} |
|||
} |
|||
$url .= $suffix . $anchor; |
|||
} |
|||
} else { |
|||
$url .= $suffix . $anchor; |
|||
} |
|||
// 检测域名 |
|||
$domain = self::parseDomain($url, $domain); |
|||
// URL组装 |
|||
$url = $domain . rtrim(self::$root ?: Request::instance()->root(), '/') . '/' . ltrim($url, '/'); |
|||
|
|||
self::$bindCheck = false; |
|||
return $url; |
|||
} |
|||
|
|||
// 直接解析URL地址 |
|||
protected static function parseUrl($url, &$domain) |
|||
{ |
|||
$request = Request::instance(); |
|||
if (0 === strpos($url, '/')) { |
|||
// 直接作为路由地址解析 |
|||
$url = substr($url, 1); |
|||
} elseif (false !== strpos($url, '\\')) { |
|||
// 解析到类 |
|||
$url = ltrim(str_replace('\\', '/', $url), '/'); |
|||
} elseif (0 === strpos($url, '@')) { |
|||
// 解析到控制器 |
|||
$url = substr($url, 1); |
|||
} else { |
|||
// 解析到 模块/控制器/操作 |
|||
$module = $request->module(); |
|||
$domains = Route::rules('domain'); |
|||
if (true === $domain && 2 == substr_count($url, '/')) { |
|||
$current = $request->host(); |
|||
$match = []; |
|||
$pos = []; |
|||
foreach ($domains as $key => $item) { |
|||
if (isset($item['[bind]']) && 0 === strpos($url, $item['[bind]'][0])) { |
|||
$pos[$key] = strlen($item['[bind]'][0]) + 1; |
|||
$match[] = $key; |
|||
$module = ''; |
|||
} |
|||
} |
|||
if ($match) { |
|||
$domain = current($match); |
|||
foreach ($match as $item) { |
|||
if (0 === strpos($current, $item)) { |
|||
$domain = $item; |
|||
} |
|||
} |
|||
self::$bindCheck = true; |
|||
$url = substr($url, $pos[$domain]); |
|||
} |
|||
} elseif ($domain) { |
|||
if (isset($domains[$domain]['[bind]'][0])) { |
|||
$bindModule = $domains[$domain]['[bind]'][0]; |
|||
if ($bindModule && !in_array($bindModule[0], ['\\', '@'])) { |
|||
$module = ''; |
|||
} |
|||
} |
|||
} |
|||
$module = $module ? $module . '/' : ''; |
|||
|
|||
$controller = Loader::parseName($request->controller()); |
|||
if ($controller && !preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) { |
|||
|
|||
throw new HttpException(404, 'controller not exists:' . $controller); |
|||
} |
|||
if ('' == $url) { |
|||
// 空字符串输出当前的 模块/控制器/操作 |
|||
$url = $module . $controller . '/' . $request->action(); |
|||
} else { |
|||
$path = explode('/', $url); |
|||
$action = Config::get('url_convert') ? strtolower(array_pop($path)) : array_pop($path); |
|||
$controller = empty($path) ? $controller : (Config::get('url_convert') ? Loader::parseName(array_pop($path)) : array_pop($path)); |
|||
$module = empty($path) ? $module : array_pop($path) . '/'; |
|||
$url = $module . $controller . '/' . $action; |
|||
} |
|||
} |
|||
return $url; |
|||
} |
|||
|
|||
// 检测域名 |
|||
protected static function parseDomain(&$url, $domain) |
|||
{ |
|||
if (!$domain) { |
|||
return ''; |
|||
} |
|||
$request = Request::instance(); |
|||
$rootDomain = Config::get('url_domain_root'); |
|||
if (true === $domain) { |
|||
// 自动判断域名 |
|||
$domain = $request->host(); |
|||
|
|||
$domains = Route::rules('domain'); |
|||
if ($domains) { |
|||
$route_domain = array_keys($domains); |
|||
foreach ($route_domain as $domain_prefix) { |
|||
if (0 === strpos($domain_prefix, '*.') && strpos($domain, ltrim($domain_prefix, '*.')) !== false) { |
|||
foreach ($domains as $key => $rule) { |
|||
$rule = is_array($rule) ? $rule[0] : $rule; |
|||
if (is_string($rule) && false === strpos($key, '*') && 0 === strpos($url, $rule)) { |
|||
$url = ltrim($url, $rule); |
|||
$domain = $key; |
|||
// 生成对应子域名 |
|||
if (!empty($rootDomain)) { |
|||
$domain .= $rootDomain; |
|||
} |
|||
break; |
|||
} elseif (false !== strpos($key, '*')) { |
|||
if (!empty($rootDomain)) { |
|||
$domain .= $rootDomain; |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} else { |
|||
if (empty($rootDomain)) { |
|||
$host = $request->host(); |
|||
$rootDomain = substr_count($host, '.') > 1 ? substr(strstr($host, '.'), 1) : $host; |
|||
} |
|||
if (!strpos($domain, $rootDomain)) { |
|||
$domain .= '.' . $rootDomain; |
|||
} |
|||
} |
|||
return ($request->isSsl() ? 'https://' : 'http://') . $domain; |
|||
} |
|||
|
|||
// 解析URL后缀 |
|||
protected static function parseSuffix($suffix) |
|||
{ |
|||
if ($suffix) { |
|||
$suffix = true === $suffix ? Config::get('url_html_suffix') : $suffix; |
|||
if ($pos = strpos($suffix, '|')) { |
|||
$suffix = substr($suffix, 0, $pos); |
|||
} |
|||
} |
|||
return (empty($suffix) || 0 === strpos($suffix, '.')) ? $suffix : '.' . $suffix; |
|||
} |
|||
|
|||
// 匹配路由地址 |
|||
public static function getRuleUrl($rule, &$vars = []) |
|||
{ |
|||
foreach ($rule as $item) { |
|||
list($url, $pattern, $domain, $suffix) = $item; |
|||
if (empty($pattern)) { |
|||
return [$url, $domain, $suffix]; |
|||
} |
|||
foreach ($pattern as $key => $val) { |
|||
if (isset($vars[$key])) { |
|||
$url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key . '', '<' . $key . '>'], $vars[$key], $url); |
|||
unset($vars[$key]); |
|||
$result = [$url, $domain, $suffix]; |
|||
} elseif (2 == $val) { |
|||
$url = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url); |
|||
$result = [$url, $domain, $suffix]; |
|||
} else { |
|||
break; |
|||
} |
|||
} |
|||
if (isset($result)) { |
|||
return $result; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
// 指定当前生成URL地址的root |
|||
public static function root($root) |
|||
{ |
|||
self::$root = $root; |
|||
Request::instance()->root($root); |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,236 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
class View |
|||
{ |
|||
// 视图实例 |
|||
protected static $instance; |
|||
// 模板引擎实例 |
|||
public $engine; |
|||
// 模板变量 |
|||
protected $data = []; |
|||
// 用于静态赋值的模板变量 |
|||
protected static $var = []; |
|||
// 视图输出替换 |
|||
protected $replace = []; |
|||
|
|||
/** |
|||
* 构造函数 |
|||
* @access public |
|||
* @param array $engine 模板引擎参数 |
|||
* @param array $replace 字符串替换参数 |
|||
*/ |
|||
public function __construct($engine = [], $replace = []) |
|||
{ |
|||
// 初始化模板引擎 |
|||
$this->engine((array) $engine); |
|||
// 基础替换字符串 |
|||
$request = Request::instance(); |
|||
$base = $request->root(); |
|||
$root = strpos($base, '.') ? ltrim(dirname($base), DS) : $base; |
|||
if ('' != $root) { |
|||
$root = '/' . ltrim($root, '/'); |
|||
} |
|||
$baseReplace = [ |
|||
'__ROOT__' => $root, |
|||
'__URL__' => $base . '/' . $request->module() . '/' . Loader::parseName($request->controller()), |
|||
'__STATIC__' => $root . '/static', |
|||
'__CSS__' => $root . '/static/css', |
|||
'__JS__' => $root . '/static/js', |
|||
]; |
|||
$this->replace = array_merge($baseReplace, (array) $replace); |
|||
} |
|||
|
|||
/** |
|||
* 初始化视图 |
|||
* @access public |
|||
* @param array $engine 模板引擎参数 |
|||
* @param array $replace 字符串替换参数 |
|||
* @return object |
|||
*/ |
|||
public static function instance($engine = [], $replace = []) |
|||
{ |
|||
if (is_null(self::$instance)) { |
|||
self::$instance = new self($engine, $replace); |
|||
} |
|||
return self::$instance; |
|||
} |
|||
|
|||
/** |
|||
* 模板变量静态赋值 |
|||
* @access public |
|||
* @param mixed $name 变量名 |
|||
* @param mixed $value 变量值 |
|||
* @return void |
|||
*/ |
|||
public static function share($name, $value = '') |
|||
{ |
|||
if (is_array($name)) { |
|||
self::$var = array_merge(self::$var, $name); |
|||
} else { |
|||
self::$var[$name] = $value; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 模板变量赋值 |
|||
* @access public |
|||
* @param mixed $name 变量名 |
|||
* @param mixed $value 变量值 |
|||
* @return $this |
|||
*/ |
|||
public function assign($name, $value = '') |
|||
{ |
|||
if (is_array($name)) { |
|||
$this->data = array_merge($this->data, $name); |
|||
} else { |
|||
$this->data[$name] = $value; |
|||
} |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 设置当前模板解析的引擎 |
|||
* @access public |
|||
* @param array|string $options 引擎参数 |
|||
* @return $this |
|||
*/ |
|||
public function engine($options = []) |
|||
{ |
|||
if (is_string($options)) { |
|||
$type = $options; |
|||
$options = []; |
|||
} else { |
|||
$type = !empty($options['type']) ? $options['type'] : 'Think'; |
|||
} |
|||
|
|||
$class = false !== strpos($type, '\\') ? $type : '\\think\\view\\driver\\' . ucfirst($type); |
|||
if (isset($options['type'])) { |
|||
unset($options['type']); |
|||
} |
|||
$this->engine = new $class($options); |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 配置模板引擎 |
|||
* @access private |
|||
* @param string|array $name 参数名 |
|||
* @param mixed $value 参数值 |
|||
* @return void |
|||
*/ |
|||
public function config($name, $value = null) |
|||
{ |
|||
$this->engine->config($name, $value); |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 解析和获取模板内容 用于输出 |
|||
* @param string $template 模板文件名或者内容 |
|||
* @param array $vars 模板输出变量 |
|||
* @param array $replace 替换内容 |
|||
* @param array $config 模板参数 |
|||
* @param bool $renderContent 是否渲染内容 |
|||
* @return string |
|||
* @throws Exception |
|||
*/ |
|||
public function fetch($template = '', $vars = [], $replace = [], $config = [], $renderContent = false) |
|||
{ |
|||
// 模板变量 |
|||
$vars = array_merge(self::$var, $this->data, $vars); |
|||
|
|||
// 页面缓存 |
|||
ob_start(); |
|||
ob_implicit_flush(0); |
|||
|
|||
// 渲染输出 |
|||
$method = $renderContent ? 'display' : 'fetch'; |
|||
$this->engine->$method($template, $vars, $config); |
|||
|
|||
// 获取并清空缓存 |
|||
$content = ob_get_clean(); |
|||
// 内容过滤标签 |
|||
Hook::listen('view_filter', $content); |
|||
// 允许用户自定义模板的字符串替换 |
|||
$replace = array_merge($this->replace, $replace); |
|||
if (!empty($replace)) { |
|||
$content = strtr($content, $replace); |
|||
} |
|||
return $content; |
|||
} |
|||
|
|||
/** |
|||
* 视图内容替换 |
|||
* @access public |
|||
* @param string|array $content 被替换内容(支持批量替换) |
|||
* @param string $replace 替换内容 |
|||
* @return $this |
|||
*/ |
|||
public function replace($content, $replace = '') |
|||
{ |
|||
if (is_array($content)) { |
|||
$this->replace = array_merge($this->replace, $content); |
|||
} else { |
|||
$this->replace[$content] = $replace; |
|||
} |
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* 渲染内容输出 |
|||
* @access public |
|||
* @param string $content 内容 |
|||
* @param array $vars 模板输出变量 |
|||
* @param array $replace 替换内容 |
|||
* @param array $config 模板参数 |
|||
* @return mixed |
|||
*/ |
|||
public function display($content, $vars = [], $replace = [], $config = []) |
|||
{ |
|||
return $this->fetch($content, $vars, $replace, $config, true); |
|||
} |
|||
|
|||
/** |
|||
* 模板变量赋值 |
|||
* @access public |
|||
* @param string $name 变量名 |
|||
* @param mixed $value 变量值 |
|||
*/ |
|||
public function __set($name, $value) |
|||
{ |
|||
$this->data[$name] = $value; |
|||
} |
|||
|
|||
/** |
|||
* 取得模板显示变量的值 |
|||
* @access protected |
|||
* @param string $name 模板变量 |
|||
* @return mixed |
|||
*/ |
|||
public function __get($name) |
|||
{ |
|||
return $this->data[$name]; |
|||
} |
|||
|
|||
/** |
|||
* 检测模板变量是否设置 |
|||
* @access public |
|||
* @param string $name 模板变量名 |
|||
* @return bool |
|||
*/ |
|||
public function __isset($name) |
|||
{ |
|||
return isset($this->data[$name]); |
|||
} |
|||
} |
|||
@ -0,0 +1,158 @@ |
|||
<?php |
|||
|
|||
/** |
|||
* 用法: |
|||
* load_trait('controller/Jump'); |
|||
* class index |
|||
* { |
|||
* use \traits\controller\Jump; |
|||
* public function index(){ |
|||
* $this->error(); |
|||
* $this->redirect(); |
|||
* } |
|||
* } |
|||
*/ |
|||
namespace traits\controller; |
|||
|
|||
use think\Config; |
|||
use think\exception\HttpResponseException; |
|||
use think\Request; |
|||
use think\Response; |
|||
use think\response\Redirect; |
|||
use think\Url; |
|||
use think\View as ViewTemplate; |
|||
|
|||
trait Jump |
|||
{ |
|||
/** |
|||
* 操作成功跳转的快捷方法 |
|||
* @access protected |
|||
* @param mixed $msg 提示信息 |
|||
* @param string $url 跳转的URL地址 |
|||
* @param mixed $data 返回的数据 |
|||
* @param integer $wait 跳转等待时间 |
|||
* @param array $header 发送的Header信息 |
|||
* @return void |
|||
*/ |
|||
protected function success($msg = '', $url = null, $data = '', $wait = 3, array $header = []) |
|||
{ |
|||
$code = 1; |
|||
if (is_numeric($msg)) { |
|||
$code = $msg; |
|||
$msg = ''; |
|||
} |
|||
if (is_null($url) && isset($_SERVER["HTTP_REFERER"])) { |
|||
$url = $_SERVER["HTTP_REFERER"]; |
|||
} elseif ('' !== $url) { |
|||
$url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : Url::build($url); |
|||
} |
|||
$result = [ |
|||
'code' => $code, |
|||
'msg' => $msg, |
|||
'data' => $data, |
|||
'url' => $url, |
|||
'wait' => $wait, |
|||
]; |
|||
|
|||
$type = $this->getResponseType(); |
|||
if ('html' == strtolower($type)) { |
|||
$result = ViewTemplate::instance(Config::get('template'), Config::get('view_replace_str')) |
|||
->fetch(Config::get('dispatch_success_tmpl'), $result); |
|||
} |
|||
$response = Response::create($result, $type)->header($header); |
|||
throw new HttpResponseException($response); |
|||
} |
|||
|
|||
/** |
|||
* 操作错误跳转的快捷方法 |
|||
* @access protected |
|||
* @param mixed $msg 提示信息 |
|||
* @param string $url 跳转的URL地址 |
|||
* @param mixed $data 返回的数据 |
|||
* @param integer $wait 跳转等待时间 |
|||
* @param array $header 发送的Header信息 |
|||
* @return void |
|||
*/ |
|||
protected function error($msg = '', $url = null, $data = '', $wait = 3, array $header = []) |
|||
{ |
|||
$code = 0; |
|||
if (is_numeric($msg)) { |
|||
$code = $msg; |
|||
$msg = ''; |
|||
} |
|||
if (is_null($url)) { |
|||
$url = Request::instance()->isAjax() ? '' : 'javascript:history.back(-1);'; |
|||
} elseif ('' !== $url) { |
|||
$url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : Url::build($url); |
|||
} |
|||
$result = [ |
|||
'code' => $code, |
|||
'msg' => $msg, |
|||
'data' => $data, |
|||
'url' => $url, |
|||
'wait' => $wait, |
|||
]; |
|||
|
|||
$type = $this->getResponseType(); |
|||
if ('html' == strtolower($type)) { |
|||
$result = ViewTemplate::instance(Config::get('template'), Config::get('view_replace_str')) |
|||
->fetch(Config::get('dispatch_error_tmpl'), $result); |
|||
} |
|||
$response = Response::create($result, $type)->header($header); |
|||
throw new HttpResponseException($response); |
|||
} |
|||
|
|||
/** |
|||
* 返回封装后的API数据到客户端 |
|||
* @access protected |
|||
* @param mixed $data 要返回的数据 |
|||
* @param integer $code 返回的code |
|||
* @param mixed $msg 提示信息 |
|||
* @param string $type 返回数据格式 |
|||
* @param array $header 发送的Header信息 |
|||
* @return void |
|||
*/ |
|||
protected function result($data, $code = 0, $msg = '', $type = '', array $header = []) |
|||
{ |
|||
$result = [ |
|||
'code' => $code, |
|||
'msg' => $msg, |
|||
'time' => $_SERVER['REQUEST_TIME'], |
|||
'data' => $data, |
|||
]; |
|||
$type = $type ?: $this->getResponseType(); |
|||
$response = Response::create($result, $type)->header($header); |
|||
throw new HttpResponseException($response); |
|||
} |
|||
|
|||
/** |
|||
* URL重定向 |
|||
* @access protected |
|||
* @param string $url 跳转的URL表达式 |
|||
* @param array|integer $params 其它URL参数 |
|||
* @param integer $code http code |
|||
* @param array $with 隐式传参 |
|||
* @return void |
|||
*/ |
|||
protected function redirect($url, $params = [], $code = 302, $with = []) |
|||
{ |
|||
$response = new Redirect($url); |
|||
if (is_integer($params)) { |
|||
$code = $params; |
|||
$params = []; |
|||
} |
|||
$response->code($code)->params($params)->with($with); |
|||
throw new HttpResponseException($response); |
|||
} |
|||
|
|||
/** |
|||
* 获取当前的response 输出类型 |
|||
* @access protected |
|||
* @return string |
|||
*/ |
|||
protected function getResponseType() |
|||
{ |
|||
$isAjax = Request::instance()->isAjax(); |
|||
return $isAjax ? Config::get('default_ajax_return') : Config::get('default_return_type'); |
|||
} |
|||
} |
|||
@ -0,0 +1,153 @@ |
|||
<?php |
|||
|
|||
namespace traits\model; |
|||
|
|||
use think\db\Query; |
|||
|
|||
trait SoftDelete |
|||
{ |
|||
|
|||
/** |
|||
* 判断当前实例是否被软删除 |
|||
* @access public |
|||
* @return boolean |
|||
*/ |
|||
public function trashed() |
|||
{ |
|||
$field = $this->getDeleteTimeField(); |
|||
if (!empty($this->data[$field])) { |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 查询软删除数据 |
|||
* @access public |
|||
* @return Query |
|||
*/ |
|||
public static function withTrashed() |
|||
{ |
|||
$model = new static(); |
|||
$field = $model->getDeleteTimeField(true); |
|||
return $model->db(false)->removeWhereField($field); |
|||
} |
|||
|
|||
/** |
|||
* 只查询软删除数据 |
|||
* @access public |
|||
* @return Query |
|||
*/ |
|||
public static function onlyTrashed() |
|||
{ |
|||
$model = new static(); |
|||
$field = $model->getDeleteTimeField(true); |
|||
return $model->db(false)->whereNotNull($field); |
|||
} |
|||
|
|||
/** |
|||
* 删除当前的记录 |
|||
* @access public |
|||
* @param bool $force 是否强制删除 |
|||
* @return integer |
|||
*/ |
|||
public function delete($force = false) |
|||
{ |
|||
if (false === $this->trigger('before_delete', $this)) { |
|||
return false; |
|||
} |
|||
$name = $this->getDeleteTimeField(); |
|||
if (!$force) { |
|||
// 软删除 |
|||
$this->change[] = $name; |
|||
$this->data[$name] = $this->autoWriteTimestamp($name); |
|||
$result = $this->isUpdate()->save(); |
|||
} else { |
|||
$result = $this->db(false)->delete($this->data); |
|||
} |
|||
|
|||
$this->trigger('after_delete', $this); |
|||
return $result; |
|||
} |
|||
|
|||
/** |
|||
* 删除记录 |
|||
* @access public |
|||
* @param mixed $data 主键列表 支持闭包查询条件 |
|||
* @param bool $force 是否强制删除 |
|||
* @return integer 成功删除的记录数 |
|||
*/ |
|||
public static function destroy($data, $force = false) |
|||
{ |
|||
// 包含软删除数据 |
|||
$query = self::withTrashed(); |
|||
if (is_array($data) && key($data) !== 0) { |
|||
$query->where($data); |
|||
$data = null; |
|||
} elseif ($data instanceof \Closure) { |
|||
call_user_func_array($data, [ & $query]); |
|||
$data = null; |
|||
} elseif (is_null($data)) { |
|||
return 0; |
|||
} |
|||
|
|||
$resultSet = $query->select($data); |
|||
$count = 0; |
|||
if ($resultSet) { |
|||
foreach ($resultSet as $data) { |
|||
$result = $data->delete($force); |
|||
$count += $result; |
|||
} |
|||
} |
|||
return $count; |
|||
} |
|||
|
|||
/** |
|||
* 恢复被软删除的记录 |
|||
* @access public |
|||
* @param array $where 更新条件 |
|||
* @return integer |
|||
*/ |
|||
public function restore($where = []) |
|||
{ |
|||
$name = $this->getDeleteTimeField(); |
|||
if (empty($where)) { |
|||
$pk = $this->getPk(); |
|||
$where[$pk] = $this->getData($pk); |
|||
$where[$name] = ['not null', '']; |
|||
} |
|||
// 恢复删除 |
|||
return $this->db(false)->removeWhereField($this->getDeleteTimeField(true))->where($where)->update([$name => null]); |
|||
} |
|||
|
|||
/** |
|||
* 查询默认不包含软删除数据 |
|||
* @access protected |
|||
* @param Query $query 查询对象 |
|||
* @return void |
|||
*/ |
|||
protected function base($query) |
|||
{ |
|||
$field = $this->getDeleteTimeField(true); |
|||
$query->whereNull($field); |
|||
} |
|||
|
|||
/** |
|||
* 获取软删除字段 |
|||
* @access public |
|||
* @param bool $read 是否查询操作 写操作的时候会自动去掉表别名 |
|||
* @return string |
|||
*/ |
|||
protected function getDeleteTimeField($read = false) |
|||
{ |
|||
$field = isset($this->deleteTime) ? $this->deleteTime : 'delete_time'; |
|||
if (!strpos($field, '.')) { |
|||
$field = '__TABLE__.' . $field; |
|||
} |
|||
if (!$read && strpos($field, '.')) { |
|||
$array = explode('.', $field); |
|||
$field = array_pop($array); |
|||
} |
|||
return $field; |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace traits\think; |
|||
|
|||
use think\Exception; |
|||
|
|||
trait Instance |
|||
{ |
|||
protected static $instance = null; |
|||
|
|||
/** |
|||
* @param array $options |
|||
* @return static |
|||
*/ |
|||
public static function instance($options = []) |
|||
{ |
|||
if (is_null(self::$instance)) { |
|||
self::$instance = new self($options); |
|||
} |
|||
return self::$instance; |
|||
} |
|||
|
|||
// 静态调用 |
|||
public static function __callStatic($method, $params) |
|||
{ |
|||
if (is_null(self::$instance)) { |
|||
self::$instance = new self(); |
|||
} |
|||
$call = substr($method, 1); |
|||
if (0 === strpos($method, '_') && is_callable([self::$instance, $call])) { |
|||
return call_user_func_array([self::$instance, $call], $params); |
|||
} else { |
|||
throw new Exception("method not exists:" . $method); |
|||
} |
|||
} |
|||
} |
|||
|
After Width: | Height: | Size: 6.8 KiB |
@ -0,0 +1,35 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<phpunit backupGlobals="false" |
|||
backupStaticAttributes="false" |
|||
bootstrap="tests/mock.php" |
|||
colors="true" |
|||
convertErrorsToExceptions="true" |
|||
convertNoticesToExceptions="true" |
|||
convertWarningsToExceptions="true" |
|||
processIsolation="false" |
|||
stopOnFailure="false" |
|||
syntaxCheck="false"> |
|||
<testsuites> |
|||
<testsuite name="ThinkPHP Test Suite"> |
|||
<directory>./tests/thinkphp/</directory> |
|||
</testsuite> |
|||
</testsuites> |
|||
<listeners> |
|||
<listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener" /> |
|||
</listeners> |
|||
<filter> |
|||
<whitelist> |
|||
<directory suffix=".php">./</directory> |
|||
<exclude> |
|||
<directory suffix=".php">tests</directory> |
|||
<directory suffix=".php">vendor</directory> |
|||
</exclude> |
|||
</whitelist> |
|||
</filter> |
|||
<php> |
|||
<env name="APP_ENV" value="testing"/> |
|||
<env name="CACHE_DRIVER" value="array"/> |
|||
<env name="SESSION_DRIVER" value="array"/> |
|||
<env name="QUEUE_DRIVER" value="sync"/> |
|||
</php> |
|||
</phpunit> |
|||
@ -0,0 +1,18 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | ThinkPHP [ WE CAN DO IT JUST THINK ] |
|||
// +---------------------------------------------------------------------- |
|||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. |
|||
// +---------------------------------------------------------------------- |
|||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: liu21st <liu21st@gmail.com> |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace think; |
|||
|
|||
// ThinkPHP 引导文件 |
|||
// 加载基础文件 |
|||
require __DIR__ . '/base.php'; |
|||
// 执行应用 |
|||
App::run()->send(); |
|||
@ -0,0 +1,10 @@ |
|||
<?php |
|||
namespace {$app}\{$module}{layer}; |
|||
|
|||
class Index{$suffix} |
|||
{ |
|||
public function index() |
|||
{ |
|||
return '<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px;} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="http://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="http://ad.topthink.com/Public/static/client.js"></script><thinkad id="ad_bd568ce7058a1091"></thinkad>'; |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
{__NOLAYOUT__}<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
|||
<html xmlns="http://www.w3.org/1999/xhtml"> |
|||
<head> |
|||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
|||
<title>跳转提示</title> |
|||
<style type="text/css"> |
|||
*{ padding: 0; margin: 0; } |
|||
body{ background: #fff; font-family: "Microsoft Yahei","Helvetica Neue",Helvetica,Arial,sans-serif; color: #333; font-size: 16px; } |
|||
.system-message{ padding: 24px 48px; } |
|||
.system-message h1{ font-size: 100px; font-weight: normal; line-height: 120px; margin-bottom: 12px; } |
|||
.system-message .jump{ padding-top: 10px; } |
|||
.system-message .jump a{ color: #333; } |
|||
.system-message .success,.system-message .error{ line-height: 1.8em; font-size: 36px; } |
|||
.system-message .detail{ font-size: 12px; line-height: 20px; margin-top: 12px; display: none; } |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div class="system-message"> |
|||
<?php switch ($code) {?> |
|||
<?php case 1:?> |
|||
<h1>:)</h1> |
|||
<p class="success"><?php echo(strip_tags($msg));?></p> |
|||
<?php break;?> |
|||
<?php case 0:?> |
|||
<h1>:(</h1> |
|||
<p class="error"><?php echo(strip_tags($msg));?></p> |
|||
<?php break;?> |
|||
<?php } ?> |
|||
<p class="detail"></p> |
|||
<p class="jump"> |
|||
页面自动 <a id="href" href="<?php echo($url);?>">跳转</a> 等待时间: <b id="wait"><?php echo($wait);?></b> |
|||
</p> |
|||
</div> |
|||
<script type="text/javascript"> |
|||
(function(){ |
|||
var wait = document.getElementById('wait'), |
|||
href = document.getElementById('href').href; |
|||
var interval = setInterval(function(){ |
|||
var time = --wait.innerHTML; |
|||
if(time <= 0) { |
|||
location.href = href; |
|||
clearInterval(interval); |
|||
}; |
|||
}, 1000); |
|||
})(); |
|||
</script> |
|||
</body> |
|||
</html> |
|||
File diff suppressed because one or more lines are too long
@ -0,0 +1,537 @@ |
|||
<?php |
|||
if(!function_exists('parse_padding')){ |
|||
function parse_padding($source) |
|||
{ |
|||
$length = strlen(strval(count($source['source']) + $source['first'])); |
|||
return 40 + ($length - 1) * 8; |
|||
} |
|||
} |
|||
|
|||
if(!function_exists('parse_class')){ |
|||
function parse_class($name) |
|||
{ |
|||
$names = explode('\\', $name); |
|||
return '<abbr title="'.$name.'">'.end($names).'</abbr>'; |
|||
} |
|||
} |
|||
|
|||
if(!function_exists('parse_file')){ |
|||
function parse_file($file, $line) |
|||
{ |
|||
return '<a class="toggle" title="'."{$file} line {$line}".'">'.basename($file)." line {$line}".'</a>'; |
|||
} |
|||
} |
|||
|
|||
if(!function_exists('parse_args')){ |
|||
function parse_args($args) |
|||
{ |
|||
$result = []; |
|||
|
|||
foreach ($args as $key => $item) { |
|||
switch (true) { |
|||
case is_object($item): |
|||
$value = sprintf('<em>object</em>(%s)', parse_class(get_class($item))); |
|||
break; |
|||
case is_array($item): |
|||
if(count($item) > 3){ |
|||
$value = sprintf('[%s, ...]', parse_args(array_slice($item, 0, 3))); |
|||
} else { |
|||
$value = sprintf('[%s]', parse_args($item)); |
|||
} |
|||
break; |
|||
case is_string($item): |
|||
if(strlen($item) > 20){ |
|||
$value = sprintf( |
|||
'\'<a class="toggle" title="%s">%s...</a>\'', |
|||
htmlentities($item), |
|||
htmlentities(substr($item, 0, 20)) |
|||
); |
|||
} else { |
|||
$value = sprintf("'%s'", htmlentities($item)); |
|||
} |
|||
break; |
|||
case is_int($item): |
|||
case is_float($item): |
|||
$value = $item; |
|||
break; |
|||
case is_null($item): |
|||
$value = '<em>null</em>'; |
|||
break; |
|||
case is_bool($item): |
|||
$value = '<em>' . ($item ? 'true' : 'false') . '</em>'; |
|||
break; |
|||
case is_resource($item): |
|||
$value = '<em>resource</em>'; |
|||
break; |
|||
default: |
|||
$value = htmlentities(str_replace("\n", '', var_export(strval($item), true))); |
|||
break; |
|||
} |
|||
|
|||
$result[] = is_int($key) ? $value : "'{$key}' => {$value}"; |
|||
} |
|||
|
|||
return implode(', ', $result); |
|||
} |
|||
} |
|||
?> |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<title><?php echo lang('System Error'); ?></title> |
|||
<meta name="robots" content="noindex,nofollow" /> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> |
|||
<style> |
|||
/* Base */ |
|||
body { |
|||
color: #333; |
|||
font: 14px Verdana, "Helvetica Neue", helvetica, Arial, 'Microsoft YaHei', sans-serif; |
|||
margin: 0; |
|||
padding: 0 20px 20px; |
|||
word-break: break-word; |
|||
} |
|||
h1{ |
|||
margin: 10px 0 0; |
|||
font-size: 28px; |
|||
font-weight: 500; |
|||
line-height: 32px; |
|||
} |
|||
h2{ |
|||
color: #4288ce; |
|||
font-weight: 400; |
|||
padding: 6px 0; |
|||
margin: 6px 0 0; |
|||
font-size: 18px; |
|||
border-bottom: 1px solid #eee; |
|||
} |
|||
h3.subheading { |
|||
color: #4288ce; |
|||
margin: 6px 0 0; |
|||
font-weight: 400; |
|||
} |
|||
h3{ |
|||
margin: 12px; |
|||
font-size: 16px; |
|||
font-weight: bold; |
|||
} |
|||
abbr{ |
|||
cursor: help; |
|||
text-decoration: underline; |
|||
text-decoration-style: dotted; |
|||
} |
|||
a{ |
|||
color: #868686; |
|||
cursor: pointer; |
|||
} |
|||
a:hover{ |
|||
text-decoration: underline; |
|||
} |
|||
.line-error{ |
|||
background: #f8cbcb; |
|||
} |
|||
|
|||
.echo table { |
|||
width: 100%; |
|||
} |
|||
|
|||
.echo pre { |
|||
padding: 16px; |
|||
overflow: auto; |
|||
font-size: 85%; |
|||
line-height: 1.45; |
|||
background-color: #f7f7f7; |
|||
border: 0; |
|||
border-radius: 3px; |
|||
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; |
|||
} |
|||
|
|||
.echo pre > pre { |
|||
padding: 0; |
|||
margin: 0; |
|||
} |
|||
/* Layout */ |
|||
.col-md-3 { |
|||
width: 25%; |
|||
} |
|||
.col-md-9 { |
|||
width: 75%; |
|||
} |
|||
[class^="col-md-"] { |
|||
float: left; |
|||
} |
|||
.clearfix { |
|||
clear:both; |
|||
} |
|||
@media only screen |
|||
and (min-device-width : 375px) |
|||
and (max-device-width : 667px) { |
|||
.col-md-3, |
|||
.col-md-9 { |
|||
width: 100%; |
|||
} |
|||
} |
|||
/* Exception Info */ |
|||
.exception { |
|||
margin-top: 20px; |
|||
} |
|||
.exception .message{ |
|||
padding: 12px; |
|||
border: 1px solid #ddd; |
|||
border-bottom: 0 none; |
|||
line-height: 18px; |
|||
font-size:16px; |
|||
border-top-left-radius: 4px; |
|||
border-top-right-radius: 4px; |
|||
font-family: Consolas,"Liberation Mono",Courier,Verdana,"微软雅黑"; |
|||
} |
|||
|
|||
.exception .code{ |
|||
float: left; |
|||
text-align: center; |
|||
color: #fff; |
|||
margin-right: 12px; |
|||
padding: 16px; |
|||
border-radius: 4px; |
|||
background: #999; |
|||
} |
|||
.exception .source-code{ |
|||
padding: 6px; |
|||
border: 1px solid #ddd; |
|||
|
|||
background: #f9f9f9; |
|||
overflow-x: auto; |
|||
|
|||
} |
|||
.exception .source-code pre{ |
|||
margin: 0; |
|||
} |
|||
.exception .source-code pre ol{ |
|||
margin: 0; |
|||
color: #4288ce; |
|||
display: inline-block; |
|||
min-width: 100%; |
|||
box-sizing: border-box; |
|||
font-size:14px; |
|||
font-family: "Century Gothic",Consolas,"Liberation Mono",Courier,Verdana; |
|||
padding-left: <?php echo (isset($source) && !empty($source)) ? parse_padding($source) : 40; ?>px; |
|||
} |
|||
.exception .source-code pre li{ |
|||
border-left: 1px solid #ddd; |
|||
height: 18px; |
|||
line-height: 18px; |
|||
} |
|||
.exception .source-code pre code{ |
|||
color: #333; |
|||
height: 100%; |
|||
display: inline-block; |
|||
border-left: 1px solid #fff; |
|||
font-size:14px; |
|||
font-family: Consolas,"Liberation Mono",Courier,Verdana,"微软雅黑"; |
|||
} |
|||
.exception .trace{ |
|||
padding: 6px; |
|||
border: 1px solid #ddd; |
|||
border-top: 0 none; |
|||
line-height: 16px; |
|||
font-size:14px; |
|||
font-family: Consolas,"Liberation Mono",Courier,Verdana,"微软雅黑"; |
|||
} |
|||
.exception .trace ol{ |
|||
margin: 12px; |
|||
} |
|||
.exception .trace ol li{ |
|||
padding: 2px 4px; |
|||
} |
|||
.exception div:last-child{ |
|||
border-bottom-left-radius: 4px; |
|||
border-bottom-right-radius: 4px; |
|||
} |
|||
|
|||
/* Exception Variables */ |
|||
.exception-var table{ |
|||
width: 100%; |
|||
margin: 12px 0; |
|||
box-sizing: border-box; |
|||
table-layout:fixed; |
|||
word-wrap:break-word; |
|||
} |
|||
.exception-var table caption{ |
|||
text-align: left; |
|||
font-size: 16px; |
|||
font-weight: bold; |
|||
padding: 6px 0; |
|||
} |
|||
.exception-var table caption small{ |
|||
font-weight: 300; |
|||
display: inline-block; |
|||
margin-left: 10px; |
|||
color: #ccc; |
|||
} |
|||
.exception-var table tbody{ |
|||
font-size: 13px; |
|||
font-family: Consolas,"Liberation Mono",Courier,"微软雅黑"; |
|||
} |
|||
.exception-var table td{ |
|||
padding: 0 6px; |
|||
vertical-align: top; |
|||
word-break: break-all; |
|||
} |
|||
.exception-var table td:first-child{ |
|||
width: 28%; |
|||
font-weight: bold; |
|||
white-space: nowrap; |
|||
} |
|||
.exception-var table td pre{ |
|||
margin: 0; |
|||
} |
|||
|
|||
/* Copyright Info */ |
|||
.copyright{ |
|||
margin-top: 24px; |
|||
padding: 12px 0; |
|||
border-top: 1px solid #eee; |
|||
} |
|||
|
|||
/* SPAN elements with the classes below are added by prettyprint. */ |
|||
pre.prettyprint .pln { color: #000 } /* plain text */ |
|||
pre.prettyprint .str { color: #080 } /* string content */ |
|||
pre.prettyprint .kwd { color: #008 } /* a keyword */ |
|||
pre.prettyprint .com { color: #800 } /* a comment */ |
|||
pre.prettyprint .typ { color: #606 } /* a type name */ |
|||
pre.prettyprint .lit { color: #066 } /* a literal value */ |
|||
/* punctuation, lisp open bracket, lisp close bracket */ |
|||
pre.prettyprint .pun, pre.prettyprint .opn, pre.prettyprint .clo { color: #660 } |
|||
pre.prettyprint .tag { color: #008 } /* a markup tag name */ |
|||
pre.prettyprint .atn { color: #606 } /* a markup attribute name */ |
|||
pre.prettyprint .atv { color: #080 } /* a markup attribute value */ |
|||
pre.prettyprint .dec, pre.prettyprint .var { color: #606 } /* a declaration; a variable name */ |
|||
pre.prettyprint .fun { color: red } /* a function name */ |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div class="echo"> |
|||
<?php echo $echo;?> |
|||
</div> |
|||
<?php if(\think\App::$debug) { ?> |
|||
<div class="exception"> |
|||
<div class="message"> |
|||
|
|||
<div class="info"> |
|||
<div> |
|||
<h2>[<?php echo $code; ?>] <?php echo sprintf('%s in %s', parse_class($name), parse_file($file, $line)); ?></h2> |
|||
</div> |
|||
<div><h1><?php echo nl2br(htmlentities($message)); ?></h1></div> |
|||
</div> |
|||
|
|||
</div> |
|||
<?php if(!empty($source)){?> |
|||
<div class="source-code"> |
|||
<pre class="prettyprint lang-php"><ol start="<?php echo $source['first']; ?>"><?php foreach ((array) $source['source'] as $key => $value) { ?><li class="line-<?php echo $key + $source['first']; ?>"><code><?php echo htmlentities($value); ?></code></li><?php } ?></ol></pre> |
|||
</div> |
|||
<?php }?> |
|||
<div class="trace"> |
|||
<h2>Call Stack</h2> |
|||
<ol> |
|||
<li><?php echo sprintf('in %s', parse_file($file, $line)); ?></li> |
|||
<?php foreach ((array) $trace as $value) { ?> |
|||
<li> |
|||
<?php |
|||
// Show Function |
|||
if($value['function']){ |
|||
echo sprintf( |
|||
'at %s%s%s(%s)', |
|||
isset($value['class']) ? parse_class($value['class']) : '', |
|||
isset($value['type']) ? $value['type'] : '', |
|||
$value['function'], |
|||
isset($value['args'])?parse_args($value['args']):'' |
|||
); |
|||
} |
|||
|
|||
// Show line |
|||
if (isset($value['file']) && isset($value['line'])) { |
|||
echo sprintf(' in %s', parse_file($value['file'], $value['line'])); |
|||
} |
|||
?> |
|||
</li> |
|||
<?php } ?> |
|||
</ol> |
|||
</div> |
|||
</div> |
|||
<?php } else { ?> |
|||
<div class="exception"> |
|||
|
|||
<div class="info"><h1><?php echo htmlentities($message); ?></h1></div> |
|||
|
|||
</div> |
|||
<?php } ?> |
|||
|
|||
<?php if(!empty($datas)){ ?> |
|||
<div class="exception-var"> |
|||
<h2>Exception Datas</h2> |
|||
<?php foreach ((array) $datas as $label => $value) { ?> |
|||
<table> |
|||
<?php if(empty($value)){ ?> |
|||
<caption><?php echo $label; ?><small>empty</small></caption> |
|||
<?php } else { ?> |
|||
<caption><?php echo $label; ?></caption> |
|||
<tbody> |
|||
<?php foreach ((array) $value as $key => $val) { ?> |
|||
<tr> |
|||
<td><?php echo htmlentities($key); ?></td> |
|||
<td> |
|||
<?php |
|||
if(is_array($val) || is_object($val)){ |
|||
echo htmlentities(json_encode($val, JSON_PRETTY_PRINT)); |
|||
} else if(is_bool($val)) { |
|||
echo $val ? 'true' : 'false'; |
|||
} else if(is_scalar($val)) { |
|||
echo htmlentities($val); |
|||
} else { |
|||
echo 'Resource'; |
|||
} |
|||
?> |
|||
</td> |
|||
</tr> |
|||
<?php } ?> |
|||
</tbody> |
|||
<?php } ?> |
|||
</table> |
|||
<?php } ?> |
|||
</div> |
|||
<?php } ?> |
|||
|
|||
<?php if(!empty($tables)){ ?> |
|||
<div class="exception-var"> |
|||
<h2>Environment Variables</h2> |
|||
<?php foreach ((array) $tables as $label => $value) { ?> |
|||
<div> |
|||
<?php if(empty($value)){ ?> |
|||
<div class="clearfix"> |
|||
<div class="col-md-3"><strong><?php echo $label; ?></strong></div> |
|||
<div class="col-md-9"><small>empty</small></div> |
|||
</div> |
|||
<?php } else { ?> |
|||
<h3 class="subheading"><?php echo $label; ?></h3> |
|||
<div> |
|||
<?php foreach ((array) $value as $key => $val) { ?> |
|||
<div class="clearfix"> |
|||
<div class="col-md-3"><strong><?php echo htmlentities($key); ?></strong></div> |
|||
<div class="col-md-9"><small> |
|||
<?php |
|||
if(is_array($val) || is_object($val)){ |
|||
echo htmlentities(json_encode($val, JSON_PRETTY_PRINT)); |
|||
} else if(is_bool($val)) { |
|||
echo $val ? 'true' : 'false'; |
|||
} else if(is_scalar($val)) { |
|||
echo htmlentities($val); |
|||
} else { |
|||
echo 'Resource'; |
|||
} |
|||
?> |
|||
</small></div> |
|||
</div> |
|||
<?php } ?> |
|||
</div> |
|||
<?php } ?> |
|||
</div> |
|||
<?php } ?> |
|||
</div> |
|||
<?php } ?> |
|||
|
|||
<div class="copyright"> |
|||
<a title="官方网站" href="http://www.thinkphp.cn">ThinkPHP</a> |
|||
<span>V<?php echo THINK_VERSION; ?></span> |
|||
<span>{ 十年磨一剑-为API开发设计的高性能框架 }</span> |
|||
</div> |
|||
<?php if(\think\App::$debug) { ?> |
|||
<script> |
|||
var LINE = <?php echo $line; ?>; |
|||
|
|||
function $(selector, node){ |
|||
var elements; |
|||
|
|||
node = node || document; |
|||
if(document.querySelectorAll){ |
|||
elements = node.querySelectorAll(selector); |
|||
} else { |
|||
switch(selector.substr(0, 1)){ |
|||
case '#': |
|||
elements = [node.getElementById(selector.substr(1))]; |
|||
break; |
|||
case '.': |
|||
if(document.getElementsByClassName){ |
|||
elements = node.getElementsByClassName(selector.substr(1)); |
|||
} else { |
|||
elements = get_elements_by_class(selector.substr(1), node); |
|||
} |
|||
break; |
|||
default: |
|||
elements = node.getElementsByTagName(); |
|||
} |
|||
} |
|||
return elements; |
|||
|
|||
function get_elements_by_class(search_class, node, tag) { |
|||
var elements = [], eles, |
|||
pattern = new RegExp('(^|\\s)' + search_class + '(\\s|$)'); |
|||
|
|||
node = node || document; |
|||
tag = tag || '*'; |
|||
|
|||
eles = node.getElementsByTagName(tag); |
|||
for(var i = 0; i < eles.length; i++) { |
|||
if(pattern.test(eles[i].className)) { |
|||
elements.push(eles[i]) |
|||
} |
|||
} |
|||
|
|||
return elements; |
|||
} |
|||
} |
|||
|
|||
$.getScript = function(src, func){ |
|||
var script = document.createElement('script'); |
|||
|
|||
script.async = 'async'; |
|||
script.src = src; |
|||
script.onload = func || function(){}; |
|||
|
|||
$('head')[0].appendChild(script); |
|||
} |
|||
|
|||
;(function(){ |
|||
var files = $('.toggle'); |
|||
var ol = $('ol', $('.prettyprint')[0]); |
|||
var li = $('li', ol[0]); |
|||
|
|||
// 短路径和长路径变换 |
|||
for(var i = 0; i < files.length; i++){ |
|||
files[i].ondblclick = function(){ |
|||
var title = this.title; |
|||
|
|||
this.title = this.innerHTML; |
|||
this.innerHTML = title; |
|||
} |
|||
} |
|||
|
|||
// 设置出错行 |
|||
var err_line = $('.line-' + LINE, ol[0])[0]; |
|||
err_line.className = err_line.className + ' line-error'; |
|||
|
|||
$.getScript('//cdn.bootcss.com/prettify/r298/prettify.min.js', function(){ |
|||
prettyPrint(); |
|||
|
|||
// 解决Firefox浏览器一个很诡异的问题 |
|||
// 当代码高亮后,ol的行号莫名其妙的错位 |
|||
// 但是只要刷新li里面的html重新渲染就没有问题了 |
|||
if(window.navigator.userAgent.indexOf('Firefox') >= 0){ |
|||
ol[0].innerHTML = ol[0].innerHTML; |
|||
} |
|||
}); |
|||
|
|||
})(); |
|||
</script> |
|||
<?php } ?> |
|||
</body> |
|||
</html> |
|||
@ -0,0 +1,558 @@ |
|||
<?php |
|||
$versionNum = 206; // remark: require update CC_HTTP_TUNNEL_SCRIPT_LATEST_VERSION_MYSQL |
|||
|
|||
//set allowTestMenu to false to disable System/Server test page |
|||
$allowTestMenu = true; |
|||
|
|||
$use_mysqli = function_exists("mysqli_connect"); |
|||
|
|||
header("Content-Type: text/plain; charset=x-user-defined"); |
|||
error_reporting(0); |
|||
set_time_limit(0); |
|||
|
|||
function phpversion_int() |
|||
{ |
|||
list($maVer, $miVer, $edVer) = preg_split("(/|\.|-)", phpversion()); |
|||
return $maVer*10000 + $miVer*100 + $edVer; |
|||
} |
|||
|
|||
if (phpversion_int() < 50300) |
|||
{ |
|||
set_magic_quotes_runtime(0); |
|||
} |
|||
|
|||
function GetLongBinary($num) |
|||
{ |
|||
return pack("N",$num); |
|||
} |
|||
|
|||
function GetShortBinary($num) |
|||
{ |
|||
return pack("n",$num); |
|||
} |
|||
|
|||
function GetDummy($count) |
|||
{ |
|||
$str = ""; |
|||
for($i=0;$i<$count;$i++) |
|||
$str .= "\x00"; |
|||
return $str; |
|||
} |
|||
|
|||
function GetBlock($val) |
|||
{ |
|||
$len = strlen($val); |
|||
if( $len < 254 ) |
|||
return chr($len).$val; |
|||
else |
|||
return "\xFE".GetLongBinary($len).$val; |
|||
} |
|||
|
|||
function EchoHeader($errno) |
|||
{ |
|||
global $versionNum; |
|||
|
|||
$str = GetLongBinary(1111); |
|||
$str .= GetShortBinary($versionNum); |
|||
$str .= GetLongBinary($errno); |
|||
$str .= GetDummy(6); |
|||
echo $str; |
|||
} |
|||
|
|||
function EchoConnInfo($conn) |
|||
{ |
|||
if ($GLOBALS['use_mysqli']) { |
|||
$str = GetBlock(mysqli_get_host_info($conn)); |
|||
$str .= GetBlock(mysqli_get_proto_info($conn)); |
|||
$str .= GetBlock(mysqli_get_server_info($conn)); |
|||
echo $str; |
|||
} else { |
|||
$str = GetBlock(mysql_get_host_info($conn)); |
|||
$str .= GetBlock(mysql_get_proto_info($conn)); |
|||
$str .= GetBlock(mysql_get_server_info($conn)); |
|||
echo $str; |
|||
} |
|||
} |
|||
|
|||
function EchoResultSetHeader($errno, $affectrows, $insertid, $numfields, $numrows) |
|||
{ |
|||
$str = GetLongBinary($errno); |
|||
$str .= GetLongBinary($affectrows); |
|||
$str .= GetLongBinary($insertid); |
|||
$str .= GetLongBinary($numfields); |
|||
$str .= GetLongBinary($numrows); |
|||
$str .= GetDummy(12); |
|||
echo $str; |
|||
} |
|||
|
|||
function EchoFieldsHeader($res, $numfields) |
|||
{ |
|||
$str = ""; |
|||
for( $i = 0; $i < $numfields; $i++ ) { |
|||
if ($GLOBALS['use_mysqli']) { |
|||
$finfo = mysqli_fetch_field_direct($res, $i); |
|||
$str .= GetBlock($finfo->name); |
|||
$str .= GetBlock($finfo->table); |
|||
|
|||
$type = $finfo->type; |
|||
$length = $finfo->length; |
|||
|
|||
$str .= GetLongBinary($type); |
|||
|
|||
$intflag = $finfo->flags; |
|||
$str .= GetLongBinary($intflag); |
|||
|
|||
$str .= GetLongBinary($length); |
|||
} else { |
|||
$str .= GetBlock(mysql_field_name($res, $i)); |
|||
$str .= GetBlock(mysql_field_table($res, $i)); |
|||
|
|||
$type = mysql_field_type($res, $i); |
|||
$length = mysql_field_len($res, $i); |
|||
switch ($type) { |
|||
case "int": |
|||
if( $length > 11 ) $type = 8; |
|||
else $type = 3; |
|||
break; |
|||
case "real": |
|||
if( $length == 12 ) $type = 4; |
|||
elseif( $length == 22 ) $type = 5; |
|||
else $type = 0; |
|||
break; |
|||
case "null": |
|||
$type = 6; |
|||
break; |
|||
case "timestamp": |
|||
$type = 7; |
|||
break; |
|||
case "date": |
|||
$type = 10; |
|||
break; |
|||
case "time": |
|||
$type = 11; |
|||
break; |
|||
case "datetime": |
|||
$type = 12; |
|||
break; |
|||
case "year": |
|||
$type = 13; |
|||
break; |
|||
case "blob": |
|||
if( $length > 16777215 ) $type = 251; |
|||
elseif( $length > 65535 ) $type = 250; |
|||
elseif( $length > 255 ) $type = 252; |
|||
else $type = 249; |
|||
break; |
|||
default: |
|||
$type = 253; |
|||
} |
|||
$str .= GetLongBinary($type); |
|||
|
|||
$flags = explode( " ", mysql_field_flags ( $res, $i ) ); |
|||
$intflag = 0; |
|||
if(in_array( "not_null", $flags )) $intflag += 1; |
|||
if(in_array( "primary_key", $flags )) $intflag += 2; |
|||
if(in_array( "unique_key", $flags )) $intflag += 4; |
|||
if(in_array( "multiple_key", $flags )) $intflag += 8; |
|||
if(in_array( "blob", $flags )) $intflag += 16; |
|||
if(in_array( "unsigned", $flags )) $intflag += 32; |
|||
if(in_array( "zerofill", $flags )) $intflag += 64; |
|||
if(in_array( "binary", $flags)) $intflag += 128; |
|||
if(in_array( "enum", $flags )) $intflag += 256; |
|||
if(in_array( "auto_increment", $flags )) $intflag += 512; |
|||
if(in_array( "timestamp", $flags )) $intflag += 1024; |
|||
if(in_array( "set", $flags )) $intflag += 2048; |
|||
$str .= GetLongBinary($intflag); |
|||
|
|||
$str .= GetLongBinary($length); |
|||
} |
|||
} |
|||
echo $str; |
|||
} |
|||
|
|||
function EchoData($res, $numfields, $numrows) |
|||
{ |
|||
for( $i = 0; $i < $numrows; $i++ ) { |
|||
$str = ""; |
|||
$row = null; |
|||
if ($GLOBALS['use_mysqli']) |
|||
$row = mysqli_fetch_row( $res ); |
|||
else |
|||
$row = mysql_fetch_row( $res ); |
|||
for( $j = 0; $j < $numfields; $j++ ){ |
|||
if( is_null($row[$j]) ) |
|||
$str .= "\xFF"; |
|||
else |
|||
$str .= GetBlock($row[$j]); |
|||
} |
|||
echo $str; |
|||
} |
|||
} |
|||
|
|||
|
|||
function doSystemTest() |
|||
{ |
|||
global $versionNum; |
|||
|
|||
function output($description, $succ, $resStr) { |
|||
echo "<tr><td class=\"TestDesc\">$description</td><td "; |
|||
echo ($succ)? "class=\"TestSucc\">$resStr[0]</td></tr>" : "class=\"TestFail\">$resStr[1]</td></tr>"; |
|||
} |
|||
output("PHP version >= 4.0.5", phpversion_int() >= 40005, array("Yes (".phpversion().")", "No (".phpversion().")")); |
|||
output("mysql_connect() available", function_exists("mysql_connect"), array("Yes", "No")); |
|||
output("mysqli_connect() available", function_exists("mysqli_connect"), array("Yes", "No")); |
|||
if (phpversion_int() >= 40302 && substr($_SERVER["SERVER_SOFTWARE"], 0, 6) == "Apache" && function_exists("apache_get_modules")){ |
|||
if (in_array("mod_security2", apache_get_modules())) |
|||
output("Mod Security 2 installed", false, array("No", "Yes")); |
|||
} |
|||
output("Current tunnel file version", true, array((int)($versionNum / 100) .".". ($versionNum % 100), "")); |
|||
} |
|||
|
|||
///////////////////////////////////////////////////////////////////////////// |
|||
//// |
|||
|
|||
if (phpversion_int() < 40005) { |
|||
EchoHeader(201); |
|||
echo GetBlock("unsupported php version"); |
|||
exit(); |
|||
} |
|||
|
|||
if (phpversion_int() < 40010) { |
|||
global $HTTP_POST_VARS; |
|||
$_POST = &$HTTP_POST_VARS; |
|||
} |
|||
|
|||
if (!isset($_POST["actn"]) || !isset($_POST["host"]) || !isset($_POST["port"]) || !isset($_POST["login"])) { |
|||
$testMenu = $allowTestMenu; |
|||
if (!$testMenu){ |
|||
EchoHeader(202); |
|||
echo GetBlock("invalid parameters"); |
|||
exit(); |
|||
} |
|||
} |
|||
|
|||
if (!$testMenu){ |
|||
if ($_POST["encodeBase64"] == '1') { |
|||
for($i=0;$i<count($_POST["q"]);$i++) |
|||
$_POST["q"][$i] = base64_decode($_POST["q"][$i]); |
|||
} |
|||
|
|||
if (!function_exists("mysql_connect") && !function_exists("mysqli_connect")) { |
|||
EchoHeader(203); |
|||
echo GetBlock("MySQL not supported on the server"); |
|||
exit(); |
|||
} |
|||
|
|||
$errno_c = 0; |
|||
$hs = $_POST["host"]; |
|||
if ($use_mysqli) { |
|||
mysqli_report(MYSQLI_REPORT_OFF); |
|||
|
|||
if( $_POST["port"] ) |
|||
$conn = mysqli_connect($hs, $_POST["login"], $_POST["password"], '', $_POST["port"]); |
|||
else |
|||
$conn = mysqli_connect($hs, $_POST["login"], $_POST["password"]); |
|||
$errno_c = mysqli_connect_errno(); |
|||
if($errno_c > 0) { |
|||
EchoHeader($errno_c); |
|||
echo GetBlock(mysqli_connect_error()); |
|||
exit; |
|||
} |
|||
if (phpversion_int() >= 50005){ // for unicode database name |
|||
mysqli_set_charset($conn, 'UTF8'); |
|||
} |
|||
|
|||
if(($errno_c <= 0) && ( $_POST["db"] != "" )) { |
|||
$res = mysqli_select_db($conn, $_POST["db"] ); |
|||
$errno_c = mysqli_errno($conn); |
|||
} |
|||
|
|||
EchoHeader($errno_c); |
|||
if($errno_c > 0) { |
|||
echo GetBlock(mysqli_error($conn)); |
|||
} elseif($_POST["actn"] == "C") { |
|||
EchoConnInfo($conn); |
|||
} elseif($_POST["actn"] == "Q") { |
|||
for($i=0;$i<count($_POST["q"]);$i++) { |
|||
$query = $_POST["q"][$i]; |
|||
if($query == "") continue; |
|||
if (phpversion_int() < 50400){ |
|||
if(get_magic_quotes_gpc()) |
|||
$query = stripslashes($query); |
|||
} |
|||
mysqli_real_query($conn, $query); |
|||
$res = false; |
|||
if (mysqli_field_count($conn)) |
|||
$res = mysqli_store_result($conn); |
|||
$errno = mysqli_errno($conn); |
|||
$affectedrows = mysqli_affected_rows($conn); |
|||
$insertid = mysqli_insert_id($conn); |
|||
if (false !== $res) { |
|||
$numfields = mysqli_field_count($conn); |
|||
$numrows = mysqli_num_rows($res); |
|||
} |
|||
else { |
|||
$numfields = 0; |
|||
$numrows = 0; |
|||
} |
|||
EchoResultSetHeader($errno, $affectedrows, $insertid, $numfields, $numrows); |
|||
if($errno > 0) |
|||
echo GetBlock(mysqli_error($conn)); |
|||
else { |
|||
if($numfields > 0) { |
|||
EchoFieldsHeader($res, $numfields); |
|||
EchoData($res, $numfields, $numrows); |
|||
} else { |
|||
if(phpversion_int() >= 40300) |
|||
echo GetBlock(mysqli_info($conn)); |
|||
else |
|||
echo GetBlock(""); |
|||
} |
|||
} |
|||
if($i<(count($_POST["q"])-1)) |
|||
echo "\x01"; |
|||
else |
|||
echo "\x00"; |
|||
if (false !== $res) |
|||
mysqli_free_result($res); |
|||
} |
|||
} |
|||
} else { |
|||
if( $_POST["port"] ) $hs .= ":".$_POST["port"]; |
|||
$conn = mysql_connect($hs, $_POST["login"], $_POST["password"]); |
|||
$errno_c = mysql_errno(); |
|||
if (phpversion_int() >= 50203){ // for unicode database name |
|||
mysql_set_charset('UTF8', $conn); |
|||
} |
|||
if(($errno_c <= 0) && ( $_POST["db"] != "" )) { |
|||
$res = mysql_select_db( $_POST["db"], $conn); |
|||
$errno_c = mysql_errno(); |
|||
} |
|||
|
|||
EchoHeader($errno_c); |
|||
if($errno_c > 0) { |
|||
echo GetBlock(mysql_error()); |
|||
} elseif($_POST["actn"] == "C") { |
|||
EchoConnInfo($conn); |
|||
} elseif($_POST["actn"] == "Q") { |
|||
for($i=0;$i<count($_POST["q"]);$i++) { |
|||
$query = $_POST["q"][$i]; |
|||
if($query == "") continue; |
|||
if (phpversion_int() < 50400){ |
|||
if(get_magic_quotes_gpc()) |
|||
$query = stripslashes($query); |
|||
} |
|||
$res = mysql_query($query, $conn); |
|||
$errno = mysql_errno(); |
|||
$affectedrows = mysql_affected_rows($conn); |
|||
$insertid = mysql_insert_id($conn); |
|||
$numfields = mysql_num_fields($res); |
|||
$numrows = mysql_num_rows($res); |
|||
EchoResultSetHeader($errno, $affectedrows, $insertid, $numfields, $numrows); |
|||
if($errno > 0) |
|||
echo GetBlock(mysql_error()); |
|||
else { |
|||
if($numfields > 0) { |
|||
EchoFieldsHeader($res, $numfields); |
|||
EchoData($res, $numfields, $numrows); |
|||
} else { |
|||
if(phpversion_int() >= 40300) |
|||
echo GetBlock(mysql_info($conn)); |
|||
else |
|||
echo GetBlock(""); |
|||
} |
|||
} |
|||
if($i<(count($_POST["q"])-1)) |
|||
echo "\x01"; |
|||
else |
|||
echo "\x00"; |
|||
mysql_free_result($res); |
|||
} |
|||
} |
|||
} |
|||
exit(); |
|||
} |
|||
|
|||
header("Content-Type: text/html"); |
|||
//// |
|||
///////////////////////////////////////////////////////////////////////////// |
|||
?> |
|||
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> |
|||
<html> |
|||
<head> |
|||
<title>Navicat HTTP Tunnel Tester</title> |
|||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> |
|||
<style type="text/css"> |
|||
body{ |
|||
margin: 30px; |
|||
font-family: Tahoma; |
|||
font-weight: normal; |
|||
font-size: 14px; |
|||
color: #222222; |
|||
} |
|||
table{ |
|||
width: 100%; |
|||
border: 0px; |
|||
} |
|||
input{ |
|||
font-family:Tahoma,sans-serif; |
|||
border-style:solid; |
|||
border-color:#666666; |
|||
border-width:1px; |
|||
} |
|||
fieldset{ |
|||
border-style:solid; |
|||
border-color:#666666; |
|||
border-width:1px; |
|||
} |
|||
.Title1{ |
|||
font-size: 30px; |
|||
color: #003366; |
|||
} |
|||
.Title2{ |
|||
font-size: 10px; |
|||
color: #999966; |
|||
} |
|||
.TestDesc{ |
|||
width:70% |
|||
} |
|||
.TestSucc{ |
|||
color: #00BB00; |
|||
} |
|||
.TestFail{ |
|||
color: #DD0000; |
|||
} |
|||
.mysql{ |
|||
} |
|||
.pgsql{ |
|||
display:none; |
|||
} |
|||
.sqlite{ |
|||
display:none; |
|||
} |
|||
#page{ |
|||
max-width: 42em; |
|||
min-width: 36em; |
|||
border-width: 0px; |
|||
margin: auto auto; |
|||
} |
|||
#host, #dbfile{ |
|||
width: 300px; |
|||
} |
|||
#port{ |
|||
width: 75px; |
|||
} |
|||
#login, #password, #db{ |
|||
width: 150px; |
|||
} |
|||
#Copyright{ |
|||
text-align: right; |
|||
font-size: 10px; |
|||
color: #888888; |
|||
} |
|||
</style> |
|||
<script type="text/javascript"> |
|||
function getInternetExplorerVersion(){ |
|||
var ver = -1; |
|||
if (navigator.appName == "Microsoft Internet Explorer"){ |
|||
var regex = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); |
|||
if (regex.exec(navigator.userAgent)) |
|||
ver = parseFloat(RegExp.$1); |
|||
} |
|||
return ver; |
|||
} |
|||
function setText(element, text, succ){ |
|||
element.className = (succ)?"TestSucc":"TestFail"; |
|||
element.innerHTML = text; |
|||
} |
|||
function getByteAt(str, offset){ |
|||
return str.charCodeAt(offset) & 0xff; |
|||
} |
|||
function getIntAt(binStr, offset){ |
|||
return (getByteAt(binStr, offset) << 24)+ |
|||
(getByteAt(binStr, offset+1) << 16)+ |
|||
(getByteAt(binStr, offset+2) << 8)+ |
|||
(getByteAt(binStr, offset+3) >>> 0); |
|||
} |
|||
function getBlockStr(binStr, offset){ |
|||
if (getByteAt(binStr, offset) < 254) |
|||
return binStr.substring(offset+1, offset+1+binStr.charCodeAt(offset)); |
|||
else |
|||
return binStr.substring(offset+5, offset+5+getIntAt(binStr, offset+1)); |
|||
} |
|||
function doServerTest(){ |
|||
var version = getInternetExplorerVersion(); |
|||
if (version==-1 || version>=9.0){ |
|||
var xmlhttp = (window.XMLHttpRequest)? new XMLHttpRequest() : xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); |
|||
|
|||
xmlhttp.onreadystatechange=function(){ |
|||
var outputDiv = document.getElementById("ServerTest"); |
|||
if (xmlhttp.readyState == 4){ |
|||
if (xmlhttp.status == 200){ |
|||
var errno = getIntAt(xmlhttp.responseText, 6); |
|||
if (errno == 0) |
|||
setText(outputDiv, "Connection Success!", true); |
|||
else |
|||
setText(outputDiv, parseInt(errno)+" - "+getBlockStr(xmlhttp.responseText, 16), false); |
|||
}else |
|||
setText(outputDiv, "HTTP Error - "+xmlhttp.status, false); |
|||
} |
|||
} |
|||
|
|||
var params = ""; |
|||
var form = document.getElementById("TestServerForm"); |
|||
for (var i=0; i<form.elements.length; i++){ |
|||
if (i>0) params += "&"; |
|||
params += form.elements[i].id+"="+form.elements[i].value.replace("&", "%26"); |
|||
} |
|||
|
|||
document.getElementById("ServerTest").className = ""; |
|||
document.getElementById("ServerTest").innerHTML = "Connecting..."; |
|||
xmlhttp.open("POST", "", true); |
|||
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); |
|||
xmlhttp.setRequestHeader("Content-length", params.length); |
|||
xmlhttp.setRequestHeader("Connection", "close"); |
|||
xmlhttp.send(params); |
|||
}else{ |
|||
document.getElementById("ServerTest").className = ""; |
|||
document.getElementById("ServerTest").innerHTML = "Internet Explorer "+version+" is not supported, please use Internet explorer 9.0 or above, firefox, chrome or safari"; |
|||
} |
|||
} |
|||
</script> |
|||
</head> |
|||
|
|||
<body> |
|||
<div id="page"> |
|||
<p> |
|||
<font class="Title1">Navicat™</font><br> |
|||
<font class="Title2">The gateway to your database!</font> |
|||
</p> |
|||
<fieldset> |
|||
<legend>System Environment Test</legend> |
|||
<table> |
|||
<tr style="<?php echo "display:none"; ?>"><td width=70%>PHP installed properly</td><td class="TestFail">No</td></tr> |
|||
<?php echo doSystemTest();?> |
|||
</table> |
|||
</fieldset> |
|||
<br> |
|||
<fieldset> |
|||
<legend>Server Test</legend> |
|||
<form id="TestServerForm" action="#" onSubmit="return false;"> |
|||
<input type=hidden id="actn" value="C"> |
|||
<table> |
|||
<tr class="mysql"><td width="35%">Hostname/IP Address:</td><td><input type=text id="host" placeholder="localhost"></td></tr> |
|||
<tr class="mysql"><td>Port:</td><td><input type=text id="port" placeholder="3306"></td></tr> |
|||
<tr class="pgsql"><td>Initial Database:</td><td><input type=text id="db" placeholder="template1"></td></tr> |
|||
<tr class="mysql"><td>Username:</td><td><input type=text id="login" placeholder="root"></td></tr> |
|||
<tr class="mysql"><td>Password:</td><td><input type=password id="password" placeholder=""></td></tr> |
|||
<tr class="sqlite"><td>Database File:</td><td><input type=text id="dbfile" placeholder="sqlite.db"></td></tr> |
|||
<tr><td></td><td><br><input id="TestButton" type="submit" value="Test Connection" onClick="doServerTest()"></td></tr> |
|||
</table> |
|||
</form> |
|||
<div id="ServerTest"><br></div> |
|||
</fieldset> |
|||
<p id="Copyright">Copyright © PremiumSoft ™ CyberTech Ltd. All Rights Reserved.</p> |
|||
</div> |
|||
</body> |
|||
</html> |
|||
@ -0,0 +1 @@ |
|||
42AhqtyCnXpW1br5 |
|||
@ -0,0 +1,100 @@ |
|||
<?php |
|||
/** |
|||
* 微信oAuth认证示例 |
|||
*/ |
|||
include("wechat.class.php"); |
|||
class wxauth { |
|||
private $options; |
|||
public $open_id; |
|||
public $wxuser; |
|||
|
|||
public function __construct($options){ |
|||
$this->options = $options; |
|||
$this->wxoauth(); |
|||
session_start(); |
|||
} |
|||
|
|||
public function wxoauth(){ |
|||
$scope = 'snsapi_base'; |
|||
$code = isset($_GET['code'])?$_GET['code']:''; |
|||
$token_time = isset($_SESSION['token_time'])?$_SESSION['token_time']:0; |
|||
if(!$code && isset($_SESSION['open_id']) && isset($_SESSION['user_token']) && $token_time>time()-3600) |
|||
{ |
|||
if (!$this->wxuser) { |
|||
$this->wxuser = $_SESSION['wxuser']; |
|||
} |
|||
$this->open_id = $_SESSION['open_id']; |
|||
return $this->open_id; |
|||
}else{ |
|||
$options = array( |
|||
'token'=>$this->options["token"], //填写你设定的key |
|||
'appid'=>$this->options["appid"], //填写高级调用功能的app id |
|||
'appsecret'=>$this->options["appsecret"] //填写高级调用功能的密钥 |
|||
); |
|||
$we_obj = new Wechat($options); |
|||
if ($code) { |
|||
$json = $we_obj->getOauthAccessToken(); |
|||
|
|||
if (!$json) { |
|||
if(isset($_SESSION['wx_redirect']))unset($_SESSION['wx_redirect']); |
|||
die('获取用户授权失败,请重新确认'); |
|||
} |
|||
$_SESSION['open_id'] = $this->open_id = $json["openid"]; |
|||
$access_token = $json['access_token']; |
|||
$_SESSION['user_token'] = $access_token; |
|||
$_SESSION['token_time'] = time(); |
|||
$userinfo = $we_obj->getUserInfo($this->open_id); |
|||
|
|||
if($userinfo && !empty($userinfo['nickname'])) { |
|||
$this->wxuser = array( |
|||
'open_id'=>$this->open_id, |
|||
'nickname'=>$userinfo['nickname'], |
|||
'sex'=>intval($userinfo['sex']), |
|||
'location'=>$userinfo['province'].'-'.$userinfo['city'], |
|||
'avatar'=>$userinfo['headimgurl'] |
|||
); |
|||
} elseif (strstr($json['scope'],'snsapi_userinfo')!==false) { |
|||
$userinfo = $we_obj->getOauthUserinfo($access_token,$this->open_id); |
|||
if ($userinfo && !empty($userinfo['nickname'])) { |
|||
$this->wxuser = array( |
|||
'open_id'=>$this->open_id, |
|||
'nickname'=>$userinfo['nickname'], |
|||
'sex'=>intval($userinfo['sex']), |
|||
'location'=>$userinfo['province'].'-'.$userinfo['city'], |
|||
'avatar'=>$userinfo['headimgurl'] |
|||
); |
|||
} else { |
|||
return $this->open_id; |
|||
} |
|||
} |
|||
if ($this->wxuser) { |
|||
$_SESSION['wxuser'] = $this->wxuser; |
|||
$_SESSION['open_id'] = $json["openid"]; |
|||
unset($_SESSION['wx_redirect']); |
|||
return $this->open_id; |
|||
} |
|||
$scope = 'snsapi_userinfo'; |
|||
} |
|||
if ($scope=='snsapi_base') { |
|||
$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; |
|||
$_SESSION['wx_redirect'] = $url; |
|||
} else { |
|||
$url = $_SESSION['wx_redirect']; |
|||
} |
|||
if (!$url) { |
|||
if(isset($_SESSION['wx_redirect']))unset($_SESSION['wx_redirect']); |
|||
die('获取用户授权失败'); |
|||
} |
|||
$oauth_url = $we_obj->getOauthRedirect($url,"wxbase",$scope); |
|||
header('Location: ' . $oauth_url); |
|||
} |
|||
} |
|||
} |
|||
$token=md5('szcaee_culture'); |
|||
$options = array( |
|||
'token'=>$token, //填写你设定的key |
|||
'appid'=>'wxa0cb14dc087a5d99', //填写高级调用功能的app id, 请在微信开发模式后台查询 |
|||
'appsecret'=>'738fbf2a4a9fc7324bf0a2fd07083498', //填写高级调用功能的密钥 |
|||
); |
|||
$auth = new wxauth($options); |
|||
var_dump($auth->wxuser); |
|||
@ -0,0 +1,44 @@ |
|||
<?php |
|||
include("lib/wechat.class.php"); |
|||
$token=md5('szcaee_culture'); |
|||
$options = array( |
|||
'token'=>$token, //填写你设定的key |
|||
'appid'=>'wx2104ec4219a6c4c3', //填写高级调用功能的app id, 请在微信开发模式后台查询 |
|||
'appsecret'=>'1322ae83f14427dea0af85b877b57376', //填写高级调用功能的密钥 |
|||
); |
|||
$we_obj = new Wechat($options); |
|||
$code = isset($_GET['code'])?$_GET['code']:''; |
|||
if ($code) { |
|||
$json = $we_obj->getOauthAccessToken(); |
|||
if (!$json) { |
|||
echo json_encode(['status'=>0,'msg'=>'获取用户授权失败,请重新确认']);exit; |
|||
} |
|||
$open_id = $json["openid"]; |
|||
$access_token = $json['access_token']; |
|||
$userinfo = $we_obj->getUserInfo($open_id); |
|||
//var_dump($userinfo); |
|||
$wxuser=[]; |
|||
if($userinfo && !empty($userinfo['nickname'])) { |
|||
$wxuser = array( |
|||
'open_id'=>$this->open_id, |
|||
'nickname'=>$userinfo['nickname'], |
|||
'sex'=>intval($userinfo['sex']), |
|||
'location'=>$userinfo['province'].'-'.$userinfo['city'], |
|||
'avatar'=>$userinfo['headimgurl'] |
|||
); |
|||
} elseif (strstr($json['scope'],'snsapi_userinfo')!==false) { |
|||
$userinfo = $we_obj->getOauthUserinfo($access_token,$open_id); |
|||
if ($userinfo && !empty($userinfo['nickname'])) { |
|||
$wxuser = array( |
|||
'open_id'=>$this->open_id, |
|||
'nickname'=>$userinfo['nickname'], |
|||
'sex'=>intval($userinfo['sex']), |
|||
'location'=>$userinfo['province'].'-'.$userinfo['city'], |
|||
'avatar'=>$userinfo['headimgurl'] |
|||
); |
|||
} |
|||
} |
|||
echo json_encode(['status'=>1,'data'=>$wxuser]);exit; |
|||
} |
|||
|
|||
?> |
|||
@ -0,0 +1,610 @@ |
|||
<?php |
|||
require_once "WxPay.Exception.php"; |
|||
require_once "WxPay.Config.Interface.php"; |
|||
require_once "WxPay.Data.php"; |
|||
|
|||
/** |
|||
* |
|||
* 接口访问类,包含所有微信支付API列表的封装,类中方法为static方法, |
|||
* 每个接口有默认超时时间(除提交被扫支付为10s,上报超时时间为1s外,其他均为6s) |
|||
* @author widyhu |
|||
* |
|||
*/ |
|||
class WxPayApi |
|||
{ |
|||
/** |
|||
* |
|||
* 统一下单,WxPayUnifiedOrder中out_trade_no、body、total_fee、trade_type必填 |
|||
* appid、mchid、spbill_create_ip、nonce_str不需要填入 |
|||
* @param WxPayConfigInterface $config 配置对象 |
|||
* @param WxPayUnifiedOrder $inputObj |
|||
* @param int $timeOut |
|||
* @throws WxPayException |
|||
* @return 成功时返回,其他抛异常 |
|||
*/ |
|||
public static function unifiedOrder($config, $inputObj, $timeOut = 6) |
|||
{ |
|||
$url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; |
|||
//检测必填参数 |
|||
if(!$inputObj->IsOut_trade_noSet()) { |
|||
throw new WxPayException("缺少统一支付接口必填参数out_trade_no!"); |
|||
}else if(!$inputObj->IsBodySet()){ |
|||
throw new WxPayException("缺少统一支付接口必填参数body!"); |
|||
}else if(!$inputObj->IsTotal_feeSet()) { |
|||
throw new WxPayException("缺少统一支付接口必填参数total_fee!"); |
|||
}else if(!$inputObj->IsTrade_typeSet()) { |
|||
throw new WxPayException("缺少统一支付接口必填参数trade_type!"); |
|||
} |
|||
|
|||
//关联参数 |
|||
if($inputObj->GetTrade_type() == "JSAPI" && !$inputObj->IsOpenidSet()){ |
|||
throw new WxPayException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!"); |
|||
} |
|||
if($inputObj->GetTrade_type() == "NATIVE" && !$inputObj->IsProduct_idSet()){ |
|||
throw new WxPayException("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!"); |
|||
} |
|||
|
|||
//异步通知url未设置,则使用配置文件中的url |
|||
if(!$inputObj->IsNotify_urlSet() && $config->GetNotifyUrl() != ""){ |
|||
$inputObj->SetNotify_url($config->GetNotifyUrl());//异步通知url |
|||
} |
|||
|
|||
$inputObj->SetAppid($config->GetAppId());//公众账号ID |
|||
$inputObj->SetMch_id($config->GetMerchantId());//商户号 |
|||
$inputObj->SetSpbill_create_ip($_SERVER['REMOTE_ADDR']);//终端ip |
|||
$inputObj->SetNonce_str(self::getNonceStr());//随机字符串 |
|||
|
|||
//签名 |
|||
$inputObj->SetSign($config); |
|||
$xml = $inputObj->ToXml(); |
|||
|
|||
$startTimeStamp = self::getMillisecond();//请求开始时间 |
|||
$response = self::postXmlCurl($config, $xml, $url, false, $timeOut); |
|||
$result = WxPayResults::Init($config, $response); |
|||
self::reportCostTime($config, $url, $startTimeStamp, $result);//上报请求花费时间 |
|||
|
|||
return $result; |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* 查询订单,WxPayOrderQuery中out_trade_no、transaction_id至少填一个 |
|||
* appid、mchid、spbill_create_ip、nonce_str不需要填入 |
|||
* @param WxPayConfigInterface $config 配置对象 |
|||
* @param WxPayOrderQuery $inputObj |
|||
* @param int $timeOut |
|||
* @throws WxPayException |
|||
* @return 成功时返回,其他抛异常 |
|||
*/ |
|||
public static function orderQuery($config, $inputObj, $timeOut = 6) |
|||
{ |
|||
$url = "https://api.mch.weixin.qq.com/pay/orderquery"; |
|||
//检测必填参数 |
|||
if(!$inputObj->IsOut_trade_noSet() && !$inputObj->IsTransaction_idSet()) { |
|||
throw new WxPayException("订单查询接口中,out_trade_no、transaction_id至少填一个!"); |
|||
} |
|||
$inputObj->SetAppid($config->GetAppId());//公众账号ID |
|||
$inputObj->SetMch_id($config->GetMerchantId());//商户号 |
|||
$inputObj->SetNonce_str(self::getNonceStr());//随机字符串 |
|||
|
|||
$inputObj->SetSign($config);//签名 |
|||
$xml = $inputObj->ToXml(); |
|||
|
|||
$startTimeStamp = self::getMillisecond();//请求开始时间 |
|||
$response = self::postXmlCurl($config, $xml, $url, false, $timeOut); |
|||
$result = WxPayResults::Init($config, $response); |
|||
self::reportCostTime($config, $url, $startTimeStamp, $result);//上报请求花费时间 |
|||
|
|||
return $result; |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* 关闭订单,WxPayCloseOrder中out_trade_no必填 |
|||
* appid、mchid、spbill_create_ip、nonce_str不需要填入 |
|||
* @param WxPayConfigInterface $config 配置对象 |
|||
* @param WxPayCloseOrder $inputObj |
|||
* @param int $timeOut |
|||
* @throws WxPayException |
|||
* @return 成功时返回,其他抛异常 |
|||
*/ |
|||
public static function closeOrder($config, $inputObj, $timeOut = 6) |
|||
{ |
|||
$url = "https://api.mch.weixin.qq.com/pay/closeorder"; |
|||
//检测必填参数 |
|||
if(!$inputObj->IsOut_trade_noSet()) { |
|||
throw new WxPayException("订单查询接口中,out_trade_no必填!"); |
|||
} |
|||
$inputObj->SetAppid($config->GetAppId());//公众账号ID |
|||
$inputObj->SetMch_id($config->GetMerchantId());//商户号 |
|||
$inputObj->SetNonce_str(self::getNonceStr());//随机字符串 |
|||
|
|||
$inputObj->SetSign($config);//签名 |
|||
$xml = $inputObj->ToXml(); |
|||
|
|||
$startTimeStamp = self::getMillisecond();//请求开始时间 |
|||
$response = self::postXmlCurl($config, $xml, $url, false, $timeOut); |
|||
$result = WxPayResults::Init($config, $response); |
|||
self::reportCostTime($config, $url, $startTimeStamp, $result);//上报请求花费时间 |
|||
|
|||
return $result; |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* 申请退款,WxPayRefund中out_trade_no、transaction_id至少填一个且 |
|||
* out_refund_no、total_fee、refund_fee、op_user_id为必填参数 |
|||
* appid、mchid、spbill_create_ip、nonce_str不需要填入 |
|||
* @param WxPayConfigInterface $config 配置对象 |
|||
* @param WxPayRefund $inputObj |
|||
* @param int $timeOut |
|||
* @throws WxPayException |
|||
* @return 成功时返回,其他抛异常 |
|||
*/ |
|||
public static function refund($config, $inputObj, $timeOut = 6) |
|||
{ |
|||
$url = "https://api.mch.weixin.qq.com/secapi/pay/refund"; |
|||
//检测必填参数 |
|||
if(!$inputObj->IsOut_trade_noSet() && !$inputObj->IsTransaction_idSet()) { |
|||
throw new WxPayException("退款申请接口中,out_trade_no、transaction_id至少填一个!"); |
|||
}else if(!$inputObj->IsOut_refund_noSet()){ |
|||
throw new WxPayException("退款申请接口中,缺少必填参数out_refund_no!"); |
|||
}else if(!$inputObj->IsTotal_feeSet()){ |
|||
throw new WxPayException("退款申请接口中,缺少必填参数total_fee!"); |
|||
}else if(!$inputObj->IsRefund_feeSet()){ |
|||
throw new WxPayException("退款申请接口中,缺少必填参数refund_fee!"); |
|||
}else if(!$inputObj->IsOp_user_idSet()){ |
|||
throw new WxPayException("退款申请接口中,缺少必填参数op_user_id!"); |
|||
} |
|||
$inputObj->SetAppid($config->GetAppId());//公众账号ID |
|||
$inputObj->SetMch_id($config->GetMerchantId());//商户号 |
|||
$inputObj->SetNonce_str(self::getNonceStr());//随机字符串 |
|||
|
|||
$inputObj->SetSign($config);//签名 |
|||
$xml = $inputObj->ToXml(); |
|||
$startTimeStamp = self::getMillisecond();//请求开始时间 |
|||
$response = self::postXmlCurl($config, $xml, $url, true, $timeOut); |
|||
$result = WxPayResults::Init($config, $response); |
|||
self::reportCostTime($config, $url, $startTimeStamp, $result);//上报请求花费时间 |
|||
|
|||
return $result; |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* 查询退款 |
|||
* 提交退款申请后,通过调用该接口查询退款状态。退款有一定延时, |
|||
* 用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。 |
|||
* WxPayRefundQuery中out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个 |
|||
* appid、mchid、spbill_create_ip、nonce_str不需要填入 |
|||
* @param WxPayConfigInterface $config 配置对象 |
|||
* @param WxPayRefundQuery $inputObj |
|||
* @param int $timeOut |
|||
* @throws WxPayException |
|||
* @return 成功时返回,其他抛异常 |
|||
*/ |
|||
public static function refundQuery($config, $inputObj, $timeOut = 6) |
|||
{ |
|||
$url = "https://api.mch.weixin.qq.com/pay/refundquery"; |
|||
//检测必填参数 |
|||
if(!$inputObj->IsOut_refund_noSet() && |
|||
!$inputObj->IsOut_trade_noSet() && |
|||
!$inputObj->IsTransaction_idSet() && |
|||
!$inputObj->IsRefund_idSet()) { |
|||
throw new WxPayException("退款查询接口中,out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个!"); |
|||
} |
|||
$inputObj->SetAppid($config->GetAppId());//公众账号ID |
|||
$inputObj->SetMch_id($config->GetMerchantId());//商户号 |
|||
$inputObj->SetNonce_str(self::getNonceStr());//随机字符串 |
|||
|
|||
$inputObj->SetSign($config);//签名 |
|||
$xml = $inputObj->ToXml(); |
|||
|
|||
$startTimeStamp = self::getMillisecond();//请求开始时间 |
|||
$response = self::postXmlCurl($config, $xml, $url, false, $timeOut); |
|||
$result = WxPayResults::Init($config, $response); |
|||
self::reportCostTime($config, $url, $startTimeStamp, $result);//上报请求花费时间 |
|||
|
|||
return $result; |
|||
} |
|||
|
|||
/** |
|||
* 下载对账单,WxPayDownloadBill中bill_date为必填参数 |
|||
* appid、mchid、spbill_create_ip、nonce_str不需要填入 |
|||
* @param WxPayConfigInterface $config 配置对象 |
|||
* @param WxPayDownloadBill $inputObj |
|||
* @param int $timeOut |
|||
* @throws WxPayException |
|||
* @return 成功时返回,其他抛异常 |
|||
*/ |
|||
public static function downloadBill($config, $inputObj, $timeOut = 6) |
|||
{ |
|||
$url = "https://api.mch.weixin.qq.com/pay/downloadbill"; |
|||
//检测必填参数 |
|||
if(!$inputObj->IsBill_dateSet()) { |
|||
throw new WxPayException("对账单接口中,缺少必填参数bill_date!"); |
|||
} |
|||
$inputObj->SetAppid($config->GetAppId());//公众账号ID |
|||
$inputObj->SetMch_id($config->GetMerchantId());//商户号 |
|||
$inputObj->SetNonce_str(self::getNonceStr());//随机字符串 |
|||
|
|||
$inputObj->SetSign($config);//签名 |
|||
$xml = $inputObj->ToXml(); |
|||
|
|||
$response = self::postXmlCurl($config, $xml, $url, false, $timeOut); |
|||
if(substr($response, 0 , 5) == "<xml>"){ |
|||
return ""; |
|||
} |
|||
return $response; |
|||
} |
|||
|
|||
/** |
|||
* 提交被扫支付API |
|||
* 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台, |
|||
* 由商户收银台或者商户后台调用该接口发起支付。 |
|||
* WxPayWxPayMicroPay中body、out_trade_no、total_fee、auth_code参数必填 |
|||
* appid、mchid、spbill_create_ip、nonce_str不需要填入 |
|||
* @param WxPayConfigInterface $config 配置对象 |
|||
* @param WxPayWxPayMicroPay $inputObj |
|||
* @param int $timeOut |
|||
*/ |
|||
public static function micropay($config, $inputObj, $timeOut = 10) |
|||
{ |
|||
$url = "https://api.mch.weixin.qq.com/pay/micropay"; |
|||
//检测必填参数 |
|||
if(!$inputObj->IsBodySet()) { |
|||
throw new WxPayException("提交被扫支付API接口中,缺少必填参数body!"); |
|||
} else if(!$inputObj->IsOut_trade_noSet()) { |
|||
throw new WxPayException("提交被扫支付API接口中,缺少必填参数out_trade_no!"); |
|||
} else if(!$inputObj->IsTotal_feeSet()) { |
|||
throw new WxPayException("提交被扫支付API接口中,缺少必填参数total_fee!"); |
|||
} else if(!$inputObj->IsAuth_codeSet()) { |
|||
throw new WxPayException("提交被扫支付API接口中,缺少必填参数auth_code!"); |
|||
} |
|||
|
|||
$inputObj->SetSpbill_create_ip($_SERVER['REMOTE_ADDR']);//终端ip |
|||
$inputObj->SetAppid($config->GetAppId());//公众账号ID |
|||
$inputObj->SetMch_id($config->GetMerchantId());//商户号 |
|||
$inputObj->SetNonce_str(self::getNonceStr());//随机字符串 |
|||
|
|||
$inputObj->SetSign($config);//签名 |
|||
$xml = $inputObj->ToXml(); |
|||
|
|||
$startTimeStamp = self::getMillisecond();//请求开始时间 |
|||
$response = self::postXmlCurl($config, $xml, $url, false, $timeOut); |
|||
$result = WxPayResults::Init($config, $response); |
|||
self::reportCostTime($config, $url, $startTimeStamp, $result);//上报请求花费时间 |
|||
|
|||
return $result; |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* 撤销订单API接口,WxPayReverse中参数out_trade_no和transaction_id必须填写一个 |
|||
* appid、mchid、spbill_create_ip、nonce_str不需要填入 |
|||
* @param WxPayConfigInterface $config 配置对象 |
|||
* @param WxPayReverse $inputObj |
|||
* @param int $timeOut |
|||
* @throws WxPayException |
|||
*/ |
|||
public static function reverse($config, $inputObj, $timeOut = 6) |
|||
{ |
|||
$url = "https://api.mch.weixin.qq.com/secapi/pay/reverse"; |
|||
//检测必填参数 |
|||
if(!$inputObj->IsOut_trade_noSet() && !$inputObj->IsTransaction_idSet()) { |
|||
throw new WxPayException("撤销订单API接口中,参数out_trade_no和transaction_id必须填写一个!"); |
|||
} |
|||
|
|||
$inputObj->SetAppid($config->GetAppId());//公众账号ID |
|||
$inputObj->SetMch_id($config->GetMerchantId());//商户号 |
|||
$inputObj->SetNonce_str(self::getNonceStr());//随机字符串 |
|||
|
|||
$inputObj->SetSign($config);//签名 |
|||
$xml = $inputObj->ToXml(); |
|||
|
|||
$startTimeStamp = self::getMillisecond();//请求开始时间 |
|||
$response = self::postXmlCurl($config, $xml, $url, true, $timeOut); |
|||
$result = WxPayResults::Init($config, $response); |
|||
self::reportCostTime($config, $url, $startTimeStamp, $result);//上报请求花费时间 |
|||
|
|||
return $result; |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* 测速上报,该方法内部封装在report中,使用时请注意异常流程 |
|||
* WxPayReport中interface_url、return_code、result_code、user_ip、execute_time_必填 |
|||
* appid、mchid、spbill_create_ip、nonce_str不需要填入 |
|||
* @param WxPayConfigInterface $config 配置对象 |
|||
* @param WxPayReport $inputObj |
|||
* @param int $timeOut |
|||
* @throws WxPayException |
|||
* @return 成功时返回,其他抛异常 |
|||
*/ |
|||
public static function report($config, $inputObj, $timeOut = 1) |
|||
{ |
|||
$url = "https://api.mch.weixin.qq.com/payitil/report"; |
|||
//检测必填参数 |
|||
if(!$inputObj->IsInterface_urlSet()) { |
|||
throw new WxPayException("接口URL,缺少必填参数interface_url!"); |
|||
} if(!$inputObj->IsReturn_codeSet()) { |
|||
throw new WxPayException("返回状态码,缺少必填参数return_code!"); |
|||
} if(!$inputObj->IsResult_codeSet()) { |
|||
throw new WxPayException("业务结果,缺少必填参数result_code!"); |
|||
} if(!$inputObj->IsUser_ipSet()) { |
|||
throw new WxPayException("访问接口IP,缺少必填参数user_ip!"); |
|||
} if(!$inputObj->IsExecute_time_Set()) { |
|||
throw new WxPayException("接口耗时,缺少必填参数execute_time_!"); |
|||
} |
|||
$inputObj->SetAppid($config->GetAppId());//公众账号ID |
|||
$inputObj->SetMch_id($config->GetMerchantId());//商户号 |
|||
$inputObj->SetUser_ip($_SERVER['REMOTE_ADDR']);//终端ip |
|||
$inputObj->SetTime(date("YmdHis"));//商户上报时间 |
|||
$inputObj->SetNonce_str(self::getNonceStr());//随机字符串 |
|||
|
|||
$inputObj->SetSign($config);//签名 |
|||
$xml = $inputObj->ToXml(); |
|||
|
|||
$startTimeStamp = self::getMillisecond();//请求开始时间 |
|||
$response = self::postXmlCurl($config, $xml, $url, false, $timeOut); |
|||
return $response; |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* 生成二维码规则,模式一生成支付二维码 |
|||
* appid、mchid、spbill_create_ip、nonce_str不需要填入 |
|||
* @param WxPayConfigInterface $config 配置对象 |
|||
* @param WxPayBizPayUrl $inputObj |
|||
* @param int $timeOut |
|||
* @throws WxPayException |
|||
* @return 成功时返回,其他抛异常 |
|||
*/ |
|||
public static function bizpayurl($config, $inputObj, $timeOut = 6) |
|||
{ |
|||
if(!$inputObj->IsProduct_idSet()){ |
|||
throw new WxPayException("生成二维码,缺少必填参数product_id!"); |
|||
} |
|||
|
|||
$inputObj->SetAppid($config->GetAppId());//公众账号ID |
|||
$inputObj->SetMch_id($config->GetMerchantId());//商户号 |
|||
$inputObj->SetTime_stamp(time());//时间戳 |
|||
$inputObj->SetNonce_str(self::getNonceStr());//随机字符串 |
|||
|
|||
$inputObj->SetSign($config);//签名 |
|||
|
|||
return $inputObj->GetValues(); |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* 转换短链接 |
|||
* 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX), |
|||
* 减小二维码数据量,提升扫描速度和精确度。 |
|||
* appid、mchid、spbill_create_ip、nonce_str不需要填入 |
|||
* @param WxPayConfigInterface $config 配置对象 |
|||
* @param WxPayShortUrl $inputObj |
|||
* @param int $timeOut |
|||
* @throws WxPayException |
|||
* @return 成功时返回,其他抛异常 |
|||
*/ |
|||
public static function shorturl($config, $inputObj, $timeOut = 6) |
|||
{ |
|||
$url = "https://api.mch.weixin.qq.com/tools/shorturl"; |
|||
//检测必填参数 |
|||
if(!$inputObj->IsLong_urlSet()) { |
|||
throw new WxPayException("需要转换的URL,签名用原串,传输需URL encode!"); |
|||
} |
|||
$inputObj->SetAppid($config->GetAppId());//公众账号ID |
|||
$inputObj->SetMch_id($config->GetMerchantId());//商户号 |
|||
$inputObj->SetNonce_str(self::getNonceStr());//随机字符串 |
|||
|
|||
$inputObj->SetSign($config);//签名 |
|||
$xml = $inputObj->ToXml(); |
|||
|
|||
$startTimeStamp = self::getMillisecond();//请求开始时间 |
|||
$response = self::postXmlCurl($config, $xml, $url, false, $timeOut); |
|||
$result = WxPayResults::Init($config, $response); |
|||
self::reportCostTime($config, $url, $startTimeStamp, $result);//上报请求花费时间 |
|||
|
|||
return $result; |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* 支付结果通用通知 |
|||
* @param function $callback |
|||
* 直接回调函数使用方法: notify(you_function); |
|||
* 回调类成员函数方法:notify(array($this, you_function)); |
|||
* $callback 原型为:function function_name($data){} |
|||
*/ |
|||
public static function notify($config, $callback, &$msg) |
|||
{ |
|||
if (!isset($GLOBALS['HTTP_RAW_POST_DATA'])) { |
|||
# 如果没有数据,直接返回失败 |
|||
return false; |
|||
} |
|||
|
|||
//如果返回成功则验证签名 |
|||
try { |
|||
//获取通知的数据 |
|||
$xml = $GLOBALS['HTTP_RAW_POST_DATA']; |
|||
$result = WxPayNotifyResults::Init($config, $xml); |
|||
} catch (WxPayException $e){ |
|||
$msg = $e->errorMessage(); |
|||
return false; |
|||
} |
|||
|
|||
return call_user_func($callback, $result); |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* 产生随机字符串,不长于32位 |
|||
* @param int $length |
|||
* @return 产生的随机字符串 |
|||
*/ |
|||
public static function getNonceStr($length = 32) |
|||
{ |
|||
$chars = "abcdefghijklmnopqrstuvwxyz0123456789"; |
|||
$str =""; |
|||
for ( $i = 0; $i < $length; $i++ ) { |
|||
$str .= substr($chars, mt_rand(0, strlen($chars)-1), 1); |
|||
} |
|||
return $str; |
|||
} |
|||
|
|||
/** |
|||
* 直接输出xml |
|||
* @param string $xml |
|||
*/ |
|||
public static function replyNotify($xml) |
|||
{ |
|||
echo $xml; |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* 上报数据, 上报的时候将屏蔽所有异常流程 |
|||
* @param WxPayConfigInterface $config 配置对象 |
|||
* @param string $usrl |
|||
* @param int $startTimeStamp |
|||
* @param array $data |
|||
*/ |
|||
private static function reportCostTime($config, $url, $startTimeStamp, $data) |
|||
{ |
|||
//如果不需要上报数据 |
|||
$reportLevenl = $config->GetReportLevenl(); |
|||
if($reportLevenl == 0){ |
|||
return; |
|||
} |
|||
//如果仅失败上报 |
|||
if($reportLevenl == 1 && |
|||
array_key_exists("return_code", $data) && |
|||
$data["return_code"] == "SUCCESS" && |
|||
array_key_exists("result_code", $data) && |
|||
$data["result_code"] == "SUCCESS") |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
//上报逻辑 |
|||
$endTimeStamp = self::getMillisecond(); |
|||
$objInput = new WxPayReport(); |
|||
$objInput->SetInterface_url($url); |
|||
$objInput->SetExecute_time_($endTimeStamp - $startTimeStamp); |
|||
//返回状态码 |
|||
if(array_key_exists("return_code", $data)){ |
|||
$objInput->SetReturn_code($data["return_code"]); |
|||
} |
|||
//返回信息 |
|||
if(array_key_exists("return_msg", $data)){ |
|||
$objInput->SetReturn_msg($data["return_msg"]); |
|||
} |
|||
//业务结果 |
|||
if(array_key_exists("result_code", $data)){ |
|||
$objInput->SetResult_code($data["result_code"]); |
|||
} |
|||
//错误代码 |
|||
if(array_key_exists("err_code", $data)){ |
|||
$objInput->SetErr_code($data["err_code"]); |
|||
} |
|||
//错误代码描述 |
|||
if(array_key_exists("err_code_des", $data)){ |
|||
$objInput->SetErr_code_des($data["err_code_des"]); |
|||
} |
|||
//商户订单号 |
|||
if(array_key_exists("out_trade_no", $data)){ |
|||
$objInput->SetOut_trade_no($data["out_trade_no"]); |
|||
} |
|||
//设备号 |
|||
if(array_key_exists("device_info", $data)){ |
|||
$objInput->SetDevice_info($data["device_info"]); |
|||
} |
|||
|
|||
try{ |
|||
self::report($config, $objInput); |
|||
} catch (WxPayException $e){ |
|||
//不做任何处理 |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 以post方式提交xml到对应的接口url |
|||
* |
|||
* @param WxPayConfigInterface $config 配置对象 |
|||
* @param string $xml 需要post的xml数据 |
|||
* @param string $url url |
|||
* @param bool $useCert 是否需要证书,默认不需要 |
|||
* @param int $second url执行超时时间,默认30s |
|||
* @throws WxPayException |
|||
*/ |
|||
private static function postXmlCurl($config, $xml, $url, $useCert = false, $second = 30) |
|||
{ |
|||
$ch = curl_init(); |
|||
$curlVersion = curl_version(); |
|||
$ua = "WXPaySDK/3.0.9 (".PHP_OS.") PHP/".PHP_VERSION." CURL/".$curlVersion['version']." " |
|||
.$config->GetMerchantId(); |
|||
|
|||
//设置超时 |
|||
curl_setopt($ch, CURLOPT_TIMEOUT, $second); |
|||
|
|||
$proxyHost = "0.0.0.0"; |
|||
$proxyPort = 0; |
|||
$config->GetProxy($proxyHost, $proxyPort); |
|||
//如果有配置代理这里就设置代理 |
|||
if($proxyHost != "0.0.0.0" && $proxyPort != 0){ |
|||
curl_setopt($ch,CURLOPT_PROXY, $proxyHost); |
|||
curl_setopt($ch,CURLOPT_PROXYPORT, $proxyPort); |
|||
} |
|||
curl_setopt($ch,CURLOPT_URL, $url); |
|||
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE); |
|||
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验 |
|||
curl_setopt($ch,CURLOPT_USERAGENT, $ua); |
|||
//设置header |
|||
curl_setopt($ch, CURLOPT_HEADER, FALSE); |
|||
//要求结果为字符串且输出到屏幕上 |
|||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); |
|||
|
|||
if($useCert == true){ |
|||
//设置证书 |
|||
//使用证书:cert 与 key 分别属于两个.pem文件 |
|||
//证书文件请放入服务器的非web目录下 |
|||
$sslCertPath = ""; |
|||
$sslKeyPath = ""; |
|||
$config->GetSSLCertPath($sslCertPath, $sslKeyPath); |
|||
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); |
|||
curl_setopt($ch,CURLOPT_SSLCERT, $sslCertPath); |
|||
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); |
|||
curl_setopt($ch,CURLOPT_SSLKEY, $sslKeyPath); |
|||
} |
|||
//post提交方式 |
|||
curl_setopt($ch, CURLOPT_POST, TRUE); |
|||
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); |
|||
//运行curl |
|||
$data = curl_exec($ch); |
|||
//返回结果 |
|||
if($data){ |
|||
curl_close($ch); |
|||
return $data; |
|||
} else { |
|||
$error = curl_errno($ch); |
|||
curl_close($ch); |
|||
throw new WxPayException("curl出错,错误码:$error"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取毫秒级别的时间戳 |
|||
*/ |
|||
private static function getMillisecond() |
|||
{ |
|||
//获取毫秒的时间戳 |
|||
$time = explode ( " ", microtime () ); |
|||
$time = $time[1] . ($time[0] * 1000); |
|||
$time2 = explode( ".", $time ); |
|||
$time = $time2[0]; |
|||
return $time; |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,76 @@ |
|||
<?php |
|||
/** |
|||
* 配置账号信息 |
|||
*/ |
|||
|
|||
abstract class WxPayConfigInterface |
|||
{ |
|||
//=======【基本信息设置】===================================== |
|||
/** |
|||
* TODO: 修改这里配置为您自己申请的商户信息 |
|||
* 微信公众号信息配置 |
|||
* |
|||
* APPID:绑定支付的APPID(必须配置,开户邮件中可查看) |
|||
* |
|||
* MCHID:商户号(必须配置,开户邮件中可查看) |
|||
* |
|||
*/ |
|||
public abstract function GetAppId(); |
|||
public abstract function GetMerchantId(); |
|||
|
|||
|
|||
//=======【支付相关配置:支付成功回调地址/签名方式】=================================== |
|||
/** |
|||
* TODO:支付回调url |
|||
* 签名和验证签名方式, 支持md5和sha256方式 |
|||
**/ |
|||
public abstract function GetNotifyUrl(); |
|||
public abstract function GetSignType(); |
|||
|
|||
//=======【curl代理设置】=================================== |
|||
/** |
|||
* TODO:这里设置代理机器,只有需要代理的时候才设置,不需要代理,请设置为0.0.0.0和0 |
|||
* 本例程通过curl使用HTTP POST方法,此处可修改代理服务器, |
|||
* 默认CURL_PROXY_HOST=0.0.0.0和CURL_PROXY_PORT=0,此时不开启代理(如有需要才设置) |
|||
* @var unknown_type |
|||
*/ |
|||
public abstract function GetProxy(&$proxyHost, &$proxyPort); |
|||
|
|||
|
|||
//=======【上报信息配置】=================================== |
|||
/** |
|||
* TODO:接口调用上报等级,默认紧错误上报(注意:上报超时间为【1s】,上报无论成败【永不抛出异常】, |
|||
* 不会影响接口调用流程),开启上报之后,方便微信监控请求调用的质量,建议至少 |
|||
* 开启错误上报。 |
|||
* 上报等级,0.关闭上报; 1.仅错误出错上报; 2.全量上报 |
|||
* @var int |
|||
*/ |
|||
public abstract function GetReportLevenl(); |
|||
|
|||
|
|||
//=======【商户密钥信息-需要业务方继承】=================================== |
|||
/* |
|||
* KEY:商户支付密钥,参考开户邮件设置(必须配置,登录商户平台自行设置), 请妥善保管, 避免密钥泄露 |
|||
* 设置地址:https://pay.weixin.qq.com/index.php/account/api_cert |
|||
* |
|||
* APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置, 登录公众平台,进入开发者中心可设置), 请妥善保管, 避免密钥泄露 |
|||
* 获取地址:https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=2005451881&lang=zh_CN |
|||
* @var string |
|||
*/ |
|||
public abstract function GetKey(); |
|||
public abstract function GetAppSecret(); |
|||
|
|||
|
|||
//=======【证书路径设置-需要业务方继承】===================================== |
|||
/** |
|||
* TODO:设置商户证书路径 |
|||
* 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要,可登录商户平台下载, |
|||
* API证书下载地址:https://pay.weixin.qq.com/index.php/account/api_cert,下载之前需要安装商户操作证书) |
|||
* 注意: |
|||
* 1.证书文件不能放在web服务器虚拟目录,应放在有访问权限控制的目录中,防止被他人下载; |
|||
* 2.建议将证书文件名改为复杂且不容易猜测的文件名; |
|||
* 3.商户服务器要做好病毒和木马防护工作,不被非法侵入者窃取证书文件。 |
|||
* @var path |
|||
*/ |
|||
public abstract function GetSSLCertPath(&$sslCertPath, &$sslKeyPath); |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,13 @@ |
|||
<?php |
|||
/** |
|||
* |
|||
* 微信支付API异常类 |
|||
* @author widyhu |
|||
* |
|||
*/ |
|||
class WxPayException extends Exception { |
|||
public function errorMessage() |
|||
{ |
|||
return $this->getMessage(); |
|||
} |
|||
} |
|||
@ -0,0 +1,105 @@ |
|||
<?php |
|||
/** |
|||
* |
|||
* 回调基础类 |
|||
* @author widyhu |
|||
* |
|||
*/ |
|||
class WxPayNotify extends WxPayNotifyReply |
|||
{ |
|||
private $config = null; |
|||
/** |
|||
* |
|||
* 回调入口 |
|||
* @param bool $needSign 是否需要签名返回 |
|||
*/ |
|||
final public function Handle($config, $needSign = true) |
|||
{ |
|||
$this->config = $config; |
|||
$msg = "OK"; |
|||
//当返回false的时候,表示notify中调用NotifyCallBack回调失败获取签名校验失败,此时直接回复失败 |
|||
$result = WxpayApi::notify($config, array($this, 'NotifyCallBack'), $msg); |
|||
if($result == false){ |
|||
$this->SetReturn_code("FAIL"); |
|||
$this->SetReturn_msg($msg); |
|||
$this->ReplyNotify(false); |
|||
return; |
|||
} else { |
|||
//该分支在成功回调到NotifyCallBack方法,处理完成之后流程 |
|||
$this->SetReturn_code("SUCCESS"); |
|||
$this->SetReturn_msg("OK"); |
|||
} |
|||
$this->ReplyNotify($needSign); |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* 回调方法入口,子类可重写该方法 |
|||
//TODO 1、进行参数校验 |
|||
//TODO 2、进行签名验证 |
|||
//TODO 3、处理业务逻辑 |
|||
* 注意: |
|||
* 1、微信回调超时时间为2s,建议用户使用异步处理流程,确认成功之后立刻回复微信服务器 |
|||
* 2、微信服务器在调用失败或者接到回包为非确认包的时候,会发起重试,需确保你的回调是可以重入 |
|||
* @param WxPayNotifyResults $objData 回调解释出的参数 |
|||
* @param WxPayConfigInterface $config |
|||
* @param string $msg 如果回调处理失败,可以将错误信息输出到该方法 |
|||
* @return true回调出来完成不需要继续回调,false回调处理未完成需要继续回调 |
|||
*/ |
|||
public function NotifyProcess($objData, $config, &$msg) |
|||
{ |
|||
//TODO 用户基础该类之后需要重写该方法,成功的时候返回true,失败返回false |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* 业务可以继承该方法,打印XML方便定位. |
|||
* @param string $xmlData 返回的xml参数 |
|||
* |
|||
**/ |
|||
public function LogAfterProcess($xmlData) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* notify回调方法,该方法中需要赋值需要输出的参数,不可重写 |
|||
* @param array $data |
|||
* @return true回调出来完成不需要继续回调,false回调处理未完成需要继续回调 |
|||
*/ |
|||
final public function NotifyCallBack($data) |
|||
{ |
|||
$msg = "OK"; |
|||
$result = $this->NotifyProcess($data, $this->config, $msg); |
|||
|
|||
if($result == true){ |
|||
$this->SetReturn_code("SUCCESS"); |
|||
$this->SetReturn_msg("OK"); |
|||
} else { |
|||
$this->SetReturn_code("FAIL"); |
|||
$this->SetReturn_msg($msg); |
|||
} |
|||
return $result; |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* 回复通知 |
|||
* @param bool $needSign 是否需要签名输出 |
|||
*/ |
|||
final private function ReplyNotify($needSign = true) |
|||
{ |
|||
//如果需要签名 |
|||
if($needSign == true && |
|||
$this->GetReturn_code() == "SUCCESS") |
|||
{ |
|||
$this->SetSign($this->config); |
|||
} |
|||
|
|||
$xml = $this->ToXml(); |
|||
$this->LogAfterProcess($xml); |
|||
WxpayApi::replyNotify($xml); |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,30 @@ |
|||
<?php |
|||
/** |
|||
* 微信公共接口测试 |
|||
* |
|||
*/ |
|||
include("../wechat.class.php"); |
|||
|
|||
function logdebug($text){ |
|||
file_put_contents('../data/log.txt',$text."\n",FILE_APPEND); |
|||
}; |
|||
$options = array( |
|||
'token'=>'tokenaccesskey', //填写你设定的key |
|||
'debug'=>true, |
|||
'logcallback'=>'logdebug' |
|||
); |
|||
$weObj = new Wechat($options); |
|||
$weObj->valid(); |
|||
$type = $weObj->getRev()->getRevType(); |
|||
switch($type) { |
|||
case Wechat::MSGTYPE_TEXT: |
|||
$weObj->text("hello, I'm wechat")->reply(); |
|||
exit; |
|||
break; |
|||
case Wechat::MSGTYPE_EVENT: |
|||
break; |
|||
case Wechat::MSGTYPE_IMAGE: |
|||
break; |
|||
default: |
|||
$weObj->text("help info")->reply(); |
|||
} |
|||
File diff suppressed because it is too large
Loading…
Reference in new issue