[更新]ComposerUpdate

This commit is contained in:
Anyon 2019-02-18 15:16:48 +08:00
parent 432149160b
commit 48f8d1188d
586 changed files with 12418 additions and 6464 deletions

View File

@ -1,13 +0,0 @@
:: Composer 安装更新脚本
@echo off
title Composer Plugs Install And Optimize
echo.
echo ========= 1. 清理已安装插件 =========
rmdir /s/q vendor thinkphp runtime
echo.
echo ========= 2. 下载并安装插件 =========
composer update --profile --prefer-dist --optimize-autoloader
echo.
echo ========= 3. 压缩并发布插件 =========
composer dump-autoload --optimize
exit

View File

@ -20,11 +20,11 @@
"qiniu/php-sdk": "^7.0", "qiniu/php-sdk": "^7.0",
"zoujingli/wechat-php-sdk": "dev-master", "zoujingli/wechat-php-sdk": "dev-master",
"zoujingli/ip2region": "^1.0", "zoujingli/ip2region": "^1.0",
"topthink/framework": "^5.0", "topthink/framework": "5.0.*",
"topthink/think-captcha": "^1.0", "topthink/think-captcha": "^1.0",
"topthink/think-mongo": "^1.1", "topthink/think-mongo": "^1.1",
"topthink/think-queue": "^1.0", "topthink/think-queue": "^1.0",
"endroid/qrcode": "^1.9", "endroid/qr-code": "^1.9",
"aliyuncs/oss-sdk-php": "^2.2" "aliyuncs/oss-sdk-php": "^2.2"
}, },
"extra": { "extra": {

View File

@ -7,7 +7,7 @@
ThinkPHP 目前使用 Git 来控制程序版本,如果你想为 ThinkPHP 贡献源代码,请先大致了解 Git 的使用方法。我们目前把项目托管在 GitHub 上,任何 GitHub 用户都可以向我们贡献代码。 ThinkPHP 目前使用 Git 来控制程序版本,如果你想为 ThinkPHP 贡献源代码,请先大致了解 Git 的使用方法。我们目前把项目托管在 GitHub 上,任何 GitHub 用户都可以向我们贡献代码。
参与的方式很简单,`fork`一份 ThinkPHP 的代码到你的仓库中,修改后提交,并向我们发起`pull request`申请,我们会及时对代码进行审查并处理你的申请。审查通过后,你的代码将被`merge`进我们的仓库中,这样你就会自动出现在贡献者名单里了,非常方便。 参与的方式很简单,`fork`一份 ThinkPHP 的代码到你的仓库中,修改后提交,并向我们发起`pull request`申请,我们会及时对代码进行审查并处理你的申请。审查通过后,你的代码将被`merge`进我们的仓库中,这样你就会自动出现在贡献者名单里了,非常方便。
我们希望你贡献的代码符合: 我们希望你贡献的代码符合:
@ -60,7 +60,7 @@ GitHub 提供了 Issue 功能,该功能可以用于:
6. 变基(衍合 `rebase`)你的分支到上游 master 分支; 6. 变基(衍合 `rebase`)你的分支到上游 master 分支;
7. `push` 你的本地仓库到 GitHub 7. `push` 你的本地仓库到 GitHub
8. 提交 `pull request` 8. 提交 `pull request`
9. 等待 CI 验证(若不通过则重复 5~7GitHub 会自动更新你的 `pull request` 9. 等待 CI 验证(若不通过则重复 5~7不需要重新提交 `pull request`GitHub 会自动更新你的 `pull request`
10. 等待管理员处理,并及时 `rebase` 你的分支到上游 master 分支(若上游 master 分支有修改)。 10. 等待管理员处理,并及时 `rebase` 你的分支到上游 master 分支(若上游 master 分支有修改)。
*若有必要,可以 `git push -f` 强行推送 rebase 后的分支到自己的 `fork`* *若有必要,可以 `git push -f` 强行推送 rebase 后的分支到自己的 `fork`*

View File

@ -1,6 +1,6 @@
ThinkPHP遵循Apache2开源协议发布并提供免费使用。 ThinkPHP遵循Apache2开源协议发布并提供免费使用。
版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn) 版权所有Copyright © 2006-2017 by ThinkPHP (http://thinkphp.cn)
All rights reserved。 All rights reserved。
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。 ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。

View File

@ -92,7 +92,7 @@ www WEB部署目录或者子目录
## 命名规范 ## 命名规范
ThinkPHP5的命名规范遵循PSR-2规范以及PSR-4自动加载规范。 ThinkPHP5的命名规范遵循`PSR-2`规范以及`PSR-4`自动加载规范。
## 参与开发 ## 参与开发
注册并登录 Github 帐号, fork 本项目并进行改动。 注册并登录 Github 帐号, fork 本项目并进行改动。
@ -105,7 +105,7 @@ ThinkPHP遵循Apache2开源协议发布并提供免费使用。
本项目包含的第三方源码和二进制文件之版权信息另行标注。 本项目包含的第三方源码和二进制文件之版权信息另行标注。
版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn) 版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn)
All rights reserved。 All rights reserved。

View File

@ -2,14 +2,14 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com> // | Author: liu21st <liu21st@gmail.com>
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
define('THINK_VERSION', '5.0.10'); define('THINK_VERSION', '5.0.24');
define('THINK_START_TIME', microtime(true)); define('THINK_START_TIME', microtime(true));
define('THINK_START_MEM', memory_get_usage()); define('THINK_START_MEM', memory_get_usage());
define('EXT', '.php'); define('EXT', '.php');
@ -40,8 +40,10 @@ require CORE_PATH . 'Loader.php';
// 加载环境变量配置文件 // 加载环境变量配置文件
if (is_file(ROOT_PATH . '.env')) { if (is_file(ROOT_PATH . '.env')) {
$env = parse_ini_file(ROOT_PATH . '.env', true); $env = parse_ini_file(ROOT_PATH . '.env', true);
foreach ($env as $key => $val) { foreach ($env as $key => $val) {
$name = ENV_PREFIX . strtoupper($key); $name = ENV_PREFIX . strtoupper($key);
if (is_array($val)) { if (is_array($val)) {
foreach ($val as $k => $v) { foreach ($val as $k => $v) {
$item = $name . '_' . strtoupper($k); $item = $name . '_' . strtoupper($k);

View File

@ -116,6 +116,8 @@ return [
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
'template' => [ 'template' => [
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写
'auto_rule' => 1,
// 模板引擎类型 支持 php think 支持扩展 // 模板引擎类型 支持 php think 支持扩展
'type' => 'Think', 'type' => 'Think',
// 视图基础目录,配置目录为所有模块的视图起始目录 // 视图基础目录,配置目录为所有模块的视图起始目录
@ -286,4 +288,11 @@ return [
'list_rows' => 15, 'list_rows' => 15,
], ],
//控制台配置
'console' => [
'name' => 'Think Console',
'version' => '0.1',
'user' => null,
],
]; ];

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -127,7 +127,7 @@ if (!function_exists('input')) {
if ($pos = strpos($key, '.')) { if ($pos = strpos($key, '.')) {
// 指定参数来源 // 指定参数来源
list($method, $key) = explode('.', $key, 2); list($method, $key) = explode('.', $key, 2);
if (!in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) { if (!in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'route', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) {
$key = $method . '.' . $key; $key = $method . '.' . $key;
$method = 'param'; $method = 'param';
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -12,57 +12,125 @@
// 核心中文语言包 // 核心中文语言包
return [ return [
// 系统错误提示 // 系统错误提示
'Undefined variable' => '未定义变量', 'Undefined variable' => '未定义变量',
'Undefined index' => '未定义数组索引', 'Undefined index' => '未定义数组索引',
'Undefined offset' => '未定义数组下标', 'Undefined offset' => '未定义数组下标',
'Parse error' => '语法解析错误', 'Parse error' => '语法解析错误',
'Type error' => '类型错误', 'Type error' => '类型错误',
'Fatal error' => '致命错误', 'Fatal error' => '致命错误',
'syntax error' => '语法错误', 'syntax error' => '语法错误',
// 框架核心错误提示 // 框架核心错误提示
'dispatch type not support' => '不支持的调度类型', 'dispatch type not support' => '不支持的调度类型',
'method param miss' => '方法参数错误', 'method param miss' => '方法参数错误',
'method not exists' => '方法不存在', 'method not exists' => '方法不存在',
'module not exists' => '模块不存在', 'module not exists' => '模块不存在',
'controller not exists' => '控制器不存在', 'controller not exists' => '控制器不存在',
'class not exists' => '类不存在', 'class not exists' => '类不存在',
'property not exists' => '类的属性不存在', 'property not exists' => '类的属性不存在',
'template not exists' => '模板文件不存在', 'template not exists' => '模板文件不存在',
'illegal controller name' => '非法的控制器名称', 'illegal controller name' => '非法的控制器名称',
'illegal action name' => '非法的操作名称', 'illegal action name' => '非法的操作名称',
'url suffix deny' => '禁止的URL后缀访问', 'url suffix deny' => '禁止的URL后缀访问',
'Route Not Found' => '当前访问路由未定义', 'Route Not Found' => '当前访问路由未定义',
'Undefined db type' => '未定义数据库类型', 'Undefined db type' => '未定义数据库类型',
'variable type error' => '变量类型错误', 'variable type error' => '变量类型错误',
'PSR-4 error' => 'PSR-4 规范错误', 'PSR-4 error' => 'PSR-4 规范错误',
'not support total' => '简洁模式下不能获取数据总数', 'not support total' => '简洁模式下不能获取数据总数',
'not support last' => '简洁模式下不能获取最后一页', 'not support last' => '简洁模式下不能获取最后一页',
'error session handler' => '错误的SESSION处理器类', 'error session handler' => '错误的SESSION处理器类',
'not allow php tag' => '模板不允许使用PHP语法', 'not allow php tag' => '模板不允许使用PHP语法',
'not support' => '不支持', 'not support' => '不支持',
'redisd master' => 'Redisd 主服务器错误', 'redisd master' => 'Redisd 主服务器错误',
'redisd slave' => 'Redisd 从服务器错误', 'redisd slave' => 'Redisd 从服务器错误',
'must run at sae' => '必须在SAE运行', 'must run at sae' => '必须在SAE运行',
'memcache init error' => '未开通Memcache服务请在SAE管理平台初始化Memcache服务', 'memcache init error' => '未开通Memcache服务请在SAE管理平台初始化Memcache服务',
'KVDB init error' => '没有初始化KVDB请在SAE管理平台初始化KVDB服务', 'KVDB init error' => '没有初始化KVDB请在SAE管理平台初始化KVDB服务',
'fields not exists' => '数据表字段不存在', 'fields not exists' => '数据表字段不存在',
'where express error' => '查询表达式错误', 'where express error' => '查询表达式错误',
'no data to update' => '没有任何数据需要更新', 'not support data' => '不支持的数据表达式',
'miss data to insert' => '缺少需要写入的数据', 'no data to update' => '没有任何数据需要更新',
'miss complex primary data' => '缺少复合主键数据', 'miss data to insert' => '缺少需要写入的数据',
'miss update condition' => '缺少更新条件', 'miss complex primary data' => '缺少复合主键数据',
'model data Not Found' => '模型数据不存在', 'miss update condition' => '缺少更新条件',
'table data not Found' => '表数据不存在', 'model data Not Found' => '模型数据不存在',
'delete without condition' => '没有条件不会执行删除操作', 'table data not Found' => '表数据不存在',
'miss relation data' => '缺少关联表数据', 'delete without condition' => '没有条件不会执行删除操作',
'tag attr must' => '模板标签属性必须', 'miss relation data' => '缺少关联表数据',
'tag error' => '模板标签错误', 'tag attr must' => '模板标签属性必须',
'cache write error' => '缓存写入失败', 'tag error' => '模板标签错误',
'sae mc write error' => 'SAE mc 写入错误', 'cache write error' => '缓存写入失败',
'route name not exists' => '路由标识不存在(或参数不够)', 'sae mc write error' => 'SAE mc 写入错误',
'invalid request' => '非法请求', 'route name not exists' => '路由标识不存在(或参数不够)',
'bind attr has exists' => '模型的属性已经存在', 'invalid request' => '非法请求',
'relation data not exists' => '关联数据不存在', 'bind attr has exists' => '模型的属性已经存在',
'relation not support' => '关联不支持', 'relation data not exists' => '关联数据不存在',
'relation not support' => '关联不支持',
'chunk not support order' => 'Chunk不支持调用order方法',
'closure not support cache(true)' => '使用闭包查询不支持cache(true)请指定缓存Key',
// 上传错误信息
'unknown upload error' => '未知上传错误!',
'file write error' => '文件写入失败!',
'upload temp dir not found' => '找不到临时文件夹!',
'no file to uploaded' => '没有文件被上传!',
'only the portion of file is uploaded' => '文件只有部分被上传!',
'upload File size exceeds the maximum value' => '上传文件大小超过了最大值!',
'upload write error' => '文件上传保存错误!',
'has the same filename: {:filename}' => '存在同名文件:{:filename}',
'upload illegal files' => '非法上传文件',
'illegal image files' => '非法图片文件',
'extensions to upload is not allowed' => '上传文件后缀不允许',
'mimetype to upload is not allowed' => '上传文件MIME类型不允许',
'filesize not match' => '上传文件大小不符!',
'directory {:path} creation failed' => '目录 {:path} 创建失败!',
// Validate Error Message
':attribute require' => ':attribute不能为空',
':attribute must be numeric' => ':attribute必须是数字',
':attribute must be integer' => ':attribute必须是整数',
':attribute must be float' => ':attribute必须是浮点数',
':attribute must be bool' => ':attribute必须是布尔值',
':attribute not a valid email address' => ':attribute格式不符',
':attribute not a valid mobile' => ':attribute格式不符',
':attribute must be a array' => ':attribute必须是数组',
':attribute must be yes,on or 1' => ':attribute必须是yes、on或者1',
':attribute not a valid datetime' => ':attribute不是一个有效的日期或时间格式',
':attribute not a valid file' => ':attribute不是有效的上传文件',
':attribute not a valid image' => ':attribute不是有效的图像文件',
':attribute must be alpha' => ':attribute只能是字母',
':attribute must be alpha-numeric' => ':attribute只能是字母和数字',
':attribute must be alpha-numeric, dash, underscore' => ':attribute只能是字母、数字和下划线_及破折号-',
':attribute not a valid domain or ip' => ':attribute不是有效的域名或者IP',
':attribute must be chinese' => ':attribute只能是汉字',
':attribute must be chinese or alpha' => ':attribute只能是汉字、字母',
':attribute must be chinese,alpha-numeric' => ':attribute只能是汉字、字母和数字',
':attribute must be chinese,alpha-numeric,underscore, dash' => ':attribute只能是汉字、字母、数字和下划线_及破折号-',
':attribute not a valid url' => ':attribute不是有效的URL地址',
':attribute not a valid ip' => ':attribute不是有效的IP地址',
':attribute must be dateFormat of :rule' => ':attribute必须使用日期格式 :rule',
':attribute must be in :rule' => ':attribute必须在 :rule 范围内',
':attribute be notin :rule' => ':attribute不能在 :rule 范围内',
':attribute must between :1 - :2' => ':attribute只能在 :1 - :2 之间',
':attribute not between :1 - :2' => ':attribute不能在 :1 - :2 之间',
'size of :attribute must be :rule' => ':attribute长度不符合要求 :rule',
'max size of :attribute must be :rule' => ':attribute长度不能超过 :rule',
'min size of :attribute must be :rule' => ':attribute长度不能小于 :rule',
':attribute cannot be less than :rule' => ':attribute日期不能小于 :rule',
':attribute cannot exceed :rule' => ':attribute日期不能超过 :rule',
':attribute not within :rule' => '不在有效期内 :rule',
'access IP is not allowed' => '不允许的IP访问',
'access IP denied' => '禁止的IP访问',
':attribute out of accord with :2' => ':attribute和确认字段:2不一致',
':attribute cannot be same with :2' => ':attribute和比较字段:2不能相同',
':attribute must greater than or equal :rule' => ':attribute必须大于等于 :rule',
':attribute must greater than :rule' => ':attribute必须大于 :rule',
':attribute must less than or equal :rule' => ':attribute必须小于等于 :rule',
':attribute must less than :rule' => ':attribute必须小于 :rule',
':attribute must equal :rule' => ':attribute必须等于 :rule',
':attribute has exists' => ':attribute已存在',
':attribute not conform to the rules' => ':attribute不符合指定规则',
'invalid Request method' => '无效的请求类型',
'invalid token' => '令牌数据无效',
'not conform to the rules' => '规则错误',
]; ];

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -11,13 +11,14 @@
namespace think; namespace think;
use think\exception\ClassNotFoundException;
use think\exception\HttpException; use think\exception\HttpException;
use think\exception\HttpResponseException; use think\exception\HttpResponseException;
use think\exception\RouteNotFoundException; use think\exception\RouteNotFoundException;
/** /**
* App 应用管理 * App 应用管理
* @author liu21st <liu21st@gmail.com> * @author liu21st <liu21st@gmail.com>
*/ */
class App class App
{ {
@ -56,24 +57,32 @@ class App
*/ */
protected static $routeMust; protected static $routeMust;
/**
* @var array 请求调度分发
*/
protected static $dispatch; protected static $dispatch;
/**
* @var array 额外加载文件
*/
protected static $file = []; protected static $file = [];
/** /**
* 执行应用程序 * 执行应用程序
* @access public * @access public
* @param Request $request Request对象 * @param Request $request 请求对象
* @return Response * @return Response
* @throws Exception * @throws Exception
*/ */
public static function run(Request $request = null) public static function run(Request $request = null)
{ {
is_null($request) && $request = Request::instance(); $request = is_null($request) ? Request::instance() : $request;
try { try {
$config = self::initCommon(); $config = self::initCommon();
// 模块/控制器绑定
if (defined('BIND_MODULE')) { if (defined('BIND_MODULE')) {
// 模块/控制器绑定
BIND_MODULE && Route::bind(BIND_MODULE); BIND_MODULE && Route::bind(BIND_MODULE);
} elseif ($config['auto_bind_module']) { } elseif ($config['auto_bind_module']) {
// 入口自动绑定 // 入口自动绑定
@ -87,10 +96,8 @@ class App
// 默认语言 // 默认语言
Lang::range($config['default_lang']); Lang::range($config['default_lang']);
if ($config['lang_switch_on']) { // 开启多语言机制 检测当前语言
// 开启多语言机制 检测当前语言 $config['lang_switch_on'] && Lang::detect();
Lang::detect();
}
$request->langset(Lang::range()); $request->langset(Lang::range());
// 加载系统语言包 // 加载系统语言包
@ -99,12 +106,16 @@ class App
APP_PATH . 'lang' . DS . $request->langset() . EXT, APP_PATH . 'lang' . DS . $request->langset() . EXT,
]); ]);
// 监听 app_dispatch
Hook::listen('app_dispatch', self::$dispatch);
// 获取应用调度信息 // 获取应用调度信息
$dispatch = self::$dispatch; $dispatch = self::$dispatch;
// 未设置调度信息则进行 URL 路由检测
if (empty($dispatch)) { if (empty($dispatch)) {
// 进行URL路由检测
$dispatch = self::routeCheck($request, $config); $dispatch = self::routeCheck($request, $config);
} }
// 记录当前调度信息 // 记录当前调度信息
$request->dispatch($dispatch); $request->dispatch($dispatch);
@ -115,10 +126,15 @@ class App
Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info'); Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info');
} }
// 监听app_begin // 监听 app_begin
Hook::listen('app_begin', $dispatch); Hook::listen('app_begin', $dispatch);
// 请求缓存检查 // 请求缓存检查
$request->cache($config['request_cache'], $config['request_cache_expire'], $config['request_cache_except']); $request->cache(
$config['request_cache'],
$config['request_cache_expire'],
$config['request_cache_except']
);
$data = self::exec($dispatch, $config); $data = self::exec($dispatch, $config);
} catch (HttpResponseException $exception) { } catch (HttpResponseException $exception) {
@ -133,290 +149,33 @@ class App
$response = $data; $response = $data;
} elseif (!is_null($data)) { } elseif (!is_null($data)) {
// 默认自动识别响应输出类型 // 默认自动识别响应输出类型
$isAjax = $request->isAjax(); $type = $request->isAjax() ?
$type = $isAjax ? Config::get('default_ajax_return') : Config::get('default_return_type'); Config::get('default_ajax_return') :
Config::get('default_return_type');
$response = Response::create($data, $type); $response = Response::create($data, $type);
} else { } else {
$response = Response::create(); $response = Response::create();
} }
// 监听app_end // 监听 app_end
Hook::listen('app_end', $response); Hook::listen('app_end', $response);
return $response; return $response;
} }
/** /**
* 设置当前请求的调度信息 * 初始化应用,并返回配置信息
* @access public * @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 private
* @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
* @param array $vars 变量
* @return array * @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 = [];
if ($reflect->getNumberOfParameters() > 0) {
// 判断数组类型 数字数组时按顺序绑定参数
reset($vars);
$type = key($vars) === 0 ? 1 : 0;
$params = $reflect->getParameters();
foreach ($params as $param) {
$args[] = self::getParamValue($param, $vars, $type);
}
}
return $args;
}
/**
* 获取参数值
* @access private
* @param \ReflectionParameter $param
* @param array $vars 变量
* @param string $type
* @return array
*/
private static function getParamValue($param, &$vars, $type)
{
$name = $param->getName();
$class = $param->getClass();
if ($class) {
$className = $class->getName();
$bind = Request::instance()->$name;
if ($bind instanceof $className) {
$result = $bind;
} else {
if (method_exists($className, 'invoke')) {
$method = new \ReflectionMethod($className, 'invoke');
if ($method->isPublic() && $method->isStatic()) {
return $className::invoke(Request::instance());
}
}
$result = method_exists($className, 'instance') ? $className::instance() : new $className;
}
} elseif (1 == $type && !empty($vars)) {
$result = array_shift($vars);
} elseif (0 == $type && isset($vars[$name])) {
$result = $vars[$name];
} elseif ($param->isDefaultValueAvailable()) {
$result = $param->getDefaultValue();
} else {
throw new \InvalidArgumentException('method param miss:' . $name);
}
return $result;
}
protected static function exec($dispatch, $config)
{
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');
}
return $data;
}
/**
* 执行模块
* @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;
// 获取操作名
$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() public static function initCommon()
{ {
if (empty(self::$init)) { if (empty(self::$init)) {
if (defined('APP_NAMESPACE')) { if (defined('APP_NAMESPACE')) {
self::$namespace = APP_NAMESPACE; self::$namespace = APP_NAMESPACE;
} }
Loader::addNamespace(self::$namespace, APP_PATH); Loader::addNamespace(self::$namespace, APP_PATH);
// 初始化应用 // 初始化应用
@ -425,17 +184,21 @@ class App
// 应用调试模式 // 应用调试模式
self::$debug = Env::get('app_debug', Config::get('app_debug')); self::$debug = Env::get('app_debug', Config::get('app_debug'));
if (!self::$debug) { if (!self::$debug) {
ini_set('display_errors', 'Off'); ini_set('display_errors', 'Off');
} elseif (!IS_CLI) { } elseif (!IS_CLI) {
//重新申请一块比较大的buffer // 重新申请一块比较大的 buffer
if (ob_get_level() > 0) { if (ob_get_level() > 0) {
$output = ob_get_clean(); $output = ob_get_clean();
} }
ob_start(); ob_start();
if (!empty($output)) { if (!empty($output)) {
echo $output; echo $output;
} }
} }
if (!empty($config['root_namespace'])) { if (!empty($config['root_namespace'])) {
@ -456,11 +219,12 @@ class App
// 设置系统时区 // 设置系统时区
date_default_timezone_set($config['default_timezone']); date_default_timezone_set($config['default_timezone']);
// 监听app_init // 监听 app_init
Hook::listen('app_init'); Hook::listen('app_init');
self::$init = true; self::$init = true;
} }
return Config::get(); return Config::get();
} }
@ -481,12 +245,13 @@ class App
} elseif (is_file(RUNTIME_PATH . $module . 'init' . EXT)) { } elseif (is_file(RUNTIME_PATH . $module . 'init' . EXT)) {
include RUNTIME_PATH . $module . 'init' . EXT; include RUNTIME_PATH . $module . 'init' . EXT;
} else { } else {
$path = APP_PATH . $module;
// 加载模块配置 // 加载模块配置
$config = Config::load(CONF_PATH . $module . 'config' . CONF_EXT); $config = Config::load(CONF_PATH . $module . 'config' . CONF_EXT);
// 读取数据库配置文件 // 读取数据库配置文件
$filename = CONF_PATH . $module . 'database' . CONF_EXT; $filename = CONF_PATH . $module . 'database' . CONF_EXT;
Config::load($filename, 'database'); Config::load($filename, 'database');
// 读取扩展配置文件 // 读取扩展配置文件
if (is_dir(CONF_PATH . $module . 'extra')) { if (is_dir(CONF_PATH . $module . 'extra')) {
$dir = CONF_PATH . $module . 'extra'; $dir = CONF_PATH . $module . 'extra';
@ -501,7 +266,7 @@ class App
// 加载应用状态配置 // 加载应用状态配置
if ($config['app_status']) { if ($config['app_status']) {
$config = Config::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT); Config::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT);
} }
// 加载行为扩展文件 // 加载行为扩展文件
@ -510,6 +275,7 @@ class App
} }
// 加载公共文件 // 加载公共文件
$path = APP_PATH . $module;
if (is_file($path . 'common' . EXT)) { if (is_file($path . 'common' . EXT)) {
include $path . 'common' . EXT; include $path . 'common' . EXT;
} }
@ -519,14 +285,337 @@ class App
Lang::load($path . 'lang' . DS . Request::instance()->langset() . EXT); Lang::load($path . 'lang' . DS . Request::instance()->langset() . EXT);
} }
} }
return Config::get(); return Config::get();
} }
/**
* 设置当前请求的调度信息
* @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();
$args = $constructor ? self::bindParams($constructor, $vars) : [];
return $reflect->newInstanceArgs($args);
}
/**
* 绑定参数
* @access private
* @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
* @param array $vars 变量
* @return array
*/
private static function bindParams($reflect, $vars = [])
{
// 自动获取请求变量
if (empty($vars)) {
$vars = Config::get('url_param_type') ?
Request::instance()->route() :
Request::instance()->param();
}
$args = [];
if ($reflect->getNumberOfParameters() > 0) {
// 判断数组类型 数字数组时按顺序绑定参数
reset($vars);
$type = key($vars) === 0 ? 1 : 0;
foreach ($reflect->getParameters() as $param) {
$args[] = self::getParamValue($param, $vars, $type);
}
}
return $args;
}
/**
* 获取参数值
* @access private
* @param \ReflectionParameter $param 参数
* @param array $vars 变量
* @param string $type 类别
* @return array
*/
private static function getParamValue($param, &$vars, $type)
{
$name = $param->getName();
$class = $param->getClass();
if ($class) {
$className = $class->getName();
$bind = Request::instance()->$name;
if ($bind instanceof $className) {
$result = $bind;
} else {
if (method_exists($className, 'invoke')) {
$method = new \ReflectionMethod($className, 'invoke');
if ($method->isPublic() && $method->isStatic()) {
return $className::invoke(Request::instance());
}
}
$result = method_exists($className, 'instance') ?
$className::instance() :
new $className;
}
} elseif (1 == $type && !empty($vars)) {
$result = array_shift($vars);
} elseif (0 == $type && isset($vars[$name])) {
$result = $vars[$name];
} elseif ($param->isDefaultValueAvailable()) {
$result = $param->getDefaultValue();
} else {
throw new \InvalidArgumentException('method param miss:' . $name);
}
return $result;
}
/**
* 执行调用分发
* @access protected
* @param array $dispatch 调用信息
* @param array $config 配置信息
* @return Response|mixed
* @throws \InvalidArgumentException
*/
protected static function exec($dispatch, $config)
{
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': // Response 实例
$data = $dispatch['response'];
break;
default:
throw new \InvalidArgumentException('dispatch type not support');
}
return $data;
}
/**
* 执行模块
* @access public
* @param array $result 模块/控制器/操作
* @param array $config 配置参数
* @param bool $convert 是否自动转换控制器和操作名
* @return mixed
* @throws HttpException
*/
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);
}
// 设置默认过滤机制
$request->filter($config['default_filter']);
// 当前模块路径
App::$modulePath = APP_PATH . ($module ? $module . DS : '');
// 是否自动转换控制器和操作名
$convert = is_bool($convert) ? $convert : $config['url_convert'];
// 获取控制器名
$controller = strip_tags($result[1] ?: $config['default_controller']);
if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) {
throw new HttpException(404, 'controller not exists:' . $controller);
}
$controller = $convert ? strtolower($controller) : $controller;
// 获取操作名
$actionName = strip_tags($result[2] ?: $config['default_action']);
if (!empty($config['action_convert'])) {
$actionName = Loader::parseName($actionName, 1);
} else {
$actionName = $convert ? strtolower($actionName) : $actionName;
}
// 设置当前请求的控制器、操作
$request->controller(Loader::parseName($controller, 1))->action($actionName);
// 监听module_init
Hook::listen('module_init', $request);
try {
$instance = Loader::controller(
$controller,
$config['url_controller_layer'],
$config['controller_suffix'],
$config['empty_controller']
);
} catch (ClassNotFoundException $e) {
throw new HttpException(404, 'controller not exists:' . $e->getClass());
}
// 获取当前操作名
$action = $actionName . $config['action_suffix'];
$vars = [];
if (is_callable([$instance, $action])) {
// 执行操作方法
$call = [$instance, $action];
// 严格获取当前操作方法名
$reflect = new \ReflectionMethod($instance, $action);
$methodName = $reflect->getName();
$suffix = $config['action_suffix'];
$actionName = $suffix ? substr($methodName, 0, -strlen($suffix)) : $methodName;
$request->action($actionName);
} 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);
}
/** /**
* URL路由检测根据PATH_INFO) * URL路由检测根据PATH_INFO)
* @access public * @access public
* @param \think\Request $request * @param \think\Request $request 请求实例
* @param array $config * @param array $config 配置信息
* @return array * @return array
* @throws \think\Exception * @throws \think\Exception
*/ */
@ -535,6 +624,7 @@ class App
$path = $request->path(); $path = $request->path();
$depr = $config['pathinfo_depr']; $depr = $config['pathinfo_depr'];
$result = false; $result = false;
// 路由检测 // 路由检测
$check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on']; $check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on'];
if ($check) { if ($check) {
@ -542,18 +632,14 @@ class App
if (is_file(RUNTIME_PATH . 'route.php')) { if (is_file(RUNTIME_PATH . 'route.php')) {
// 读取路由缓存 // 读取路由缓存
$rules = include RUNTIME_PATH . 'route.php'; $rules = include RUNTIME_PATH . 'route.php';
if (is_array($rules)) { is_array($rules) && Route::rules($rules);
Route::rules($rules);
}
} else { } else {
$files = $config['route_config_file']; $files = $config['route_config_file'];
foreach ($files as $file) { foreach ($files as $file) {
if (is_file(CONF_PATH . $file . CONF_EXT)) { if (is_file(CONF_PATH . $file . CONF_EXT)) {
// 导入路由配置 // 导入路由配置
$rules = include CONF_PATH . $file . CONF_EXT; $rules = include CONF_PATH . $file . CONF_EXT;
if (is_array($rules)) { is_array($rules) && Route::import($rules);
Route::import($rules);
}
} }
} }
} }
@ -561,15 +647,18 @@ class App
// 路由检测根据路由定义返回不同的URL调度 // 路由检测根据路由定义返回不同的URL调度
$result = Route::check($request, $path, $depr, $config['url_domain_deploy']); $result = Route::check($request, $path, $depr, $config['url_domain_deploy']);
$must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must']; $must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must'];
if ($must && false === $result) { if ($must && false === $result) {
// 路由无效 // 路由无效
throw new RouteNotFoundException(); throw new RouteNotFoundException();
} }
} }
// 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索
if (false === $result) { if (false === $result) {
// 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索
$result = Route::parseUrl($path, $depr, $config['controller_auto_search']); $result = Route::parseUrl($path, $depr, $config['controller_auto_search']);
} }
return $result; return $result;
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -14,36 +14,44 @@ namespace think;
class Build class Build
{ {
/** /**
* 根据传入的build资料创建目录和文件 * 根据传入的 build 资料创建目录和文件
* @access protected * @access public
* @param array $build build列表 * @param array $build build 列表
* @param string $namespace 应用类库命名空间 * @param string $namespace 应用类库命名空间
* @param bool $suffix 类库后缀 * @param bool $suffix 类库后缀
* @return void * @return void
* @throws Exception
*/ */
public static function run(array $build = [], $namespace = 'app', $suffix = false) public static function run(array $build = [], $namespace = 'app', $suffix = false)
{ {
// 锁定 // 锁定
$lockfile = APP_PATH . 'build.lock'; $lock = APP_PATH . 'build.lock';
if (is_writable($lockfile)) {
return; // 如果锁定文件不可写(不存在)则进行处理,否则表示已经有程序在处理了
} elseif (!touch($lockfile)) { if (!is_writable($lock)) {
throw new Exception('应用目录[' . APP_PATH . ']不可写,目录无法自动生成!<BR>请手动生成项目目录~', 10006); if (!touch($lock)) {
} throw new Exception(
foreach ($build as $module => $list) { '应用目录[' . APP_PATH . ']不可写,目录无法自动生成!<BR>请手动生成项目目录~',
if ('__dir__' == $module) { 10006
// 创建目录列表 );
self::buildDir($list);
} elseif ('__file__' == $module) {
// 创建文件列表
self::buildFile($list);
} else {
// 创建模块
self::module($module, $list, $namespace, $suffix);
} }
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($lock);
} }
// 解除锁定
unlink($lockfile);
} }
/** /**
@ -55,10 +63,8 @@ class Build
protected static function buildDir($list) protected static function buildDir($list)
{ {
foreach ($list as $dir) { foreach ($list as $dir) {
if (!is_dir(APP_PATH . $dir)) { // 目录不存在则创建目录
// 创建目录 !is_dir(APP_PATH . $dir) && mkdir(APP_PATH . $dir, 0755, true);
mkdir(APP_PATH . $dir, 0755, true);
}
} }
} }
@ -71,12 +77,17 @@ class Build
protected static function buildFile($list) protected static function buildFile($list)
{ {
foreach ($list as $file) { foreach ($list as $file) {
// 先创建目录
if (!is_dir(APP_PATH . dirname($file))) { if (!is_dir(APP_PATH . dirname($file))) {
// 创建目录
mkdir(APP_PATH . dirname($file), 0755, true); mkdir(APP_PATH . dirname($file), 0755, true);
} }
// 再创建文件
if (!is_file(APP_PATH . $file)) { if (!is_file(APP_PATH . $file)) {
file_put_contents(APP_PATH . $file, 'php' == pathinfo($file, PATHINFO_EXTENSION) ? "<?php\n" : ''); file_put_contents(
APP_PATH . $file,
'php' == pathinfo($file, PATHINFO_EXTENSION) ? "<?php\n" : ''
);
} }
} }
} }
@ -84,35 +95,37 @@ class Build
/** /**
* 创建模块 * 创建模块
* @access public * @access public
* @param string $module 模块名 * @param string $module 模块名
* @param array $list build列表 * @param array $list build 列表
* @param string $namespace 应用类库命名空间 * @param string $namespace 应用类库命名空间
* @param bool $suffix 类库后缀 * @param bool $suffix 类库后缀
* @return void * @return void
*/ */
public static function module($module = '', $list = [], $namespace = 'app', $suffix = false) public static function module($module = '', $list = [], $namespace = 'app', $suffix = false)
{ {
$module = $module ? $module : ''; $module = $module ?: '';
if (!is_dir(APP_PATH . $module)) {
// 创建模块目录 // 创建模块目录
mkdir(APP_PATH . $module); !is_dir(APP_PATH . $module) && mkdir(APP_PATH . $module);
}
// 如果不是 runtime 目录则需要创建配置文件和公共文件、创建模块的默认页面
if (basename(RUNTIME_PATH) != $module) { if (basename(RUNTIME_PATH) != $module) {
// 创建配置文件和公共文件
self::buildCommon($module); self::buildCommon($module);
// 创建模块的默认页面
self::buildHello($module, $namespace, $suffix); self::buildHello($module, $namespace, $suffix);
} }
// 未指定文件和目录,则创建默认的模块目录和文件
if (empty($list)) { if (empty($list)) {
// 创建默认的模块目录和文件
$list = [ $list = [
'__file__' => ['config.php', 'common.php'], '__file__' => ['config.php', 'common.php'],
'__dir__' => ['controller', 'model', 'view'], '__dir__' => ['controller', 'model', 'view'],
]; ];
} }
// 创建子目录和文件 // 创建子目录和文件
foreach ($list as $path => $file) { foreach ($list as $path => $file) {
$modulePath = APP_PATH . $module . DS; $modulePath = APP_PATH . $module . DS;
if ('__dir__' == $path) { if ('__dir__' == $path) {
// 生成子目录 // 生成子目录
foreach ($file as $dir) { foreach ($file as $dir) {
@ -122,16 +135,20 @@ class Build
// 生成(空白)文件 // 生成(空白)文件
foreach ($file as $name) { foreach ($file as $name) {
if (!is_file($modulePath . $name)) { if (!is_file($modulePath . $name)) {
file_put_contents($modulePath . $name, 'php' == pathinfo($name, PATHINFO_EXTENSION) ? "<?php\n" : ''); file_put_contents(
$modulePath . $name,
'php' == pathinfo($name, PATHINFO_EXTENSION) ? "<?php\n" : ''
);
} }
} }
} else { } else {
// 生成相关MVC文件 // 生成相关 MVC 文件
foreach ($file as $val) { foreach ($file as $val) {
$val = trim($val); $val = trim($val);
$filename = $modulePath . $path . DS . $val . ($suffix ? ucfirst($path) : '') . EXT; $filename = $modulePath . $path . DS . $val . ($suffix ? ucfirst($path) : '') . EXT;
$space = $namespace . '\\' . ($module ? $module . '\\' : '') . $path; $space = $namespace . '\\' . ($module ? $module . '\\' : '') . $path;
$class = $val . ($suffix ? ucfirst($path) : ''); $class = $val . ($suffix ? ucfirst($path) : '');
switch ($path) { switch ($path) {
case 'controller': // 控制器 case 'controller': // 控制器
$content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}"; $content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
@ -159,18 +176,27 @@ class Build
/** /**
* 创建模块的欢迎页面 * 创建模块的欢迎页面
* @access public * @access protected
* @param string $module 模块名 * @param string $module 模块名
* @param string $namespace 应用类库命名空间 * @param string $namespace 应用类库命名空间
* @param bool $suffix 类库后缀 * @param bool $suffix 类库后缀
* @return void * @return void
*/ */
protected static function buildHello($module, $namespace, $suffix = false) protected static function buildHello($module, $namespace, $suffix = false)
{ {
$filename = APP_PATH . ($module ? $module . DS : '') . 'controller' . DS . 'Index' . ($suffix ? 'Controller' : '') . EXT; $filename = APP_PATH . ($module ? $module . DS : '') .
'controller' . DS . 'Index' .
($suffix ? 'Controller' : '') . EXT;
if (!is_file($filename)) { if (!is_file($filename)) {
$content = file_get_contents(THINK_PATH . 'tpl' . DS . 'default_index.tpl'); $module = $module ? $module . '\\' : '';
$content = str_replace(['{$app}', '{$module}', '{layer}', '{$suffix}'], [$namespace, $module ? $module . '\\' : '', 'controller', $suffix ? 'Controller' : ''], $content); $suffix = $suffix ? 'Controller' : '';
$content = str_replace(
['{$app}', '{$module}', '{layer}', '{$suffix}'],
[$namespace, $module, 'controller', $suffix],
file_get_contents(THINK_PATH . 'tpl' . DS . 'default_index.tpl')
);
self::checkDirBuild(dirname($filename)); self::checkDirBuild(dirname($filename));
file_put_contents($filename, $content); file_put_contents($filename, $content);
} }
@ -178,28 +204,32 @@ class Build
/** /**
* 创建模块的公共文件 * 创建模块的公共文件
* @access public * @access protected
* @param string $module 模块名 * @param string $module 模块名
* @return void * @return void
*/ */
protected static function buildCommon($module) protected static function buildCommon($module)
{ {
$filename = CONF_PATH . ($module ? $module . DS : '') . 'config.php'; $config = CONF_PATH . ($module ? $module . DS : '') . 'config.php';
self::checkDirBuild(dirname($filename)); self::checkDirBuild(dirname($config));
if (!is_file($filename)) {
file_put_contents($filename, "<?php\n//配置文件\nreturn [\n\n];"); if (!is_file($config)) {
} file_put_contents($config, "<?php\n//配置文件\nreturn [\n\n];");
$filename = APP_PATH . ($module ? $module . DS : '') . 'common.php';
if (!is_file($filename)) {
file_put_contents($filename, "<?php\n");
} }
$common = APP_PATH . ($module ? $module . DS : '') . 'common.php';
if (!is_file($common)) file_put_contents($common, "<?php\n");
} }
/**
* 创建目录
* @access protected
* @param string $dirname 目录名称
* @return void
*/
protected static function checkDirBuild($dirname) protected static function checkDirBuild($dirname)
{ {
if (!is_dir($dirname)) { !is_dir($dirname) && mkdir($dirname, 0755, true);
mkdir($dirname, 0755, true);
}
} }
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -15,71 +15,86 @@ use think\cache\Driver;
class Cache class Cache
{ {
protected static $instance = [];
public static $readTimes = 0;
public static $writeTimes = 0;
/** /**
* 操作句柄 * @var array 缓存的实例
* @var object
* @access protected
*/ */
protected static $handler; public static $instance = [];
/** /**
* 连接缓存 * @var int 缓存读取次数
*/
public static $readTimes = 0;
/**
* @var int 缓存写入次数
*/
public static $writeTimes = 0;
/**
* @var object 操作句柄
*/
public static $handler;
/**
* 连接缓存驱动
* @access public * @access public
* @param array $options 配置数组 * @param array $options 配置数组
* @param bool|string $name 缓存连接标识 true 强制重新连接 * @param bool|string $name 缓存连接标识 true 强制重新连接
* @return Driver * @return Driver
*/ */
public static function connect(array $options = [], $name = false) public static function connect(array $options = [], $name = false)
{ {
$type = !empty($options['type']) ? $options['type'] : 'File'; $type = !empty($options['type']) ? $options['type'] : 'File';
if (false === $name) { if (false === $name) {
$name = md5(serialize($options)); $name = md5(serialize($options));
} }
if (true === $name || !isset(self::$instance[$name])) { if (true === $name || !isset(self::$instance[$name])) {
$class = false !== strpos($type, '\\') ? $type : '\\think\\cache\\driver\\' . ucwords($type); $class = false === strpos($type, '\\') ?
'\\think\\cache\\driver\\' . ucwords($type) :
$type;
// 记录初始化信息 // 记录初始化信息
App::$debug && Log::record('[ CACHE ] INIT ' . $type, 'info'); App::$debug && Log::record('[ CACHE ] INIT ' . $type, 'info');
if (true === $name) { if (true === $name) {
return new $class($options); return new $class($options);
} else {
self::$instance[$name] = new $class($options);
} }
self::$instance[$name] = new $class($options);
} }
return self::$instance[$name]; return self::$instance[$name];
} }
/** /**
* 自动初始化缓存 * 自动初始化缓存
* @access public * @access public
* @param array $options 配置数组 * @param array $options 配置数组
* @return Driver * @return Driver
*/ */
public static function init(array $options = []) public static function init(array $options = [])
{ {
if (is_null(self::$handler)) { if (is_null(self::$handler)) {
// 自动初始化缓存 if (empty($options) && 'complex' == Config::get('cache.type')) {
if (!empty($options)) { $default = Config::get('cache.default');
$connect = self::connect($options); // 获取默认缓存配置,并连接
} elseif ('complex' == Config::get('cache.type')) { $options = Config::get('cache.' . $default['type']) ?: $default;
$connect = self::connect(Config::get('cache.default')); } elseif (empty($options)) {
} else { $options = Config::get('cache');
$connect = self::connect(Config::get('cache'));
} }
self::$handler = $connect;
self::$handler = self::connect($options);
} }
return self::$handler; return self::$handler;
} }
/** /**
* 切换缓存类型 需要配置 cache.type complex * 切换缓存类型 需要配置 cache.type complex
* @access public * @access public
* @param string $name 缓存标识 * @param string $name 缓存标识
* @return Driver * @return Driver
*/ */
public static function store($name = '') public static function store($name = '')
@ -87,131 +102,141 @@ class Cache
if ('' !== $name && 'complex' == Config::get('cache.type')) { if ('' !== $name && 'complex' == Config::get('cache.type')) {
return self::connect(Config::get('cache.' . $name), strtolower($name)); return self::connect(Config::get('cache.' . $name), strtolower($name));
} }
return self::init(); return self::init();
} }
/** /**
* 判断缓存是否存在 * 判断缓存是否存在
* @access public * @access public
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @return bool * @return bool
*/ */
public static function has($name) public static function has($name)
{ {
self::$readTimes++; self::$readTimes++;
return self::init()->has($name); return self::init()->has($name);
} }
/** /**
* 读取缓存 * 读取缓存
* @access public * @access public
* @param string $name 缓存标识 * @param string $name 缓存标识
* @param mixed $default 默认值 * @param mixed $default 默认值
* @return mixed * @return mixed
*/ */
public static function get($name, $default = false) public static function get($name, $default = false)
{ {
self::$readTimes++; self::$readTimes++;
return self::init()->get($name, $default); return self::init()->get($name, $default);
} }
/** /**
* 写入缓存 * 写入缓存
* @access public * @access public
* @param string $name 缓存标识 * @param string $name 缓存标识
* @param mixed $value 存储数据 * @param mixed $value 存储数据
* @param int|null $expire 有效时间 0为永久 * @param int|null $expire 有效时间 0为永久
* @return boolean * @return boolean
*/ */
public static function set($name, $value, $expire = null) public static function set($name, $value, $expire = null)
{ {
self::$writeTimes++; self::$writeTimes++;
return self::init()->set($name, $value, $expire); return self::init()->set($name, $value, $expire);
} }
/** /**
* 自增缓存(针对数值缓存) * 自增缓存(针对数值缓存)
* @access public * @access public
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @param int $step 步长 * @param int $step 步长
* @return false|int * @return false|int
*/ */
public static function inc($name, $step = 1) public static function inc($name, $step = 1)
{ {
self::$writeTimes++; self::$writeTimes++;
return self::init()->inc($name, $step); return self::init()->inc($name, $step);
} }
/** /**
* 自减缓存(针对数值缓存) * 自减缓存(针对数值缓存)
* @access public * @access public
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @param int $step 步长 * @param int $step 步长
* @return false|int * @return false|int
*/ */
public static function dec($name, $step = 1) public static function dec($name, $step = 1)
{ {
self::$writeTimes++; self::$writeTimes++;
return self::init()->dec($name, $step); return self::init()->dec($name, $step);
} }
/** /**
* 删除缓存 * 删除缓存
* @access public * @access public
* @param string $name 缓存标识 * @param string $name 缓存标识
* @return boolean * @return boolean
*/ */
public static function rm($name) public static function rm($name)
{ {
self::$writeTimes++; self::$writeTimes++;
return self::init()->rm($name); return self::init()->rm($name);
} }
/** /**
* 清除缓存 * 清除缓存
* @access public * @access public
* @param string $tag 标签名 * @param string $tag 标签名
* @return boolean * @return boolean
*/ */
public static function clear($tag = null) public static function clear($tag = null)
{ {
self::$writeTimes++; self::$writeTimes++;
return self::init()->clear($tag); return self::init()->clear($tag);
} }
/** /**
* 读取缓存并删除 * 读取缓存并删除
* @access public * @access public
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @return mixed * @return mixed
*/ */
public static function pull($name) public static function pull($name)
{ {
self::$readTimes++; self::$readTimes++;
self::$writeTimes++; self::$writeTimes++;
return self::init()->pull($name); return self::init()->pull($name);
} }
/** /**
* 如果不存在则写入缓存 * 如果不存在则写入缓存
* @access public * @access public
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @param mixed $value 存储数据 * @param mixed $value 存储数据
* @param int $expire 有效时间 0为永久 * @param int $expire 有效时间 0为永久
* @return mixed * @return mixed
*/ */
public static function remember($name, $value, $expire = null) public static function remember($name, $value, $expire = null)
{ {
self::$readTimes++; self::$readTimes++;
return self::init()->remember($name, $value, $expire); return self::init()->remember($name, $value, $expire);
} }
/** /**
* 缓存标签 * 缓存标签
* @access public * @access public
* @param string $name 标签名 * @param string $name 标签名
* @param string|array $keys 缓存标识 * @param string|array $keys 缓存标识
* @param bool $overlay 是否覆盖 * @param bool $overlay 是否覆盖
* @return Driver * @return Driver
*/ */
public static function tag($name, $keys = null, $overlay = false) public static function tag($name, $keys = null, $overlay = false)

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -19,20 +19,35 @@ use JsonSerializable;
class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
{ {
/**
* @var array 数据
*/
protected $items = []; protected $items = [];
/**
* Collection constructor.
* @access public
* @param array $items 数据
*/
public function __construct($items = []) public function __construct($items = [])
{ {
$this->items = $this->convertToArray($items); $this->items = $this->convertToArray($items);
} }
/**
* 创建 Collection 实例
* @access public
* @param array $items 数据
* @return static
*/
public static function make($items = []) public static function make($items = [])
{ {
return new static($items); return new static($items);
} }
/** /**
* 是否为空 * 判断数据是否为空
* @access public
* @return bool * @return bool
*/ */
public function isEmpty() public function isEmpty()
@ -40,43 +55,33 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
return empty($this->items); return empty($this->items);
} }
/**
* 将数据转成数组
* @access public
* @return array
*/
public function toArray() public function toArray()
{ {
return array_map(function ($value) { return array_map(function ($value) {
return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value; return ($value instanceof Model || $value instanceof self) ?
$value->toArray() :
$value;
}, $this->items); }, $this->items);
} }
/**
* 获取全部的数据
* @access public
* @return array
*/
public function all() public function all()
{ {
return $this->items; 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)));
}
/** /**
* 交换数组中的键和值 * 交换数组中的键和值
* * @access public
* @return static * @return static
*/ */
public function flip() public function flip()
@ -85,19 +90,8 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
} }
/** /**
* 比较数组,返回交集 * 返回数组中所有的键名组成的新 Collection 实例
* * @access public
* @param mixed $items
* @return static
*/
public function intersect($items)
{
return new static(array_intersect($this->items, $this->convertToArray($items)));
}
/**
* 返回数组中所有的键名
*
* @return static * @return static
*/ */
public function keys() public function keys()
@ -106,8 +100,51 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
} }
/** /**
* 删除数组的最后一个元素(出栈) * 返回数组中所有的值组成的新 Collection 实例
* * @access public
* @return static
*/
public function values()
{
return new static(array_values($this->items));
}
/**
* 合并数组并返回一个新的 Collection 实例
* @access public
* @param mixed $items 新的数据
* @return static
*/
public function merge($items)
{
return new static(array_merge($this->items, $this->convertToArray($items)));
}
/**
* 比较数组,返回差集生成的新 Collection 实例
* @access public
* @param mixed $items 做比较的数据
* @return static
*/
public function diff($items)
{
return new static(array_diff($this->items, $this->convertToArray($items)));
}
/**
* 比较数组,返回交集组成的 Collection 新实例
* @access public
* @param mixed $items 比较数据
* @return static
*/
public function intersect($items)
{
return new static(array_intersect($this->items, $this->convertToArray($items)));
}
/**
* 返回并删除数据中的的最后一个元素(出栈)
* @access public
* @return mixed * @return mixed
*/ */
public function pop() public function pop()
@ -116,30 +153,8 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
} }
/** /**
* 通过使用用户自定义函数,以字符串返回数组 * 返回并删除数据中首个元素
* * @access public
* @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 * @return mixed
*/ */
public function shift() public function shift()
@ -148,10 +163,64 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
} }
/** /**
* 把一个数组分割为新的数组块. * 在数组开头插入一个元素
* * @access public
* @param int $size * @param mixed $value
* @param bool $preserveKeys * @param mixed $key 键名
* @return void
*/
public function unshift($value, $key = null)
{
if (is_null($key)) {
array_unshift($this->items, $value);
} else {
$this->items = [$key => $value] + $this->items;
}
}
/**
* 在数组结尾插入一个元素
* @access public
* @param mixed $value
* @param mixed $key 键名
* @return void
*/
public function push($value, $key = null)
{
if (is_null($key)) {
$this->items[] = $value;
} else {
$this->items[$key] = $value;
}
}
/**
* 通过使用用户自定义函数,以字符串返回数组
* @access public
* @param callable $callback 回调函数
* @param mixed $initial 初始值
* @return mixed
*/
public function reduce(callable $callback, $initial = null)
{
return array_reduce($this->items, $callback, $initial);
}
/**
* 以相反的顺序创建一个新的 Collection 实例
* @access public
* @return static
*/
public function reverse()
{
return new static(array_reverse($this->items));
}
/**
* 把数据分割为新的数组块
* @access public
* @param int $size 分隔长度
* @param bool $preserveKeys 是否保持原数据索引
* @return static * @return static
*/ */
public function chunk($size, $preserveKeys = false) public function chunk($size, $preserveKeys = false)
@ -166,78 +235,70 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
} }
/** /**
* 在数组开头插入一个元素 * 给数据中的每个元素执行回调
* @param mixed $value * @access public
* @param null $key * @param callable $callback 回调函数
* @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 * @return $this
*/ */
public function each(callable $callback) public function each(callable $callback)
{ {
foreach ($this->items as $key => $item) { foreach ($this->items as $key => $item) {
if ($callback($item, $key) === false) { $result = $callback($item, $key);
if (false === $result) {
break; break;
} }
if (!is_object($item)) {
$this->items[$key] = $result;
}
} }
return $this; return $this;
} }
/** /**
* 用回调函数过滤数组中的元素 * 用回调函数过滤数据中的元素
* @param callable|null $callback * @access public
* @param callable|null $callback 回调函数
* @return static * @return static
*/ */
public function filter(callable $callback = null) public function filter(callable $callback = null)
{ {
if ($callback) { return new static(array_filter($this->items, $callback ?: null));
return new static(array_filter($this->items, $callback));
}
return new static(array_filter($this->items));
} }
/** /**
* 返回数组中指定的一列 * 返回数据中指定的一列
* @param $column_key * @access public
* @param null $index_key * @param mixed $columnKey 键名
* @param null $indexKey 作为索引值的列
* @return array * @return array
*/ */
public function column($column_key, $index_key = null) public function column($columnKey, $indexKey = null)
{ {
if (function_exists('array_column')) { if (function_exists('array_column')) {
return array_column($this->items, $column_key, $index_key); return array_column($this->items, $columnKey, $indexKey);
} }
$result = []; $result = [];
foreach ($this->items as $row) { foreach ($this->items as $row) {
$key = $value = null; $key = $value = null;
$keySet = $valueSet = false; $keySet = $valueSet = false;
if (null !== $index_key && array_key_exists($index_key, $row)) {
if (null !== $indexKey && array_key_exists($indexKey, $row)) {
$key = (string) $row[$indexKey];
$keySet = true; $keySet = true;
$key = (string) $row[$index_key];
} }
if (null === $column_key) {
if (null === $columnKey) {
$valueSet = true; $valueSet = true;
$value = $row; $value = $row;
} elseif (is_array($row) && array_key_exists($column_key, $row)) { } elseif (is_array($row) && array_key_exists($columnKey, $row)) {
$valueSet = true; $valueSet = true;
$value = $row[$column_key]; $value = $row[$columnKey];
} }
if ($valueSet) { if ($valueSet) {
if ($keySet) { if ($keySet) {
$result[$key] = $value; $result[$key] = $value;
@ -246,34 +307,30 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
} }
} }
} }
return $result; return $result;
} }
/** /**
* 对数组排序 * 对数据排序,并返回排序后的数据组成的新 Collection 实例
* * @access public
* @param callable|null $callback * @param callable|null $callback 回调函数
* @return static * @return static
*/ */
public function sort(callable $callback = null) public function sort(callable $callback = null)
{ {
$items = $this->items; $items = $this->items;
$callback = $callback ?: function ($a, $b) {
$callback ? uasort($items, $callback) : uasort($items, function ($a, $b) { return $a == $b ? 0 : (($a < $b) ? -1 : 1);
};
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
});
uasort($items, $callback);
return new static($items); return new static($items);
} }
/** /**
* 将数组打乱 * 将数据打乱后组成新的 Collection 实例
* * @access public
* @return static * @return static
*/ */
public function shuffle() public function shuffle()
@ -281,16 +338,15 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
$items = $this->items; $items = $this->items;
shuffle($items); shuffle($items);
return new static($items); return new static($items);
} }
/** /**
* 截取数 * 截取数据并返回新的 Collection 实例
* * @access public
* @param int $offset * @param int $offset 起始位置
* @param int $length * @param int $length 截取长度
* @param bool $preserveKeys * @param bool $preserveKeys 是否保持原先的键名
* @return static * @return static
*/ */
public function slice($offset, $length = null, $preserveKeys = false) public function slice($offset, $length = null, $preserveKeys = false)
@ -298,17 +354,35 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
return new static(array_slice($this->items, $offset, $length, $preserveKeys)); return new static(array_slice($this->items, $offset, $length, $preserveKeys));
} }
// ArrayAccess /**
* 指定的键是否存在
* @access public
* @param mixed $offset 键名
* @return bool
*/
public function offsetExists($offset) public function offsetExists($offset)
{ {
return array_key_exists($offset, $this->items); return array_key_exists($offset, $this->items);
} }
/**
* 获取指定键对应的值
* @access public
* @param mixed $offset 键名
* @return mixed
*/
public function offsetGet($offset) public function offsetGet($offset)
{ {
return $this->items[$offset]; return $this->items[$offset];
} }
/**
* 设置键值
* @access public
* @param mixed $offset 键名
* @param mixed $value
* @return void
*/
public function offsetSet($offset, $value) public function offsetSet($offset, $value)
{ {
if (is_null($offset)) { if (is_null($offset)) {
@ -318,33 +392,51 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
} }
} }
/**
* 删除指定键值
* @access public
* @param mixed $offset 键名
* @return void
*/
public function offsetUnset($offset) public function offsetUnset($offset)
{ {
unset($this->items[$offset]); unset($this->items[$offset]);
} }
//Countable /**
* 统计数据的个数
* @access public
* @return int
*/
public function count() public function count()
{ {
return count($this->items); return count($this->items);
} }
//IteratorAggregate /**
* 获取数据的迭代器
* @access public
* @return ArrayIterator
*/
public function getIterator() public function getIterator()
{ {
return new ArrayIterator($this->items); return new ArrayIterator($this->items);
} }
//JsonSerializable /**
* 将数据反序列化成数组
* @access public
* @return array
*/
public function jsonSerialize() public function jsonSerialize()
{ {
return $this->toArray(); return $this->toArray();
} }
/** /**
* 转换当前数据集为JSON字符串 * 转换当前数据集为 JSON 字符串
* @access public * @access public
* @param integer $options json参数 * @param integer $options json 参数
* @return string * @return string
*/ */
public function toJson($options = JSON_UNESCAPED_UNICODE) public function toJson($options = JSON_UNESCAPED_UNICODE)
@ -352,22 +444,24 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
return json_encode($this->toArray(), $options); return json_encode($this->toArray(), $options);
} }
/**
* 将数据转换成字符串
* @access public
* @return string
*/
public function __toString() public function __toString()
{ {
return $this->toJson(); return $this->toJson();
} }
/** /**
* 转换成数组 * 将数据转换成数组
* * @access protected
* @param mixed $items * @param mixed $items 数据
* @return array * @return array
*/ */
protected function convertToArray($items) protected function convertToArray($items)
{ {
if ($items instanceof self) { return $items instanceof self ? $items->all() : (array) $items;
return $items->all();
}
return (array) $items;
} }
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -13,70 +13,88 @@ namespace think;
class Config class Config
{ {
// 配置参数 /**
* @var array 配置参数
*/
private static $config = []; private static $config = [];
// 参数作用域
/**
* @var string 参数作用域
*/
private static $range = '_sys_'; private static $range = '_sys_';
// 设定配置参数的作用域 /**
* 设定配置参数的作用域
* @access public
* @param string $range 作用域
* @return void
*/
public static function range($range) public static function range($range)
{ {
self::$range = $range; self::$range = $range;
if (!isset(self::$config[$range])) {
self::$config[$range] = []; if (!isset(self::$config[$range])) self::$config[$range] = [];
}
} }
/** /**
* 解析配置文件或内容 * 解析配置文件或内容
* @param string $config 配置文件路径或内容 * @access public
* @param string $type 配置解析类型 * @param string $config 配置文件路径或内容
* @param string $name 配置名(如设置即表示二级配置) * @param string $type 配置解析类型
* @param string $range 作用域 * @param string $name 配置名(如设置即表示二级配置)
* @param string $range 作用域
* @return mixed * @return mixed
*/ */
public static function parse($config, $type = '', $name = '', $range = '') public static function parse($config, $type = '', $name = '', $range = '')
{ {
$range = $range ?: self::$range; $range = $range ?: self::$range;
if (empty($type)) {
$type = pathinfo($config, PATHINFO_EXTENSION); if (empty($type)) $type = pathinfo($config, PATHINFO_EXTENSION);
}
$class = false !== strpos($type, '\\') ? $type : '\\think\\config\\driver\\' . ucwords($type); $class = false !== strpos($type, '\\') ?
$type :
'\\think\\config\\driver\\' . ucwords($type);
return self::set((new $class())->parse($config), $name, $range); return self::set((new $class())->parse($config), $name, $range);
} }
/** /**
* 加载配置文件PHP格式 * 加载配置文件PHP格式
* @param string $file 配置文件名 * @access public
* @param string $name 配置名(如设置即表示二级配置) * @param string $file 配置文件名
* @param string $range 作用域 * @param string $name 配置名(如设置即表示二级配置)
* @param string $range 作用域
* @return mixed * @return mixed
*/ */
public static function load($file, $name = '', $range = '') public static function load($file, $name = '', $range = '')
{ {
$range = $range ?: self::$range; $range = $range ?: self::$range;
if (!isset(self::$config[$range])) {
self::$config[$range] = []; if (!isset(self::$config[$range])) self::$config[$range] = [];
}
if (is_file($file)) { if (is_file($file)) {
$name = strtolower($name); $name = strtolower($name);
$type = pathinfo($file, PATHINFO_EXTENSION); $type = pathinfo($file, PATHINFO_EXTENSION);
if ('php' == $type) { if ('php' == $type) {
return self::set(include $file, $name, $range); 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]; if ('yaml' == $type && function_exists('yaml_parse_file')) {
return self::set(yaml_parse_file($file), $name, $range);
}
return self::parse($file, $type, $name, $range);
} }
return self::$config[$range];
} }
/** /**
* 检测配置是否存在 * 检测配置是否存在
* @param string $name 配置参数名(支持二级配置 .号分割) * @access public
* @param string $range 作用域 * @param string $name 配置参数名(支持二级配置 . 号分割)
* @param string $range 作用域
* @return bool * @return bool
*/ */
public static function has($name, $range = '') public static function has($name, $range = '')
@ -85,82 +103,108 @@ class Config
if (!strpos($name, '.')) { if (!strpos($name, '.')) {
return isset(self::$config[$range][strtolower($name)]); return isset(self::$config[$range][strtolower($name)]);
} else {
// 二维数组设置和获取支持
$name = explode('.', $name, 2);
return isset(self::$config[$range][strtolower($name[0])][$name[1]]);
} }
// 二维数组设置和获取支持
$name = explode('.', $name, 2);
return isset(self::$config[$range][strtolower($name[0])][$name[1]]);
} }
/** /**
* 获取配置参数 为空则获取所有配置 * 获取配置参数 为空则获取所有配置
* @param string $name 配置参数名(支持二级配置 .号分割) * @access public
* @param string $range 作用域 * @param string $name 配置参数名(支持二级配置 . 号分割)
* @param string $range 作用域
* @return mixed * @return mixed
*/ */
public static function get($name = null, $range = '') public static function get($name = null, $range = '')
{ {
$range = $range ?: self::$range; $range = $range ?: self::$range;
// 无参数时获取所有 // 无参数时获取所有
if (empty($name) && isset(self::$config[$range])) { if (empty($name) && isset(self::$config[$range])) {
return self::$config[$range]; return self::$config[$range];
} }
// 非二级配置时直接返回
if (!strpos($name, '.')) { if (!strpos($name, '.')) {
$name = strtolower($name); $name = strtolower($name);
return isset(self::$config[$range][$name]) ? self::$config[$range][$name] : null; 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 = explode('.', $name, 2);
$name[0] = strtolower($name[0]);
if (!isset(self::$config[$range][$name[0]])) {
// 动态载入额外配置
$module = Request::instance()->module();
$file = CONF_PATH . ($module ? $module . DS : '') . 'extra' . DS . $name[0] . CONF_EXT;
is_file($file) && self::load($file, $name[0]);
}
return isset(self::$config[$range][$name[0]][$name[1]]) ?
self::$config[$range][$name[0]][$name[1]] :
null;
} }
/** /**
* 设置配置参数 name为数组则为批量设置 * 设置配置参数 name 为数组则为批量设置
* @param string|array $name 配置参数名(支持二级配置 .号分割) * @access public
* @param mixed $value 配置值 * @param string|array $name 配置参数名(支持二级配置 . 号分割)
* @param string $range 作用域 * @param mixed $value 配置值
* @param string $range 作用域
* @return mixed * @return mixed
*/ */
public static function set($name, $value = null, $range = '') public static function set($name, $value = null, $range = '')
{ {
$range = $range ?: self::$range; $range = $range ?: self::$range;
if (!isset(self::$config[$range])) {
self::$config[$range] = []; if (!isset(self::$config[$range])) self::$config[$range] = [];
}
// 字符串则表示单个配置设置
if (is_string($name)) { if (is_string($name)) {
if (!strpos($name, '.')) { if (!strpos($name, '.')) {
self::$config[$range][strtolower($name)] = $value; self::$config[$range][strtolower($name)] = $value;
} else { } else {
// 二维数组设置和获取支持 // 二维数组
$name = explode('.', $name, 2); $name = explode('.', $name, 2);
self::$config[$range][strtolower($name[0])][$name[1]] = $value; self::$config[$range][strtolower($name[0])][$name[1]] = $value;
} }
return;
} elseif (is_array($name)) { return $value;
// 批量设置 }
// 数组则表示批量设置
if (is_array($name)) {
if (!empty($value)) { if (!empty($value)) {
self::$config[$range][$value] = isset(self::$config[$range][$value]) ? self::$config[$range][$value] = isset(self::$config[$range][$value]) ?
array_merge(self::$config[$range][$value], $name) : array_merge(self::$config[$range][$value], $name) :
self::$config[$range][$value] = $name; $name;
return self::$config[$range][$value]; 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] = array_merge(
return self::$config[$range]; self::$config[$range], array_change_key_case($name)
);
} }
// 为空直接返回已有配置
return self::$config[$range];
} }
/** /**
* 重置配置参数 * 重置配置参数
* @access public
* @param string $range 作用域
* @return void
*/ */
public static function reset($range = '') public static function reset($range = '')
{ {
$range = $range ?: self::$range; $range = $range ?: self::$range;
if (true === $range) { if (true === $range) {
self::$config = []; self::$config = [];
} else { } else {

View File

@ -20,20 +20,49 @@ use think\console\output\driver\Buffer;
class Console class Console
{ {
/**
* @var string 命令名称
*/
private $name; private $name;
/**
* @var string 命令版本
*/
private $version; private $version;
/** @var Command[] */ /**
* @var Command[] 命令
*/
private $commands = []; private $commands = [];
/**
* @var bool 是否需要帮助信息
*/
private $wantHelps = false; private $wantHelps = false;
/**
* @var bool 是否捕获异常
*/
private $catchExceptions = true; private $catchExceptions = true;
private $autoExit = true;
/**
* @var bool 是否自动退出执行
*/
private $autoExit = true;
/**
* @var InputDefinition 输入定义
*/
private $definition; private $definition;
/**
* @var string 默认执行的命令
*/
private $defaultCommand; private $defaultCommand;
/**
* @var array 默认提供的命令
*/
private static $defaultCommands = [ private static $defaultCommands = [
"think\\console\\command\\Help", "think\\console\\command\\Help",
"think\\console\\command\\Lists", "think\\console\\command\\Lists",
@ -47,11 +76,22 @@ class Console
"think\\console\\command\\optimize\\Schema", "think\\console\\command\\optimize\\Schema",
]; ];
public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') /**
* Console constructor.
* @access public
* @param string $name 名称
* @param string $version 版本
* @param null|string $user 执行用户
*/
public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN', $user = null)
{ {
$this->name = $name; $this->name = $name;
$this->version = $version; $this->version = $version;
if ($user) {
$this->setUser($user);
}
$this->defaultCommand = 'list'; $this->defaultCommand = 'list';
$this->definition = $this->getDefaultInputDefinition(); $this->definition = $this->getDefaultInputDefinition();
@ -60,38 +100,58 @@ class Console
} }
} }
public static function init($run = true) /**
* 设置执行用户
* @param $user
*/
public function setUser($user)
{ {
static $console; $user = posix_getpwnam($user);
if (!$console) { if ($user) {
// 实例化console posix_setuid($user['uid']);
$console = new self('Think Console', '0.1'); posix_setgid($user['gid']);
// 读取指令集
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 * 初始化 Console
* @param array $parameters * @access public
* @param string $driver * @param bool $run 是否运行 Console
* @return Output|Buffer * @return int|Console
*/
public static function init($run = true)
{
static $console;
if (!$console) {
$config = Config::get('console');
// 实例化 console
$console = new self($config['name'], $config['version'], $config['user']);
// 读取指令集
if (is_file(CONF_PATH . 'command' . EXT)) {
$commands = include CONF_PATH . 'command' . EXT;
if (is_array($commands)) {
foreach ($commands as $command) {
class_exists($command) &&
is_subclass_of($command, "\\think\\console\\Command") &&
$console->add(new $command()); // 注册指令
}
}
}
}
return $run ? $console->run() : $console;
}
/**
* 调用命令
* @access public
* @param string $command
* @param array $parameters
* @param string $driver
* @return Output
*/ */
public static function call($command, array $parameters = [], $driver = 'buffer') public static function call($command, array $parameters = [], $driver = 'buffer')
{ {
@ -110,9 +170,9 @@ class Console
/** /**
* 执行当前的指令 * 执行当前的指令
* @access public
* @return int * @return int
* @throws \Exception * @throws \Exception
* @api
*/ */
public function run() public function run()
{ {
@ -124,27 +184,21 @@ class Console
try { try {
$exitCode = $this->doRun($input, $output); $exitCode = $this->doRun($input, $output);
} catch (\Exception $e) { } catch (\Exception $e) {
if (!$this->catchExceptions) { if (!$this->catchExceptions) throw $e;
throw $e;
}
$output->renderException($e); $output->renderException($e);
$exitCode = $e->getCode(); $exitCode = $e->getCode();
if (is_numeric($exitCode)) { if (is_numeric($exitCode)) {
$exitCode = (int) $exitCode; $exitCode = ((int) $exitCode) ?: 1;
if (0 === $exitCode) {
$exitCode = 1;
}
} else { } else {
$exitCode = 1; $exitCode = 1;
} }
} }
if ($this->autoExit) { if ($this->autoExit) {
if ($exitCode > 255) { if ($exitCode > 255) $exitCode = 255;
$exitCode = 255;
}
exit($exitCode); exit($exitCode);
} }
@ -154,12 +208,14 @@ class Console
/** /**
* 执行指令 * 执行指令
* @param Input $input * @access public
* @param Output $output * @param Input $input 输入
* @param Output $output 输出
* @return int * @return int
*/ */
public function doRun(Input $input, Output $output) public function doRun(Input $input, Output $output)
{ {
// 获取版本信息
if (true === $input->hasParameterOption(['--version', '-V'])) { if (true === $input->hasParameterOption(['--version', '-V'])) {
$output->writeln($this->getLongVersion()); $output->writeln($this->getLongVersion());
@ -168,6 +224,7 @@ class Console
$name = $this->getCommandName($input); $name = $this->getCommandName($input);
// 获取帮助信息
if (true === $input->hasParameterOption(['--help', '-h'])) { if (true === $input->hasParameterOption(['--help', '-h'])) {
if (!$name) { if (!$name) {
$name = 'help'; $name = 'help';
@ -182,25 +239,26 @@ class Console
$input = new Input([$this->defaultCommand]); $input = new Input([$this->defaultCommand]);
} }
$command = $this->find($name); return $this->doRunCommand($this->find($name), $input, $output);
$exitCode = $this->doRunCommand($command, $input, $output);
return $exitCode;
} }
/** /**
* 设置输入参数定义 * 设置输入参数定义
* @param InputDefinition $definition * @access public
* @param InputDefinition $definition 输入定义
* @return $this;
*/ */
public function setDefinition(InputDefinition $definition) public function setDefinition(InputDefinition $definition)
{ {
$this->definition = $definition; $this->definition = $definition;
return $this;
} }
/** /**
* 获取输入参数定义 * 获取输入参数定义
* @return InputDefinition The InputDefinition instance * @access public
* @return InputDefinition
*/ */
public function getDefinition() public function getDefinition()
{ {
@ -208,8 +266,9 @@ class Console
} }
/** /**
* Gets the help message. * 获取帮助信息
* @return string A help message. * @access public
* @return string
*/ */
public function getHelp() public function getHelp()
{ {
@ -217,27 +276,34 @@ class Console
} }
/** /**
* 是否捕获异常 * 设置是否捕获异常
* @param bool $boolean * @access public
* @api * @param bool $boolean 是否捕获
* @return $this
*/ */
public function setCatchExceptions($boolean) public function setCatchExceptions($boolean)
{ {
$this->catchExceptions = (bool) $boolean; $this->catchExceptions = (bool) $boolean;
return $this;
} }
/** /**
* 是否自动退出 * 设置是否自动退出
* @param bool $boolean * @access public
* @api * @param bool $boolean 是否自动退出
* @return $this
*/ */
public function setAutoExit($boolean) public function setAutoExit($boolean)
{ {
$this->autoExit = (bool) $boolean; $this->autoExit = (bool) $boolean;
return $this;
} }
/** /**
* 获取名称 * 获取名称
* @access public
* @return string * @return string
*/ */
public function getName() public function getName()
@ -247,17 +313,21 @@ class Console
/** /**
* 设置名称 * 设置名称
* @param string $name * @access public
* @param string $name 名称
* @return $this
*/ */
public function setName($name) public function setName($name)
{ {
$this->name = $name; $this->name = $name;
return $this;
} }
/** /**
* 获取版本 * 获取版本
* @access public
* @return string * @return string
* @api
*/ */
public function getVersion() public function getVersion()
{ {
@ -266,21 +336,30 @@ class Console
/** /**
* 设置版本 * 设置版本
* @param string $version * @access public
* @param string $version 版本信息
* @return $this
*/ */
public function setVersion($version) public function setVersion($version)
{ {
$this->version = $version; $this->version = $version;
return $this;
} }
/** /**
* 获取完整的版本号 * 获取完整的版本号
* @access public
* @return string * @return string
*/ */
public function getLongVersion() public function getLongVersion()
{ {
if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) { if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion()); return sprintf(
'<info>%s</info> version <comment>%s</comment>',
$this->getName(),
$this->getVersion()
);
} }
return '<info>Console Tool</info>'; return '<info>Console Tool</info>';
@ -288,7 +367,8 @@ class Console
/** /**
* 注册一个指令 * 注册一个指令
* @param string $name * @access public
* @param string $name 指令名称
* @return Command * @return Command
*/ */
public function register($name) public function register($name)
@ -297,32 +377,37 @@ class Console
} }
/** /**
* 添加指令 * 批量添加指令
* @param Command[] $commands * @access public
* @param Command[] $commands 指令实例
* @return $this
*/ */
public function addCommands(array $commands) public function addCommands(array $commands)
{ {
foreach ($commands as $command) { foreach ($commands as $command) $this->add($command);
$this->add($command);
} return $this;
} }
/** /**
* 添加一个指令 * 添加一个指令
* @param Command $command * @access public
* @return Command * @param Command $command 命令实例
* @return Command|bool
*/ */
public function add(Command $command) public function add(Command $command)
{ {
$command->setConsole($this);
if (!$command->isEnabled()) { if (!$command->isEnabled()) {
$command->setConsole(null); $command->setConsole(null);
return; return false;
} }
$command->setConsole($this);
if (null === $command->getDefinition()) { 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))); 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; $this->commands[$command->getName()] = $command;
@ -336,14 +421,17 @@ class Console
/** /**
* 获取指令 * 获取指令
* @param string $name 指令名称 * @access public
* @param string $name 指令名称
* @return Command * @return Command
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*/ */
public function get($name) public function get($name)
{ {
if (!isset($this->commands[$name])) { if (!isset($this->commands[$name])) {
throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name)); throw new \InvalidArgumentException(
sprintf('The command "%s" does not exist.', $name)
);
} }
$command = $this->commands[$name]; $command = $this->commands[$name];
@ -363,7 +451,8 @@ class Console
/** /**
* 某个指令是否存在 * 某个指令是否存在
* @param string $name 指令名称 * @access public
* @param string $name 指令名称
* @return bool * @return bool
*/ */
public function has($name) public function has($name)
@ -373,16 +462,22 @@ class Console
/** /**
* 获取所有的命名空间 * 获取所有的命名空间
* @access public
* @return array * @return array
*/ */
public function getNamespaces() public function getNamespaces()
{ {
$namespaces = []; $namespaces = [];
foreach ($this->commands as $command) { foreach ($this->commands as $command) {
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName())); $namespaces = array_merge(
$namespaces, $this->extractAllNamespaces($command->getName())
);
foreach ($command->getAliases() as $alias) { foreach ($command->getAliases() as $alias) {
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias)); $namespaces = array_merge(
$namespaces, $this->extractAllNamespaces($alias)
);
} }
} }
@ -390,21 +485,25 @@ class Console
} }
/** /**
* 查找注册命名空间中的名称或缩写。 * 查找注册命名空间中的名称或缩写
* @access public
* @param string $namespace * @param string $namespace
* @return string * @return string
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*/ */
public function findNamespace($namespace) public function findNamespace($namespace)
{ {
$allNamespaces = $this->getNamespaces(); $expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
return preg_quote($matches[1]) . '[^:]*'; return preg_quote($matches[1]) . '[^:]*';
}, $namespace); }, $namespace);
$namespaces = preg_grep('{^' . $expr . '}', $allNamespaces);
$allNamespaces = $this->getNamespaces();
$namespaces = preg_grep('{^' . $expr . '}', $allNamespaces);
if (empty($namespaces)) { if (empty($namespaces)) {
$message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); $message = sprintf(
'There are no commands defined in the "%s" namespace.', $namespace
);
if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) { if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
if (1 == count($alternatives)) { if (1 == count($alternatives)) {
@ -420,8 +519,14 @@ class Console
} }
$exact = in_array($namespace, $namespaces, true); $exact = in_array($namespace, $namespaces, true);
if (count($namespaces) > 1 && !$exact) { if (count($namespaces) > 1 && !$exact) {
throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces)))); throw new \InvalidArgumentException(
sprintf(
'The namespace "%s" is ambiguous (%s).',
$namespace,
$this->getAbbreviationSuggestions(array_values($namespaces)))
);
} }
return $exact ? $namespace : reset($namespaces); return $exact ? $namespace : reset($namespaces);
@ -429,20 +534,22 @@ class Console
/** /**
* 查找指令 * 查找指令
* @param string $name 名称或者别名 * @access public
* @param string $name 名称或者别名
* @return Command * @return Command
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*/ */
public function find($name) public function find($name)
{ {
$allCommands = array_keys($this->commands); $expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
return preg_quote($matches[1]) . '[^:]*'; return preg_quote($matches[1]) . '[^:]*';
}, $name); }, $name);
$commands = preg_grep('{^' . $expr . '}', $allCommands);
$allCommands = array_keys($this->commands);
$commands = preg_grep('{^' . $expr . '}', $allCommands);
if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) { if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) {
if (false !== $pos = strrpos($name, ':')) { if (false !== ($pos = strrpos($name, ':'))) {
$this->findNamespace(substr($name, 0, $pos)); $this->findNamespace(substr($name, 0, $pos));
} }
@ -473,7 +580,9 @@ class Console
if (count($commands) > 1 && !$exact) { if (count($commands) > 1 && !$exact) {
$suggestions = $this->getAbbreviationSuggestions(array_values($commands)); $suggestions = $this->getAbbreviationSuggestions(array_values($commands));
throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions)); throw new \InvalidArgumentException(
sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions)
);
} }
return $this->get($exact ? $name : reset($commands)); return $this->get($exact ? $name : reset($commands));
@ -481,21 +590,20 @@ class Console
/** /**
* 获取所有的指令 * 获取所有的指令
* @param string $namespace 命名空间 * @access public
* @param string $namespace 命名空间
* @return Command[] * @return Command[]
* @api
*/ */
public function all($namespace = null) public function all($namespace = null)
{ {
if (null === $namespace) { if (null === $namespace) return $this->commands;
return $this->commands;
}
$commands = []; $commands = [];
foreach ($this->commands as $name => $command) { foreach ($this->commands as $name => $command) {
if ($this->extractNamespace($name, substr_count($namespace, ':') + 1) === $namespace) { $ext = $this->extractNamespace($name, substr_count($namespace, ':') + 1);
$commands[$name] = $command;
} if ($ext === $namespace) $commands[$name] = $command;
} }
return $commands; return $commands;
@ -503,7 +611,8 @@ class Console
/** /**
* 获取可能的指令名 * 获取可能的指令名
* @param array $names * @access public
* @param array $names 指令名
* @return array * @return array
*/ */
public static function getAbbreviations($names) public static function getAbbreviations($names)
@ -520,9 +629,11 @@ class Console
} }
/** /**
* 配置基于用户的参数和选项的输入和输出实例。 * 配置基于用户的参数和选项的输入和输出实例
* @param Input $input 输入实例 * @access protected
* @param Output $output 输出实例 * @param Input $input 输入实例
* @param Output $output 输出实例
* @return void
*/ */
protected function configureIO(Input $input, Output $output) protected function configureIO(Input $input, Output $output)
{ {
@ -551,9 +662,10 @@ class Console
/** /**
* 执行指令 * 执行指令
* @param Command $command 指令实例 * @access protected
* @param Input $input 输入实例 * @param Command $command 指令实例
* @param Output $output 输出实例 * @param Input $input 输入实例
* @param Output $output 输出实例
* @return int * @return int
* @throws \Exception * @throws \Exception
*/ */
@ -563,8 +675,9 @@ class Console
} }
/** /**
* 获取指令的基础名称 * 获取指令的名称
* @param Input $input * @access protected
* @param Input $input 输入实例
* @return string * @return string
*/ */
protected function getCommandName(Input $input) protected function getCommandName(Input $input)
@ -574,6 +687,7 @@ class Console
/** /**
* 获取默认输入定义 * 获取默认输入定义
* @access protected
* @return InputDefinition * @return InputDefinition
*/ */
protected function getDefaultInputDefinition() protected function getDefaultInputDefinition()
@ -591,41 +705,55 @@ class Console
} }
/** /**
* 设置默认命令 * 获取默认命令
* @return Command[] An array of default Command instances * @access protected
* @return Command[]
*/ */
protected function getDefaultCommands() protected function getDefaultCommands()
{ {
$defaultCommands = []; $defaultCommands = [];
foreach (self::$defaultCommands as $classname) { foreach (self::$defaultCommands as $class) {
if (class_exists($classname) && is_subclass_of($classname, "think\\console\\Command")) { if (class_exists($class) && is_subclass_of($class, "think\\console\\Command")) {
$defaultCommands[] = new $classname(); $defaultCommands[] = new $class();
} }
} }
return $defaultCommands; return $defaultCommands;
} }
public static function addDefaultCommands(array $classnames) /**
* 添加默认指令
* @access public
* @param array $classes 指令
* @return void
*/
public static function addDefaultCommands(array $classes)
{ {
self::$defaultCommands = array_merge(self::$defaultCommands, $classnames); self::$defaultCommands = array_merge(self::$defaultCommands, $classes);
} }
/** /**
* 获取可能的建议 * 获取可能的建议
* @param array $abbrevs * @access private
* @param array $abbrevs
* @return string * @return string
*/ */
private function getAbbreviationSuggestions($abbrevs) private function getAbbreviationSuggestions($abbrevs)
{ {
return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''); return sprintf(
'%s, %s%s',
$abbrevs[0],
$abbrevs[1],
count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''
);
} }
/** /**
* 返回命名空间部分 * 返回指令的命名空间部分
* @param string $name 指令 * @access public
* @param string $limit 部分的命名空间的最大数量 * @param string $name 指令名称
* @param string $limit 部分的命名空间的最大数量
* @return string * @return string
*/ */
public function extractNamespace($name, $limit = null) public function extractNamespace($name, $limit = null)
@ -638,16 +766,17 @@ class Console
/** /**
* 查找可替代的建议 * 查找可替代的建议
* @param string $name * @access private
* @param array|\Traversable $collection * @param string $name 指令名称
* @param array|\Traversable $collection 建议集合
* @return array * @return array
*/ */
private function findAlternatives($name, $collection) private function findAlternatives($name, $collection)
{ {
$threshold = 1e3; $threshold = 1e3;
$alternatives = []; $alternatives = [];
$collectionParts = []; $collectionParts = [];
foreach ($collection as $item) { foreach ($collection as $item) {
$collectionParts[$item] = explode(':', $item); $collectionParts[$item] = explode(':', $item);
} }
@ -655,6 +784,7 @@ class Console
foreach (explode(':', $name) as $i => $subname) { foreach (explode(':', $name) as $i => $subname) {
foreach ($collectionParts as $collectionName => $parts) { foreach ($collectionParts as $collectionName => $parts) {
$exists = isset($alternatives[$collectionName]); $exists = isset($alternatives[$collectionName]);
if (!isset($parts[$i]) && $exists) { if (!isset($parts[$i]) && $exists) {
$alternatives[$collectionName] += $threshold; $alternatives[$collectionName] += $threshold;
continue; continue;
@ -663,8 +793,14 @@ class Console
} }
$lev = levenshtein($subname, $parts[$i]); $lev = levenshtein($subname, $parts[$i]);
if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
$alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev; if ($lev <= strlen($subname) / 3 ||
'' !== $subname &&
false !== strpos($parts[$i], $subname)
) {
$alternatives[$collectionName] = $exists ?
$alternatives[$collectionName] + $lev :
$lev;
} elseif ($exists) { } elseif ($exists) {
$alternatives[$collectionName] += $threshold; $alternatives[$collectionName] += $threshold;
} }
@ -673,14 +809,18 @@ class Console
foreach ($collection as $item) { foreach ($collection as $item) {
$lev = levenshtein($name, $item); $lev = levenshtein($name, $item);
if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
$alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; $alternatives[$item] = isset($alternatives[$item]) ?
$alternatives[$item] - $lev :
$lev;
} }
} }
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) { $alternatives = array_filter($alternatives, function ($lev) use ($threshold) {
return $lev < 2 * $threshold; return $lev < 2 * $threshold;
}); });
asort($alternatives); asort($alternatives);
return array_keys($alternatives); return array_keys($alternatives);
@ -688,24 +828,28 @@ class Console
/** /**
* 设置默认的指令 * 设置默认的指令
* @param string $commandName The Command name * @access public
* @param string $commandName 指令名称
* @return $this
*/ */
public function setDefaultCommand($commandName) public function setDefaultCommand($commandName)
{ {
$this->defaultCommand = $commandName; $this->defaultCommand = $commandName;
return $this;
} }
/** /**
* 返回所有的命名空间 * 返回所有的命名空间
* @param string $name * @access private
* @param string $name 指令名称
* @return array * @return array
*/ */
private function extractAllNamespaces($name) private function extractAllNamespaces($name)
{ {
$parts = explode(':', $name, -1);
$namespaces = []; $namespaces = [];
foreach ($parts as $part) { foreach (explode(':', $name, -1) as $part) {
if (count($namespaces)) { if (count($namespaces)) {
$namespaces[] = end($namespaces) . ':' . $part; $namespaces[] = end($namespaces) . ':' . $part;
} else { } else {

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -11,46 +11,49 @@
namespace think; namespace think;
\think\Loader::import('controller/Jump', TRAIT_PATH, EXT);
use think\exception\ValidateException; use think\exception\ValidateException;
use traits\controller\Jump;
Loader::import('controller/Jump', TRAIT_PATH, EXT);
class Controller class Controller
{ {
use \traits\controller\Jump; use Jump;
/** /**
* @var \think\View 视图类实例 * @var \think\View 视图类实例
*/ */
protected $view; protected $view;
/** /**
* @var \think\Request Request实例 * @var \think\Request Request 实例
*/ */
protected $request; protected $request;
// 验证失败是否抛出异常
/**
* @var bool 验证失败是否抛出异常
*/
protected $failException = false; protected $failException = false;
// 是否批量验证
/**
* @var bool 是否批量验证
*/
protected $batchValidate = false; protected $batchValidate = false;
/** /**
* 前置操作方法列表 * @var array 前置操作方法列表
* @var array $beforeActionList
* @access protected
*/ */
protected $beforeActionList = []; protected $beforeActionList = [];
/** /**
* 构造方法 * 构造方法
* @param Request $request Request对象
* @access public * @access public
* @param Request $request Request 对象
*/ */
public function __construct(Request $request = null) 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->view = View::instance(Config::get('template'), Config::get('view_replace_str'));
$this->request = $request; $this->request = is_null($request) ? Request::instance() : $request;
// 控制器初始化 // 控制器初始化
$this->_initialize(); $this->_initialize();
@ -65,7 +68,10 @@ class Controller
} }
} }
// 初始化 /**
* 初始化操作
* @access protected
*/
protected function _initialize() protected function _initialize()
{ {
} }
@ -73,8 +79,9 @@ class Controller
/** /**
* 前置操作 * 前置操作
* @access protected * @access protected
* @param string $method 前置操作方法名 * @param string $method 前置操作方法名
* @param array $options 调用参数 ['only'=>[...]] 或者['except'=>[...]] * @param array $options 调用参数 ['only'=>[...]] 或者 ['except'=>[...]]
* @return void
*/ */
protected function beforeAction($method, $options = []) protected function beforeAction($method, $options = [])
{ {
@ -82,6 +89,7 @@ class Controller
if (is_string($options['only'])) { if (is_string($options['only'])) {
$options['only'] = explode(',', $options['only']); $options['only'] = explode(',', $options['only']);
} }
if (!in_array($this->request->action(), $options['only'])) { if (!in_array($this->request->action(), $options['only'])) {
return; return;
} }
@ -89,6 +97,7 @@ class Controller
if (is_string($options['except'])) { if (is_string($options['except'])) {
$options['except'] = explode(',', $options['except']); $options['except'] = explode(',', $options['except']);
} }
if (in_array($this->request->action(), $options['except'])) { if (in_array($this->request->action(), $options['except'])) {
return; return;
} }
@ -100,10 +109,10 @@ class Controller
/** /**
* 加载模板输出 * 加载模板输出
* @access protected * @access protected
* @param string $template 模板文件名 * @param string $template 模板文件名
* @param array $vars 模板输出变量 * @param array $vars 模板输出变量
* @param array $replace 模板替换 * @param array $replace 模板替换
* @param array $config 模板参数 * @param array $config 模板参数
* @return mixed * @return mixed
*/ */
protected function fetch($template = '', $vars = [], $replace = [], $config = []) protected function fetch($template = '', $vars = [], $replace = [], $config = [])
@ -114,10 +123,10 @@ class Controller
/** /**
* 渲染内容输出 * 渲染内容输出
* @access protected * @access protected
* @param string $content 模板内容 * @param string $content 模板内容
* @param array $vars 模板输出变量 * @param array $vars 模板输出变量
* @param array $replace 替换内容 * @param array $replace 替换内容
* @param array $config 模板参数 * @param array $config 模板参数
* @return mixed * @return mixed
*/ */
protected function display($content = '', $vars = [], $replace = [], $config = []) protected function display($content = '', $vars = [], $replace = [], $config = [])
@ -128,24 +137,28 @@ class Controller
/** /**
* 模板变量赋值 * 模板变量赋值
* @access protected * @access protected
* @param mixed $name 要显示的模板变量 * @param mixed $name 要显示的模板变量
* @param mixed $value 变量的值 * @param mixed $value 变量的值
* @return void * @return $this
*/ */
protected function assign($name, $value = '') protected function assign($name, $value = '')
{ {
$this->view->assign($name, $value); $this->view->assign($name, $value);
return $this;
} }
/** /**
* 初始化模板引擎 * 初始化模板引擎
* @access protected * @access protected
* @param array|string $engine 引擎参数 * @param array|string $engine 引擎参数
* @return void * @return $this
*/ */
protected function engine($engine) protected function engine($engine)
{ {
$this->view->engine($engine); $this->view->engine($engine);
return $this;
} }
/** /**
@ -157,17 +170,18 @@ class Controller
protected function validateFailException($fail = true) protected function validateFailException($fail = true)
{ {
$this->failException = $fail; $this->failException = $fail;
return $this; return $this;
} }
/** /**
* 验证数据 * 验证数据
* @access protected * @access protected
* @param array $data 数据 * @param array $data 数据
* @param string|array $validate 验证器名或者验证规则数组 * @param string|array $validate 验证器名或者验证规则数组
* @param array $message 提示信息 * @param array $message 提示信息
* @param bool $batch 是否批量验证 * @param bool $batch 是否批量验证
* @param mixed $callback 回调方法(闭包) * @param mixed $callback 回调方法(闭包)
* @return array|string|true * @return array|string|true
* @throws ValidateException * @throws ValidateException
*/ */
@ -177,24 +191,27 @@ class Controller
$v = Loader::validate(); $v = Loader::validate();
$v->rule($validate); $v->rule($validate);
} else { } else {
// 支持场景
if (strpos($validate, '.')) { if (strpos($validate, '.')) {
// 支持场景
list($validate, $scene) = explode('.', $validate); list($validate, $scene) = explode('.', $validate);
} }
$v = Loader::validate($validate); $v = Loader::validate($validate);
if (!empty($scene)) {
$v->scene($scene); !empty($scene) && $v->scene($scene);
}
} }
// 是否批量验证
// 批量验证
if ($batch || $this->batchValidate) { if ($batch || $this->batchValidate) {
$v->batch(true); $v->batch(true);
} }
// 设置错误信息
if (is_array($message)) { if (is_array($message)) {
$v->message($message); $v->message($message);
} }
// 使用回调验证
if ($callback && is_callable($callback)) { if ($callback && is_callable($callback)) {
call_user_func_array($callback, [$v, &$data]); call_user_func_array($callback, [$v, &$data]);
} }
@ -202,11 +219,11 @@ class Controller
if (!$v->check($data)) { if (!$v->check($data)) {
if ($this->failException) { if ($this->failException) {
throw new ValidateException($v->getError()); throw new ValidateException($v->getError());
} else {
return $v->getError();
} }
} else {
return true; return $v->getError();
} }
return true;
} }
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -13,28 +13,28 @@ namespace think;
class Cookie class Cookie
{ {
/**
* @var array cookie 设置参数
*/
protected static $config = [ protected static $config = [
// cookie 名称前缀 'prefix' => '', // cookie 名称前缀
'prefix' => '', 'expire' => 0, // cookie 保存时间
// cookie 保存时间 'path' => '/', // cookie 保存路径
'expire' => 0, 'domain' => '', // cookie 有效域名
// cookie 保存路径 'secure' => false, // cookie 启用安全传输
'path' => '/', 'httponly' => false, // httponly 设置
// cookie 有效域名 'setcookie' => true, // 是否使用 setcookie
'domain' => '',
// cookie 启用安全传输
'secure' => false,
// httponly设置
'httponly' => '',
// 是否使用 setcookie
'setcookie' => true,
]; ];
/**
* @var bool 是否完成初始化了
*/
protected static $init; protected static $init;
/** /**
* Cookie初始化 * Cookie初始化
* @param array $config * @access public
* @param array $config 配置参数
* @return void * @return void
*/ */
public static function init(array $config = []) public static function init(array $config = [])
@ -42,39 +42,43 @@ class Cookie
if (empty($config)) { if (empty($config)) {
$config = Config::get('cookie'); $config = Config::get('cookie');
} }
self::$config = array_merge(self::$config, array_change_key_case($config)); self::$config = array_merge(self::$config, array_change_key_case($config));
if (!empty(self::$config['httponly'])) { if (!empty(self::$config['httponly'])) {
ini_set('session.cookie_httponly', 1); ini_set('session.cookie_httponly', 1);
} }
self::$init = true; self::$init = true;
} }
/** /**
* 设置或者获取cookie作用域前缀 * 设置或者获取 cookie 作用域(前缀)
* @param string $prefix * @access public
* @return string|void * @param string $prefix 前缀
* @return string|
*/ */
public static function prefix($prefix = '') public static function prefix($prefix = '')
{ {
if (empty($prefix)) { if (empty($prefix)) {
return self::$config['prefix']; return self::$config['prefix'];
} }
self::$config['prefix'] = $prefix;
return self::$config['prefix'] = $prefix;
} }
/** /**
* Cookie 设置、获取、删除 * Cookie 设置、获取、删除
* * @access public
* @param string $name cookie名称 * @param string $name cookie 名称
* @param mixed $value cookie值 * @param mixed $value cookie
* @param mixed $option 可选参数 可能会是 null|integer|string * @param mixed $option 可选参数 可能会是 null|integer|string
* * @return void
* @return mixed
* @internal param mixed $options cookie参数
*/ */
public static function set($name, $value = '', $option = null) public static function set($name, $value = '', $option = null)
{ {
!isset(self::$init) && self::init(); !isset(self::$init) && self::init();
// 参数设置(会覆盖黙认设置) // 参数设置(会覆盖黙认设置)
if (!is_null($option)) { if (!is_null($option)) {
if (is_numeric($option)) { if (is_numeric($option)) {
@ -82,28 +86,40 @@ class Cookie
} elseif (is_string($option)) { } elseif (is_string($option)) {
parse_str($option, $option); parse_str($option, $option);
} }
$config = array_merge(self::$config, array_change_key_case($option)); $config = array_merge(self::$config, array_change_key_case($option));
} else { } else {
$config = self::$config; $config = self::$config;
} }
$name = $config['prefix'] . $name; $name = $config['prefix'] . $name;
// 设置cookie
// 设置 cookie
if (is_array($value)) { if (is_array($value)) {
array_walk_recursive($value, 'self::jsonFormatProtect', 'encode'); array_walk_recursive($value, 'self::jsonFormatProtect', 'encode');
$value = 'think:' . json_encode($value); $value = 'think:' . json_encode($value);
} }
$expire = !empty($config['expire']) ? $_SERVER['REQUEST_TIME'] + intval($config['expire']) : 0;
$expire = !empty($config['expire']) ?
$_SERVER['REQUEST_TIME'] + intval($config['expire']) :
0;
if ($config['setcookie']) { if ($config['setcookie']) {
setcookie($name, $value, $expire, $config['path'], $config['domain'], $config['secure'], $config['httponly']); setcookie(
$name, $value, $expire, $config['path'], $config['domain'],
$config['secure'], $config['httponly']
);
} }
$_COOKIE[$name] = $value; $_COOKIE[$name] = $value;
} }
/** /**
* 永久保存Cookie数据 * 永久保存 Cookie 数据
* @param string $name cookie名称 * @access public
* @param mixed $value cookie值 * @param string $name cookie 名称
* @param mixed $option 可选参数 可能会是 null|integer|string * @param mixed $value cookie
* @param mixed $option 可选参数 可能会是 null|integer|string
* @return void * @return void
*/ */
public static function forever($name, $value = '', $option = null) public static function forever($name, $value = '', $option = null)
@ -111,33 +127,39 @@ class Cookie
if (is_null($option) || is_numeric($option)) { if (is_null($option) || is_numeric($option)) {
$option = []; $option = [];
} }
$option['expire'] = 315360000; $option['expire'] = 315360000;
self::set($name, $value, $option); self::set($name, $value, $option);
} }
/** /**
* 判断Cookie数据 * 判断是否有 Cookie 数据
* @param string $name cookie名称 * @access public
* @param string|null $prefix cookie前缀 * @param string $name cookie 名称
* @param string|null $prefix cookie 前缀
* @return bool * @return bool
*/ */
public static function has($name, $prefix = null) public static function has($name, $prefix = null)
{ {
!isset(self::$init) && self::init(); !isset(self::$init) && self::init();
$prefix = !is_null($prefix) ? $prefix : self::$config['prefix']; $prefix = !is_null($prefix) ? $prefix : self::$config['prefix'];
$name = $prefix . $name;
return isset($_COOKIE[$name]); return isset($_COOKIE[$prefix . $name]);
} }
/** /**
* Cookie获取 * 获取 Cookie 的值
* @param string $name cookie名称 * @access public
* @param string|null $prefix cookie前缀 * @param string $name cookie 名称
* @param string|null $prefix cookie 前缀
* @return mixed * @return mixed
*/ */
public static function get($name = '', $prefix = null) public static function get($name = '', $prefix = null)
{ {
!isset(self::$init) && self::init(); !isset(self::$init) && self::init();
$prefix = !is_null($prefix) ? $prefix : self::$config['prefix']; $prefix = !is_null($prefix) ? $prefix : self::$config['prefix'];
$key = $prefix . $name; $key = $prefix . $name;
@ -145,80 +167,102 @@ class Cookie
// 获取全部 // 获取全部
if ($prefix) { if ($prefix) {
$value = []; $value = [];
foreach ($_COOKIE as $k => $val) { foreach ($_COOKIE as $k => $val) {
if (0 === strpos($k, $prefix)) { if (0 === strpos($k, $prefix)) {
$value[$k] = $val; $value[$k] = $val;
} }
} }
} else { } else {
$value = $_COOKIE; $value = $_COOKIE;
} }
} elseif (isset($_COOKIE[$key])) { } elseif (isset($_COOKIE[$key])) {
$value = $_COOKIE[$key]; $value = $_COOKIE[$key];
if (0 === strpos($value, 'think:')) { if (0 === strpos($value, 'think:')) {
$value = substr($value, 6); $value = json_decode(substr($value, 6), true);
$value = json_decode($value, true);
array_walk_recursive($value, 'self::jsonFormatProtect', 'decode'); array_walk_recursive($value, 'self::jsonFormatProtect', 'decode');
} }
} else { } else {
$value = null; $value = null;
} }
return $value; return $value;
} }
/** /**
* Cookie删除 * 删除 Cookie
* @param string $name cookie名称 * @access public
* @param string|null $prefix cookie前缀 * @param string $name cookie 名称
* @return mixed * @param string|null $prefix cookie 前缀
* @return void
*/ */
public static function delete($name, $prefix = null) public static function delete($name, $prefix = null)
{ {
!isset(self::$init) && self::init(); !isset(self::$init) && self::init();
$config = self::$config; $config = self::$config;
$prefix = !is_null($prefix) ? $prefix : $config['prefix']; $prefix = !is_null($prefix) ? $prefix : $config['prefix'];
$name = $prefix . $name; $name = $prefix . $name;
if ($config['setcookie']) { if ($config['setcookie']) {
setcookie($name, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'], $config['domain'], $config['secure'], $config['httponly']); setcookie(
$name, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'],
$config['domain'], $config['secure'], $config['httponly']
);
} }
// 删除指定cookie
// 删除指定 cookie
unset($_COOKIE[$name]); unset($_COOKIE[$name]);
} }
/** /**
* Cookie清空 * 清除指定前缀的所有 cookie
* @param string|null $prefix cookie前缀 * @access public
* @return mixed * @param string|null $prefix cookie 前缀
* @return void
*/ */
public static function clear($prefix = null) public static function clear($prefix = null)
{ {
// 清除指定前缀的所有cookie
if (empty($_COOKIE)) { if (empty($_COOKIE)) {
return; return;
} }
!isset(self::$init) && self::init(); !isset(self::$init) && self::init();
// 要删除的cookie前缀不指定则删除config设置的指定前缀
// 要删除的 cookie 前缀,不指定则删除 config 设置的指定前缀
$config = self::$config; $config = self::$config;
$prefix = !is_null($prefix) ? $prefix : $config['prefix']; $prefix = !is_null($prefix) ? $prefix : $config['prefix'];
if ($prefix) { if ($prefix) {
// 如果前缀为空字符串将不作处理直接返回
foreach ($_COOKIE as $key => $val) { foreach ($_COOKIE as $key => $val) {
if (0 === strpos($key, $prefix)) { if (0 === strpos($key, $prefix)) {
if ($config['setcookie']) { if ($config['setcookie']) {
setcookie($key, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'], $config['domain'], $config['secure'], $config['httponly']); setcookie(
$key, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'],
$config['domain'], $config['secure'], $config['httponly']
);
} }
unset($_COOKIE[$key]); unset($_COOKIE[$key]);
} }
} }
} }
return;
} }
private static function jsonFormatProtect(&$val, $key, $type = 'encode') /**
* json 转换时的格式保护
* @access protected
* @param mixed $val 要转换的值
* @param string $key 键名
* @param string $type 转换类别
* @return void
*/
protected static function jsonFormatProtect(&$val, $key, $type = 'encode')
{ {
if (!empty($val) && true !== $val) { if (!empty($val) && true !== $val) {
$val = 'decode' == $type ? urldecode($val) : urlencode($val); $val = 'decode' == $type ? urldecode($val) : urlencode($val);
} }
} }
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -49,19 +49,26 @@ use think\db\Query;
*/ */
class Db class Db
{ {
// 数据库连接实例 /**
* @var Connection[] 数据库连接实例
*/
private static $instance = []; private static $instance = [];
// 查询次数
/**
* @var int 查询次数
*/
public static $queryTimes = 0; public static $queryTimes = 0;
// 执行次数
/**
* @var int 执行次数
*/
public static $executeTimes = 0; public static $executeTimes = 0;
/** /**
* 数据库初始化 并取得数据库类实例 * 数据库初始化,并取得数据库类实例
* @static
* @access public * @access public
* @param mixed $config 连接配置 * @param mixed $config 连接配置
* @param bool|string $name 连接标识 true 强制重新连接 * @param bool|string $name 连接标识 true 强制重新连接
* @return Connection * @return Connection
* @throws Exception * @throws Exception
*/ */
@ -70,31 +77,48 @@ class Db
if (false === $name) { if (false === $name) {
$name = md5(serialize($config)); $name = md5(serialize($config));
} }
if (true === $name || !isset(self::$instance[$name])) { if (true === $name || !isset(self::$instance[$name])) {
// 解析连接参数 支持数组和字符串 // 解析连接参数 支持数组和字符串
$options = self::parseConfig($config); $options = self::parseConfig($config);
if (empty($options['type'])) { if (empty($options['type'])) {
throw new \InvalidArgumentException('Undefined db type'); throw new \InvalidArgumentException('Undefined db type');
} }
$class = false !== strpos($options['type'], '\\') ? $options['type'] : '\\think\\db\\connector\\' . ucwords($options['type']);
$class = false !== strpos($options['type'], '\\') ?
$options['type'] :
'\\think\\db\\connector\\' . ucwords($options['type']);
// 记录初始化信息 // 记录初始化信息
if (App::$debug) { if (App::$debug) {
Log::record('[ DB ] INIT ' . $options['type'], 'info'); Log::record('[ DB ] INIT ' . $options['type'], 'info');
} }
if (true === $name) { if (true === $name) {
return new $class($options); $name = md5(serialize($config));
} else {
self::$instance[$name] = new $class($options);
} }
self::$instance[$name] = new $class($options);
} }
return self::$instance[$name]; return self::$instance[$name];
} }
/**
* 清除连接实例
* @access public
* @return void
*/
public static function clear()
{
self::$instance = [];
}
/** /**
* 数据库连接参数解析 * 数据库连接参数解析
* @static
* @access private * @access private
* @param mixed $config * @param mixed $config 连接参数
* @return array * @return array
*/ */
private static function parseConfig($config) private static function parseConfig($config)
@ -102,30 +126,27 @@ class Db
if (empty($config)) { if (empty($config)) {
$config = Config::get('database'); $config = Config::get('database');
} elseif (is_string($config) && false === strpos($config, '/')) { } elseif (is_string($config) && false === strpos($config, '/')) {
// 支持读取配置参数 $config = Config::get($config); // 支持读取配置参数
$config = Config::get($config);
}
if (is_string($config)) {
return self::parseDsn($config);
} else {
return $config;
} }
return is_string($config) ? self::parseDsn($config) : $config;
} }
/** /**
* DSN解析 * DSN 解析
* 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1&param2=val2#utf8 * 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1&param2=val2#utf8
* @static
* @access private * @access private
* @param string $dsnStr * @param string $dsnStr 数据库 DSN 字符串解析
* @return array * @return array
*/ */
private static function parseDsn($dsnStr) private static function parseDsn($dsnStr)
{ {
$info = parse_url($dsnStr); $info = parse_url($dsnStr);
if (!$info) { if (!$info) {
return []; return [];
} }
$dsn = [ $dsn = [
'type' => $info['scheme'], 'type' => $info['scheme'],
'username' => isset($info['user']) ? $info['user'] : '', 'username' => isset($info['user']) ? $info['user'] : '',
@ -141,13 +162,19 @@ class Db
} else { } else {
$dsn['params'] = []; $dsn['params'] = [];
} }
return $dsn; return $dsn;
} }
// 调用驱动类的方法 /**
* 调用驱动类的方法
* @access public
* @param string $method 方法名
* @param array $params 参数
* @return mixed
*/
public static function __callStatic($method, $params) public static function __callStatic($method, $params)
{ {
// 自动初始化数据库
return call_user_func_array([self::connect(), $method], $params); return call_user_func_array([self::connect(), $method], $params);
} }
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -16,21 +16,27 @@ use think\response\Redirect;
class Debug class Debug
{ {
// 区间时间信息 /**
* @var array 区间时间信息
*/
protected static $info = []; protected static $info = [];
// 区间内存信息
/**
* @var array 区间内存信息
*/
protected static $mem = []; protected static $mem = [];
/** /**
* 记录时间(微秒)和内存使用情况 * 记录时间(微秒)和内存使用情况
* @param string $name 标记位置 * @access public
* @param mixed $value 标记值 留空则取当前 time 表示仅记录时间 否则同时记录时间和内存 * @param string $name 标记位置
* @return mixed * @param mixed $value 标记值(留空则取当前 time 表示仅记录时间 否则同时记录时间和内存)
* @return void
*/ */
public static function remark($name, $value = '') public static function remark($name, $value = '')
{ {
// 记录时间和内存使用
self::$info[$name] = is_float($value) ? $value : microtime(true); self::$info[$name] = is_float($value) ? $value : microtime(true);
if ('time' != $value) { if ('time' != $value) {
self::$mem['mem'][$name] = is_float($value) ? $value : memory_get_usage(); self::$mem['mem'][$name] = is_float($value) ? $value : memory_get_usage();
self::$mem['peak'][$name] = memory_get_peak_usage(); self::$mem['peak'][$name] = memory_get_peak_usage();
@ -39,23 +45,26 @@ class Debug
/** /**
* 统计某个区间的时间(微秒)使用情况 返回值以秒为单位 * 统计某个区间的时间(微秒)使用情况 返回值以秒为单位
* @param string $start 开始标签 * @access public
* @param string $end 结束标签 * @param string $start 开始标签
* @param integer|string $dec 小数位 * @param string $end 结束标签
* @return integer * @param integer $dec 小数位
* @return string
*/ */
public static function getRangeTime($start, $end, $dec = 6) public static function getRangeTime($start, $end, $dec = 6)
{ {
if (!isset(self::$info[$end])) { if (!isset(self::$info[$end])) {
self::$info[$end] = microtime(true); self::$info[$end] = microtime(true);
} }
return number_format((self::$info[$end] - self::$info[$start]), $dec); return number_format((self::$info[$end] - self::$info[$start]), $dec);
} }
/** /**
* 统计从开始到统计时的时间(微秒)使用情况 返回值以秒为单位 * 统计从开始到统计时的时间(微秒)使用情况 返回值以秒为单位
* @param integer|string $dec 小数位 * @access public
* @return integer * @param integer $dec 小数位
* @return string
*/ */
public static function getUseTime($dec = 6) public static function getUseTime($dec = 6)
{ {
@ -64,6 +73,7 @@ class Debug
/** /**
* 获取当前访问的吞吐率情况 * 获取当前访问的吞吐率情况
* @access public
* @return string * @return string
*/ */
public static function getThroughputRate() public static function getThroughputRate()
@ -73,9 +83,10 @@ class Debug
/** /**
* 记录区间的内存使用情况 * 记录区间的内存使用情况
* @param string $start 开始标签 * @access public
* @param string $end 结束标签 * @param string $start 开始标签
* @param integer|string $dec 小数位 * @param string $end 结束标签
* @param integer $dec 小数位
* @return string * @return string
*/ */
public static function getRangeMem($start, $end, $dec = 2) public static function getRangeMem($start, $end, $dec = 2)
@ -83,19 +94,23 @@ class Debug
if (!isset(self::$mem['mem'][$end])) { if (!isset(self::$mem['mem'][$end])) {
self::$mem['mem'][$end] = memory_get_usage(); self::$mem['mem'][$end] = memory_get_usage();
} }
$size = self::$mem['mem'][$end] - self::$mem['mem'][$start]; $size = self::$mem['mem'][$end] - self::$mem['mem'][$start];
$a = ['B', 'KB', 'MB', 'GB', 'TB']; $a = ['B', 'KB', 'MB', 'GB', 'TB'];
$pos = 0; $pos = 0;
while ($size >= 1024) { while ($size >= 1024) {
$size /= 1024; $size /= 1024;
$pos++; $pos++;
} }
return round($size, $dec) . " " . $a[$pos]; return round($size, $dec) . " " . $a[$pos];
} }
/** /**
* 统计从开始到统计时的内存使用情况 * 统计从开始到统计时的内存使用情况
* @param integer|string $dec 小数位 * @access public
* @param integer $dec 小数位
* @return string * @return string
*/ */
public static function getUseMem($dec = 2) public static function getUseMem($dec = 2)
@ -103,103 +118,128 @@ class Debug
$size = memory_get_usage() - THINK_START_MEM; $size = memory_get_usage() - THINK_START_MEM;
$a = ['B', 'KB', 'MB', 'GB', 'TB']; $a = ['B', 'KB', 'MB', 'GB', 'TB'];
$pos = 0; $pos = 0;
while ($size >= 1024) { while ($size >= 1024) {
$size /= 1024; $size /= 1024;
$pos++; $pos++;
} }
return round($size, $dec) . " " . $a[$pos]; return round($size, $dec) . " " . $a[$pos];
} }
/** /**
* 统计区间的内存峰值情况 * 统计区间的内存峰值情况
* @param string $start 开始标签 * @access public
* @param string $end 结束标签 * @param string $start 开始标签
* @param integer|string $dec 小数位 * @param string $end 结束标签
* @return mixed * @param integer $dec 小数位
* @return string
*/ */
public static function getMemPeak($start, $end, $dec = 2) public static function getMemPeak($start, $end, $dec = 2)
{ {
if (!isset(self::$mem['peak'][$end])) { if (!isset(self::$mem['peak'][$end])) {
self::$mem['peak'][$end] = memory_get_peak_usage(); self::$mem['peak'][$end] = memory_get_peak_usage();
} }
$size = self::$mem['peak'][$end] - self::$mem['peak'][$start]; $size = self::$mem['peak'][$end] - self::$mem['peak'][$start];
$a = ['B', 'KB', 'MB', 'GB', 'TB']; $a = ['B', 'KB', 'MB', 'GB', 'TB'];
$pos = 0; $pos = 0;
while ($size >= 1024) { while ($size >= 1024) {
$size /= 1024; $size /= 1024;
$pos++; $pos++;
} }
return round($size, $dec) . " " . $a[$pos]; return round($size, $dec) . " " . $a[$pos];
} }
/** /**
* 获取文件加载信息 * 获取文件加载信息
* @param bool $detail 是否显示详细 * @access public
* @param bool $detail 是否显示详细
* @return integer|array * @return integer|array
*/ */
public static function getFile($detail = false) public static function getFile($detail = false)
{ {
$files = get_included_files();
if ($detail) { if ($detail) {
$files = get_included_files(); $info = [];
$info = [];
foreach ($files as $key => $file) { foreach ($files as $file) {
$info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )'; $info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )';
} }
return $info; return $info;
} }
return count(get_included_files());
return count($files);
} }
/** /**
* 浏览器友好的变量输出 * 浏览器友好的变量输出
* @param mixed $var 变量 * @access public
* @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串 * @param mixed $var 变量
* @param string $label 标签 默认为空 * @param boolean $echo 是否输出(默认为 true,为 false 则返回输出字符串)
* @param integer $flags htmlspecialchars flags * @param string|null $label 标签(默认为空)
* @return void|string * @param integer $flags htmlspecialchars 的标志
* @return null|string
*/ */
public static function dump($var, $echo = true, $label = null, $flags = ENT_SUBSTITUTE) public static function dump($var, $echo = true, $label = null, $flags = ENT_SUBSTITUTE)
{ {
$label = (null === $label) ? '' : rtrim($label) . ':'; $label = (null === $label) ? '' : rtrim($label) . ':';
ob_start(); ob_start();
var_dump($var); var_dump($var);
$output = ob_get_clean(); $output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', ob_get_clean());
$output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output);
if (IS_CLI) { if (IS_CLI) {
$output = PHP_EOL . $label . $output . PHP_EOL; $output = PHP_EOL . $label . $output . PHP_EOL;
} else { } else {
if (!extension_loaded('xdebug')) { if (!extension_loaded('xdebug')) {
$output = htmlspecialchars($output, $flags); $output = htmlspecialchars($output, $flags);
} }
$output = '<pre>' . $label . $output . '</pre>'; $output = '<pre>' . $label . $output . '</pre>';
} }
if ($echo) { if ($echo) {
echo($output); echo($output);
return; return;
} else {
return $output;
} }
return $output;
} }
/**
* 调试信息注入到响应中
* @access public
* @param Response $response 响应实例
* @param string $content 返回的字符串
* @return void
*/
public static function inject(Response $response, &$content) public static function inject(Response $response, &$content)
{ {
$config = Config::get('trace'); $config = Config::get('trace');
$type = isset($config['type']) ? $config['type'] : 'Html'; $type = isset($config['type']) ? $config['type'] : 'Html';
$request = Request::instance(); $class = false !== strpos($type, '\\') ? $type : '\\think\\debug\\' . ucwords($type);
$class = false !== strpos($type, '\\') ? $type : '\\think\\debug\\' . ucwords($type);
unset($config['type']); unset($config['type']);
if (class_exists($class)) {
$trace = new $class($config); if (!class_exists($class)) {
} else {
throw new ClassNotFoundException('class not exists:' . $class, $class); throw new ClassNotFoundException('class not exists:' . $class, $class);
} }
/** @var \think\debug\Console|\think\debug\Html $trace */
$trace = new $class($config);
if ($response instanceof Redirect) { if ($response instanceof Redirect) {
//TODO 记录 // TODO 记录
} else { } else {
$output = $trace->output($response, Log::getLog()); $output = $trace->output($response, Log::getLog());
if (is_string($output)) { if (is_string($output)) {
// trace调试信息注入 // trace 调试信息注入
$pos = strripos($content, '</body>'); $pos = strripos($content, '</body>');
if (false !== $pos) { if (false !== $pos) {
$content = substr($content, 0, $pos) . $output . substr($content, $pos); $content = substr($content, 0, $pos) . $output . substr($content, $pos);

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -15,22 +15,25 @@ class Env
{ {
/** /**
* 获取环境变量值 * 获取环境变量值
* @param string $name 环境变量名(支持二级 .号分割) * @access public
* @param string $default 默认值 * @param string $name 环境变量名(支持二级 . 号分割)
* @param string $default 默认值
* @return mixed * @return mixed
*/ */
public static function get($name, $default = null) public static function get($name, $default = null)
{ {
$result = getenv(ENV_PREFIX . strtoupper(str_replace('.', '_', $name))); $result = getenv(ENV_PREFIX . strtoupper(str_replace('.', '_', $name)));
if (false !== $result) { if (false !== $result) {
if ('false' === $result) { if ('false' === $result) {
$result = false; $result = false;
} elseif ('true' === $result) { } elseif ('true' === $result) {
$result = true; $result = true;
} }
return $result; return $result;
} else {
return $default;
} }
return $default;
} }
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -20,6 +20,7 @@ class Error
{ {
/** /**
* 注册异常处理 * 注册异常处理
* @access public
* @return void * @return void
*/ */
public static function register() public static function register()
@ -31,8 +32,10 @@ class Error
} }
/** /**
* Exception Handler * 异常处理
* @param \Exception|\Throwable $e * @access public
* @param \Exception|\Throwable $e 异常
* @return void
*/ */
public static function appException($e) public static function appException($e)
{ {
@ -40,44 +43,50 @@ class Error
$e = new ThrowableError($e); $e = new ThrowableError($e);
} }
self::getExceptionHandler()->report($e); $handler = self::getExceptionHandler();
$handler->report($e);
if (IS_CLI) { if (IS_CLI) {
self::getExceptionHandler()->renderForConsole(new ConsoleOutput, $e); $handler->renderForConsole(new ConsoleOutput, $e);
} else { } else {
self::getExceptionHandler()->render($e)->send(); $handler->render($e)->send();
} }
} }
/** /**
* Error Handler * 错误处理
* @param integer $errno 错误编号 * @access public
* @param integer $errstr 详细错误信息 * @param integer $errno 错误编号
* @param string $errfile 出错的文件 * @param integer $errstr 详细错误信息
* @param integer $errline 出错行号 * @param string $errfile 出错的文件
* @param array $errcontext * @param integer $errline 出错行号
* @return void
* @throws ErrorException * @throws ErrorException
*/ */
public static function appError($errno, $errstr, $errfile = '', $errline = 0, $errcontext = []) public static function appError($errno, $errstr, $errfile = '', $errline = 0)
{ {
$exception = new ErrorException($errno, $errstr, $errfile, $errline, $errcontext); $exception = new ErrorException($errno, $errstr, $errfile, $errline);
// 符合异常处理的则将错误信息托管至 think\exception\ErrorException
if (error_reporting() & $errno) { if (error_reporting() & $errno) {
// 将错误信息托管至 think\exception\ErrorException
throw $exception; throw $exception;
} else {
self::getExceptionHandler()->report($exception);
} }
self::getExceptionHandler()->report($exception);
} }
/** /**
* Shutdown Handler * 异常中止处理
* @access public
* @return void
*/ */
public static function appShutdown() public static function appShutdown()
{ {
// 将错误信息托管至 think\ErrorException
if (!is_null($error = error_get_last()) && self::isFatal($error['type'])) { if (!is_null($error = error_get_last()) && self::isFatal($error['type'])) {
// 将错误信息托管至think\ErrorException self::appException(new ErrorException(
$exception = new ErrorException($error['type'], $error['message'], $error['file'], $error['line']); $error['type'], $error['message'], $error['file'], $error['line']
));
self::appException($exception);
} }
// 写入日志 // 写入日志
@ -86,8 +95,8 @@ class Error
/** /**
* 确定错误类型是否致命 * 确定错误类型是否致命
* * @access protected
* @param int $type * @param int $type 错误类型
* @return bool * @return bool
*/ */
protected static function isFatal($type) protected static function isFatal($type)
@ -96,22 +105,32 @@ class Error
} }
/** /**
* Get an instance of the exception handler. * 获取异常处理的实例
* * @access public
* @return Handle * @return Handle
*/ */
public static function getExceptionHandler() public static function getExceptionHandler()
{ {
static $handle; static $handle;
if (!$handle) { if (!$handle) {
// 异常处理handle // 异常处理 handle
$class = Config::get('exception_handle'); $class = Config::get('exception_handle');
if ($class && class_exists($class) && is_subclass_of($class, "\\think\\exception\\Handle")) {
if ($class && is_string($class) && class_exists($class) &&
is_subclass_of($class, "\\think\\exception\\Handle")
) {
$handle = new $class; $handle = new $class;
} else { } else {
$handle = new Handle; $handle = new Handle;
if ($class instanceof \Closure) {
$handle->setRender($class);
}
} }
} }
return $handle; return $handle;
} }
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -13,15 +13,13 @@ namespace think;
class Exception extends \Exception class Exception extends \Exception
{ {
/** /**
* 保存异常页面显示的额外Debug数据 * @var array 保存异常页面显示的额外 Debug 数据
* @var array
*/ */
protected $data = []; protected $data = [];
/** /**
* 设置异常额外的Debug数据 * 设置异常额外的 Debug 数据
* 数据将会显示为下面的格式 * 数据将会显示为下面的格式
* *
* Exception Data * Exception Data
@ -33,8 +31,10 @@ class Exception extends \Exception
* key1 value1 * key1 value1
* key2 value2 * key2 value2
* *
* @param string $label 数据分类,用于异常页面显示 * @access protected
* @param array $data 需要显示的数据,必须为关联数组 * @param string $label 数据分类,用于异常页面显示
* @param array $data 需要显示的数据,必须为关联数组
* @return void
*/ */
final protected function setData($label, array $data) final protected function setData($label, array $data)
{ {
@ -42,13 +42,14 @@ class Exception extends \Exception
} }
/** /**
* 获取异常额外Debug数据 * 获取异常额外 Debug 数据
* 主要用于输出到异常页面便于调试 * 主要用于输出到异常页面便于调试
* @return array 由setData设置的Debug数据 * @access public
* @return array
*/ */
final public function getData() final public function getData()
{ {
return $this->data; return $this->data;
} }
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -16,25 +16,51 @@ use SplFileObject;
class File extends SplFileObject class File extends SplFileObject
{ {
/** /**
* 错误信息 * @var string 错误信息
* @var string
*/ */
private $error = ''; private $error = '';
// 当前完整文件名
/**
* @var string 当前完整文件名
*/
protected $filename; protected $filename;
// 上传文件名
/**
* @var string 上传文件名
*/
protected $saveName; protected $saveName;
// 文件上传命名规则
/**
* @var string 文件上传命名规则
*/
protected $rule = 'date'; protected $rule = 'date';
// 文件上传验证规则
/**
* @var array 文件上传验证规则
*/
protected $validate = []; protected $validate = [];
// 单元测试
/**
* @var bool 单元测试
*/
protected $isTest; protected $isTest;
// 上传文件信息
/**
* @var array 上传文件信息
*/
protected $info; protected $info;
// 文件hash信息
/**
* @var array 文件 hash 信息
*/
protected $hash = []; protected $hash = [];
/**
* File constructor.
* @access public
* @param string $filename 文件名称
* @param string $mode 访问模式
*/
public function __construct($filename, $mode = 'r') public function __construct($filename, $mode = 'r')
{ {
parent::__construct($filename, $mode); parent::__construct($filename, $mode);
@ -42,30 +68,35 @@ class File extends SplFileObject
} }
/** /**
* 是否测试 * 设置是否是单元测试
* @param bool $test 是否测试 * @access public
* @param bool $test 是否是测试
* @return $this * @return $this
*/ */
public function isTest($test = false) public function isTest($test = false)
{ {
$this->isTest = $test; $this->isTest = $test;
return $this; return $this;
} }
/** /**
* 设置上传信息 * 设置上传信息
* @param array $info 上传文件信息 * @access public
* @param array $info 上传文件信息
* @return $this * @return $this
*/ */
public function setUploadInfo($info) public function setUploadInfo($info)
{ {
$this->info = $info; $this->info = $info;
return $this; return $this;
} }
/** /**
* 获取上传文件的信息 * 获取上传文件的信息
* @param string $name * @access public
* @param string $name 信息名称
* @return array|string * @return array|string
*/ */
public function getInfo($name = '') public function getInfo($name = '')
@ -75,6 +106,7 @@ class File extends SplFileObject
/** /**
* 获取上传文件的文件名 * 获取上传文件的文件名
* @access public
* @return string * @return string
*/ */
public function getSaveName() public function getSaveName()
@ -84,93 +116,101 @@ class File extends SplFileObject
/** /**
* 设置上传文件的保存文件名 * 设置上传文件的保存文件名
* @param string $saveName * @access public
* @param string $saveName 保存名称
* @return $this * @return $this
*/ */
public function setSaveName($saveName) public function setSaveName($saveName)
{ {
$this->saveName = $saveName; $this->saveName = $saveName;
return $this; return $this;
} }
/** /**
* 获取文件的哈希散列值 * 获取文件的哈希散列值
* @return $string * @access public
* @param string $type 类型
* @return string
*/ */
public function hash($type = 'sha1') public function hash($type = 'sha1')
{ {
if (!isset($this->hash[$type])) { if (!isset($this->hash[$type])) {
$this->hash[$type] = hash_file($type, $this->filename); $this->hash[$type] = hash_file($type, $this->filename);
} }
return $this->hash[$type]; return $this->hash[$type];
} }
/** /**
* 检查目录是否可写 * 检查目录是否可写
* @param string $path 目录 * @access protected
* @param string $path 目录
* @return boolean * @return boolean
*/ */
protected function checkPath($path) protected function checkPath($path)
{ {
if (is_dir($path)) { if (is_dir($path) || mkdir($path, 0755, true)) {
return true; return true;
} }
if (mkdir($path, 0755, true)) { $this->error = ['directory {:path} creation failed', ['path' => $path]];
return true;
} else { return false;
$this->error = "目录 {$path} 创建失败!";
return false;
}
} }
/** /**
* 获取文件类型信息 * 获取文件类型信息
* @access public
* @return string * @return string
*/ */
public function getMime() public function getMime()
{ {
$finfo = finfo_open(FILEINFO_MIME_TYPE); $finfo = finfo_open(FILEINFO_MIME_TYPE);
return finfo_file($finfo, $this->filename); return finfo_file($finfo, $this->filename);
} }
/** /**
* 设置文件的命名规则 * 设置文件的命名规则
* @param string $rule 文件命名规则 * @access public
* @param string $rule 文件命名规则
* @return $this * @return $this
*/ */
public function rule($rule) public function rule($rule)
{ {
$this->rule = $rule; $this->rule = $rule;
return $this; return $this;
} }
/** /**
* 设置上传文件的验证规则 * 设置上传文件的验证规则
* @param array $rule 验证规则 * @access public
* @param array $rule 验证规则
* @return $this * @return $this
*/ */
public function validate($rule = []) public function validate(array $rule = [])
{ {
$this->validate = $rule; $this->validate = $rule;
return $this; return $this;
} }
/** /**
* 检测是否合法的上传文件 * 检测是否合法的上传文件
* @access public
* @return bool * @return bool
*/ */
public function isValid() public function isValid()
{ {
if ($this->isTest) { return $this->isTest ? is_file($this->filename) : is_uploaded_file($this->filename);
return is_file($this->filename);
}
return is_uploaded_file($this->filename);
} }
/** /**
* 检测上传文件 * 检测上传文件
* @param array $rule 验证规则 * @access public
* @param array $rule 验证规则
* @return bool * @return bool
*/ */
public function check($rule = []) public function check($rule = [])
@ -179,25 +219,25 @@ class File extends SplFileObject
/* 检查文件大小 */ /* 检查文件大小 */
if (isset($rule['size']) && !$this->checkSize($rule['size'])) { if (isset($rule['size']) && !$this->checkSize($rule['size'])) {
$this->error = '上传文件大小不符!'; $this->error = 'filesize not match';
return false; return false;
} }
/* 检查文件Mime类型 */ /* 检查文件 Mime 类型 */
if (isset($rule['type']) && !$this->checkMime($rule['type'])) { if (isset($rule['type']) && !$this->checkMime($rule['type'])) {
$this->error = '上传文件MIME类型不允许'; $this->error = 'mimetype to upload is not allowed';
return false; return false;
} }
/* 检查文件后缀 */ /* 检查文件后缀 */
if (isset($rule['ext']) && !$this->checkExt($rule['ext'])) { if (isset($rule['ext']) && !$this->checkExt($rule['ext'])) {
$this->error = '上传文件后缀不允许'; $this->error = 'extensions to upload is not allowed';
return false; return false;
} }
/* 检查图像文件 */ /* 检查图像文件 */
if (!$this->checkImg()) { if (!$this->checkImg()) {
$this->error = '非法图像文件!'; $this->error = 'illegal image files';
return false; return false;
} }
@ -206,7 +246,8 @@ class File extends SplFileObject
/** /**
* 检测上传文件后缀 * 检测上传文件后缀
* @param array|string $ext 允许后缀 * @access public
* @param array|string $ext 允许后缀
* @return bool * @return bool
*/ */
public function checkExt($ext) public function checkExt($ext)
@ -214,73 +255,76 @@ class File extends SplFileObject
if (is_string($ext)) { if (is_string($ext)) {
$ext = explode(',', $ext); $ext = explode(',', $ext);
} }
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION)); $extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
if (!in_array($extension, $ext)) {
return false; return in_array($extension, $ext);
}
return true;
} }
/** /**
* 检测图像文件 * 检测图像文件
* @access public
* @return bool * @return bool
*/ */
public function checkImg() public function checkImg()
{ {
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION)); $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])) { // 如果上传的不是图片,或者是图片而且后缀确实符合图片类型则返回 true
return false; return !in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) || in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6, 13]);
}
return true;
} }
// 判断图像类型 /**
* 判断图像类型
* @access protected
* @param string $image 图片名称
* @return bool|int
*/
protected function getImageType($image) protected function getImageType($image)
{ {
if (function_exists('exif_imagetype')) { if (function_exists('exif_imagetype')) {
return exif_imagetype($image); return exif_imagetype($image);
} else { }
try {
$info = getimagesize($image); $info = getimagesize($image);
return $info[2]; return $info ? $info[2] : false;
} catch (\Exception $e) {
return false;
} }
} }
/** /**
* 检测上传文件大小 * 检测上传文件大小
* @param integer $size 最大大小 * @access public
* @param integer $size 最大大小
* @return bool * @return bool
*/ */
public function checkSize($size) public function checkSize($size)
{ {
if ($this->getSize() > $size) { return $this->getSize() <= $size;
return false;
}
return true;
} }
/** /**
* 检测上传文件类型 * 检测上传文件类型
* @param array|string $mime 允许类型 * @access public
* @param array|string $mime 允许类型
* @return bool * @return bool
*/ */
public function checkMime($mime) public function checkMime($mime)
{ {
if (is_string($mime)) { $mime = is_string($mime) ? explode(',', $mime) : $mime;
$mime = explode(',', $mime);
} return in_array(strtolower($this->getMime()), $mime);
if (!in_array(strtolower($this->getMime()), $mime)) {
return false;
}
return true;
} }
/** /**
* 移动文件 * 移动文件
* @param string $path 保存路径 * @access public
* @param string|bool $savename 保存的文件名 默认自动生成 * @param string $path 保存路径
* @param boolean $replace 同名文件是否覆盖 * @param string|bool $savename 保存的文件名 默认自动生成
* @return false|File false-失败 否则返回File实例 * @param boolean $replace 同名文件是否覆盖
* @return false|File
*/ */
public function move($path, $savename = true, $replace = true) public function move($path, $savename = true, $replace = true)
{ {
@ -292,7 +336,7 @@ class File extends SplFileObject
// 检测合法性 // 检测合法性
if (!$this->isValid()) { if (!$this->isValid()) {
$this->error = '非法上传文件'; $this->error = 'upload illegal files';
return false; return false;
} }
@ -300,6 +344,7 @@ class File extends SplFileObject
if (!$this->check()) { if (!$this->check()) {
return false; return false;
} }
$path = rtrim($path, DS) . DS; $path = rtrim($path, DS) . DS;
// 文件保存命名规则 // 文件保存命名规则
$saveName = $this->buildSaveName($savename); $saveName = $this->buildSaveName($savename);
@ -310,9 +355,9 @@ class File extends SplFileObject
return false; return false;
} }
/* 不覆盖同名文件 */ // 不覆盖同名文件
if (!$replace && is_file($filename)) { if (!$replace && is_file($filename)) {
$this->error = '存在同名文件' . $filename; $this->error = ['has the same filename: {:filename}', ['filename' => $filename]];
return false; return false;
} }
@ -320,25 +365,27 @@ class File extends SplFileObject
if ($this->isTest) { if ($this->isTest) {
rename($this->filename, $filename); rename($this->filename, $filename);
} elseif (!move_uploaded_file($this->filename, $filename)) { } elseif (!move_uploaded_file($this->filename, $filename)) {
$this->error = '文件上传保存错误!'; $this->error = 'upload write error';
return false; return false;
} }
// 返回 File对象实例
// 返回 File 对象实例
$file = new self($filename); $file = new self($filename);
$file->setSaveName($saveName); $file->setSaveName($saveName)->setUploadInfo($this->info);
$file->setUploadInfo($this->info);
return $file; return $file;
} }
/** /**
* 获取保存文件名 * 获取保存文件名
* @param string|bool $savename 保存的文件名 默认自动生成 * @access protected
* @param string|bool $savename 保存的文件名 默认自动生成
* @return string * @return string
*/ */
protected function buildSaveName($savename) protected function buildSaveName($savename)
{ {
// 自动生成文件名
if (true === $savename) { if (true === $savename) {
// 自动生成文件名
if ($this->rule instanceof \Closure) { if ($this->rule instanceof \Closure) {
$savename = call_user_func_array($this->rule, [$this]); $savename = call_user_func_array($this->rule, [$this]);
} else { } else {
@ -357,52 +404,73 @@ class File extends SplFileObject
} }
} }
} }
} elseif ('' === $savename) { } elseif ('' === $savename || false === $savename) {
$savename = $this->getInfo('name'); $savename = $this->getInfo('name');
} }
if (!strpos($savename, '.')) { if (!strpos($savename, '.')) {
$savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION); $savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION);
} }
return $savename; return $savename;
} }
/** /**
* 获取错误代码信息 * 获取错误代码信息
* @param int $errorNo 错误号 * @access private
* @param int $errorNo 错误号
* @return $this
*/ */
private function error($errorNo) private function error($errorNo)
{ {
switch ($errorNo) { switch ($errorNo) {
case 1: case 1:
case 2: case 2:
$this->error = '上传文件大小超过了最大值!'; $this->error = 'upload File size exceeds the maximum value';
break; break;
case 3: case 3:
$this->error = '文件只有部分被上传!'; $this->error = 'only the portion of file is uploaded';
break; break;
case 4: case 4:
$this->error = '没有文件被上传!'; $this->error = 'no file to uploaded';
break; break;
case 6: case 6:
$this->error = '找不到临时文件夹!'; $this->error = 'upload temp dir not found';
break; break;
case 7: case 7:
$this->error = '文件写入失败!'; $this->error = 'file write error';
break; break;
default: default:
$this->error = '未知上传错误!'; $this->error = 'unknown upload error';
} }
return $this;
} }
/** /**
* 获取错误信息 * 获取错误信息(支持多语言)
* @return mixed * @access public
* @return string
*/ */
public function getError() public function getError()
{ {
return $this->error; if (is_array($this->error)) {
list($msg, $vars) = $this->error;
} else {
$msg = $this->error;
$vars = [];
}
return Lang::has($msg) ? Lang::get($msg, $vars) : $msg;
} }
/**
* 魔法方法,获取文件的 hash
* @access public
* @param string $method 方法名
* @param mixed $args 调用参数
* @return string
*/
public function __call($method, $args) public function __call($method, $args)
{ {
return $this->hash($method); return $this->hash($method);

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -13,19 +13,23 @@ namespace think;
class Hook class Hook
{ {
/**
* @var array 标签
*/
private static $tags = []; private static $tags = [];
/** /**
* 动态添加行为扩展到某个标签 * 动态添加行为扩展到某个标签
* @param string $tag 标签名称 * @access public
* @param mixed $behavior 行为名称 * @param string $tag 标签名称
* @param bool $first 是否放到开头执行 * @param mixed $behavior 行为名称
* @param bool $first 是否放到开头执行
* @return void * @return void
*/ */
public static function add($tag, $behavior, $first = false) public static function add($tag, $behavior, $first = false)
{ {
isset(self::$tags[$tag]) || self::$tags[$tag] = []; isset(self::$tags[$tag]) || self::$tags[$tag] = [];
if (is_array($behavior) && !is_callable($behavior)) { if (is_array($behavior) && !is_callable($behavior)) {
if (!array_key_exists('_overlay', $behavior) || !$behavior['_overlay']) { if (!array_key_exists('_overlay', $behavior) || !$behavior['_overlay']) {
unset($behavior['_overlay']); unset($behavior['_overlay']);
@ -43,8 +47,10 @@ class Hook
/** /**
* 批量导入插件 * 批量导入插件
* @param array $tags 插件信息 * @access public
* @param boolean $recursive 是否递归合并 * @param array $tags 插件信息
* @param boolean $recursive 是否递归合并
* @return void
*/ */
public static function import(array $tags, $recursive = true) public static function import(array $tags, $recursive = true)
{ {
@ -59,55 +65,59 @@ class Hook
/** /**
* 获取插件信息 * 获取插件信息
* @param string $tag 插件位置 留空获取全部 * @access public
* @param string $tag 插件位置(留空获取全部)
* @return array * @return array
*/ */
public static function get($tag = '') public static function get($tag = '')
{ {
if (empty($tag)) { if (empty($tag)) {
//获取全部的插件信息
return self::$tags; return self::$tags;
} else {
return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : [];
} }
return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : [];
} }
/** /**
* 监听标签的行为 * 监听标签的行为
* @param string $tag 标签名称 * @access public
* @param mixed $params 传入参数 * @param string $tag 标签名称
* @param mixed $extra 额外参数 * @param mixed $params 传入参数
* @param bool $once 只获取一个有效返回值 * @param mixed $extra 额外参数
* @param bool $once 只获取一个有效返回值
* @return mixed * @return mixed
*/ */
public static function listen($tag, &$params = null, $extra = null, $once = false) public static function listen($tag, &$params = null, $extra = null, $once = false)
{ {
$results = []; $results = [];
$tags = static::get($tag);
foreach ($tags as $key => $name) { foreach (static::get($tag) as $key => $name) {
$results[$key] = self::exec($name, $tag, $params, $extra); $results[$key] = self::exec($name, $tag, $params, $extra);
if (false === $results[$key]) {
// 如果返回false 则中断行为执行 // 如果返回 false或者仅获取一个有效返回则中断行为执行
break; if (false === $results[$key] || (!is_null($results[$key]) && $once)) {
} elseif (!is_null($results[$key]) && $once) {
break; break;
} }
} }
return $once ? end($results) : $results; return $once ? end($results) : $results;
} }
/** /**
* 执行某个行为 * 执行某个行为
* @param mixed $class 要执行的行为 * @access public
* @param string $tag 方法名(标签名) * @param mixed $class 要执行的行为
* @param Mixed $params 传人的参数 * @param string $tag 方法名(标签名)
* @param mixed $extra 额外参数 * @param mixed $params 传人的参数
* @param mixed $extra 额外参数
* @return mixed * @return mixed
*/ */
public static function exec($class, $tag = '', &$params = null, $extra = null) public static function exec($class, $tag = '', &$params = null, $extra = null)
{ {
App::$debug && Debug::remark('behavior_start', 'time'); App::$debug && Debug::remark('behavior_start', 'time');
$method = Loader::parseName($tag, 1, false); $method = Loader::parseName($tag, 1, false);
if ($class instanceof \Closure) { if ($class instanceof \Closure) {
$result = call_user_func_array($class, [ & $params, $extra]); $result = call_user_func_array($class, [ & $params, $extra]);
$class = 'Closure'; $class = 'Closure';
@ -126,10 +136,12 @@ class Hook
$method = ($tag && is_callable([$obj, $method])) ? $method : 'run'; $method = ($tag && is_callable([$obj, $method])) ? $method : 'run';
$result = $obj->$method($params, $extra); $result = $obj->$method($params, $extra);
} }
if (App::$debug) { if (App::$debug) {
Debug::remark('behavior_end', 'time'); Debug::remark('behavior_end', 'time');
Log::record('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info'); Log::record('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info');
} }
return $result; return $result;
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -13,114 +13,148 @@ namespace think;
class Lang class Lang
{ {
// 语言数据 /**
* @var array 语言数据
*/
private static $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 = [];
// Accept-Language转义为对应语言包名称 系统默认配置
protected static $acceptLanguage = [
'zh-hans-cn' => 'zh-cn',
];
// 设定当前的语言 /**
* @var string 语言作用域
*/
private static $range = 'zh-cn';
/**
* @var string 语言自动侦测的变量
*/
protected static $langDetectVar = 'lang';
/**
* @var string 语言 Cookie 变量
*/
protected static $langCookieVar = 'think_var';
/**
* @var int 语言 Cookie 的过期时间
*/
protected static $langCookieExpire = 3600;
/**
* @var array 允许语言列表
*/
protected static $allowLangList = [];
/**
* @var array Accept-Language 转义为对应语言包名称 系统默认配置
*/
protected static $acceptLanguage = ['zh-hans-cn' => 'zh-cn'];
/**
* 设定当前的语言
* @access public
* @param string $range 语言作用域
* @return string
*/
public static function range($range = '') public static function range($range = '')
{ {
if ('' == $range) { if ($range) {
return self::$range;
} else {
self::$range = $range; self::$range = $range;
} }
return self::$range; return self::$range;
} }
/** /**
* 设置语言定义(不区分大小写) * 设置语言定义(不区分大小写)
* @param string|array $name 语言变量 * @access public
* @param string $value 语言值 * @param string|array $name 语言变量
* @param string $range 语言作用域 * @param string $value 语言值
* @param string $range 语言作用域
* @return mixed * @return mixed
*/ */
public static function set($name, $value = null, $range = '') public static function set($name, $value = null, $range = '')
{ {
$range = $range ?: self::$range; $range = $range ?: self::$range;
// 批量定义
if (!isset(self::$lang[$range])) { if (!isset(self::$lang[$range])) {
self::$lang[$range] = []; self::$lang[$range] = [];
} }
if (is_array($name)) { if (is_array($name)) {
return self::$lang[$range] = array_change_key_case($name) + self::$lang[$range]; return self::$lang[$range] = array_change_key_case($name) + self::$lang[$range];
} else {
return self::$lang[$range][strtolower($name)] = $value;
} }
return self::$lang[$range][strtolower($name)] = $value;
} }
/** /**
* 加载语言定义(不区分大小写) * 加载语言定义(不区分大小写)
* @param string $file 语言文件 * @access public
* @param string $range 语言作用域 * @param array|string $file 语言文件
* @param string $range 语言作用域
* @return mixed * @return mixed
*/ */
public static function load($file, $range = '') public static function load($file, $range = '')
{ {
$range = $range ?: self::$range; $range = $range ?: self::$range;
$file = is_string($file) ? [$file] : $file;
if (!isset(self::$lang[$range])) { if (!isset(self::$lang[$range])) {
self::$lang[$range] = []; self::$lang[$range] = [];
} }
// 批量定义
if (is_string($file)) {
$file = [$file];
}
$lang = []; $lang = [];
foreach ($file as $_file) { foreach ($file as $_file) {
if (is_file($_file)) { if (is_file($_file)) {
// 记录加载信息 // 记录加载信息
App::$debug && Log::record('[ LANG ] ' . $_file, 'info'); App::$debug && Log::record('[ LANG ] ' . $_file, 'info');
$_lang = include $_file; $_lang = include $_file;
if (is_array($_lang)) { if (is_array($_lang)) {
$lang = array_change_key_case($_lang) + $lang; $lang = array_change_key_case($_lang) + $lang;
} }
} }
} }
if (!empty($lang)) { if (!empty($lang)) {
self::$lang[$range] = $lang + self::$lang[$range]; self::$lang[$range] = $lang + self::$lang[$range];
} }
return self::$lang[$range]; return self::$lang[$range];
} }
/** /**
* 获取语言定义(不区分大小写) * 获取语言定义(不区分大小写)
* @param string|null $name 语言变量 * @access public
* @param string $range 语言作用域 * @param string|null $name 语言变量
* @param string $range 语言作用域
* @return mixed * @return mixed
*/ */
public static function has($name, $range = '') public static function has($name, $range = '')
{ {
$range = $range ?: self::$range; $range = $range ?: self::$range;
return isset(self::$lang[$range][strtolower($name)]); return isset(self::$lang[$range][strtolower($name)]);
} }
/** /**
* 获取语言定义(不区分大小写) * 获取语言定义(不区分大小写)
* @param string|null $name 语言变量 * @access public
* @param array $vars 变量替换 * @param string|null $name 语言变量
* @param string $range 语言作用域 * @param array $vars 变量替换
* @param string $range 语言作用域
* @return mixed * @return mixed
*/ */
public static function get($name = null, $vars = [], $range = '') public static function get($name = null, $vars = [], $range = '')
{ {
$range = $range ?: self::$range; $range = $range ?: self::$range;
// 空参数返回所有定义 // 空参数返回所有定义
if (empty($name)) { if (empty($name)) {
return self::$lang[$range]; return self::$lang[$range];
} }
$key = strtolower($name); $key = strtolower($name);
$value = isset(self::$lang[$range][$key]) ? self::$lang[$range][$key] : $name; $value = isset(self::$lang[$range][$key]) ? self::$lang[$range][$key] : $name;
@ -145,42 +179,50 @@ class Lang
} }
} }
return $value; return $value;
} }
/** /**
* 自动侦测设置获取语言选择 * 自动侦测设置获取语言选择
* @access public
* @return string * @return string
*/ */
public static function detect() public static function detect()
{ {
// 自动侦测设置获取语言选择
$langSet = ''; $langSet = '';
if (isset($_GET[self::$langDetectVar])) { if (isset($_GET[self::$langDetectVar])) {
// url中设置了语言变量 // url 中设置了语言变量
$langSet = strtolower($_GET[self::$langDetectVar]); $langSet = strtolower($_GET[self::$langDetectVar]);
} elseif (isset($_COOKIE[self::$langCookieVar])) {
// Cookie 中设置了语言变量
$langSet = strtolower($_COOKIE[self::$langCookieVar]);
} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { } elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
// 自动侦测浏览器语言 // 自动侦测浏览器语言
preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches); preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
$langSet = strtolower($matches[1]); $langSet = strtolower($matches[1]);
$acceptLangs = Config::get('header_accept_lang'); $acceptLangs = Config::get('header_accept_lang');
if (isset($acceptLangs[$langSet])) { if (isset($acceptLangs[$langSet])) {
$langSet = $acceptLangs[$langSet]; $langSet = $acceptLangs[$langSet];
} elseif (isset(self::$acceptLanguage[$langSet])) { } elseif (isset(self::$acceptLanguage[$langSet])) {
$langSet = self::$acceptLanguage[$langSet]; $langSet = self::$acceptLanguage[$langSet];
} }
} }
// 合法的语言
if (empty(self::$allowLangList) || in_array($langSet, self::$allowLangList)) { if (empty(self::$allowLangList) || in_array($langSet, self::$allowLangList)) {
// 合法的语言
self::$range = $langSet ?: self::$range; self::$range = $langSet ?: self::$range;
} }
return self::$range; return self::$range;
} }
/** /**
* 设置语言自动侦测的变量 * 设置语言自动侦测的变量
* @param string $var 变量名称 * @access public
* @param string $var 变量名称
* @return void * @return void
*/ */
public static function setLangDetectVar($var) public static function setLangDetectVar($var)
@ -189,8 +231,9 @@ class Lang
} }
/** /**
* 设置语言的cookie保存变量 * 设置语言的 cookie 保存变量
* @param string $var 变量名称 * @access public
* @param string $var 变量名称
* @return void * @return void
*/ */
public static function setLangCookieVar($var) public static function setLangCookieVar($var)
@ -199,8 +242,9 @@ class Lang
} }
/** /**
* 设置语言的cookie的过期时间 * 设置语言的 cookie 的过期时间
* @param string $expire 过期时间 * @access public
* @param string $expire 过期时间
* @return void * @return void
*/ */
public static function setLangCookieExpire($expire) public static function setLangCookieExpire($expire)
@ -210,7 +254,8 @@ class Lang
/** /**
* 设置允许的语言列表 * 设置允许的语言列表
* @param array $list 语言列表 * @access public
* @param array $list 语言列表
* @return void * @return void
*/ */
public static function setAllowLangList($list) public static function setAllowLangList($list)

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -15,26 +15,57 @@ use think\exception\ClassNotFoundException;
class Loader class Loader
{ {
/**
* @var array 实例数组
*/
protected static $instance = []; protected static $instance = [];
// 类名映射
protected static $map = [];
// 命名空间别名 /**
* @var array 类名映射
*/
protected static $classMap = [];
/**
* @var array 命名空间别名
*/
protected static $namespaceAlias = []; protected static $namespaceAlias = [];
// PSR-4 /**
* @var array PSR-4 命名空间前缀长度映射
*/
private static $prefixLengthsPsr4 = []; private static $prefixLengthsPsr4 = [];
private static $prefixDirsPsr4 = [];
private static $fallbackDirsPsr4 = [];
// PSR-0 /**
private static $prefixesPsr0 = []; * @var array PSR-4 的加载目录
*/
private static $prefixDirsPsr4 = [];
/**
* @var array PSR-4 加载失败的回退目录
*/
private static $fallbackDirsPsr4 = [];
/**
* @var array PSR-0 命名空间前缀映射
*/
private static $prefixesPsr0 = [];
/**
* @var array PSR-0 加载失败的回退目录
*/
private static $fallbackDirsPsr0 = []; private static $fallbackDirsPsr0 = [];
// 自动加载的文件 /**
private static $autoloadFiles = []; * @var array 需要加载的文件
*/
private static $files = [];
// 自动加载 /**
* 自动加载
* @access public
* @param string $class 类名
* @return bool
*/
public static function autoload($class) public static function autoload($class)
{ {
// 检测命名空间别名 // 检测命名空间别名
@ -49,33 +80,33 @@ class Loader
} }
if ($file = self::findFile($class)) { if ($file = self::findFile($class)) {
// 非 Win 环境不严格区分大小写
// Win环境严格区分大小写 if (!IS_WIN || pathinfo($file, PATHINFO_FILENAME) == pathinfo(realpath($file), PATHINFO_FILENAME)) {
if (IS_WIN && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) { __include_file($file);
return false; return true;
} }
__include_file($file);
return true;
} }
return false;
} }
/** /**
* 查找文件 * 查找文件
* @param $class * @access private
* @return bool * @param string $class 类名
* @return bool|string
*/ */
private static function findFile($class) private static function findFile($class)
{ {
if (!empty(self::$map[$class])) { // 类库映射
// 类库映射 if (!empty(self::$classMap[$class])) {
return self::$map[$class]; return self::$classMap[$class];
} }
// 查找 PSR-4 // 查找 PSR-4
$logicalPathPsr4 = strtr($class, '\\', DS) . EXT; $logicalPathPsr4 = strtr($class, '\\', DS) . EXT;
$first = $class[0];
$first = $class[0];
if (isset(self::$prefixLengthsPsr4[$first])) { if (isset(self::$prefixLengthsPsr4[$first])) {
foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) { foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) {
if (0 === strpos($class, $prefix)) { if (0 === strpos($class, $prefix)) {
@ -97,7 +128,7 @@ class Loader
// 查找 PSR-0 // 查找 PSR-0
if (false !== $pos = strrpos($class, '\\')) { if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name // namespace class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DS); . strtr(substr($logicalPathPsr4, $pos + 1), '_', DS);
} else { } else {
@ -124,20 +155,33 @@ class Loader
} }
} }
return self::$map[$class] = false; // 找不到则设置映射为 false 并返回
return self::$classMap[$class] = false;
} }
// 注册classmap /**
* 注册 classmap
* @access public
* @param string|array $class 类名
* @param string $map 映射
* @return void
*/
public static function addClassMap($class, $map = '') public static function addClassMap($class, $map = '')
{ {
if (is_array($class)) { if (is_array($class)) {
self::$map = array_merge(self::$map, $class); self::$classMap = array_merge(self::$classMap, $class);
} else { } else {
self::$map[$class] = $map; self::$classMap[$class] = $map;
} }
} }
// 注册命名空间 /**
* 注册命名空间
* @access public
* @param string|array $namespace 命名空间
* @param string $path 路径
* @return void
*/
public static function addNamespace($namespace, $path = '') public static function addNamespace($namespace, $path = '')
{ {
if (is_array($namespace)) { if (is_array($namespace)) {
@ -149,84 +193,77 @@ class Loader
} }
} }
// 添加Ps0空间 /**
* 添加 PSR-0 命名空间
* @access private
* @param array|string $prefix 空间前缀
* @param array $paths 路径
* @param bool $prepend 预先设置的优先级更高
* @return void
*/
private static function addPsr0($prefix, $paths, $prepend = false) private static function addPsr0($prefix, $paths, $prepend = false)
{ {
if (!$prefix) { if (!$prefix) {
if ($prepend) { self::$fallbackDirsPsr0 = $prepend ?
self::$fallbackDirsPsr0 = array_merge( array_merge((array) $paths, self::$fallbackDirsPsr0) :
(array) $paths, array_merge(self::$fallbackDirsPsr0, (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 { } else {
self::$prefixesPsr0[$first][$prefix] = array_merge( $first = $prefix[0];
self::$prefixesPsr0[$first][$prefix],
(array) $paths if (!isset(self::$prefixesPsr0[$first][$prefix])) {
); self::$prefixesPsr0[$first][$prefix] = (array) $paths;
} else {
self::$prefixesPsr0[$first][$prefix] = $prepend ?
array_merge((array) $paths, self::$prefixesPsr0[$first][$prefix]) :
array_merge(self::$prefixesPsr0[$first][$prefix], (array) $paths);
}
} }
} }
// 添加Psr4空间 /**
* 添加 PSR-4 空间
* @access private
* @param array|string $prefix 空间前缀
* @param string $paths 路径
* @param bool $prepend 预先设置的优先级更高
* @return void
*/
private static function addPsr4($prefix, $paths, $prepend = false) private static function addPsr4($prefix, $paths, $prepend = false)
{ {
if (!$prefix) { if (!$prefix) {
// Register directories for the root namespace. // Register directories for the root namespace.
if ($prepend) { self::$fallbackDirsPsr4 = $prepend ?
self::$fallbackDirsPsr4 = array_merge( array_merge((array) $paths, self::$fallbackDirsPsr4) :
(array) $paths, array_merge(self::$fallbackDirsPsr4, (array) $paths);
self::$fallbackDirsPsr4
);
} else {
self::$fallbackDirsPsr4 = array_merge(
self::$fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset(self::$prefixDirsPsr4[$prefix])) { } elseif (!isset(self::$prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace. // Register directories for a new namespace.
$length = strlen($prefix); $length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) { if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); throw new \InvalidArgumentException(
"A non-empty PSR-4 prefix must end with a namespace separator."
);
} }
self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length; self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
self::$prefixDirsPsr4[$prefix] = (array) $paths; 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 { } else {
self::$prefixDirsPsr4[$prefix] = $prepend ?
// Prepend directories for an already registered namespace.
array_merge((array) $paths, self::$prefixDirsPsr4[$prefix]) :
// Append directories for an already registered namespace. // Append directories for an already registered namespace.
self::$prefixDirsPsr4[$prefix] = array_merge( array_merge(self::$prefixDirsPsr4[$prefix], (array) $paths);
self::$prefixDirsPsr4[$prefix],
(array) $paths
);
} }
} }
// 注册命名空间别名 /**
* 注册命名空间别名
* @access public
* @param array|string $namespace 命名空间
* @param string $original 源文件
* @return void
*/
public static function addNamespaceAlias($namespace, $original = '') public static function addNamespaceAlias($namespace, $original = '')
{ {
if (is_array($namespace)) { if (is_array($namespace)) {
@ -236,32 +273,58 @@ class Loader
} }
} }
// 注册自动加载机制 /**
public static function register($autoload = '') * 注册自动加载机制
* @access public
* @param callable $autoload 自动加载处理方法
* @return void
*/
public static function register($autoload = null)
{ {
// 注册系统自动加载 // 注册系统自动加载
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true); spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
// Composer 自动加载支持
if (is_dir(VENDOR_PATH . 'composer')) {
if (PHP_VERSION_ID >= 50600 && is_file(VENDOR_PATH . 'composer' . DS . 'autoload_static.php')) {
require VENDOR_PATH . 'composer' . DS . 'autoload_static.php';
$declaredClass = get_declared_classes();
$composerClass = array_pop($declaredClass);
foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) {
if (property_exists($composerClass, $attr)) {
self::${$attr} = $composerClass::${$attr};
}
}
} else {
self::registerComposerLoader();
}
}
// 注册命名空间定义 // 注册命名空间定义
self::addNamespace([ self::addNamespace([
'think' => LIB_PATH . 'think' . DS, 'think' => LIB_PATH . 'think' . DS,
'behavior' => LIB_PATH . 'behavior' . DS, 'behavior' => LIB_PATH . 'behavior' . DS,
'traits' => LIB_PATH . 'traits' . DS, 'traits' => LIB_PATH . 'traits' . DS,
]); ]);
// 加载类库映射文件 // 加载类库映射文件
if (is_file(RUNTIME_PATH . 'classmap' . EXT)) { if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {
self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT)); self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));
} }
// Composer自动加载支持 self::loadComposerAutoloadFiles();
if (is_dir(VENDOR_PATH . 'composer')) {
self::registerComposerLoader();
}
// 自动加载extend目录 // 自动加载 extend 目录
self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS); self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);
} }
// 注册composer自动加载 /**
* 注册 composer 自动加载
* @access private
* @return void
*/
private static function registerComposerLoader() private static function registerComposerLoader()
{ {
if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) { if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) {
@ -286,28 +349,36 @@ class Loader
} }
if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) { if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) {
$includeFiles = require VENDOR_PATH . 'composer/autoload_files.php'; self::$files = require VENDOR_PATH . 'composer/autoload_files.php';
foreach ($includeFiles as $fileIdentifier => $file) { }
if (empty(self::$autoloadFiles[$fileIdentifier])) { }
__require_file($file);
self::$autoloadFiles[$fileIdentifier] = true; // 加载composer autofile文件
} public static function loadComposerAutoloadFiles()
{
foreach (self::$files as $fileIdentifier => $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
__require_file($file);
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
} }
} }
} }
/** /**
* 导入所需的类库 同java的Import 本函数有缓存功能 * 导入所需的类库 Java Import 本函数有缓存功能
* @param string $class 类库命名空间字符串 * @access public
* @param string $baseUrl 起始路径 * @param string $class 类库命名空间字符串
* @param string $ext 导入的文件扩展名 * @param string $baseUrl 起始路径
* @return boolean * @param string $ext 导入的文件扩展名
* @return bool
*/ */
public static function import($class, $baseUrl = '', $ext = EXT) public static function import($class, $baseUrl = '', $ext = EXT)
{ {
static $_file = []; static $_file = [];
$key = $class . $baseUrl; $key = $class . $baseUrl;
$class = str_replace(['.', '#'], [DS, '.'], $class); $class = str_replace(['.', '#'], [DS, '.'], $class);
if (isset($_file[$key])) { if (isset($_file[$key])) {
return true; return true;
} }
@ -319,7 +390,7 @@ class Loader
// 注册的命名空间 // 注册的命名空间
$baseUrl = self::$prefixDirsPsr4[$name . '\\']; $baseUrl = self::$prefixDirsPsr4[$name . '\\'];
} elseif ('@' == $name) { } elseif ('@' == $name) {
//加载当前模块应用类库 // 加载当前模块应用类库
$baseUrl = App::$modulePath; $baseUrl = App::$modulePath;
} elseif (is_dir(EXTEND_PATH . $name)) { } elseif (is_dir(EXTEND_PATH . $name)) {
$baseUrl = EXTEND_PATH . $name . DS; $baseUrl = EXTEND_PATH . $name . DS;
@ -330,11 +401,11 @@ class Loader
} elseif (substr($baseUrl, -1) != DS) { } elseif (substr($baseUrl, -1) != DS) {
$baseUrl .= DS; $baseUrl .= DS;
} }
// 如果类存在 则导入类库文件
// 如果类存在则导入类库文件
if (is_array($baseUrl)) { if (is_array($baseUrl)) {
foreach ($baseUrl as $path) { foreach ($baseUrl as $path) {
$filename = $path . DS . $class . $ext; if (is_file($filename = $path . DS . $class . $ext)) {
if (is_file($filename)) {
break; break;
} }
} }
@ -342,135 +413,154 @@ class Loader
$filename = $baseUrl . $class . $ext; $filename = $baseUrl . $class . $ext;
} }
if (!empty($filename) && is_file($filename)) { if (!empty($filename) &&
// 开启调试模式Win环境严格区分大小写 is_file($filename) &&
if (IS_WIN && pathinfo($filename, PATHINFO_FILENAME) != pathinfo(realpath($filename), PATHINFO_FILENAME)) { (!IS_WIN || pathinfo($filename, PATHINFO_FILENAME) == pathinfo(realpath($filename), PATHINFO_FILENAME))
return false; ) {
}
__include_file($filename); __include_file($filename);
$_file[$key] = true; $_file[$key] = true;
return true; return true;
} }
return false; return false;
} }
/** /**
* 实例化(分层)模型 * 实例化(分层)模型
* @param string $name Model名称 * @access public
* @param string $layer 业务层名称 * @param string $name Model名称
* @param bool $appendSuffix 是否添加类名后缀 * @param string $layer 业务层名称
* @param string $common 公共模块名 * @param bool $appendSuffix 是否添加类名后缀
* @return Object * @param string $common 公共模块名
* @return object
* @throws ClassNotFoundException * @throws ClassNotFoundException
*/ */
public static function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common') public static function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common')
{ {
$guid = $name . $layer; $uid = $name . $layer;
if (isset(self::$instance[$guid])) {
return self::$instance[$guid]; if (isset(self::$instance[$uid])) {
} return self::$instance[$uid];
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);
} }
list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
if (class_exists($class)) { if (class_exists($class)) {
$model = new $class(); $model = new $class();
} else { } else {
$class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class); $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
if (class_exists($class)) { if (class_exists($class)) {
$model = new $class(); $model = new $class();
} else { } else {
throw new ClassNotFoundException('class not exists:' . $class, $class); throw new ClassNotFoundException('class not exists:' . $class, $class);
} }
} }
self::$instance[$guid] = $model;
return $model; return self::$instance[$uid] = $model;
} }
/** /**
* 实例化(分层)控制器 格式:[模块名/]控制器名 * 实例化(分层)控制器 格式:[模块名/]控制器名
* @param string $name 资源地址 * @access public
* @param string $layer 控制层名称 * @param string $name 资源地址
* @param bool $appendSuffix 是否添加类名后缀 * @param string $layer 控制层名称
* @param string $empty 空控制器名称 * @param bool $appendSuffix 是否添加类名后缀
* @return Object|false * @param string $empty 空控制器名称
* @return object
* @throws ClassNotFoundException * @throws ClassNotFoundException
*/ */
public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '') public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
{ {
if (false !== strpos($name, '\\')) { list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
$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)) { if (class_exists($class)) {
return App::invokeClass($class); return App::invokeClass($class);
} elseif ($empty && class_exists($emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix))) {
return new $emptyClass(Request::instance());
} }
if ($empty) {
$emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix);
if (class_exists($emptyClass)) {
return new $emptyClass(Request::instance());
}
}
throw new ClassNotFoundException('class not exists:' . $class, $class);
} }
/** /**
* 实例化验证类 格式:[模块名/]验证器名 * 实例化验证类 格式:[模块名/]验证器名
* @param string $name 资源地址 * @access public
* @param string $layer 验证层名称 * @param string $name 资源地址
* @param bool $appendSuffix 是否添加类名后缀 * @param string $layer 验证层名称
* @param string $common 公共模块名 * @param bool $appendSuffix 是否添加类名后缀
* @return Object|false * @param string $common 公共模块名
* @return object|false
* @throws ClassNotFoundException * @throws ClassNotFoundException
*/ */
public static function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common') public static function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common')
{ {
$name = $name ?: Config::get('default_validate'); $name = $name ?: Config::get('default_validate');
if (empty($name)) { if (empty($name)) {
return new Validate; return new Validate;
} }
$guid = $name . $layer;
if (isset(self::$instance[$guid])) { $uid = $name . $layer;
return self::$instance[$guid]; if (isset(self::$instance[$uid])) {
} return self::$instance[$uid];
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);
} }
list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
if (class_exists($class)) { if (class_exists($class)) {
$validate = new $class; $validate = new $class;
} else { } else {
$class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class); $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
if (class_exists($class)) { if (class_exists($class)) {
$validate = new $class; $validate = new $class;
} else { } else {
throw new ClassNotFoundException('class not exists:' . $class, $class); throw new ClassNotFoundException('class not exists:' . $class, $class);
} }
} }
self::$instance[$guid] = $validate;
return $validate; return self::$instance[$uid] = $validate;
}
/**
* 解析模块和类名
* @access protected
* @param string $name 资源地址
* @param string $layer 验证层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return array
*/
protected static function getModuleAndClass($name, $layer, $appendSuffix)
{
if (false !== strpos($name, '\\')) {
$module = Request::instance()->module();
$class = $name;
} else {
if (strpos($name, '/')) {
list($module, $name) = explode('/', $name, 2);
} else {
$module = Request::instance()->module();
}
$class = self::parseClass($module, $layer, $name, $appendSuffix);
}
return [$module, $class];
} }
/** /**
* 数据库初始化 并取得数据库类实例 * 数据库初始化 并取得数据库类实例
* @param mixed $config 数据库配置 * @access public
* @param bool|string $name 连接标识 true 强制重新连接 * @param mixed $config 数据库配置
* @param bool|string $name 连接标识 true 强制重新连接
* @return \think\db\Connection * @return \think\db\Connection
*/ */
public static function db($config = [], $name = false) public static function db($config = [], $name = false)
@ -480,10 +570,11 @@ class Loader
/** /**
* 远程调用模块的操作方法 参数格式 [模块/控制器/]操作 * 远程调用模块的操作方法 参数格式 [模块/控制器/]操作
* @param string $url 调用地址 * @access public
* @param string|array $vars 调用参数 支持字符串和数组 * @param string $url 调用地址
* @param string $layer 要调用的控制层名称 * @param string|array $vars 调用参数 支持字符串和数组
* @param bool $appendSuffix 是否添加类名后缀 * @param string $layer 要调用的控制层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return mixed * @return mixed
*/ */
public static function action($url, $vars = [], $layer = 'controller', $appendSuffix = false) public static function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
@ -492,6 +583,7 @@ class Loader
$action = $info['basename']; $action = $info['basename'];
$module = '.' != $info['dirname'] ? $info['dirname'] : Request::instance()->controller(); $module = '.' != $info['dirname'] ? $info['dirname'] : Request::instance()->controller();
$class = self::controller($module, $layer, $appendSuffix); $class = self::controller($module, $layer, $appendSuffix);
if ($class) { if ($class) {
if (is_scalar($vars)) { if (is_scalar($vars)) {
if (strpos($vars, '=')) { if (strpos($vars, '=')) {
@ -500,16 +592,20 @@ class Loader
$vars = [$vars]; $vars = [$vars];
} }
} }
return App::invokeMethod([$class, $action . Config::get('action_suffix')], $vars); return App::invokeMethod([$class, $action . Config::get('action_suffix')], $vars);
} }
return false;
} }
/** /**
* 字符串命名风格转换 * 字符串命名风格转换
* type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格 * type 0 Java 风格转换为 C 的风格 1 C 风格转换为 Java 的风格
* @param string $name 字符串 * @access public
* @param integer $type 转换类型 * @param string $name 字符串
* @param bool $ucfirst 首字母是否大写(驼峰规则) * @param integer $type 转换类型
* @param bool $ucfirst 首字母是否大写(驼峰规则)
* @return string * @return string
*/ */
public static function parseName($name, $type = 0, $ucfirst = true) public static function parseName($name, $type = 0, $ucfirst = true)
@ -518,31 +614,38 @@ class Loader
$name = preg_replace_callback('/_([a-zA-Z])/', function ($match) { $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
return strtoupper($match[1]); return strtoupper($match[1]);
}, $name); }, $name);
return $ucfirst ? ucfirst($name) : lcfirst($name); return $ucfirst ? ucfirst($name) : lcfirst($name);
} else {
return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
} }
return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
} }
/** /**
* 解析应用类的类名 * 解析应用类的类名
* @param string $module 模块名 * @access public
* @param string $layer 层名 controller model ... * @param string $module 模块名
* @param string $name 类名 * @param string $layer 层名 controller model ...
* @param bool $appendSuffix * @param string $name 类名
* @param bool $appendSuffix 是否添加类名后缀
* @return string * @return string
*/ */
public static function parseClass($module, $layer, $name, $appendSuffix = false) public static function parseClass($module, $layer, $name, $appendSuffix = false)
{ {
$name = str_replace(['/', '.'], '\\', $name);
$array = explode('\\', $name); $array = explode('\\', str_replace(['/', '.'], '\\', $name));
$class = self::parseName(array_pop($array), 1) . (App::$suffix || $appendSuffix ? ucfirst($layer) : ''); $class = self::parseName(array_pop($array), 1);
$class = $class . (App::$suffix || $appendSuffix ? ucfirst($layer) : '');
$path = $array ? implode('\\', $array) . '\\' : ''; $path = $array ? implode('\\', $array) . '\\' : '';
return App::$namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $path . $class;
return App::$namespace . '\\' .
($module ? $module . '\\' : '') .
$layer . '\\' . $path . $class;
} }
/** /**
* 初始化类的实例 * 初始化类的实例
* @access public
* @return void * @return void
*/ */
public static function clearInstance() public static function clearInstance()
@ -551,10 +654,11 @@ class Loader
} }
} }
// 作用范围隔离
/** /**
* 作用范围隔离 * include
* * @param string $file 文件路径
* @param $file
* @return mixed * @return mixed
*/ */
function __include_file($file) function __include_file($file)
@ -562,6 +666,11 @@ function __include_file($file)
return include $file; return include $file;
} }
/**
* require
* @param string $file 文件路径
* @return mixed
*/
function __require_file($file) function __require_file($file)
{ {
return require $file; return require $file;

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -17,12 +17,12 @@ use think\exception\ClassNotFoundException;
* Class Log * Class Log
* @package think * @package think
* *
* @method void log($msg) static * @method void log($msg) static 记录一般日志
* @method void error($msg) static * @method void error($msg) static 记录错误日志
* @method void info($msg) static * @method void info($msg) static 记录一般信息日志
* @method void sql($msg) static * @method void sql($msg) static 记录 SQL 查询日志
* @method void notice($msg) static * @method void notice($msg) static 记录提示日志
* @method void alert($msg) static * @method void alert($msg) static 记录报警日志
*/ */
class Log class Log
{ {
@ -34,41 +34,60 @@ class Log
const ALERT = 'alert'; const ALERT = 'alert';
const DEBUG = 'debug'; const DEBUG = 'debug';
// 日志信息 /**
* @var array 日志信息
*/
protected static $log = []; protected static $log = [];
// 配置参数
/**
* @var array 配置参数
*/
protected static $config = []; protected static $config = [];
// 日志类型
/**
* @var array 日志类型
*/
protected static $type = ['log', 'error', 'info', 'sql', 'notice', 'alert', 'debug']; protected static $type = ['log', 'error', 'info', 'sql', 'notice', 'alert', 'debug'];
// 日志写入驱动
/**
* @var log\driver\File|log\driver\Test|log\driver\Socket 日志写入驱动
*/
protected static $driver; protected static $driver;
// 当前日志授权key /**
* @var string 当前日志授权 key
*/
protected static $key; protected static $key;
/** /**
* 日志初始化 * 日志初始化
* @param array $config * @access public
* @param array $config 配置参数
* @return void
*/ */
public static function init($config = []) public static function init($config = [])
{ {
$type = isset($config['type']) ? $config['type'] : 'File'; $type = isset($config['type']) ? $config['type'] : 'File';
$class = false !== strpos($type, '\\') ? $type : '\\think\\log\\driver\\' . ucwords($type); $class = false !== strpos($type, '\\') ? $type : '\\think\\log\\driver\\' . ucwords($type);
self::$config = $config; self::$config = $config;
unset($config['type']); unset($config['type']);
if (class_exists($class)) { if (class_exists($class)) {
self::$driver = new $class($config); self::$driver = new $class($config);
} else { } else {
throw new ClassNotFoundException('class not exists:' . $class, $class); throw new ClassNotFoundException('class not exists:' . $class, $class);
} }
// 记录初始化信息 // 记录初始化信息
App::$debug && Log::record('[ LOG ] INIT ' . $type, 'info'); App::$debug && Log::record('[ LOG ] INIT ' . $type, 'info');
} }
/** /**
* 获取日志信息 * 获取日志信息
* @param string $type 信息类型 * @access public
* @return array * @param string $type 信息类型
* @return array|string
*/ */
public static function getLog($type = '') public static function getLog($type = '')
{ {
@ -77,21 +96,22 @@ class Log
/** /**
* 记录调试信息 * 记录调试信息
* @param mixed $msg 调试信息 * @access public
* @param string $type 信息类型 * @param mixed $msg 调试信息
* @param string $type 信息类型
* @return void * @return void
*/ */
public static function record($msg, $type = 'log') public static function record($msg, $type = 'log')
{ {
self::$log[$type][] = $msg; self::$log[$type][] = $msg;
if (IS_CLI) {
// 命令行下面日志写入改进 // 命令行下面日志写入改进
self::save(); IS_CLI && self::save();
}
} }
/** /**
* 清空日志信息 * 清空日志信息
* @access public
* @return void * @return void
*/ */
public static function clear() public static function clear()
@ -100,8 +120,9 @@ class Log
} }
/** /**
* 当前日志记录的授权key * 设置当前日志记录的授权 key
* @param string $key 授权key * @access public
* @param string $key 授权 key
* @return void * @return void
*/ */
public static function key($key) public static function key($key)
@ -111,102 +132,105 @@ class Log
/** /**
* 检查日志写入权限 * 检查日志写入权限
* @param array $config 当前日志配置参数 * @access public
* @param array $config 当前日志配置参数
* @return bool * @return bool
*/ */
public static function check($config) public static function check($config)
{ {
if (self::$key && !empty($config['allow_key']) && !in_array(self::$key, $config['allow_key'])) { return !self::$key || empty($config['allow_key']) || in_array(self::$key, $config['allow_key']);
return false;
}
return true;
} }
/** /**
* 保存调试信息 * 保存调试信息
* @access public
* @return bool * @return bool
*/ */
public static function save() public static function save()
{ {
if (!empty(self::$log)) { // 没有需要保存的记录则直接返回
if (is_null(self::$driver)) { if (empty(self::$log)) {
self::init(Config::get('log')); return true;
}
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 = [];
}
Hook::listen('log_write_done', $log);
return $result;
} }
return true;
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];
}
}
}
if ($result = self::$driver->save($log, true)) {
self::$log = [];
}
Hook::listen('log_write_done', $log);
return $result;
} }
/** /**
* 实时写入日志信息 并支持行为 * 实时写入日志信息 并支持行为
* @param mixed $msg 调试信息 * @access public
* @param string $type 信息类型 * @param mixed $msg 调试信息
* @param bool $force 是否强制写入 * @param string $type 信息类型
* @param bool $force 是否强制写入
* @return bool * @return bool
*/ */
public static function write($msg, $type = 'log', $force = false) public static function write($msg, $type = 'log', $force = false)
{ {
$log = self::$log; $log = self::$log;
// 封装日志信息
if (true === $force || empty(self::$config['level'])) { // 如果不是强制写入,而且信息类型不在可记录的类别中则直接返回 false 不做记录
$log[$type][] = $msg; if (true !== $force && !empty(self::$config['level']) && !in_array($type, self::$config['level'])) {
} elseif (in_array($type, self::$config['level'])) {
$log[$type][] = $msg;
} else {
return false; return false;
} }
// 监听log_write // 封装日志信息
$log[$type][] = $msg;
// 监听 log_write
Hook::listen('log_write', $log); Hook::listen('log_write', $log);
if (is_null(self::$driver)) {
self::init(Config::get('log')); is_null(self::$driver) && self::init(Config::get('log'));
}
// 写入日志 // 写入日志
$result = self::$driver->save($log); if ($result = self::$driver->save($log, false)) {
if ($result) {
self::$log = []; self::$log = [];
} }
return $result; return $result;
} }
/** /**
* 静态调用 * 静态方法调用
* @param $method * @access public
* @param $args * @param string $method 调用方法
* @return mixed * @param mixed $args 参数
* @return void
*/ */
public static function __callStatic($method, $args) public static function __callStatic($method, $args)
{ {
if (in_array($method, self::$type)) { if (in_array($method, self::$type)) {
array_push($args, $method); array_push($args, $method);
return call_user_func_array('\\think\\Log::record', $args);
call_user_func_array('\\think\\Log::record', $args);
} }
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -11,6 +11,7 @@
namespace think; namespace think;
use BadMethodCallException;
use InvalidArgumentException; use InvalidArgumentException;
use think\db\Query; use think\db\Query;
use think\exception\ValidateException; use think\exception\ValidateException;
@ -56,6 +57,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
protected $pk; protected $pk;
// 数据表字段信息 留空则自动获取 // 数据表字段信息 留空则自动获取
protected $field = []; protected $field = [];
// 数据排除字段
protected $except = [];
// 数据废弃字段
protected $disuse = [];
// 只读字段 // 只读字段
protected $readonly = []; protected $readonly = [];
// 显示属性 // 显示属性
@ -89,6 +94,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
protected $type = []; protected $type = [];
// 是否为更新数据 // 是否为更新数据
protected $isUpdate = false; protected $isUpdate = false;
// 是否使用Replace
protected $replace = false;
// 是否强制更新所有数据
protected $force = false;
// 更新条件 // 更新条件
protected $updateWhere; protected $updateWhere;
// 验证失败是否抛出异常 // 验证失败是否抛出异常
@ -109,6 +118,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
*/ */
protected static $initialized = []; protected static $initialized = [];
/**
* 是否从主库读取(主从分布式有效)
* @var array
*/
protected static $readMaster;
/** /**
* 构造方法 * 构造方法
* @access public * @access public
@ -121,6 +136,16 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
} else { } else {
$this->data = $data; $this->data = $data;
} }
if ($this->disuse) {
// 废弃字段
foreach ((array) $this->disuse as $key) {
if (array_key_exists($key, $this->data)) {
unset($this->data[$key]);
}
}
}
// 记录原始数据 // 记录原始数据
$this->origin = $this->data; $this->origin = $this->data;
@ -154,6 +179,20 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$this->initialize(); $this->initialize();
} }
/**
* 是否从主库读取数据(主从分布有效)
* @access public
* @param bool $all 是否所有模型生效
* @return $this
*/
public function readMaster($all = false)
{
$model = $all ? '*' : $this->class;
static::$readMaster[$model] = true;
return $this;
}
/** /**
* 创建模型的查询对象 * 创建模型的查询对象
* @access protected * @access protected
@ -175,7 +214,11 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$con = Db::connect($connection); $con = Db::connect($connection);
// 设置当前模型 确保查询返回模型对象 // 设置当前模型 确保查询返回模型对象
$queryClass = $this->query ?: $con->getConfig('query'); $queryClass = $this->query ?: $con->getConfig('query');
$query = new $queryClass($con, $this->class); $query = new $queryClass($con, $this);
if (isset(static::$readMaster['*']) || isset(static::$readMaster[$this->class])) {
$query->master(true);
}
// 设置当前数据表和模型名 // 设置当前数据表和模型名
if (!empty($this->table)) { if (!empty($this->table)) {
@ -191,6 +234,19 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
return $query; return $query;
} }
/**
* 创建新的模型实例
* @access public
* @param array|object $data 数据
* @param bool $isUpdate 是否为更新
* @param mixed $where 更新条件
* @return Model
*/
public function newInstance($data = [], $isUpdate = false, $where = null)
{
return (new static($data))->isUpdate($isUpdate, $where);
}
/** /**
* 获取当前模型的查询对象 * 获取当前模型的查询对象
* @access public * @access public
@ -261,7 +317,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
public function setParent($model) public function setParent($model)
{ {
$this->parent = $model; $this->parent = $model;
return $this; return $this;
} }
@ -336,6 +391,18 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
return $this; return $this;
} }
/**
* 更新是否强制写入数据 而不做比较
* @access public
* @param bool $force
* @return $this
*/
public function force($force = true)
{
$this->force = $force;
return $this;
}
/** /**
* 修改器 设置数据对象值 * 修改器 设置数据对象值
* @access public * @access public
@ -567,14 +634,19 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
* @access public * @access public
* @param Relation $modelRelation 模型关联对象 * @param Relation $modelRelation 模型关联对象
* @return mixed * @return mixed
* @throws BadMethodCallException
*/ */
protected function getRelationData(Relation $modelRelation) protected function getRelationData(Relation $modelRelation)
{ {
if ($this->parent && get_class($this->parent) == $modelRelation->getModel()) { if ($this->parent && !$modelRelation->isSelfRelation() && get_class($modelRelation->getModel()) == get_class($this->parent)) {
$value = $this->parent; $value = $this->parent;
} else { } else {
// 首先获取关联数据 // 首先获取关联数据
$value = $modelRelation->getRelation(); if (method_exists($modelRelation, 'getRelation')) {
$value = $modelRelation->getRelation();
} else {
throw new BadMethodCallException('method not exists:' . get_class($modelRelation) . '-> getRelation');
}
} }
return $value; return $value;
} }
@ -633,7 +705,11 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$value = empty($value) ? new \stdClass() : json_decode($value); $value = empty($value) ? new \stdClass() : json_decode($value);
break; break;
case 'serialize': case 'serialize':
$value = unserialize($value); try {
$value = unserialize($value);
} catch (\Exception $e) {
$value = null;
}
break; break;
default: default:
if (false !== strpos($type, '\\')) { if (false !== strpos($type, '\\')) {
@ -686,7 +762,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
if (isset($this->data[$key])) { if (isset($this->data[$key])) {
throw new Exception('bind attr has exists:' . $key); throw new Exception('bind attr has exists:' . $key);
} else { } else {
$this->data[$key] = $model->$attr; $this->data[$key] = $model->getAttr($attr);
} }
} }
} }
@ -820,7 +896,29 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$relation = $this->getAttr($key); $relation = $this->getAttr($key);
$item[$key] = $relation->append([$attr])->toArray(); $item[$key] = $relation->append([$attr])->toArray();
} else { } else {
$item[$name] = $this->getAttr($name); $relation = Loader::parseName($name, 1, false);
if (method_exists($this, $relation)) {
$modelRelation = $this->$relation();
$value = $this->getRelationData($modelRelation);
if (method_exists($modelRelation, 'getBindAttr')) {
$bindAttr = $modelRelation->getBindAttr();
if ($bindAttr) {
foreach ($bindAttr as $key => $attr) {
$key = is_numeric($key) ? $attr : $key;
if (isset($this->data[$key])) {
throw new Exception('bind attr has exists:' . $key);
} else {
$item[$key] = $value ? $value->getAttr($attr) : null;
}
}
continue;
}
}
$item[$name] = $value;
} else {
$item[$name] = $this->getAttr($name);
}
} }
} }
} }
@ -917,6 +1015,18 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
return false; return false;
} }
/**
* 新增数据是否使用Replace
* @access public
* @param bool $replace
* @return $this
*/
public function replace($replace = true)
{
$this->replace = $replace;
return $this;
}
/** /**
* 保存当前数据对象 * 保存当前数据对象
* @access public * @access public
@ -927,18 +1037,26 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
*/ */
public function save($data = [], $where = [], $sequence = null) public function save($data = [], $where = [], $sequence = null)
{ {
if (is_string($data)) {
$sequence = $data;
$data = [];
}
// 数据自动验证
if (!empty($data)) { if (!empty($data)) {
// 数据自动验证
if (!$this->validateData($data)) { if (!$this->validateData($data)) {
return false; return false;
} }
// 数据对象赋值 // 数据对象赋值
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
$this->setAttr($key, $value, $data); $this->setAttr($key, $value, $data);
} }
if (!empty($where)) { }
$this->isUpdate = true;
} if (!empty($where)) {
$this->isUpdate = true;
$this->updateWhere = $where;
} }
// 自动关联写入 // 自动关联写入
@ -1009,12 +1127,17 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
} }
} }
if (is_string($pk) && isset($data[$pk])) { $array = [];
if (!isset($where[$pk])) {
unset($where); foreach ((array) $pk as $key) {
$where[$pk] = $data[$pk]; if (isset($data[$key])) {
$array[$key] = $data[$key];
unset($data[$key]);
} }
unset($data[$pk]); }
if (!empty($array)) {
$where = $array;
} }
// 检测字段 // 检测字段
@ -1056,16 +1179,17 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
// 检测字段 // 检测字段
$allowFields = $this->checkAllowField(array_merge($this->auto, $this->insert)); $allowFields = $this->checkAllowField(array_merge($this->auto, $this->insert));
if (!empty($allowFields)) { if (!empty($allowFields)) {
$result = $this->getQuery()->strict(false)->field($allowFields)->insert($this->data); $result = $this->getQuery()->strict(false)->field($allowFields)->insert($this->data, $this->replace, false, $sequence);
} else { } else {
$result = $this->getQuery()->insert($this->data); $result = $this->getQuery()->insert($this->data, $this->replace, false, $sequence);
} }
// 获取自动增长主键 // 获取自动增长主键
if ($result && is_string($pk) && (!isset($this->data[$pk]) || '' == $this->data[$pk])) { if ($result && $insertId = $this->getQuery()->getLastInsID($sequence)) {
$insertId = $this->getQuery()->getLastInsID($sequence); foreach ((array) $pk as $key) {
if ($insertId) { if (!isset($this->data[$key]) || '' == $this->data[$key]) {
$this->data[$pk] = $insertId; $this->data[$key] = $insertId;
}
} }
} }
@ -1099,11 +1223,21 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$field = $this->field; $field = $this->field;
} elseif (!empty($this->field)) { } elseif (!empty($this->field)) {
$field = array_merge($this->field, $auto); $field = array_merge($this->field, $auto);
if ($this->autoWriteTimestamp) {
array_push($field, $this->createTime, $this->updateTime);
}
} elseif (!empty($this->except)) {
$fields = $this->getQuery()->getTableInfo('', 'fields');
$field = array_diff($fields, (array) $this->except);
$this->field = $field;
} else { } else {
$field = []; $field = [];
} }
if ($this->disuse) {
// 废弃字段
$field = array_diff($field, (array) $this->disuse);
}
return $field; return $field;
} }
@ -1129,12 +1263,16 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
*/ */
public function getChangedData() public function getChangedData()
{ {
$data = array_udiff_assoc($this->data, $this->origin, function ($a, $b) { if ($this->force) {
if ((empty($b) || empty($b)) && $a !== $b) { $data = $this->data;
return 1; } else {
} $data = array_udiff_assoc($this->data, $this->origin, function ($a, $b) {
return is_object($a) || $a != $b ? 1 : 0; if ((empty($a) || empty($b)) && $a !== $b) {
}); return 1;
}
return is_object($a) || $a != $b ? 1 : 0;
});
}
if (!empty($this->readonly)) { if (!empty($this->readonly)) {
// 只读字段不允许更新 // 只读字段不允许更新
@ -1159,16 +1297,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
*/ */
public function setInc($field, $step = 1, $lazyTime = 0) public function setInc($field, $step = 1, $lazyTime = 0)
{ {
// 删除条件 // 更新条件
$pk = $this->getPk(); $where = $this->getWhere();
if (is_string($pk) && isset($this->data[$pk])) {
$where = [$pk => $this->data[$pk]];
} elseif (!empty($this->updateWhere)) {
$where = $this->updateWhere;
} else {
$where = null;
}
$result = $this->getQuery()->where($where)->setInc($field, $step, $lazyTime); $result = $this->getQuery()->where($where)->setInc($field, $step, $lazyTime);
if (true !== $result) { if (true !== $result) {
@ -1188,6 +1318,23 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
* @throws Exception * @throws Exception
*/ */
public function setDec($field, $step = 1, $lazyTime = 0) public function setDec($field, $step = 1, $lazyTime = 0)
{
// 更新条件
$where = $this->getWhere();
$result = $this->getQuery()->where($where)->setDec($field, $step, $lazyTime);
if (true !== $result) {
$this->data[$field] -= $step;
}
return $result;
}
/**
* 获取更新条件
* @access protected
* @return mixed
*/
protected function getWhere()
{ {
// 删除条件 // 删除条件
$pk = $this->getPk(); $pk = $this->getPk();
@ -1199,13 +1346,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
} else { } else {
$where = null; $where = null;
} }
return $where;
$result = $this->getQuery()->where($where)->setDec($field, $step, $lazyTime);
if (true !== $result) {
$this->data[$field] -= $step;
}
return $result;
} }
/** /**
@ -1237,14 +1378,14 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$auto = true; $auto = true;
} }
foreach ($dataSet as $key => $data) { foreach ($dataSet as $key => $data) {
if (!empty($auto) && isset($data[$pk])) { if ($this->isUpdate || (!empty($auto) && isset($data[$pk]))) {
$result[$key] = self::update($data, [], $this->field); $result[$key] = self::update($data, [], $this->field);
} else { } else {
$result[$key] = self::create($data, $this->field); $result[$key] = self::create($data, $this->field);
} }
} }
$db->commit(); $db->commit();
return $result; return $this->toCollection($result);
} catch (\Exception $e) { } catch (\Exception $e) {
$db->rollback(); $db->rollback();
throw $e; throw $e;
@ -1254,7 +1395,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
/** /**
* 设置允许写入的字段 * 设置允许写入的字段
* @access public * @access public
* @param mixed $field 允许写入的字段 如果为true只允许写入数据表字段 * @param string|array $field 允许写入的字段 如果为true只允许写入数据表字段
* @return $this * @return $this
*/ */
public function allowField($field) public function allowField($field)
@ -1266,6 +1407,21 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
return $this; return $this;
} }
/**
* 设置排除写入的字段
* @access public
* @param string|array $field 排除允许写入的字段
* @return $this
*/
public function except($field)
{
if (is_string($field)) {
$field = explode(',', $field);
}
$this->except = $field;
return $this;
}
/** /**
* 设置只读字段 * 设置只读字段
* @access public * @access public
@ -1333,14 +1489,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
} }
// 删除条件 // 删除条件
$pk = $this->getPk(); $where = $this->getWhere();
if (is_string($pk) && isset($this->data[$pk])) {
$where = [$pk => $this->data[$pk]];
} elseif (!empty($this->updateWhere)) {
$where = $this->updateWhere;
} else {
$where = null;
}
// 删除当前模型数据 // 删除当前模型数据
$result = $this->getQuery()->where($where)->delete(); $result = $this->getQuery()->where($where)->delete();
@ -1611,14 +1760,14 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
{ {
$model = new static(); $model = new static();
$query = $model->db(); $query = $model->db();
if (is_array($data) && key($data) !== 0) { if (empty($data) && 0 !== $data) {
return 0;
} elseif (is_array($data) && key($data) !== 0) {
$query->where($data); $query->where($data);
$data = null; $data = null;
} elseif ($data instanceof \Closure) { } elseif ($data instanceof \Closure) {
call_user_func_array($data, [ & $query]); call_user_func_array($data, [ & $query]);
$data = null; $data = null;
} elseif (empty($data) && 0 !== $data) {
return 0;
} }
$resultSet = $query->select($data); $resultSet = $query->select($data);
$count = 0; $count = 0;
@ -1665,7 +1814,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
* 设置是否使用全局查询范围 * 设置是否使用全局查询范围
* @param bool $use 是否启用全局查询范围 * @param bool $use 是否启用全局查询范围
* @access public * @access public
* @return Model * @return Query
*/ */
public static function useGlobalScope($use) public static function useGlobalScope($use)
{ {
@ -1694,13 +1843,14 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
/** /**
* 根据关联条件查询当前模型 * 根据关联条件查询当前模型
* @access public * @access public
* @param string $relation 关联方法名 * @param string $relation 关联方法名
* @param mixed $where 查询条件(数组或者闭包) * @param mixed $where 查询条件(数组或者闭包)
* @param mixed $fields 字段
* @return Relation|Query * @return Relation|Query
*/ */
public static function hasWhere($relation, $where = []) public static function hasWhere($relation, $where = [], $fields = null)
{ {
return (new static())->$relation()->hasWhere($where); return (new static())->$relation()->hasWhere($where, $fields);
} }
/** /**
@ -1857,7 +2007,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
* @access public * @access public
* @param string $model 模型名 * @param string $model 模型名
* @param string $foreignKey 关联外键 * @param string $foreignKey 关联外键
* @param string $localKey 关联主键 * @param string $localKey 当前模型主键
* @param array $alias 别名定义(已经废弃) * @param array $alias 别名定义(已经废弃)
* @param string $joinType JOIN类型 * @param string $joinType JOIN类型
* @return HasOne * @return HasOne
@ -1897,7 +2047,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
* @access public * @access public
* @param string $model 模型名 * @param string $model 模型名
* @param string $foreignKey 关联外键 * @param string $foreignKey 关联外键
* @param string $localKey 关联主键 * @param string $localKey 当前模型主键
* @return HasMany * @return HasMany
*/ */
public function hasMany($model, $foreignKey = '', $localKey = '') public function hasMany($model, $foreignKey = '', $localKey = '')
@ -1916,7 +2066,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
* @param string $through 中间模型名 * @param string $through 中间模型名
* @param string $foreignKey 关联外键 * @param string $foreignKey 关联外键
* @param string $throughKey 关联外键 * @param string $throughKey 关联外键
* @param string $localKey 关联主键 * @param string $localKey 当前模型主键
* @return HasManyThrough * @return HasManyThrough
*/ */
public function hasManyThrough($model, $through, $foreignKey = '', $throughKey = '', $localKey = '') public function hasManyThrough($model, $through, $foreignKey = '', $throughKey = '', $localKey = '')
@ -1966,7 +2116,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$trace = debug_backtrace(false, 2); $trace = debug_backtrace(false, 2);
$morph = Loader::parseName($trace[1]['function']); $morph = Loader::parseName($trace[1]['function']);
} }
$type = $type ?: Loader::parseName($this->name); $type = $type ?: get_class($this);
if (is_array($morph)) { if (is_array($morph)) {
list($morphType, $foreignKey) = $morph; list($morphType, $foreignKey) = $morph;
} else { } else {
@ -1992,7 +2142,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$trace = debug_backtrace(false, 2); $trace = debug_backtrace(false, 2);
$morph = Loader::parseName($trace[1]['function']); $morph = Loader::parseName($trace[1]['function']);
} }
$type = $type ?: Loader::parseName($this->name); $type = $type ?: get_class($this);
if (is_array($morph)) { if (is_array($morph)) {
list($morphType, $foreignKey) = $morph; list($morphType, $foreignKey) = $morph;
} else { } else {
@ -2030,7 +2180,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
public function __call($method, $args) public function __call($method, $args)
{ {
$query = $this->db(true, false); $query = $this->db(true, false);
if (method_exists($this, 'scope' . $method)) { if (method_exists($this, 'scope' . $method)) {
// 动态调用命名范围 // 动态调用命名范围
$method = 'scope' . $method; $method = 'scope' . $method;
@ -2046,7 +2195,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
{ {
$model = new static(); $model = new static();
$query = $model->db(); $query = $model->db();
if (method_exists($model, 'scope' . $method)) { if (method_exists($model, 'scope' . $method)) {
// 动态调用命名范围 // 动态调用命名范围
$method = 'scope' . $method; $method = 'scope' . $method;

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -49,6 +49,9 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
'fragment' => '', 'fragment' => '',
]; ];
/** @var mixed simple模式下的下个元素 */
protected $nextItem;
public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = []) public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
{ {
$this->options = array_merge($this->options, $options); $this->options = array_merge($this->options, $options);
@ -65,7 +68,10 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
if ($simple) { if ($simple) {
$this->currentPage = $this->setCurrentPage($currentPage); $this->currentPage = $this->setCurrentPage($currentPage);
$this->hasMore = count($items) > ($this->listRows); $this->hasMore = count($items) > ($this->listRows);
$items = $items->slice(0, $this->listRows); if ($this->hasMore) {
$this->nextItem = $items->slice($this->listRows, 1);
}
$items = $items->slice(0, $this->listRows);
} else { } else {
$this->total = $total; $this->total = $total;
$this->lastPage = (int) ceil($total / $listRows); $this->lastPage = (int) ceil($total / $listRows);
@ -122,7 +128,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
} }
$url = $path; $url = $path;
if (!empty($parameters)) { if (!empty($parameters)) {
$url .= '?' . urldecode(http_build_query($parameters, null, '&')); $url .= '?' . http_build_query($parameters, null, '&');
} }
return $url . $this->buildFragment(); return $url . $this->buildFragment();
} }
@ -135,9 +141,9 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
*/ */
public static function getCurrentPage($varPage = 'page', $default = 1) public static function getCurrentPage($varPage = 'page', $default = 1)
{ {
$page = Request::instance()->request($varPage); $page = (int) Request::instance()->param($varPage);
if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) { if (filter_var($page, FILTER_VALIDATE_INT) !== false && $page >= 1) {
return $page; return $page;
} }
@ -282,8 +288,11 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
public function each(callable $callback) public function each(callable $callback)
{ {
foreach ($this->items as $key => $item) { foreach ($this->items as $key => $item) {
if ($callback($item, $key) === false) { $result = $callback($item, $key);
if (false === $result) {
break; break;
} elseif (!is_object($item)) {
$this->items[$key] = $result;
} }
} }
@ -356,19 +365,24 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
public function toArray() public function toArray()
{ {
try { if ($this->simple) {
$total = $this->total(); return [
} catch (\DomainException $e) { 'per_page' => $this->listRows,
$total = null; 'current_page' => $this->currentPage,
'has_more' => $this->hasMore,
'next_item' => $this->nextItem,
'data' => $this->items->toArray(),
];
} else {
return [
'total' => $this->total,
'per_page' => $this->listRows,
'current_page' => $this->currentPage,
'last_page' => $this->lastPage,
'data' => $this->items->toArray(),
];
} }
return [
'total' => $total,
'per_page' => $this->listRows(),
'current_page' => $this->currentPage(),
'last_page' => $this->lastPage,
'data' => $this->items->toArray(),
];
} }
/** /**
@ -381,7 +395,15 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
public function __call($name, $arguments) public function __call($name, $arguments)
{ {
return call_user_func_array([$this->getCollection(), $name], $arguments); $collection = $this->getCollection();
$result = call_user_func_array([$collection, $name], $arguments);
if ($result === $collection) {
return $this;
}
return $result;
} }
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -59,6 +59,11 @@ class Request
*/ */
protected $routeInfo = []; protected $routeInfo = [];
/**
* @var array 环境变量
*/
protected $env;
/** /**
* @var array 当前调度信息 * @var array 当前调度信息
*/ */
@ -116,6 +121,11 @@ class Request
protected $cache; protected $cache;
// 缓存是否检查 // 缓存是否检查
protected $isCheckCache; protected $isCheckCache;
/**
* 是否合并Param
* @var bool
*/
protected $mergeParam = false;
/** /**
* 构造函数 * 构造函数
@ -150,8 +160,8 @@ class Request
/** /**
* Hook 方法注入 * Hook 方法注入
* @access public * @access public
* @param string|array $method 方法名 * @param string|array $method 方法名
* @param mixed $callback callable * @param mixed $callback callable
* @return void * @return void
*/ */
public static function hook($method, $callback = null) public static function hook($method, $callback = null)
@ -177,16 +187,28 @@ class Request
return self::$instance; return self::$instance;
} }
/**
* 销毁当前请求对象
* @access public
* @return void
*/
public static function destroy()
{
if (!is_null(self::$instance)) {
self::$instance = null;
}
}
/** /**
* 创建一个URL请求 * 创建一个URL请求
* @access public * @access public
* @param string $uri URL地址 * @param string $uri URL地址
* @param string $method 请求类型 * @param string $method 请求类型
* @param array $params 请求参数 * @param array $params 请求参数
* @param array $cookie * @param array $cookie
* @param array $files * @param array $files
* @param array $server * @param array $server
* @param string $content * @param string $content
* @return \think\Request * @return \think\Request
*/ */
public static function create($uri, $method = 'GET', $params = [], $cookie = [], $files = [], $server = [], $content = null) public static function create($uri, $method = 'GET', $params = [], $cookie = [], $files = [], $server = [], $content = null)
@ -227,7 +249,7 @@ class Request
parse_str(html_entity_decode($info['query']), $query); parse_str(html_entity_decode($info['query']), $query);
if (!empty($params)) { if (!empty($params)) {
$params = array_replace($query, $params); $params = array_replace($query, $params);
$queryString = http_build_query($query, '', '&'); $queryString = http_build_query($params, '', '&');
} else { } else {
$params = $query; $params = $query;
$queryString = $info['query']; $queryString = $info['query'];
@ -474,8 +496,8 @@ class Request
/** /**
* 设置资源类型 * 设置资源类型
* @access public * @access public
* @param string|array $type 资源类型名 * @param string|array $type 资源类型名
* @param string $val 资源类型 * @param string $val 资源类型
* @return void * @return void
*/ */
public function mimeType($type, $val = '') public function mimeType($type, $val = '')
@ -490,22 +512,28 @@ class Request
/** /**
* 当前的请求类型 * 当前的请求类型
* @access public * @access public
* @param bool $method true 获取原始请求类型 * @param bool $method true 获取原始请求类型
* @return string * @return string
*/ */
public function method($method = false) public function method($method = false)
{ {
if (true === $method) { if (true === $method) {
// 获取原始请求类型 // 获取原始请求类型
return IS_CLI ? 'GET' : (isset($this->server['REQUEST_METHOD']) ? $this->server['REQUEST_METHOD'] : $_SERVER['REQUEST_METHOD']); return $this->server('REQUEST_METHOD') ?: 'GET';
} elseif (!$this->method) { } elseif (!$this->method) {
if (isset($_POST[Config::get('var_method')])) { if (isset($_POST[Config::get('var_method')])) {
$this->method = strtoupper($_POST[Config::get('var_method')]); $method = strtoupper($_POST[Config::get('var_method')]);
$this->{$this->method}($_POST); if (in_array($method, ['GET', 'POST', 'DELETE', 'PUT', 'PATCH'])) {
$this->method = $method;
$this->{$this->method}($_POST);
} else {
$this->method = 'POST';
}
unset($_POST[Config::get('var_method')]);
} elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
$this->method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']); $this->method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
} else { } else {
$this->method = IS_CLI ? 'GET' : (isset($this->server['REQUEST_METHOD']) ? $this->server['REQUEST_METHOD'] : $_SERVER['REQUEST_METHOD']); $this->method = $this->server('REQUEST_METHOD') ?: 'GET';
} }
} }
return $this->method; return $this->method;
@ -604,14 +632,14 @@ class Request
/** /**
* 获取当前请求的参数 * 获取当前请求的参数
* @access public * @access public
* @param string|array $name 变量名 * @param string|array $name 变量名
* @param mixed $default 默认值 * @param mixed $default 默认值
* @param string|array $filter 过滤方法 * @param string|array $filter 过滤方法
* @return mixed * @return mixed
*/ */
public function param($name = '', $default = null, $filter = '') public function param($name = '', $default = null, $filter = '')
{ {
if (empty($this->param)) { if (empty($this->mergeParam)) {
$method = $this->method(true); $method = $this->method(true);
// 自动获取请求变量 // 自动获取请求变量
switch ($method) { switch ($method) {
@ -627,7 +655,8 @@ class Request
$vars = []; $vars = [];
} }
// 当前请求参数和URL地址中的参数合并 // 当前请求参数和URL地址中的参数合并
$this->param = array_merge($this->get(false), $vars, $this->route(false)); $this->param = array_merge($this->param, $this->get(false), $vars, $this->route(false));
$this->mergeParam = true;
} }
if (true === $name) { if (true === $name) {
// 获取包含文件上传信息的数组 // 获取包含文件上传信息的数组
@ -641,15 +670,16 @@ class Request
/** /**
* 设置获取路由参数 * 设置获取路由参数
* @access public * @access public
* @param string|array $name 变量名 * @param string|array $name 变量名
* @param mixed $default 默认值 * @param mixed $default 默认值
* @param string|array $filter 过滤方法 * @param string|array $filter 过滤方法
* @return mixed * @return mixed
*/ */
public function route($name = '', $default = null, $filter = '') public function route($name = '', $default = null, $filter = '')
{ {
if (is_array($name)) { if (is_array($name)) {
$this->param = []; $this->param = [];
$this->mergeParam = false;
return $this->route = array_merge($this->route, $name); return $this->route = array_merge($this->route, $name);
} }
return $this->input($this->route, $name, $default, $filter); return $this->input($this->route, $name, $default, $filter);
@ -658,9 +688,9 @@ class Request
/** /**
* 设置获取GET参数 * 设置获取GET参数
* @access public * @access public
* @param string|array $name 变量名 * @param string|array $name 变量名
* @param mixed $default 默认值 * @param mixed $default 默认值
* @param string|array $filter 过滤方法 * @param string|array $filter 过滤方法
* @return mixed * @return mixed
*/ */
public function get($name = '', $default = null, $filter = '') public function get($name = '', $default = null, $filter = '')
@ -670,6 +700,7 @@ class Request
} }
if (is_array($name)) { if (is_array($name)) {
$this->param = []; $this->param = [];
$this->mergeParam = false;
return $this->get = array_merge($this->get, $name); return $this->get = array_merge($this->get, $name);
} }
return $this->input($this->get, $name, $default, $filter); return $this->input($this->get, $name, $default, $filter);
@ -678,9 +709,9 @@ class Request
/** /**
* 设置获取POST参数 * 设置获取POST参数
* @access public * @access public
* @param string $name 变量名 * @param string $name 变量名
* @param mixed $default 默认值 * @param mixed $default 默认值
* @param string|array $filter 过滤方法 * @param string|array $filter 过滤方法
* @return mixed * @return mixed
*/ */
public function post($name = '', $default = null, $filter = '') public function post($name = '', $default = null, $filter = '')
@ -695,6 +726,7 @@ class Request
} }
if (is_array($name)) { if (is_array($name)) {
$this->param = []; $this->param = [];
$this->mergeParam = false;
return $this->post = array_merge($this->post, $name); return $this->post = array_merge($this->post, $name);
} }
return $this->input($this->post, $name, $default, $filter); return $this->input($this->post, $name, $default, $filter);
@ -703,9 +735,9 @@ class Request
/** /**
* 设置获取PUT参数 * 设置获取PUT参数
* @access public * @access public
* @param string|array $name 变量名 * @param string|array $name 变量名
* @param mixed $default 默认值 * @param mixed $default 默认值
* @param string|array $filter 过滤方法 * @param string|array $filter 过滤方法
* @return mixed * @return mixed
*/ */
public function put($name = '', $default = null, $filter = '') public function put($name = '', $default = null, $filter = '')
@ -720,6 +752,7 @@ class Request
} }
if (is_array($name)) { if (is_array($name)) {
$this->param = []; $this->param = [];
$this->mergeParam = false;
return $this->put = is_null($this->put) ? $name : array_merge($this->put, $name); return $this->put = is_null($this->put) ? $name : array_merge($this->put, $name);
} }
@ -729,9 +762,9 @@ class Request
/** /**
* 设置获取DELETE参数 * 设置获取DELETE参数
* @access public * @access public
* @param string|array $name 变量名 * @param string|array $name 变量名
* @param mixed $default 默认值 * @param mixed $default 默认值
* @param string|array $filter 过滤方法 * @param string|array $filter 过滤方法
* @return mixed * @return mixed
*/ */
public function delete($name = '', $default = null, $filter = '') public function delete($name = '', $default = null, $filter = '')
@ -742,9 +775,9 @@ class Request
/** /**
* 设置获取PATCH参数 * 设置获取PATCH参数
* @access public * @access public
* @param string|array $name 变量名 * @param string|array $name 变量名
* @param mixed $default 默认值 * @param mixed $default 默认值
* @param string|array $filter 过滤方法 * @param string|array $filter 过滤方法
* @return mixed * @return mixed
*/ */
public function patch($name = '', $default = null, $filter = '') public function patch($name = '', $default = null, $filter = '')
@ -754,9 +787,9 @@ class Request
/** /**
* 获取request变量 * 获取request变量
* @param string $name 数据名称 * @param string $name 数据名称
* @param string $default 默认值 * @param string $default 默认值
* @param string|array $filter 过滤方法 * @param string|array $filter 过滤方法
* @return mixed * @return mixed
*/ */
public function request($name = '', $default = null, $filter = '') public function request($name = '', $default = null, $filter = '')
@ -766,6 +799,7 @@ class Request
} }
if (is_array($name)) { if (is_array($name)) {
$this->param = []; $this->param = [];
$this->mergeParam = false;
return $this->request = array_merge($this->request, $name); return $this->request = array_merge($this->request, $name);
} }
return $this->input($this->request, $name, $default, $filter); return $this->input($this->request, $name, $default, $filter);
@ -774,9 +808,9 @@ class Request
/** /**
* 获取session数据 * 获取session数据
* @access public * @access public
* @param string|array $name 数据名称 * @param string|array $name 数据名称
* @param string $default 默认值 * @param string $default 默认值
* @param string|array $filter 过滤方法 * @param string|array $filter 过滤方法
* @return mixed * @return mixed
*/ */
public function session($name = '', $default = null, $filter = '') public function session($name = '', $default = null, $filter = '')
@ -793,9 +827,9 @@ class Request
/** /**
* 获取cookie参数 * 获取cookie参数
* @access public * @access public
* @param string|array $name 数据名称 * @param string|array $name 数据名称
* @param string $default 默认值 * @param string $default 默认值
* @param string|array $filter 过滤方法 * @param string|array $filter 过滤方法
* @return mixed * @return mixed
*/ */
public function cookie($name = '', $default = null, $filter = '') public function cookie($name = '', $default = null, $filter = '')
@ -826,9 +860,9 @@ class Request
/** /**
* 获取server参数 * 获取server参数
* @access public * @access public
* @param string|array $name 数据名称 * @param string|array $name 数据名称
* @param string $default 默认值 * @param string $default 默认值
* @param string|array $filter 过滤方法 * @param string|array $filter 过滤方法
* @return mixed * @return mixed
*/ */
public function server($name = '', $default = null, $filter = '') public function server($name = '', $default = null, $filter = '')
@ -904,9 +938,9 @@ class Request
/** /**
* 获取环境变量 * 获取环境变量
* @param string|array $name 数据名称 * @param string|array $name 数据名称
* @param string $default 默认值 * @param string $default 默认值
* @param string|array $filter 过滤方法 * @param string|array $filter 过滤方法
* @return mixed * @return mixed
*/ */
public function env($name = '', $default = null, $filter = '') public function env($name = '', $default = null, $filter = '')
@ -923,8 +957,8 @@ class Request
/** /**
* 设置或者获取当前的Header * 设置或者获取当前的Header
* @access public * @access public
* @param string|array $name header名称 * @param string|array $name header名称
* @param string $default 默认值 * @param string $default 默认值
* @return string * @return string
*/ */
public function header($name = '', $default = null) public function header($name = '', $default = null)
@ -962,10 +996,10 @@ class Request
/** /**
* 获取变量 支持过滤和默认值 * 获取变量 支持过滤和默认值
* @param array $data 数据源 * @param array $data 数据源
* @param string|false $name 字段名 * @param string|false $name 字段名
* @param mixed $default 默认值 * @param mixed $default 默认值
* @param string|array $filter 过滤函数 * @param string|array $filter 过滤函数
* @return mixed * @return mixed
*/ */
public function input($data = [], $name = '', $default = null, $filter = '') public function input($data = [], $name = '', $default = null, $filter = '')
@ -1046,9 +1080,9 @@ class Request
/** /**
* 递归过滤给定的值 * 递归过滤给定的值
* @param mixed $value 键值 * @param mixed $value 键值
* @param mixed $key 键名 * @param mixed $key 键名
* @param array $filters 过滤方法+默认值 * @param array $filters 过滤方法+默认值
* @return mixed * @return mixed
*/ */
private function filterValue(&$value, $key, $filters) private function filterValue(&$value, $key, $filters)
@ -1088,7 +1122,7 @@ class Request
public function filterExp(&$value) public function filterExp(&$value)
{ {
// 过滤查询特殊字符 // 过滤查询特殊字符
if (is_string($value) && preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i', $value)) { if (is_string($value) && preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT LIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOT EXISTS|NOTEXISTS|EXISTS|NOT NULL|NOTNULL|NULL|BETWEEN TIME|NOT BETWEEN TIME|NOTBETWEEN TIME|NOTIN|NOT IN|IN)$/i', $value)) {
$value .= ' '; $value .= ' ';
} }
// TODO 其他安全过滤 // TODO 其他安全过滤
@ -1133,9 +1167,9 @@ class Request
/** /**
* 是否存在某个请求参数 * 是否存在某个请求参数
* @access public * @access public
* @param string $name 变量名 * @param string $name 变量名
* @param string $type 变量类型 * @param string $type 变量类型
* @param bool $checkEmpty 是否检测空值 * @param bool $checkEmpty 是否检测空值
* @return mixed * @return mixed
*/ */
public function has($name, $type = 'param', $checkEmpty = false) public function has($name, $type = 'param', $checkEmpty = false)
@ -1159,8 +1193,8 @@ class Request
/** /**
* 获取指定的参数 * 获取指定的参数
* @access public * @access public
* @param string|array $name 变量名 * @param string|array $name 变量名
* @param string $type 变量类型 * @param string $type 变量类型
* @return mixed * @return mixed
*/ */
public function only($name, $type = 'param') public function only($name, $type = 'param')
@ -1181,8 +1215,8 @@ class Request
/** /**
* 排除指定参数获取 * 排除指定参数获取
* @access public * @access public
* @param string|array $name 变量名 * @param string|array $name 变量名
* @param string $type 变量类型 * @param string $type 变量类型
* @return mixed * @return mixed
*/ */
public function except($name, $type = 'param') public function except($name, $type = 'param')
@ -1224,7 +1258,7 @@ class Request
/** /**
* 当前是否Ajax请求 * 当前是否Ajax请求
* @access public * @access public
* @param bool $ajax true 获取原始ajax请求 * @param bool $ajax true 获取原始ajax请求
* @return bool * @return bool
*/ */
public function isAjax($ajax = false) public function isAjax($ajax = false)
@ -1234,14 +1268,16 @@ class Request
if (true === $ajax) { if (true === $ajax) {
return $result; return $result;
} else { } else {
return $this->param(Config::get('var_ajax')) ? true : $result; $result = $this->param(Config::get('var_ajax')) ? true : $result;
$this->mergeParam = false;
return $result;
} }
} }
/** /**
* 当前是否Pjax请求 * 当前是否Pjax请求
* @access public * @access public
* @param bool $pjax true 获取原始pjax请求 * @param bool $pjax true 获取原始pjax请求
* @return bool * @return bool
*/ */
public function isPjax($pjax = false) public function isPjax($pjax = false)
@ -1250,17 +1286,19 @@ class Request
if (true === $pjax) { if (true === $pjax) {
return $result; return $result;
} else { } else {
return $this->param(Config::get('var_pjax')) ? true : $result; $result = $this->param(Config::get('var_pjax')) ? true : $result;
$this->mergeParam = false;
return $result;
} }
} }
/** /**
* 获取客户端IP地址 * 获取客户端IP地址
* @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字 * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
* @param boolean $adv 是否进行高级模式获取(有可能被伪装) * @param boolean $adv 是否进行高级模式获取(有可能被伪装)
* @return mixed * @return mixed
*/ */
public function ip($type = 0, $adv = false) public function ip($type = 0, $adv = true)
{ {
$type = $type ? 1 : 0; $type = $type ? 1 : 0;
static $ip = null; static $ip = null;
@ -1268,7 +1306,11 @@ class Request
return $ip[$type]; return $ip[$type];
} }
if ($adv) { $httpAgentIp = Config::get('http_agent_ip');
if ($httpAgentIp && isset($_SERVER[$httpAgentIp])) {
$ip = $_SERVER[$httpAgentIp];
} elseif ($adv) {
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$pos = array_search('unknown', $arr); $pos = array_search('unknown', $arr);
@ -1333,11 +1375,18 @@ class Request
/** /**
* 当前请求的host * 当前请求的host
* @access public * @access public
* @param bool $strict true 仅仅获取HOST
* @return string * @return string
*/ */
public function host() public function host($strict = false)
{ {
return $this->server('HTTP_HOST'); if (isset($_SERVER['HTTP_X_REAL_HOST'])) {
$host = $_SERVER['HTTP_X_REAL_HOST'];
} else {
$host = $this->server('HTTP_HOST');
}
return true === $strict && strpos($host, ':') ? strstr($host, ':', true) : $host;
} }
/** /**
@ -1407,7 +1456,7 @@ class Request
/** /**
* 设置或者获取当前请求的调度信息 * 设置或者获取当前请求的调度信息
* @access public * @access public
* @param array $dispatch 调度信息 * @param array $dispatch 调度信息
* @return array * @return array
*/ */
public function dispatch($dispatch = null) public function dispatch($dispatch = null)
@ -1458,11 +1507,12 @@ class Request
*/ */
public function action($action = null) public function action($action = null)
{ {
if (!is_null($action)) { if (!is_null($action) && !is_bool($action)) {
$this->action = $action; $this->action = $action;
return $this; return $this;
} else { } else {
return $this->action ?: ''; $name = $this->action ?: '';
return true === $action ? $name : strtolower($name);
} }
} }
@ -1526,13 +1576,19 @@ class Request
/** /**
* 设置当前地址的请求缓存 * 设置当前地址的请求缓存
* @access public * @access public
* @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id * @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id
* @param mixed $expire 缓存有效期 * @param mixed $expire 缓存有效期
* @param array $except 缓存排除 * @param array $except 缓存排除
* @param string $tag 缓存标签
* @return void * @return void
*/ */
public function cache($key, $expire = null, $except = []) public function cache($key, $expire = null, $except = [], $tag = null)
{ {
if (!is_array($except)) {
$tag = $except;
$except = [];
}
if (false !== $key && $this->isGet() && !$this->isCheckCache) { if (false !== $key && $this->isGet() && !$this->isCheckCache) {
// 标记请求缓存检查 // 标记请求缓存检查
$this->isCheckCache = true; $this->isCheckCache = true;
@ -1586,7 +1642,7 @@ class Request
$response = Response::create($content)->header($header); $response = Response::create($content)->header($header);
throw new \think\exception\HttpResponseException($response); throw new \think\exception\HttpResponseException($response);
} else { } else {
$this->cache = [$key, $expire]; $this->cache = [$key, $expire, $tag];
} }
} }
} }
@ -1604,8 +1660,8 @@ class Request
/** /**
* 设置当前请求绑定的对象实例 * 设置当前请求绑定的对象实例
* @access public * @access public
* @param string $name 绑定的对象标识 * @param string|array $name 绑定的对象标识
* @param mixed $obj 绑定的对象实例 * @param mixed $obj 绑定的对象实例
* @return mixed * @return mixed
*/ */
public function bind($name, $obj = null) public function bind($name, $obj = null)

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -69,9 +69,7 @@ class Response
*/ */
public static function create($data = '', $type = '', $code = 200, array $header = [], $options = []) 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(strtolower($type));
$class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst($type);
if (class_exists($class)) { if (class_exists($class)) {
$response = new $class($data, $code, $header, $options); $response = new $class($data, $code, $header, $options);
} else { } else {
@ -106,7 +104,7 @@ class Response
$this->header['Cache-Control'] = 'max-age=' . $cache[1] . ',must-revalidate'; $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['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'; $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]); Cache::tag($cache[2])->set($cache[0], [$data, $this->header], $cache[1]);
} }
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -68,8 +68,8 @@ class Route
/** /**
* 注册变量规则 * 注册变量规则
* @access public * @access public
* @param string|array $name 变量名 * @param string|array $name 变量名
* @param string $rule 变量规则 * @param string $rule 变量规则
* @return void * @return void
*/ */
public static function pattern($name = null, $rule = '') public static function pattern($name = null, $rule = '')
@ -84,10 +84,10 @@ class Route
/** /**
* 注册子域名部署规则 * 注册子域名部署规则
* @access public * @access public
* @param string|array $domain 子域名 * @param string|array $domain 子域名
* @param mixed $rule 路由规则 * @param mixed $rule 路由规则
* @param array $option 路由参数 * @param array $option 路由参数
* @param array $pattern 变量规则 * @param array $pattern 变量规则
* @return void * @return void
*/ */
public static function domain($domain, $rule = '', $option = [], $pattern = []) public static function domain($domain, $rule = '', $option = [], $pattern = [])
@ -121,8 +121,8 @@ class Route
/** /**
* 设置路由绑定 * 设置路由绑定
* @access public * @access public
* @param mixed $bind 绑定信息 * @param mixed $bind 绑定信息
* @param string $type 绑定类型 默认为module 支持 namespace class controller * @param string $type 绑定类型 默认为module 支持 namespace class controller
* @return mixed * @return mixed
*/ */
public static function bind($bind, $type = 'module') public static function bind($bind, $type = 'module')
@ -133,8 +133,8 @@ class Route
/** /**
* 设置或者获取路由标识 * 设置或者获取路由标识
* @access public * @access public
* @param string|array $name 路由命名标识 数组表示批量设置 * @param string|array $name 路由命名标识 数组表示批量设置
* @param array $value 路由地址及变量信息 * @param array $value 路由地址及变量信息
* @return array * @return array
*/ */
public static function name($name = '', $value = null) public static function name($name = '', $value = null)
@ -154,7 +154,7 @@ class Route
/** /**
* 读取路由绑定 * 读取路由绑定
* @access public * @access public
* @param string $type 绑定类型 * @param string $type 绑定类型
* @return mixed * @return mixed
*/ */
public static function getBind($type) public static function getBind($type)
@ -165,8 +165,8 @@ class Route
/** /**
* 导入配置文件的路由规则 * 导入配置文件的路由规则
* @access public * @access public
* @param array $rule 路由规则 * @param array $rule 路由规则
* @param string $type 请求类型 * @param string $type 请求类型
* @return void * @return void
*/ */
public static function import(array $rule, $type = '*') public static function import(array $rule, $type = '*')
@ -222,11 +222,11 @@ class Route
/** /**
* 注册路由规则 * 注册路由规则
* @access public * @access public
* @param string $rule 路由规则 * @param string|array $rule 路由规则
* @param string $route 路由地址 * @param string $route 路由地址
* @param string $type 请求类型 * @param string $type 请求类型
* @param array $option 路由参数 * @param array $option 路由参数
* @param array $pattern 变量规则 * @param array $pattern 变量规则
* @return void * @return void
*/ */
public static function rule($rule, $route = '', $type = '*', $option = [], $pattern = []) public static function rule($rule, $route = '', $type = '*', $option = [], $pattern = [])
@ -255,9 +255,11 @@ class Route
$option1 = array_merge($option, $val[1]); $option1 = array_merge($option, $val[1]);
$pattern1 = array_merge($pattern, isset($val[2]) ? $val[2] : []); $pattern1 = array_merge($pattern, isset($val[2]) ? $val[2] : []);
} else { } else {
$route = $val; $option1 = null;
$pattern1 = null;
$route = $val;
} }
self::setRule($key, $route, $type, isset($option1) ? $option1 : $option, isset($pattern1) ? $pattern1 : $pattern, $group); self::setRule($key, $route, $type, !is_null($option1) ? $option1 : $option, !is_null($pattern1) ? $pattern1 : $pattern, $group);
} }
} else { } else {
self::setRule($rule, $route, $type, $option, $pattern, $group); self::setRule($rule, $route, $type, $option, $pattern, $group);
@ -268,12 +270,12 @@ class Route
/** /**
* 设置路由规则 * 设置路由规则
* @access public * @access public
* @param string $rule 路由规则 * @param string|array $rule 路由规则
* @param string $route 路由地址 * @param string $route 路由地址
* @param string $type 请求类型 * @param string $type 请求类型
* @param array $option 路由参数 * @param array $option 路由参数
* @param array $pattern 变量规则 * @param array $pattern 变量规则
* @param string $group 所属分组 * @param string $group 所属分组
* @return void * @return void
*/ */
protected static function setRule($rule, $route, $type = '*', $option = [], $pattern = [], $group = '') protected static function setRule($rule, $route, $type = '*', $option = [], $pattern = [], $group = '')
@ -333,9 +335,9 @@ class Route
if ('*' == $type) { if ('*' == $type) {
// 注册路由快捷方式 // 注册路由快捷方式
foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) { foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) {
if (self::$domain) { if (self::$domain && !isset(self::$rules['domain'][self::$domain][$method][$rule])) {
self::$rules['domain'][self::$domain][$method][$rule] = true; self::$rules['domain'][self::$domain][$method][$rule] = true;
} else { } elseif (!self::$domain && !isset(self::$rules[$method][$rule])) {
self::$rules[$method][$rule] = true; self::$rules[$method][$rule] = true;
} }
} }
@ -346,7 +348,7 @@ class Route
/** /**
* 设置当前执行的参数信息 * 设置当前执行的参数信息
* @access public * @access public
* @param array $options 参数信息 * @param array $options 参数信息
* @return mixed * @return mixed
*/ */
protected static function setOption($options = []) protected static function setOption($options = [])
@ -367,7 +369,7 @@ class Route
/** /**
* 获取当前的分组信息 * 获取当前的分组信息
* @access public * @access public
* @param string $type 分组信息名称 name option pattern * @param string $type 分组信息名称 name option pattern
* @return mixed * @return mixed
*/ */
public static function getGroup($type) public static function getGroup($type)
@ -382,9 +384,9 @@ class Route
/** /**
* 设置当前的路由分组 * 设置当前的路由分组
* @access public * @access public
* @param string $name 分组名称 * @param string $name 分组名称
* @param array $option 分组路由参数 * @param array $option 分组路由参数
* @param array $pattern 分组变量规则 * @param array $pattern 分组变量规则
* @return void * @return void
*/ */
public static function setGroup($name, $option = [], $pattern = []) public static function setGroup($name, $option = [], $pattern = [])
@ -397,10 +399,10 @@ class Route
/** /**
* 注册路由分组 * 注册路由分组
* @access public * @access public
* @param string|array $name 分组名称或者参数 * @param string|array $name 分组名称或者参数
* @param array|\Closure $routes 路由地址 * @param array|\Closure $routes 路由地址
* @param array $option 路由参数 * @param array $option 路由参数
* @param array $pattern 变量规则 * @param array $pattern 变量规则
* @return void * @return void
*/ */
public static function group($name, $routes, $option = [], $pattern = []) public static function group($name, $routes, $option = [], $pattern = [])
@ -428,7 +430,8 @@ class Route
self::$rules['*'][$name]['pattern'] = $pattern; self::$rules['*'][$name]['pattern'] = $pattern;
} }
} else { } else {
$item = []; $item = [];
$completeMatch = Config::get('route_complete_match');
foreach ($routes as $key => $val) { foreach ($routes as $key => $val) {
if (is_numeric($key)) { if (is_numeric($key)) {
$key = array_shift($val); $key = array_shift($val);
@ -447,6 +450,8 @@ class Route
// 是否完整匹配 // 是否完整匹配
$options['complete_match'] = true; $options['complete_match'] = true;
$key = substr($key, 0, -1); $key = substr($key, 0, -1);
} elseif ($completeMatch) {
$options['complete_match'] = true;
} }
$key = trim($key, '/'); $key = trim($key, '/');
$vars = self::parseVar($key); $vars = self::parseVar($key);
@ -482,10 +487,10 @@ class Route
/** /**
* 注册路由 * 注册路由
* @access public * @access public
* @param string $rule 路由规则 * @param string|array $rule 路由规则
* @param string $route 路由地址 * @param string $route 路由地址
* @param array $option 路由参数 * @param array $option 路由参数
* @param array $pattern 变量规则 * @param array $pattern 变量规则
* @return void * @return void
*/ */
public static function any($rule, $route = '', $option = [], $pattern = []) public static function any($rule, $route = '', $option = [], $pattern = [])
@ -496,10 +501,10 @@ class Route
/** /**
* 注册GET路由 * 注册GET路由
* @access public * @access public
* @param string $rule 路由规则 * @param string|array $rule 路由规则
* @param string $route 路由地址 * @param string $route 路由地址
* @param array $option 路由参数 * @param array $option 路由参数
* @param array $pattern 变量规则 * @param array $pattern 变量规则
* @return void * @return void
*/ */
public static function get($rule, $route = '', $option = [], $pattern = []) public static function get($rule, $route = '', $option = [], $pattern = [])
@ -510,10 +515,10 @@ class Route
/** /**
* 注册POST路由 * 注册POST路由
* @access public * @access public
* @param string $rule 路由规则 * @param string|array $rule 路由规则
* @param string $route 路由地址 * @param string $route 路由地址
* @param array $option 路由参数 * @param array $option 路由参数
* @param array $pattern 变量规则 * @param array $pattern 变量规则
* @return void * @return void
*/ */
public static function post($rule, $route = '', $option = [], $pattern = []) public static function post($rule, $route = '', $option = [], $pattern = [])
@ -524,10 +529,10 @@ class Route
/** /**
* 注册PUT路由 * 注册PUT路由
* @access public * @access public
* @param string $rule 路由规则 * @param string|array $rule 路由规则
* @param string $route 路由地址 * @param string $route 路由地址
* @param array $option 路由参数 * @param array $option 路由参数
* @param array $pattern 变量规则 * @param array $pattern 变量规则
* @return void * @return void
*/ */
public static function put($rule, $route = '', $option = [], $pattern = []) public static function put($rule, $route = '', $option = [], $pattern = [])
@ -538,10 +543,10 @@ class Route
/** /**
* 注册DELETE路由 * 注册DELETE路由
* @access public * @access public
* @param string $rule 路由规则 * @param string|array $rule 路由规则
* @param string $route 路由地址 * @param string $route 路由地址
* @param array $option 路由参数 * @param array $option 路由参数
* @param array $pattern 变量规则 * @param array $pattern 变量规则
* @return void * @return void
*/ */
public static function delete($rule, $route = '', $option = [], $pattern = []) public static function delete($rule, $route = '', $option = [], $pattern = [])
@ -552,10 +557,10 @@ class Route
/** /**
* 注册PATCH路由 * 注册PATCH路由
* @access public * @access public
* @param string $rule 路由规则 * @param string|array $rule 路由规则
* @param string $route 路由地址 * @param string $route 路由地址
* @param array $option 路由参数 * @param array $option 路由参数
* @param array $pattern 变量规则 * @param array $pattern 变量规则
* @return void * @return void
*/ */
public static function patch($rule, $route = '', $option = [], $pattern = []) public static function patch($rule, $route = '', $option = [], $pattern = [])
@ -566,10 +571,10 @@ class Route
/** /**
* 注册资源路由 * 注册资源路由
* @access public * @access public
* @param string $rule 路由规则 * @param string|array $rule 路由规则
* @param string $route 路由地址 * @param string $route 路由地址
* @param array $option 路由参数 * @param array $option 路由参数
* @param array $pattern 变量规则 * @param array $pattern 变量规则
* @return void * @return void
*/ */
public static function resource($rule, $route = '', $option = [], $pattern = []) public static function resource($rule, $route = '', $option = [], $pattern = [])
@ -613,10 +618,10 @@ class Route
/** /**
* 注册控制器路由 操作方法对应不同的请求后缀 * 注册控制器路由 操作方法对应不同的请求后缀
* @access public * @access public
* @param string $rule 路由规则 * @param string $rule 路由规则
* @param string $route 路由地址 * @param string $route 路由地址
* @param array $option 路由参数 * @param array $option 路由参数
* @param array $pattern 变量规则 * @param array $pattern 变量规则
* @return void * @return void
*/ */
public static function controller($rule, $route = '', $option = [], $pattern = []) public static function controller($rule, $route = '', $option = [], $pattern = [])
@ -629,9 +634,9 @@ class Route
/** /**
* 注册别名路由 * 注册别名路由
* @access public * @access public
* @param string|array $rule 路由别名 * @param string|array $rule 路由别名
* @param string $route 路由地址 * @param string $route 路由地址
* @param array $option 路由参数 * @param array $option 路由参数
* @return void * @return void
*/ */
public static function alias($rule = null, $route = '', $option = []) public static function alias($rule = null, $route = '', $option = [])
@ -646,8 +651,8 @@ class Route
/** /**
* 设置不同请求类型下面的方法前缀 * 设置不同请求类型下面的方法前缀
* @access public * @access public
* @param string $method 请求类型 * @param string $method 请求类型
* @param string $prefix 类型前缀 * @param string $prefix 类型前缀
* @return void * @return void
*/ */
public static function setMethodPrefix($method, $prefix = '') public static function setMethodPrefix($method, $prefix = '')
@ -662,8 +667,8 @@ class Route
/** /**
* rest方法定义和修改 * rest方法定义和修改
* @access public * @access public
* @param string $name 方法名称 * @param string|array $name 方法名称
* @param array|bool $resource 资源 * @param array|bool $resource 资源
* @return void * @return void
*/ */
public static function rest($name, $resource = []) public static function rest($name, $resource = [])
@ -678,9 +683,9 @@ class Route
/** /**
* 注册未匹配路由规则后的处理 * 注册未匹配路由规则后的处理
* @access public * @access public
* @param string $route 路由地址 * @param string $route 路由地址
* @param string $method 请求类型 * @param string $method 请求类型
* @param array $option 路由参数 * @param array $option 路由参数
* @return void * @return void
*/ */
public static function miss($route, $method = '*', $option = []) public static function miss($route, $method = '*', $option = [])
@ -691,7 +696,7 @@ class Route
/** /**
* 注册一个自动解析的URL路由 * 注册一个自动解析的URL路由
* @access public * @access public
* @param string $route 路由地址 * @param string $route 路由地址
* @return void * @return void
*/ */
public static function auto($route) public static function auto($route)
@ -721,9 +726,9 @@ class Route
/** /**
* 检测子域名部署 * 检测子域名部署
* @access public * @access public
* @param Request $request Request请求对象 * @param Request $request Request请求对象
* @param array $currentRules 当前路由规则 * @param array $currentRules 当前路由规则
* @param string $method 请求类型 * @param string $method 请求类型
* @return void * @return void
*/ */
public static function checkDomain($request, &$currentRules, $method = 'get') public static function checkDomain($request, &$currentRules, $method = 'get')
@ -732,7 +737,7 @@ class Route
$rules = self::$rules['domain']; $rules = self::$rules['domain'];
// 开启子域名部署 支持二级和三级域名 // 开启子域名部署 支持二级和三级域名
if (!empty($rules)) { if (!empty($rules)) {
$host = $request->host(); $host = $request->host(true);
if (isset($rules[$host])) { if (isset($rules[$host])) {
// 完整域名或者IP配置 // 完整域名或者IP配置
$item = $rules[$host]; $item = $rules[$host];
@ -822,14 +827,23 @@ class Route
/** /**
* 检测URL路由 * 检测URL路由
* @access public * @access public
* @param Request $request Request请求对象 * @param Request $request Request请求对象
* @param string $url URL地址 * @param string $url URL地址
* @param string $depr URL分隔符 * @param string $depr URL分隔符
* @param bool $checkDomain 是否检测域名规则 * @param bool $checkDomain 是否检测域名规则
* @return false|array * @return false|array
*/ */
public static function check($request, $url, $depr = '/', $checkDomain = false) public static function check($request, $url, $depr = '/', $checkDomain = false)
{ {
//检查解析缓存
if (!App::$debug && Config::get('route_check_cache')) {
$key = self::getCheckCacheKey($request);
if (Cache::has($key)) {
list($rule, $route, $pathinfo, $option, $matches) = Cache::get($key);
return self::parseRule($rule, $route, $pathinfo, $option, $matches, true);
}
}
// 分隔符替换 确保路由定义使用统一的分隔符 // 分隔符替换 确保路由定义使用统一的分隔符
$url = str_replace($depr, '|', $url); $url = str_replace($depr, '|', $url);
@ -883,12 +897,12 @@ class Route
/** /**
* 检测路由规则 * 检测路由规则
* @access private * @access private
* @param Request $request * @param Request $request
* @param array $rules 路由规则 * @param array $rules 路由规则
* @param string $url URL地址 * @param string $url URL地址
* @param string $depr URL分割符 * @param string $depr URL分割符
* @param string $group 路由分组名 * @param string $group 路由分组名
* @param array $options 路由参数(分组) * @param array $options 路由参数(分组)
* @return mixed * @return mixed
*/ */
private static function checkRoute($request, $rules, $url, $depr = '/', $group = '', $options = []) private static function checkRoute($request, $rules, $url, $depr = '/', $group = '', $options = [])
@ -966,9 +980,9 @@ class Route
/** /**
* 检测路由别名 * 检测路由别名
* @access private * @access private
* @param Request $request * @param Request $request
* @param string $url URL地址 * @param string $url URL地址
* @param string $depr URL分隔符 * @param string $depr URL分隔符
* @return mixed * @return mixed
*/ */
private static function checkRouteAlias($request, $url, $depr) private static function checkRouteAlias($request, $url, $depr)
@ -1013,9 +1027,9 @@ class Route
/** /**
* 检测URL绑定 * 检测URL绑定
* @access private * @access private
* @param string $url URL地址 * @param string $url URL地址
* @param array $rules 路由规则 * @param array $rules 路由规则
* @param string $depr URL分隔符 * @param string $depr URL分隔符
* @return mixed * @return mixed
*/ */
private static function checkUrlBind(&$url, &$rules, $depr = '/') private static function checkUrlBind(&$url, &$rules, $depr = '/')
@ -1044,9 +1058,9 @@ class Route
/** /**
* 绑定到类 * 绑定到类
* @access public * @access public
* @param string $url URL地址 * @param string $url URL地址
* @param string $class 类名(带命名空间) * @param string $class 类名(带命名空间)
* @param string $depr URL分隔符 * @param string $depr URL分隔符
* @return array * @return array
*/ */
public static function bindToClass($url, $class, $depr = '/') public static function bindToClass($url, $class, $depr = '/')
@ -1063,9 +1077,9 @@ class Route
/** /**
* 绑定到命名空间 * 绑定到命名空间
* @access public * @access public
* @param string $url URL地址 * @param string $url URL地址
* @param string $namespace 命名空间 * @param string $namespace 命名空间
* @param string $depr URL分隔符 * @param string $depr URL分隔符
* @return array * @return array
*/ */
public static function bindToNamespace($url, $namespace, $depr = '/') public static function bindToNamespace($url, $namespace, $depr = '/')
@ -1083,9 +1097,9 @@ class Route
/** /**
* 绑定到控制器类 * 绑定到控制器类
* @access public * @access public
* @param string $url URL地址 * @param string $url URL地址
* @param string $controller 控制器名 (支持带模块名 index/user * @param string $controller 控制器名 (支持带模块名 index/user
* @param string $depr URL分隔符 * @param string $depr URL分隔符
* @return array * @return array
*/ */
public static function bindToController($url, $controller, $depr = '/') public static function bindToController($url, $controller, $depr = '/')
@ -1102,9 +1116,9 @@ class Route
/** /**
* 绑定到模块/控制器 * 绑定到模块/控制器
* @access public * @access public
* @param string $url URL地址 * @param string $url URL地址
* @param string $controller 控制器类名(带命名空间) * @param string $controller 控制器类名(带命名空间)
* @param string $depr URL分隔符 * @param string $depr URL分隔符
* @return array * @return array
*/ */
public static function bindToModule($url, $controller, $depr = '/') public static function bindToModule($url, $controller, $depr = '/')
@ -1121,8 +1135,8 @@ class Route
/** /**
* 路由参数有效性检查 * 路由参数有效性检查
* @access private * @access private
* @param array $option 路由参数 * @param array $option 路由参数
* @param Request $request Request对象 * @param Request $request Request对象
* @return bool * @return bool
*/ */
private static function checkOption($option, $request) private static function checkOption($option, $request)
@ -1148,12 +1162,12 @@ class Route
/** /**
* 检测路由规则 * 检测路由规则
* @access private * @access private
* @param string $rule 路由规则 * @param string $rule 路由规则
* @param string $route 路由地址 * @param string $route 路由地址
* @param string $url URL地址 * @param string $url URL地址
* @param array $pattern 变量规则 * @param array $pattern 变量规则
* @param array $option 路由参数 * @param array $option 路由参数
* @param string $depr URL分隔符全局 * @param string $depr URL分隔符全局
* @return array|false * @return array|false
*/ */
private static function checkRule($rule, $route, $url, $pattern, $option, $depr) private static function checkRule($rule, $route, $url, $pattern, $option, $depr)
@ -1195,9 +1209,9 @@ class Route
/** /**
* 解析模块的URL地址 [模块/控制器/操作?]参数1=值1&参数2=值2... * 解析模块的URL地址 [模块/控制器/操作?]参数1=值1&参数2=值2...
* @access public * @access public
* @param string $url URL地址 * @param string $url URL地址
* @param string $depr URL分隔符 * @param string $depr URL分隔符
* @param bool $autoSearch 是否自动深度搜索控制器 * @param bool $autoSearch 是否自动深度搜索控制器
* @return array * @return array
*/ */
public static function parseUrl($url, $depr = '/', $autoSearch = false) public static function parseUrl($url, $depr = '/', $autoSearch = false)
@ -1264,7 +1278,7 @@ class Route
/** /**
* 解析URL的pathinfo参数和变量 * 解析URL的pathinfo参数和变量
* @access private * @access private
* @param string $url URL地址 * @param string $url URL地址
* @return array * @return array
*/ */
private static function parseUrlPath($url) private static function parseUrlPath($url)
@ -1290,9 +1304,9 @@ class Route
/** /**
* 检测URL和规则路由是否匹配 * 检测URL和规则路由是否匹配
* @access private * @access private
* @param string $url URL地址 * @param string $url URL地址
* @param string $rule 路由规则 * @param string $rule 路由规则
* @param array $pattern 变量规则 * @param array $pattern 变量规则
* @return array|false * @return array|false
*/ */
private static function match($url, $rule, $pattern) private static function match($url, $rule, $pattern)
@ -1365,16 +1379,28 @@ class Route
/** /**
* 解析规则路由 * 解析规则路由
* @access private * @access private
* @param string $rule 路由规则 * @param string $rule 路由规则
* @param string $route 路由地址 * @param string $route 路由地址
* @param string $pathinfo URL地址 * @param string $pathinfo URL地址
* @param array $option 路由参数 * @param array $option 路由参数
* @param array $matches 匹配的变量 * @param array $matches 匹配的变量
* @param bool $fromCache 通过缓存解析
* @return array * @return array
*/ */
private static function parseRule($rule, $route, $pathinfo, $option = [], $matches = []) private static function parseRule($rule, $route, $pathinfo, $option = [], $matches = [], $fromCache = false)
{ {
$request = Request::instance(); $request = Request::instance();
//保存解析缓存
if (Config::get('route_check_cache') && !$fromCache) {
try {
$key = self::getCheckCacheKey($request);
Cache::tag('route_check')->set($key, [$rule, $route, $pathinfo, $option, $matches]);
} catch (\Exception $e) {
}
}
// 解析路由规则 // 解析路由规则
if ($rule) { if ($rule) {
$rule = explode('/', $rule); $rule = explode('/', $rule);
@ -1501,18 +1527,19 @@ class Route
App::$modulePath = APP_PATH . (Config::get('app_multi_module') ? $request->module() . DS : ''); App::$modulePath = APP_PATH . (Config::get('app_multi_module') ? $request->module() . DS : '');
} else { } else {
// 路由到模块/控制器/操作 // 路由到模块/控制器/操作
$result = self::parseModule($route); $result = self::parseModule($route, isset($option['convert']) ? $option['convert'] : false);
} }
// 开启请求缓存 // 开启请求缓存
if ($request->isGet() && isset($option['cache'])) { if ($request->isGet() && isset($option['cache'])) {
$cache = $option['cache']; $cache = $option['cache'];
if (is_array($cache)) { if (is_array($cache)) {
list($key, $expire) = $cache; list($key, $expire, $tag) = array_pad($cache, 3, null);
} else { } else {
$key = str_replace('|', '/', $pathinfo); $key = str_replace('|', '/', $pathinfo);
$expire = $cache; $expire = $cache;
$tag = null;
} }
$request->cache($key, $expire); $request->cache($key, $expire, $tag);
} }
return $result; return $result;
} }
@ -1520,10 +1547,11 @@ class Route
/** /**
* 解析URL地址为 模块/控制器/操作 * 解析URL地址为 模块/控制器/操作
* @access private * @access private
* @param string $url URL地址 * @param string $url URL地址
* @param bool $convert 是否自动转换URL地址
* @return array * @return array
*/ */
private static function parseModule($url) private static function parseModule($url, $convert = false)
{ {
list($path, $var) = self::parseUrlPath($url); list($path, $var) = self::parseUrlPath($url);
$action = array_pop($path); $action = array_pop($path);
@ -1537,14 +1565,14 @@ class Route
// 设置当前请求的路由变量 // 设置当前请求的路由变量
Request::instance()->route($var); Request::instance()->route($var);
// 路由到模块/控制器/操作 // 路由到模块/控制器/操作
return ['type' => 'module', 'module' => [$module, $controller, $action], 'convert' => false]; return ['type' => 'module', 'module' => [$module, $controller, $action], 'convert' => $convert];
} }
/** /**
* 解析URL地址中的参数Request对象 * 解析URL地址中的参数Request对象
* @access private * @access private
* @param string $rule 路由规则 * @param string $url 路由规则
* @param array $var 变量 * @param array $var 变量
* @return void * @return void
*/ */
private static function parseUrlParams($url, &$var = []) private static function parseUrlParams($url, &$var = [])
@ -1594,4 +1622,24 @@ class Route
} }
return $var; return $var;
} }
/**
* 获取路由解析缓存的key
* @param Request $request
* @return string
*/
private static function getCheckCacheKey(Request $request)
{
static $key;
if (empty($key)) {
if ($callback = Config::get('route_check_cache_key')) {
$key = call_user_func($callback, $request);
} else {
$key = "{$request->host(true)}|{$request->method()}|{$request->path()}";
}
}
return $key;
}
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -25,6 +25,7 @@ class Session
*/ */
public static function prefix($prefix = '') public static function prefix($prefix = '')
{ {
empty(self::$init) && self::boot();
if (empty($prefix) && null !== $prefix) { if (empty($prefix) && null !== $prefix) {
return self::$prefix; return self::$prefix;
} else { } else {
@ -56,7 +57,7 @@ class Session
$isDoStart = true; $isDoStart = true;
} }
if (isset($config['prefix']) && (self::$prefix === '' || self::$prefix === null)) { if (isset($config['prefix']) && ('' === self::$prefix || null === self::$prefix)) {
self::$prefix = $config['prefix']; self::$prefix = $config['prefix'];
} }
if (isset($config['var_session_id']) && isset($_REQUEST[$config['var_session_id']])) { if (isset($config['var_session_id']) && isset($_REQUEST[$config['var_session_id']])) {
@ -347,7 +348,7 @@ class Session
* @param bool $delete 是否删除关联会话文件 * @param bool $delete 是否删除关联会话文件
* @return void * @return void
*/ */
private static function regenerate($delete = false) public static function regenerate($delete = false)
{ {
session_regenerate_id($delete); session_regenerate_id($delete);
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -12,6 +12,7 @@
namespace think; namespace think;
use think\exception\TemplateNotFoundException; use think\exception\TemplateNotFoundException;
use think\template\TagLib;
/** /**
* ThinkPHP分离出来的模板引擎 * ThinkPHP分离出来的模板引擎
@ -59,15 +60,20 @@ class Template
/** /**
* 构造函数 * 构造函数
* @access public * @access public
* @param array $config
*/ */
public function __construct(array $config = []) public function __construct(array $config = [])
{ {
$this->config['cache_path'] = TEMP_PATH; $this->config['cache_path'] = TEMP_PATH;
$this->config = array_merge($this->config, $config); $this->config = array_merge($this->config, $config);
$this->config['taglib_begin'] = $this->stripPreg($this->config['taglib_begin']);
$this->config['taglib_end'] = $this->stripPreg($this->config['taglib_end']); $this->config['taglib_begin_origin'] = $this->config['taglib_begin'];
$this->config['tpl_begin'] = $this->stripPreg($this->config['tpl_begin']); $this->config['taglib_end_origin'] = $this->config['taglib_end'];
$this->config['tpl_end'] = $this->stripPreg($this->config['tpl_end']);
$this->config['taglib_begin'] = preg_quote($this->config['taglib_begin'], '/');
$this->config['taglib_end'] = preg_quote($this->config['taglib_end'], '/');
$this->config['tpl_begin'] = preg_quote($this->config['tpl_begin'], '/');
$this->config['tpl_end'] = preg_quote($this->config['tpl_end'], '/');
// 初始化模板编译存储器 // 初始化模板编译存储器
$type = $this->config['compile_type'] ? $this->config['compile_type'] : 'File'; $type = $this->config['compile_type'] ? $this->config['compile_type'] : 'File';
@ -75,20 +81,6 @@ class Template
$this->storage = new $class(); $this->storage = new $class();
} }
/**
* 字符串替换 避免正则混淆
* @access private
* @param string $str
* @return string
*/
private function stripPreg($str)
{
return str_replace(
['{', '}', '(', ')', '|', '[', ']', '-', '+', '*', '.', '^', '?'],
['\{', '\}', '\(', '\)', '\|', '\[', '\]', '\-', '\+', '\*', '\.', '\^', '\?'],
$str);
}
/** /**
* 模板变量赋值 * 模板变量赋值
* @access public * @access public
@ -120,7 +112,7 @@ class Template
* 模板引擎配置项 * 模板引擎配置项
* @access public * @access public
* @param array|string $config * @param array|string $config
* @return void|array * @return string|void|array
*/ */
public function config($config) public function config($config)
{ {
@ -183,7 +175,7 @@ class Template
} }
$template = $this->parseTemplateFile($template); $template = $this->parseTemplateFile($template);
if ($template) { if ($template) {
$cacheFile = $this->config['cache_path'] . $this->config['cache_prefix'] . md5($template) . '.' . ltrim($this->config['cache_suffix'], '.'); $cacheFile = $this->config['cache_path'] . $this->config['cache_prefix'] . md5($this->config['layout_name'] . $template) . '.' . ltrim($this->config['cache_suffix'], '.');
if (!$this->checkCache($cacheFile)) { if (!$this->checkCache($cacheFile)) {
// 缓存无效 重新模板编译 // 缓存无效 重新模板编译
$content = file_get_contents($template); $content = file_get_contents($template);
@ -234,7 +226,7 @@ class Template
* @access public * @access public
* @param mixed $name 布局模板名称 false 则关闭布局 * @param mixed $name 布局模板名称 false 则关闭布局
* @param string $replace 布局模板内容替换标识 * @param string $replace 布局模板内容替换标识
* @return object * @return Template
*/ */
public function layout($name, $replace = '') public function layout($name, $replace = '')
{ {
@ -688,6 +680,7 @@ class Template
} else { } else {
$className = '\\think\\template\\taglib\\' . ucwords($tagLib); $className = '\\think\\template\\taglib\\' . ucwords($tagLib);
} }
/** @var Taglib $tLib */
$tLib = new $className($this); $tLib = new $className($this);
$tLib->parseTag($content, $hide ? '' : $tagLib); $tLib->parseTag($content, $hide ? '' : $tagLib);
return; return;
@ -763,31 +756,26 @@ class Template
} else { } else {
if (isset($array[1])) { if (isset($array[1])) {
$this->parseVar($array[2]); $this->parseVar($array[2]);
$_name = ' && ' . $name . $array[1] . $array[2]; $express = $name . $array[1] . $array[2];
} else { } else {
$_name = ''; $express = false;
} }
// $name为数组 // $name为数组
switch ($first) { switch ($first) {
case '?': case '?':
// {$varname??'xxx'} $varname有定义则输出$varname,否则输出xxx // {$varname??'xxx'} $varname有定义则输出$varname,否则输出xxx
$str = '<?php echo isset(' . $name . ')' . $_name . ' ? ' . $name . ' : ' . substr($str, 1) . '; ?>'; $str = '<?php echo ' . ($express ?: 'isset(' . $name . ')') . '?' . $name . ':' . substr($str, 1) . '; ?>';
break; break;
case '=': case '=':
// {$varname?='xxx'} $varname为真时才输出xxx // {$varname?='xxx'} $varname为真时才输出xxx
$str = '<?php if(!empty(' . $name . ')' . $_name . ') echo ' . substr($str, 1) . '; ?>'; $str = '<?php if(' . ($express ?: '!empty(' . $name . ')') . ') echo ' . substr($str, 1) . '; ?>';
break; break;
case ':': case ':':
// {$varname?:'xxx'} $varname为真时输出$varname,否则输出xxx // {$varname?:'xxx'} $varname为真时输出$varname,否则输出xxx
$str = '<?php echo !empty(' . $name . ')' . $_name . '?' . $name . $str . '; ?>'; $str = '<?php echo ' . ($express ?: '!empty(' . $name . ')') . '?' . $name . $str . '; ?>';
break; break;
default: default:
if (strpos($str, ':')) { $str = '<?php echo ' . ($express ?: '!empty(' . $name . ')') . '?' . $str . '; ?>';
// {$varname ? 'a' : 'b'} $varname为真时输出a,否则输出b
$str = '<?php echo !empty(' . $name . ')' . $_name . '?' . $str . '; ?>';
} else {
$str = '<?php echo ' . $_name . '?' . $str . '; ?>';
}
} }
} }
} else { } else {
@ -1075,7 +1063,7 @@ class Template
} else { } else {
$path = isset($module) ? APP_PATH . $module . DS . basename($this->config['view_path']) . DS : $this->config['view_path']; $path = isset($module) ? APP_PATH . $module . DS . basename($this->config['view_path']) . DS : $this->config['view_path'];
} }
$template = $path . $template . '.' . ltrim($this->config['view_suffix'], '.'); $template = realpath($path . $template . '.' . ltrim($this->config['view_suffix'], '.'));
} }
if (is_file($template)) { if (is_file($template)) {

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -118,7 +118,7 @@ class Url
$type = Route::getBind('type'); $type = Route::getBind('type');
if ($type) { if ($type) {
$bind = Route::getBind($type); $bind = Route::getBind($type);
if (0 === strpos($url, $bind)) { if ($bind && 0 === strpos($url, $bind)) {
$url = substr($url, strlen($bind) + 1); $url = substr($url, strlen($bind) + 1);
} }
} }
@ -135,7 +135,7 @@ class Url
if (!empty($vars)) { if (!empty($vars)) {
// 添加参数 // 添加参数
if (Config::get('url_common_param')) { if (Config::get('url_common_param')) {
$vars = urldecode(http_build_query($vars)); $vars = http_build_query($vars);
$url .= $suffix . '?' . $vars . $anchor; $url .= $suffix . '?' . $vars . $anchor;
} else { } else {
$paramType = Config::get('url_param_type'); $paramType = Config::get('url_param_type');
@ -210,17 +210,21 @@ class Url
} }
$module = $module ? $module . '/' : ''; $module = $module ? $module . '/' : '';
$controller = Loader::parseName($request->controller()); $controller = $request->controller();
if ('' == $url) { if ('' == $url) {
// 空字符串输出当前的 模块/控制器/操作 // 空字符串输出当前的 模块/控制器/操作
$url = $module . $controller . '/' . $request->action(); $action = $request->action();
} else { } else {
$path = explode('/', $url); $path = explode('/', $url);
$action = Config::get('url_convert') ? strtolower(array_pop($path)) : array_pop($path); $action = array_pop($path);
$controller = empty($path) ? $controller : (Config::get('url_convert') ? Loader::parseName(array_pop($path)) : array_pop($path)); $controller = empty($path) ? $controller : array_pop($path);
$module = empty($path) ? $module : array_pop($path) . '/'; $module = empty($path) ? $module : array_pop($path) . '/';
$url = $module . $controller . '/' . $action;
} }
if (Config::get('url_convert')) {
$action = strtolower($action);
$controller = Loader::parseName($controller);
}
$url = $module . $controller . '/' . $action;
} }
return $url; return $url;
} }
@ -272,7 +276,7 @@ class Url
$domain .= '.' . $rootDomain; $domain .= '.' . $rootDomain;
} }
} }
if (false !== strpos($domain, ':')) { if (false !== strpos($domain, '://')) {
$scheme = ''; $scheme = '';
} else { } else {
$scheme = $request->isSsl() || Config::get('is_https') ? 'https://' : 'http://'; $scheme = $request->isSsl() || Config::get('is_https') ? 'https://' : 'http://';
@ -298,11 +302,12 @@ class Url
foreach ($rule as $item) { foreach ($rule as $item) {
list($url, $pattern, $domain, $suffix) = $item; list($url, $pattern, $domain, $suffix) = $item;
if (empty($pattern)) { if (empty($pattern)) {
return [$url, $domain, $suffix]; return [rtrim($url, '$'), $domain, $suffix];
} }
$type = Config::get('url_common_param');
foreach ($pattern as $key => $val) { foreach ($pattern as $key => $val) {
if (isset($vars[$key])) { if (isset($vars[$key])) {
$url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key . '', '<' . $key . '>'], urlencode($vars[$key]), $url); $url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key . '', '<' . $key . '>'], $type ? $vars[$key] : urlencode($vars[$key]), $url);
unset($vars[$key]); unset($vars[$key]);
$result = [$url, $domain, $suffix]; $result = [$url, $domain, $suffix];
} elseif (2 == $val) { } elseif (2 == $val) {

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -36,54 +36,56 @@ class Validate
// 验证规则默认提示信息 // 验证规则默认提示信息
protected static $typeMsg = [ protected static $typeMsg = [
'require' => ':attribute不能为空', 'require' => ':attribute require',
'number' => ':attribute必须是数字', 'number' => ':attribute must be numeric',
'float' => ':attribute必须是浮点数', 'integer' => ':attribute must be integer',
'boolean' => ':attribute必须是布尔值', 'float' => ':attribute must be float',
'email' => ':attribute格式不符', 'boolean' => ':attribute must be bool',
'array' => ':attribute必须是数组', 'email' => ':attribute not a valid email address',
'accepted' => ':attribute必须是yes、on或者1', 'array' => ':attribute must be a array',
'date' => ':attribute格式不符合', 'accepted' => ':attribute must be yes,on or 1',
'file' => ':attribute不是有效的上传文件', 'date' => ':attribute not a valid datetime',
'image' => ':attribute不是有效的图像文件', 'file' => ':attribute not a valid file',
'alpha' => ':attribute只能是字母', 'image' => ':attribute not a valid image',
'alphaNum' => ':attribute只能是字母和数字', 'alpha' => ':attribute must be alpha',
'alphaDash' => ':attribute只能是字母、数字和下划线_及破折号-', 'alphaNum' => ':attribute must be alpha-numeric',
'activeUrl' => ':attribute不是有效的域名或者IP', 'alphaDash' => ':attribute must be alpha-numeric, dash, underscore',
'chs' => ':attribute只能是汉字', 'activeUrl' => ':attribute not a valid domain or ip',
'chsAlpha' => ':attribute只能是汉字、字母', 'chs' => ':attribute must be chinese',
'chsAlphaNum' => ':attribute只能是汉字、字母和数字', 'chsAlpha' => ':attribute must be chinese or alpha',
'chsDash' => ':attribute只能是汉字、字母、数字和下划线_及破折号-', 'chsAlphaNum' => ':attribute must be chinese,alpha-numeric',
'url' => ':attribute不是有效的URL地址', 'chsDash' => ':attribute must be chinese,alpha-numeric,underscore, dash',
'ip' => ':attribute不是有效的IP地址', 'url' => ':attribute not a valid url',
'dateFormat' => ':attribute必须使用日期格式 :rule', 'ip' => ':attribute not a valid ip',
'in' => ':attribute必须在 :rule 范围内', 'dateFormat' => ':attribute must be dateFormat of :rule',
'notIn' => ':attribute不能在 :rule 范围内', 'in' => ':attribute must be in :rule',
'between' => ':attribute只能在 :1 - :2 之间', 'notIn' => ':attribute be notin :rule',
'notBetween' => ':attribute不能在 :1 - :2 之间', 'between' => ':attribute must between :1 - :2',
'length' => ':attribute长度不符合要求 :rule', 'notBetween' => ':attribute not between :1 - :2',
'max' => ':attribute长度不能超过 :rule', 'length' => 'size of :attribute must be :rule',
'min' => ':attribute长度不能小于 :rule', 'max' => 'max size of :attribute must be :rule',
'after' => ':attribute日期不能小于 :rule', 'min' => 'min size of :attribute must be :rule',
'before' => ':attribute日期不能超过 :rule', 'after' => ':attribute cannot be less than :rule',
'expire' => '不在有效期内 :rule', 'before' => ':attribute cannot exceed :rule',
'allowIp' => '不允许的IP访问', 'afterWith' => ':attribute cannot be less than :rule',
'denyIp' => '禁止的IP访问', 'beforeWith' => ':attribute cannot exceed :rule',
'confirm' => ':attribute和确认字段:2不一致', 'expire' => ':attribute not within :rule',
'different' => ':attribute和比较字段:2不能相同', 'allowIp' => 'access IP is not allowed',
'egt' => ':attribute必须大于等于 :rule', 'denyIp' => 'access IP denied',
'gt' => ':attribute必须大于 :rule', 'confirm' => ':attribute out of accord with :2',
'elt' => ':attribute必须小于等于 :rule', 'different' => ':attribute cannot be same with :2',
'lt' => ':attribute必须小于 :rule', 'egt' => ':attribute must greater than or equal :rule',
'eq' => ':attribute必须等于 :rule', 'gt' => ':attribute must greater than :rule',
'unique' => ':attribute已存在', 'elt' => ':attribute must less than or equal :rule',
'regex' => ':attribute不符合指定规则', 'lt' => ':attribute must less than :rule',
'method' => '无效的请求类型', 'eq' => ':attribute must equal :rule',
'token' => '令牌数据无效', 'unique' => ':attribute has exists',
'fileSize' => '上传文件大小不符', 'regex' => ':attribute not conform to the rules',
'fileExt' => '上传文件后缀不符', 'method' => 'invalid Request method',
'fileMime' => '上传文件类型不符', 'token' => 'invalid token',
'fileSize' => 'filesize not match',
'fileExt' => 'extensions to upload is not allowed',
'fileMime' => 'mimetype to upload is not allowed',
]; ];
// 当前验证场景 // 当前验证场景
@ -339,6 +341,41 @@ class Validate
return !empty($this->error) ? false : true; return !empty($this->error) ? false : true;
} }
/**
* 根据验证规则验证数据
* @access protected
* @param mixed $value 字段值
* @param mixed $rules 验证规则
* @return bool
*/
protected function checkRule($value, $rules)
{
if ($rules instanceof \Closure) {
return call_user_func_array($rules, [$value]);
} elseif (is_string($rules)) {
$rules = explode('|', $rules);
}
foreach ($rules as $key => $rule) {
if ($rule instanceof \Closure) {
$result = call_user_func_array($rule, [$value]);
} else {
// 判断验证类型
list($type, $rule) = $this->getValidateType($key, $rule);
$callback = isset(self::$type[$type]) ? self::$type[$type] : [$this, $type];
$result = call_user_func_array($callback, [$value, $rule]);
}
if (true !== $result) {
return $result;
}
}
return true;
}
/** /**
* 验证单个字段规则 * 验证单个字段规则
* @access protected * @access protected
@ -363,25 +400,7 @@ class Validate
$info = is_numeric($key) ? '' : $key; $info = is_numeric($key) ? '' : $key;
} else { } else {
// 判断验证类型 // 判断验证类型
if (is_numeric($key)) { list($type, $rule, $info) = $this->getValidateType($key, $rule);
if (strpos($rule, ':')) {
list($type, $rule) = explode(':', $rule, 2);
if (isset($this->alias[$type])) {
// 判断别名
$type = $this->alias[$type];
}
$info = $type;
} elseif (method_exists($this, $rule)) {
$type = $rule;
$info = $rule;
$rule = '';
} else {
$type = 'is';
$info = $rule;
}
} else {
$info = $type = $key;
}
// 如果不是require 有数据才会行验证 // 如果不是require 有数据才会行验证
if (0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) { if (0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) {
@ -417,6 +436,39 @@ class Validate
return $result; return $result;
} }
/**
* 获取当前验证类型及规则
* @access public
* @param mixed $key
* @param mixed $rule
* @return array
*/
protected function getValidateType($key, $rule)
{
// 判断验证类型
if (!is_numeric($key)) {
return [$key, $rule, $key];
}
if (strpos($rule, ':')) {
list($type, $rule) = explode(':', $rule, 2);
if (isset($this->alias[$type])) {
// 判断别名
$type = $this->alias[$type];
}
$info = $type;
} elseif (method_exists($this, $rule)) {
$type = $rule;
$info = $rule;
$rule = '';
} else {
$type = 'is';
$info = $rule;
}
return [$type, $rule, $info];
}
/** /**
* 验证是否和某个字段的值一致 * 验证是否和某个字段的值一致
* @access protected * @access protected
@ -632,8 +684,12 @@ class Validate
if (function_exists('exif_imagetype')) { if (function_exists('exif_imagetype')) {
return exif_imagetype($image); return exif_imagetype($image);
} else { } else {
$info = getimagesize($image); try {
return $info[2]; $info = getimagesize($image);
return $info ? $info[2] : false;
} catch (\Exception $e) {
return false;
}
} }
} }
@ -676,21 +732,17 @@ class Validate
*/ */
protected function fileExt($file, $rule) protected function fileExt($file, $rule)
{ {
if (!($file instanceof File)) {
return false;
}
if (is_string($rule)) {
$rule = explode(',', $rule);
}
if (is_array($file)) { if (is_array($file)) {
foreach ($file as $item) { foreach ($file as $item) {
if (!$item->checkExt($rule)) { if (!($item instanceof File) || !$item->checkExt($rule)) {
return false; return false;
} }
} }
return true; return true;
} else { } elseif ($file instanceof File) {
return $file->checkExt($rule); return $file->checkExt($rule);
} else {
return false;
} }
} }
@ -703,21 +755,17 @@ class Validate
*/ */
protected function fileMime($file, $rule) protected function fileMime($file, $rule)
{ {
if (!($file instanceof File)) {
return false;
}
if (is_string($rule)) {
$rule = explode(',', $rule);
}
if (is_array($file)) { if (is_array($file)) {
foreach ($file as $item) { foreach ($file as $item) {
if (!$item->checkMime($rule)) { if (!($item instanceof File) || !$item->checkMime($rule)) {
return false; return false;
} }
} }
return true; return true;
} else { } elseif ($file instanceof File) {
return $file->checkMime($rule); return $file->checkMime($rule);
} else {
return false;
} }
} }
@ -730,18 +778,17 @@ class Validate
*/ */
protected function fileSize($file, $rule) protected function fileSize($file, $rule)
{ {
if (!($file instanceof File)) {
return false;
}
if (is_array($file)) { if (is_array($file)) {
foreach ($file as $item) { foreach ($file as $item) {
if (!$item->checkSize($rule)) { if (!($item instanceof File) || !$item->checkSize($rule)) {
return false; return false;
} }
} }
return true; return true;
} else { } elseif ($file instanceof File) {
return $file->checkSize($rule); return $file->checkSize($rule);
} else {
return false;
} }
} }
@ -833,21 +880,26 @@ class Validate
// 支持多个字段验证 // 支持多个字段验证
$fields = explode('^', $key); $fields = explode('^', $key);
foreach ($fields as $key) { foreach ($fields as $key) {
$map[$key] = $data[$key]; if (isset($data[$key])) {
$map[$key] = $data[$key];
}
} }
} elseif (strpos($key, '=')) { } elseif (strpos($key, '=')) {
parse_str($key, $map); parse_str($key, $map);
} else { } elseif (isset($data[$field])) {
$map[$key] = $data[$field]; $map[$key] = $data[$field];
} else {
$map = [];
} }
$pk = strval(isset($rule[3]) ? $rule[3] : $db->getPk()); $pk = isset($rule[3]) ? $rule[3] : $db->getPk();
if (isset($rule[2])) { if (is_string($pk)) {
$map[$pk] = ['neq', $rule[2]]; if (isset($rule[2])) {
} elseif (isset($data[$pk])) { $map[$pk] = ['neq', $rule[2]];
$map[$pk] = ['neq', $data[$pk]]; } elseif (isset($data[$pk])) {
$map[$pk] = ['neq', $data[$pk]];
}
} }
if ($db->where($map)->field($pk)->find()) { if ($db->where($map)->field($pk)->find()) {
return false; return false;
} }
@ -899,7 +951,7 @@ class Validate
{ {
list($field, $val) = explode(',', $rule); list($field, $val) = explode(',', $rule);
if ($this->getDataValue($data, $field) == $val) { if ($this->getDataValue($data, $field) == $val) {
return !empty($value); return !empty($value) || '0' == $value;
} else { } else {
return true; return true;
} }
@ -917,7 +969,7 @@ class Validate
{ {
$result = call_user_func_array($rule, [$value, $data]); $result = call_user_func_array($rule, [$value, $data]);
if ($result) { if ($result) {
return !empty($value); return !empty($value) || '0' == $value;
} else { } else {
return true; return true;
} }
@ -935,7 +987,7 @@ class Validate
{ {
$val = $this->getDataValue($data, $rule); $val = $this->getDataValue($data, $rule);
if (!empty($val)) { if (!empty($val)) {
return !empty($value); return !empty($value) || '0' == $value;
} else { } else {
return true; return true;
} }
@ -1067,9 +1119,10 @@ class Validate
* @access protected * @access protected
* @param mixed $value 字段值 * @param mixed $value 字段值
* @param mixed $rule 验证规则 * @param mixed $rule 验证规则
* @param array $data 数据
* @return bool * @return bool
*/ */
protected function after($value, $rule) protected function after($value, $rule, $data)
{ {
return strtotime($value) >= strtotime($rule); return strtotime($value) >= strtotime($rule);
} }
@ -1079,13 +1132,42 @@ class Validate
* @access protected * @access protected
* @param mixed $value 字段值 * @param mixed $value 字段值
* @param mixed $rule 验证规则 * @param mixed $rule 验证规则
* @param array $data 数据
* @return bool * @return bool
*/ */
protected function before($value, $rule) protected function before($value, $rule, $data)
{ {
return strtotime($value) <= strtotime($rule); return strtotime($value) <= strtotime($rule);
} }
/**
* 验证日期字段
* @access protected
* @param mixed $value 字段值
* @param mixed $rule 验证规则
* @param array $data 数据
* @return bool
*/
protected function afterWith($value, $rule, $data)
{
$rule = $this->getDataValue($data, $rule);
return !is_null($rule) && strtotime($value) >= strtotime($rule);
}
/**
* 验证日期字段
* @access protected
* @param mixed $value 字段值
* @param mixed $rule 验证规则
* @param array $data 数据
* @return bool
*/
protected function beforeWith($value, $rule, $data)
{
$rule = $this->getDataValue($data, $rule);
return !is_null($rule) && strtotime($value) <= strtotime($rule);
}
/** /**
* 验证有效期 * 验证有效期
* @access protected * @access protected
@ -1149,7 +1231,7 @@ class Validate
// 不是正则表达式则两端补上/ // 不是正则表达式则两端补上/
$rule = '/^' . $rule . '$/'; $rule = '/^' . $rule . '$/';
} }
return 1 === preg_match($rule, (string) $value); return is_scalar($value) && 1 === preg_match($rule, (string) $value);
} }
/** /**
@ -1225,12 +1307,16 @@ class Validate
$msg = $this->message[$attribute]; $msg = $this->message[$attribute];
} elseif (isset(self::$typeMsg[$type])) { } elseif (isset(self::$typeMsg[$type])) {
$msg = self::$typeMsg[$type]; $msg = self::$typeMsg[$type];
} elseif (0 === strpos($type, 'require')) {
$msg = self::$typeMsg['require'];
} else { } else {
$msg = $title . '规则错误'; $msg = $title . Lang::get('not conform to the rules');
} }
if (is_string($msg) && 0 === strpos($msg, '{%')) { if (is_string($msg) && 0 === strpos($msg, '{%')) {
$msg = Lang::get(substr($msg, 2, -1)); $msg = Lang::get(substr($msg, 2, -1));
} elseif (Lang::has($msg)) {
$msg = Lang::get($msg);
} }
if (is_string($msg) && is_scalar($rule) && false !== strpos($msg, ':')) { if (is_string($msg) && is_scalar($rule) && false !== strpos($msg, ':')) {

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -33,7 +33,7 @@ class View
public function __construct($engine = [], $replace = []) public function __construct($engine = [], $replace = [])
{ {
// 初始化模板引擎 // 初始化模板引擎
$this->engine((array) $engine); $this->engine($engine);
// 基础替换字符串 // 基础替换字符串
$request = Request::instance(); $request = Request::instance();
$base = $request->root(); $base = $request->root();
@ -127,7 +127,7 @@ class View
* @access private * @access private
* @param string|array $name 参数名 * @param string|array $name 参数名
* @param mixed $value 参数值 * @param mixed $value 参数值
* @return void * @return $this
*/ */
public function config($name, $value = null) public function config($name, $value = null)
{ {
@ -155,18 +155,21 @@ class View
ob_implicit_flush(0); ob_implicit_flush(0);
// 渲染输出 // 渲染输出
$method = $renderContent ? 'display' : 'fetch'; try {
$this->engine->$method($template, $vars, $config); $method = $renderContent ? 'display' : 'fetch';
// 允许用户自定义模板的字符串替换
$replace = array_merge($this->replace, $replace, (array) $this->engine->config('tpl_replace_string'));
$this->engine->config('tpl_replace_string', $replace);
$this->engine->$method($template, $vars, $config);
} catch (\Exception $e) {
ob_end_clean();
throw $e;
}
// 获取并清空缓存 // 获取并清空缓存
$content = ob_get_clean(); $content = ob_get_clean();
// 内容过滤标签 // 内容过滤标签
Hook::listen('view_filter', $content); Hook::listen('view_filter', $content);
// 允许用户自定义模板的字符串替换
$replace = array_merge($this->replace, $replace);
if (!empty($replace)) {
$content = strtr($content, $replace);
}
return $content; return $content;
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -120,10 +120,29 @@ abstract class Driver
public function remember($name, $value, $expire = null) public function remember($name, $value, $expire = null)
{ {
if (!$this->has($name)) { if (!$this->has($name)) {
if ($value instanceof \Closure) { $time = time();
$value = call_user_func($value); while ($time + 5 > time() && $this->has($name . '_lock')) {
// 存在锁定则等待
usleep(200000);
}
try {
// 锁定
$this->set($name . '_lock', true);
if ($value instanceof \Closure) {
$value = call_user_func($value);
}
$this->set($name, $value, $expire);
// 解锁
$this->rm($name . '_lock');
} catch (\Exception $e) {
// 解锁
$this->rm($name . '_lock');
throw $e;
} catch (\throwable $e) {
$this->rm($name . '_lock');
throw $e;
} }
$this->set($name, $value, $expire);
} else { } else {
$value = $this->get($name); $value = $this->get($name);
} }
@ -140,7 +159,9 @@ abstract class Driver
*/ */
public function tag($name, $keys = null, $overlay = false) public function tag($name, $keys = null, $overlay = false)
{ {
if (is_null($keys)) { if (is_null($name)) {
} elseif (is_null($keys)) {
$this->tag = $name; $this->tag = $name;
} else { } else {
$key = 'tag_' . md5($name); $key = 'tag_' . md5($name);
@ -153,7 +174,7 @@ abstract class Driver
} else { } else {
$value = array_unique(array_merge($this->getTagItem($name), $keys)); $value = array_unique(array_merge($this->getTagItem($name), $keys));
} }
$this->set($key, implode(',', $value)); $this->set($key, implode(',', $value), 0);
} }
return $this; return $this;
} }
@ -176,7 +197,7 @@ abstract class Driver
} else { } else {
$value = $name; $value = $name;
} }
$this->set($key, $value); $this->set($key, $value, 0);
} }
} }
@ -191,7 +212,7 @@ abstract class Driver
$key = 'tag_' . md5($tag); $key = 'tag_' . md5($tag);
$value = $this->get($key); $value = $this->get($key);
if ($value) { if ($value) {
return explode(',', $value); return array_filter(explode(',', $value));
} else { } else {
return []; return [];
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -27,6 +27,8 @@ class File extends Driver
'data_compress' => false, 'data_compress' => false,
]; ];
protected $expire;
/** /**
* 构造函数 * 构造函数
* @param array $options * @param array $options
@ -61,10 +63,11 @@ class File extends Driver
/** /**
* 取得变量的存储文件名 * 取得变量的存储文件名
* @access protected * @access protected
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @param bool $auto 是否自动创建目录
* @return string * @return string
*/ */
protected function getCacheKey($name) protected function getCacheKey($name, $auto = false)
{ {
$name = md5($name); $name = md5($name);
if ($this->options['cache_subdir']) { if ($this->options['cache_subdir']) {
@ -76,7 +79,8 @@ class File extends Driver
} }
$filename = $this->options['path'] . $name . '.php'; $filename = $this->options['path'] . $name . '.php';
$dir = dirname($filename); $dir = dirname($filename);
if (!is_dir($dir)) {
if ($auto && !is_dir($dir)) {
mkdir($dir, 0755, true); mkdir($dir, 0755, true);
} }
return $filename; return $filename;
@ -106,15 +110,15 @@ class File extends Driver
if (!is_file($filename)) { if (!is_file($filename)) {
return $default; return $default;
} }
$content = file_get_contents($filename); $content = file_get_contents($filename);
$this->expire = null;
if (false !== $content) { if (false !== $content) {
$expire = (int) substr($content, 8, 12); $expire = (int) substr($content, 8, 12);
if (0 != $expire && $_SERVER['REQUEST_TIME'] > filemtime($filename) + $expire) { if (0 != $expire && time() > filemtime($filename) + $expire) {
//缓存过期删除缓存文件
$this->unlink($filename);
return $default; return $default;
} }
$content = substr($content, 20, -3); $this->expire = $expire;
$content = substr($content, 32);
if ($this->options['data_compress'] && function_exists('gzcompress')) { if ($this->options['data_compress'] && function_exists('gzcompress')) {
//启用数据压缩 //启用数据压缩
$content = gzuncompress($content); $content = gzuncompress($content);
@ -129,9 +133,9 @@ class File extends Driver
/** /**
* 写入缓存 * 写入缓存
* @access public * @access public
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @param mixed $value 存储数据 * @param mixed $value 存储数据
* @param int $expire 有效时间 0为永久 * @param integer|\DateTime $expire 有效时间(秒)
* @return boolean * @return boolean
*/ */
public function set($name, $value, $expire = null) public function set($name, $value, $expire = null)
@ -139,7 +143,10 @@ class File extends Driver
if (is_null($expire)) { if (is_null($expire)) {
$expire = $this->options['expire']; $expire = $this->options['expire'];
} }
$filename = $this->getCacheKey($name); if ($expire instanceof \DateTime) {
$expire = $expire->getTimestamp() - time();
}
$filename = $this->getCacheKey($name, true);
if ($this->tag && !is_file($filename)) { if ($this->tag && !is_file($filename)) {
$first = true; $first = true;
} }
@ -148,7 +155,7 @@ class File extends Driver
//数据压缩 //数据压缩
$data = gzcompress($data, 3); $data = gzcompress($data, 3);
} }
$data = "<?php\n//" . sprintf('%012d', $expire) . $data . "\n?>"; $data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
$result = file_put_contents($filename, $data); $result = file_put_contents($filename, $data);
if ($result) { if ($result) {
isset($first) && $this->setTagItem($filename); isset($first) && $this->setTagItem($filename);
@ -169,11 +176,14 @@ class File extends Driver
public function inc($name, $step = 1) public function inc($name, $step = 1)
{ {
if ($this->has($name)) { if ($this->has($name)) {
$value = $this->get($name) + $step; $value = $this->get($name) + $step;
$expire = $this->expire;
} else { } else {
$value = $step; $value = $step;
$expire = 0;
} }
return $this->set($name, $value, 0) ? $value : false;
return $this->set($name, $value, $expire) ? $value : false;
} }
/** /**
@ -186,11 +196,14 @@ class File extends Driver
public function dec($name, $step = 1) public function dec($name, $step = 1)
{ {
if ($this->has($name)) { if ($this->has($name)) {
$value = $this->get($name) - $step; $value = $this->get($name) - $step;
$expire = $this->expire;
} else { } else {
$value = $step; $value = -$step;
$expire = 0;
} }
return $this->set($name, $value, 0) ? $value : false;
return $this->set($name, $value, $expire) ? $value : false;
} }
/** /**
@ -201,7 +214,11 @@ class File extends Driver
*/ */
public function rm($name) public function rm($name)
{ {
return $this->unlink($this->getCacheKey($name)); $filename = $this->getCacheKey($name);
try {
return $this->unlink($filename);
} catch (\Exception $e) {
}
} }
/** /**
@ -224,7 +241,10 @@ class File extends Driver
$files = (array) glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DS : '') . '*'); $files = (array) glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DS : '') . '*');
foreach ($files as $path) { foreach ($files as $path) {
if (is_dir($path)) { if (is_dir($path)) {
array_map('unlink', glob($path . '/*.php')); $matches = glob($path . '/*.php');
if (is_array($matches)) {
array_map('unlink', $matches);
}
rmdir($path); rmdir($path);
} else { } else {
unlink($path); unlink($path);

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -77,7 +77,7 @@ class Lite extends Driver
if (is_file($filename)) { if (is_file($filename)) {
// 判断是否过期 // 判断是否过期
$mtime = filemtime($filename); $mtime = filemtime($filename);
if ($mtime < $_SERVER['REQUEST_TIME']) { if ($mtime < time()) {
// 清除已经过期的文件 // 清除已经过期的文件
unlink($filename); unlink($filename);
return $default; return $default;
@ -91,9 +91,9 @@ class Lite extends Driver
/** /**
* 写入缓存 * 写入缓存
* @access public * @access public
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @param mixed $value 存储数据 * @param mixed $value 存储数据
* @param int $expire 有效时间 0为永久 * @param integer|\DateTime $expire 有效时间(秒)
* @return bool * @return bool
*/ */
public function set($name, $value, $expire = null) public function set($name, $value, $expire = null)
@ -101,9 +101,11 @@ class Lite extends Driver
if (is_null($expire)) { if (is_null($expire)) {
$expire = $this->options['expire']; $expire = $this->options['expire'];
} }
// 模拟永久 if ($expire instanceof \DateTime) {
if (0 === $expire) { $expire = $expire->getTimestamp();
$expire = 10 * 365 * 24 * 3600; } else {
$expire = 0 === $expire ? 10 * 365 * 24 * 3600 : $expire;
$expire = time() + $expire;
} }
$filename = $this->getCacheKey($name); $filename = $this->getCacheKey($name);
if ($this->tag && !is_file($filename)) { if ($this->tag && !is_file($filename)) {
@ -113,7 +115,7 @@ class Lite extends Driver
// 通过设置修改时间实现有效期 // 通过设置修改时间实现有效期
if ($ret) { if ($ret) {
isset($first) && $this->setTagItem($filename); isset($first) && $this->setTagItem($filename);
touch($filename, $_SERVER['REQUEST_TIME'] + $expire); touch($filename, $expire);
} }
return $ret; return $ret;
} }
@ -147,7 +149,7 @@ class Lite extends Driver
if ($this->has($name)) { if ($this->has($name)) {
$value = $this->get($name) - $step; $value = $this->get($name) - $step;
} else { } else {
$value = $step; $value = -$step;
} }
return $this->set($name, $value, 0) ? $value : false; return $this->set($name, $value, 0) ? $value : false;
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -63,7 +63,7 @@ class Memcache extends Driver
public function has($name) public function has($name)
{ {
$key = $this->getCacheKey($name); $key = $this->getCacheKey($name);
return $this->handler->get($key) ? true : false; return false !== $this->handler->get($key);
} }
/** /**
@ -82,9 +82,9 @@ class Memcache extends Driver
/** /**
* 写入缓存 * 写入缓存
* @access public * @access public
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @param mixed $value 存储数据 * @param mixed $value 存储数据
* @param integer $expire 有效时间(秒) * @param integer|\DateTime $expire 有效时间(秒)
* @return bool * @return bool
*/ */
public function set($name, $value, $expire = null) public function set($name, $value, $expire = null)
@ -92,6 +92,9 @@ class Memcache extends Driver
if (is_null($expire)) { if (is_null($expire)) {
$expire = $this->options['expire']; $expire = $this->options['expire'];
} }
if ($expire instanceof \DateTime) {
$expire = $expire->getTimestamp() - time();
}
if ($this->tag && !$this->has($name)) { if ($this->tag && !$this->has($name)) {
$first = true; $first = true;
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -93,9 +93,9 @@ class Memcached extends Driver
/** /**
* 写入缓存 * 写入缓存
* @access public * @access public
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @param mixed $value 存储数据 * @param mixed $value 存储数据
* @param integer $expire 有效时间(秒) * @param integer|\DateTime $expire 有效时间(秒)
* @return bool * @return bool
*/ */
public function set($name, $value, $expire = null) public function set($name, $value, $expire = null)
@ -103,6 +103,9 @@ class Memcached extends Driver
if (is_null($expire)) { if (is_null($expire)) {
$expire = $this->options['expire']; $expire = $this->options['expire'];
} }
if ($expire instanceof \DateTime) {
$expire = $expire->getTimestamp() - time();
}
if ($this->tag && !$this->has($name)) { if ($this->tag && !$this->has($name)) {
$first = true; $first = true;
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -46,9 +46,12 @@ class Redis extends Driver
if (!empty($options)) { if (!empty($options)) {
$this->options = array_merge($this->options, $options); $this->options = array_merge($this->options, $options);
} }
$func = $this->options['persistent'] ? 'pconnect' : 'connect';
$this->handler = new \Redis; $this->handler = new \Redis;
$this->handler->$func($this->options['host'], $this->options['port'], $this->options['timeout']); if ($this->options['persistent']) {
$this->handler->pconnect($this->options['host'], $this->options['port'], $this->options['timeout'], 'persistent_id_' . $this->options['select']);
} else {
$this->handler->connect($this->options['host'], $this->options['port'], $this->options['timeout']);
}
if ('' != $this->options['password']) { if ('' != $this->options['password']) {
$this->handler->auth($this->options['password']); $this->handler->auth($this->options['password']);
@ -67,7 +70,7 @@ class Redis extends Driver
*/ */
public function has($name) public function has($name)
{ {
return $this->handler->get($this->getCacheKey($name)) ? true : false; return $this->handler->exists($this->getCacheKey($name));
} }
/** /**
@ -80,20 +83,25 @@ class Redis extends Driver
public function get($name, $default = false) public function get($name, $default = false)
{ {
$value = $this->handler->get($this->getCacheKey($name)); $value = $this->handler->get($this->getCacheKey($name));
if (is_null($value)) { if (is_null($value) || false === $value) {
return $default; return $default;
} }
$jsonData = json_decode($value, true);
// 检测是否为JSON数据 true 返回JSON解析数组, false返回源数据 byron sampson<xiaobo.sun@qq.com> try {
return (null === $jsonData) ? $value : $jsonData; $result = 0 === strpos($value, 'think_serialize:') ? unserialize(substr($value, 16)) : $value;
} catch (\Exception $e) {
$result = $default;
}
return $result;
} }
/** /**
* 写入缓存 * 写入缓存
* @access public * @access public
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @param mixed $value 存储数据 * @param mixed $value 存储数据
* @param integer $expire 有效时间(秒) * @param integer|\DateTime $expire 有效时间(秒)
* @return boolean * @return boolean
*/ */
public function set($name, $value, $expire = null) public function set($name, $value, $expire = null)
@ -101,13 +109,15 @@ class Redis extends Driver
if (is_null($expire)) { if (is_null($expire)) {
$expire = $this->options['expire']; $expire = $this->options['expire'];
} }
if ($expire instanceof \DateTime) {
$expire = $expire->getTimestamp() - time();
}
if ($this->tag && !$this->has($name)) { if ($this->tag && !$this->has($name)) {
$first = true; $first = true;
} }
$key = $this->getCacheKey($name); $key = $this->getCacheKey($name);
//对数组/对象数据进行缓存处理,保证数据完整性 byron sampson<xiaobo.sun@qq.com> $value = is_scalar($value) ? $value : 'think_serialize:' . serialize($value);
$value = (is_object($value) || is_array($value)) ? json_encode($value) : $value; if ($expire) {
if (is_int($expire) && $expire) {
$result = $this->handler->setex($key, $expire, $value); $result = $this->handler->setex($key, $expire, $value);
} else { } else {
$result = $this->handler->set($key, $value); $result = $this->handler->set($key, $value);
@ -119,26 +129,28 @@ class Redis extends Driver
/** /**
* 自增缓存(针对数值缓存) * 自增缓存(针对数值缓存)
* @access public * @access public
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @param int $step 步长 * @param int $step 步长
* @return false|int * @return false|int
*/ */
public function inc($name, $step = 1) public function inc($name, $step = 1)
{ {
$key = $this->getCacheKey($name); $key = $this->getCacheKey($name);
return $this->handler->incrby($key, $step); return $this->handler->incrby($key, $step);
} }
/** /**
* 自减缓存(针对数值缓存) * 自减缓存(针对数值缓存)
* @access public * @access public
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @param int $step 步长 * @param int $step 步长
* @return false|int * @return false|int
*/ */
public function dec($name, $step = 1) public function dec($name, $step = 1)
{ {
$key = $this->getCacheKey($name); $key = $this->getCacheKey($name);
return $this->handler->decrby($key, $step); return $this->handler->decrby($key, $step);
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -96,9 +96,9 @@ class Sqlite extends Driver
/** /**
* 写入缓存 * 写入缓存
* @access public * @access public
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @param mixed $value 存储数据 * @param mixed $value 存储数据
* @param integer $expire 有效时间(秒) * @param integer|\DateTime $expire 有效时间(秒)
* @return boolean * @return boolean
*/ */
public function set($name, $value, $expire = null) public function set($name, $value, $expire = null)
@ -108,7 +108,11 @@ class Sqlite extends Driver
if (is_null($expire)) { if (is_null($expire)) {
$expire = $this->options['expire']; $expire = $this->options['expire'];
} }
$expire = (0 == $expire) ? 0 : ($_SERVER['REQUEST_TIME'] + $expire); //缓存有效期为0表示永久缓存 if ($expire instanceof \DateTime) {
$expire = $expire->getTimestamp();
} else {
$expire = (0 == $expire) ? 0 : (time() + $expire); //缓存有效期为0表示永久缓存
}
if (function_exists('gzcompress')) { if (function_exists('gzcompress')) {
//数据压缩 //数据压缩
$value = gzcompress($value, 3); $value = gzcompress($value, 3);
@ -155,7 +159,7 @@ class Sqlite extends Driver
if ($this->has($name)) { if ($this->has($name)) {
$value = $this->get($name) - $step; $value = $this->get($name) - $step;
} else { } else {
$value = $step; $value = -$step;
} }
return $this->set($name, $value, 0) ? $value : false; return $this->set($name, $value, 0) ? $value : false;
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -68,9 +68,9 @@ class Wincache extends Driver
/** /**
* 写入缓存 * 写入缓存
* @access public * @access public
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @param mixed $value 存储数据 * @param mixed $value 存储数据
* @param integer $expire 有效时间(秒) * @param integer|\DateTime $expire 有效时间(秒)
* @return boolean * @return boolean
*/ */
public function set($name, $value, $expire = null) public function set($name, $value, $expire = null)
@ -78,6 +78,9 @@ class Wincache extends Driver
if (is_null($expire)) { if (is_null($expire)) {
$expire = $this->options['expire']; $expire = $this->options['expire'];
} }
if ($expire instanceof \DateTime) {
$expire = $expire->getTimestamp() - time();
}
$key = $this->getCacheKey($name); $key = $this->getCacheKey($name);
if ($this->tag && !$this->has($name)) { if ($this->tag && !$this->has($name)) {
$first = true; $first = true;

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -68,9 +68,9 @@ class Xcache extends Driver
/** /**
* 写入缓存 * 写入缓存
* @access public * @access public
* @param string $name 缓存变量名 * @param string $name 缓存变量名
* @param mixed $value 存储数据 * @param mixed $value 存储数据
* @param integer $expire 有效时间(秒) * @param integer|\DateTime $expire 有效时间(秒)
* @return boolean * @return boolean
*/ */
public function set($name, $value, $expire = null) public function set($name, $value, $expire = null)
@ -78,6 +78,9 @@ class Xcache extends Driver
if (is_null($expire)) { if (is_null($expire)) {
$expire = $this->options['expire']; $expire = $this->options['expire'];
} }
if ($expire instanceof \DateTime) {
$expire = $expire->getTimestamp() - time();
}
if ($this->tag && !$this->has($name)) { if ($this->tag && !$this->has($name)) {
$first = true; $first = true;
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -10,8 +10,10 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
namespace think\console\command; namespace think\console\command;
use think\Cache;
use think\console\Command; use think\console\Command;
use think\console\Input; use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option; use think\console\input\Option;
use think\console\Output; use think\console\Output;
@ -22,6 +24,7 @@ class Clear extends Command
// 指令配置 // 指令配置
$this $this
->setName('clear') ->setName('clear')
->addArgument('type', Argument::OPTIONAL, 'type to clear', null)
->addOption('path', 'd', Option::VALUE_OPTIONAL, 'path to clear', null) ->addOption('path', 'd', Option::VALUE_OPTIONAL, 'path to clear', null)
->setDescription('Clear runtime file'); ->setDescription('Clear runtime file');
} }
@ -30,8 +33,14 @@ class Clear extends Command
{ {
$path = $input->getOption('path') ?: RUNTIME_PATH; $path = $input->getOption('path') ?: RUNTIME_PATH;
if (is_dir($path)) { $type = $input->getArgument('type');
$this->clearPath($path);
if ($type == 'route') {
Cache::clear('route_check');
} else {
if (is_dir($path)) {
$this->clearPath($path);
}
} }
$output->writeln("<info>Clear Successed</info>"); $output->writeln("<info>Clear Successed</info>");

View File

@ -30,7 +30,7 @@ class Config extends Command
protected function execute(Input $input, Output $output) protected function execute(Input $input, Output $output)
{ {
if ($input->hasArgument('module')) { if ($input->getArgument('module')) {
$module = $input->getArgument('module') . DS; $module = $input->getArgument('module') . DS;
} else { } else {
$module = ''; $module = '';

View File

@ -27,6 +27,11 @@ class Route extends Command
protected function execute(Input $input, Output $output) protected function execute(Input $input, Output $output)
{ {
if (!is_dir(RUNTIME_PATH)) {
@mkdir(RUNTIME_PATH, 0755, true);
}
file_put_contents(RUNTIME_PATH . 'route.php', $this->buildRouteCache()); file_put_contents(RUNTIME_PATH . 'route.php', $this->buildRouteCache());
$output->writeln('<info>Succeed!</info>'); $output->writeln('<info>Succeed!</info>');
} }

View File

@ -44,7 +44,8 @@ class Schema extends Command
if ($input->hasOption('module')) { if ($input->hasOption('module')) {
$module = $input->getOption('module'); $module = $input->getOption('module');
// 读取模型 // 读取模型
$list = scandir(APP_PATH . $module . DS . 'model'); $path = APP_PATH . $module . DS . 'model';
$list = is_dir($path) ? scandir($path) : [];
$app = App::$namespace; $app = App::$namespace;
foreach ($list as $file) { foreach ($list as $file) {
if (0 === strpos($file, '.')) { if (0 === strpos($file, '.')) {
@ -66,7 +67,8 @@ class Schema extends Command
$tables = Db::connect($config)->getTables($dbName); $tables = Db::connect($config)->getTables($dbName);
} elseif (!\think\Config::get('app_multi_module')) { } elseif (!\think\Config::get('app_multi_module')) {
$app = App::$namespace; $app = App::$namespace;
$list = scandir(APP_PATH . 'model'); $path = APP_PATH . 'model';
$list = is_dir($path) ? scandir($path) : [];
foreach ($list as $file) { foreach ($list as $file) {
if (0 === strpos($file, '.')) { if (0 === strpos($file, '.')) {
continue; continue;

View File

@ -69,7 +69,7 @@ class Descriptor
* 描述参数 * 描述参数
* @param InputArgument $argument * @param InputArgument $argument
* @param array $options * @param array $options
* @return string|mixed * @return void
*/ */
protected function describeInputArgument(InputArgument $argument, array $options = []) protected function describeInputArgument(InputArgument $argument, array $options = [])
{ {
@ -93,7 +93,7 @@ class Descriptor
* 描述选项 * 描述选项
* @param InputOption $option * @param InputOption $option
* @param array $options * @param array $options
* @return string|mixed * @return void
*/ */
protected function describeInputOption(InputOption $option, array $options = []) protected function describeInputOption(InputOption $option, array $options = [])
{ {
@ -128,7 +128,7 @@ class Descriptor
* 描述输入 * 描述输入
* @param InputDefinition $definition * @param InputDefinition $definition
* @param array $options * @param array $options
* @return string|mixed * @return void
*/ */
protected function describeInputDefinition(InputDefinition $definition, array $options = []) protected function describeInputDefinition(InputDefinition $definition, array $options = [])
{ {
@ -173,7 +173,7 @@ class Descriptor
* 描述指令 * 描述指令
* @param Command $command * @param Command $command
* @param array $options * @param array $options
* @return string|mixed * @return void
*/ */
protected function describeCommand(Command $command, array $options = []) protected function describeCommand(Command $command, array $options = [])
{ {
@ -208,7 +208,7 @@ class Descriptor
* 描述控制台 * 描述控制台
* @param Console $console * @param Console $console
* @param array $options * @param array $options
* @return string|mixed * @return void
*/ */
protected function describeConsole(Console $console, array $options = []) protected function describeConsole(Console $console, array $options = [])
{ {

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -43,7 +43,7 @@ abstract class Rest
if ('' == $ext) { if ('' == $ext) {
// 自动检测资源类型 // 自动检测资源类型
$this->type = $request->type(); $this->type = $request->type();
} elseif (!preg_match('/\(' . $this->restTypeList . '\)$/i', $ext)) { } elseif (!preg_match('/(' . $this->restTypeList . ')$/i', $ext)) {
// 资源类型非法 则用默认资源类型访问 // 资源类型非法 则用默认资源类型访问
$this->type = $this->restDefaultType; $this->type = $this->restDefaultType;
} else { } else {
@ -51,7 +51,7 @@ abstract class Rest
} }
// 请求方式检测 // 请求方式检测
$method = strtolower($request->method()); $method = strtolower($request->method());
if (false === stripos($this->restMethodList, $method)) { if (!preg_match('/(' . $this->restMethodList . ')$/i', $method)) {
// 请求方式非法 则用默认请求方法 // 请求方式非法 则用默认请求方法
$method = $this->restDefaultMethod; $method = $this->restDefaultMethod;
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -25,7 +25,7 @@ abstract class Builder
protected $exp = ['eq' => '=', 'neq' => '<>', 'gt' => '>', 'egt' => '>=', 'lt' => '<', 'elt' => '<=', 'notlike' => 'NOT LIKE', 'not like' => 'NOT LIKE', 'like' => 'LIKE', 'in' => 'IN', 'exp' => 'EXP', 'notin' => 'NOT IN', 'not in' => 'NOT IN', 'between' => 'BETWEEN', 'not between' => 'NOT BETWEEN', 'notbetween' => 'NOT BETWEEN', 'exists' => 'EXISTS', 'notexists' => 'NOT EXISTS', 'not exists' => 'NOT EXISTS', 'null' => 'NULL', 'notnull' => 'NOT NULL', 'not null' => 'NOT NULL', '> time' => '> TIME', '< time' => '< TIME', '>= time' => '>= TIME', '<= time' => '<= TIME', 'between time' => 'BETWEEN TIME', 'not between time' => 'NOT BETWEEN TIME', 'notbetween time' => 'NOT BETWEEN TIME']; protected $exp = ['eq' => '=', 'neq' => '<>', 'gt' => '>', 'egt' => '>=', 'lt' => '<', 'elt' => '<=', 'notlike' => 'NOT LIKE', 'not like' => 'NOT LIKE', 'like' => 'LIKE', 'in' => 'IN', 'exp' => 'EXP', 'notin' => 'NOT IN', 'not in' => 'NOT IN', 'between' => 'BETWEEN', 'not between' => 'NOT BETWEEN', 'notbetween' => 'NOT BETWEEN', 'exists' => 'EXISTS', 'notexists' => 'NOT EXISTS', 'not exists' => 'NOT EXISTS', 'null' => 'NULL', 'notnull' => 'NOT NULL', 'not null' => 'NOT NULL', '> time' => '> TIME', '< time' => '< TIME', '>= time' => '>= TIME', '<= time' => '<= TIME', 'between time' => 'BETWEEN TIME', 'not between time' => 'NOT BETWEEN TIME', 'notbetween time' => 'NOT BETWEEN TIME'];
// SQL表达式 // SQL表达式
protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%'; protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT%%LOCK%%COMMENT%';
protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
@ -46,7 +46,7 @@ abstract class Builder
/** /**
* 获取当前的连接对象实例 * 获取当前的连接对象实例
* @access public * @access public
* @return void * @return Connection
*/ */
public function getConnection() public function getConnection()
{ {
@ -56,7 +56,7 @@ abstract class Builder
/** /**
* 获取当前的Query对象实例 * 获取当前的Query对象实例
* @access public * @access public
* @return void * @return Query
*/ */
public function getQuery() public function getQuery()
{ {
@ -80,6 +80,7 @@ abstract class Builder
* @param array $data 数据 * @param array $data 数据
* @param array $options 查询参数 * @param array $options 查询参数
* @return array * @return array
* @throws Exception
*/ */
protected function parseData($data, $options) protected function parseData($data, $options)
{ {
@ -97,8 +98,15 @@ abstract class Builder
$result = []; $result = [];
foreach ($data as $key => $val) { foreach ($data as $key => $val) {
$item = $this->parseKey($key, $options); if ('*' != $options['field'] && !in_array($key, $fields, true)) {
if (is_object($val) && method_exists($val, '__toString')) { continue;
}
$item = $this->parseKey($key, $options, true);
if ($val instanceof Expression) {
$result[$item] = $val->getValue();
continue;
} elseif (is_object($val) && method_exists($val, '__toString')) {
// 对象数据写入 // 对象数据写入
$val = $val->__toString(); $val = $val->__toString();
} }
@ -108,16 +116,25 @@ abstract class Builder
} }
} elseif (is_null($val)) { } elseif (is_null($val)) {
$result[$item] = 'NULL'; $result[$item] = 'NULL';
} elseif (isset($val[0]) && 'exp' == $val[0]) { } elseif (is_array($val) && !empty($val)) {
$result[$item] = $val[1]; switch (strtolower($val[0])) {
case 'inc':
$result[$item] = $item . '+' . floatval($val[1]);
break;
case 'dec':
$result[$item] = $item . '-' . floatval($val[1]);
break;
case 'exp':
throw new Exception('not support data:[' . $val[0] . ']');
}
} elseif (is_scalar($val)) { } elseif (is_scalar($val)) {
// 过滤非标量数据 // 过滤非标量数据
if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) { if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) {
$result[$item] = $val; $result[$item] = $val;
} else { } else {
$key = str_replace('.', '_', $key); $key = str_replace('.', '_', $key);
$this->query->bind('__data__' . $key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR); $this->query->bind('data__' . $key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
$result[$item] = ':__data__' . $key; $result[$item] = ':data__' . $key;
} }
} }
} }
@ -131,7 +148,7 @@ abstract class Builder
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function parseKey($key, $options = []) protected function parseKey($key, $options = [], $strict = false)
{ {
return $key; return $key;
} }
@ -172,8 +189,10 @@ abstract class Builder
// 支持 'field1'=>'field2' 这样的字段别名定义 // 支持 'field1'=>'field2' 这样的字段别名定义
$array = []; $array = [];
foreach ($fields as $key => $field) { foreach ($fields as $key => $field) {
if (!is_numeric($key)) { if ($field instanceof Expression) {
$array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options); $array[] = $field->getValue();
} elseif (!is_numeric($key)) {
$array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options, true);
} else { } else {
$array[] = $this->parseKey($field, $options); $array[] = $this->parseKey($field, $options);
} }
@ -195,9 +214,6 @@ abstract class Builder
$item = []; $item = [];
foreach ((array) $tables as $key => $table) { foreach ((array) $tables as $key => $table) {
if (!is_numeric($key)) { if (!is_numeric($key)) {
if (strpos($key, '@think')) {
$key = strstr($key, '@think', true);
}
$key = $this->parseSqlTable($key); $key = $this->parseSqlTable($key);
$item[] = $this->parseKey($key) . ' ' . (isset($options['alias'][$table]) ? $this->parseKey($options['alias'][$table]) : $this->parseKey($table)); $item[] = $this->parseKey($key) . ' ' . (isset($options['alias'][$table]) ? $this->parseKey($options['alias'][$table]) : $this->parseKey($table));
} else { } else {
@ -255,7 +271,9 @@ abstract class Builder
foreach ($where as $key => $val) { foreach ($where as $key => $val) {
$str = []; $str = [];
foreach ($val as $field => $value) { foreach ($val as $field => $value) {
if ($value instanceof \Closure) { if ($value instanceof Expression) {
$str[] = ' ' . $key . ' ( ' . $value->getValue() . ' )';
} elseif ($value instanceof \Closure) {
// 使用闭包查询 // 使用闭包查询
$query = new Query($this->connection); $query = new Query($this->connection);
call_user_func_array($value, [ & $query]); call_user_func_array($value, [ & $query]);
@ -296,11 +314,11 @@ abstract class Builder
protected function parseWhereItem($field, $val, $rule = '', $options = [], $binds = [], $bindName = null) protected function parseWhereItem($field, $val, $rule = '', $options = [], $binds = [], $bindName = null)
{ {
// 字段分析 // 字段分析
$key = $field ? $this->parseKey($field, $options) : ''; $key = $field ? $this->parseKey($field, $options, true) : '';
// 查询规则和条件 // 查询规则和条件
if (!is_array($val)) { if (!is_array($val)) {
$val = ['=', $val]; $val = is_null($val) ? ['null', ''] : ['=', $val];
} }
list($exp, $value) = $val; list($exp, $value) = $val;
@ -329,12 +347,19 @@ abstract class Builder
throw new Exception('where express error:' . $exp); throw new Exception('where express error:' . $exp);
} }
} }
$bindName = $bindName ?: 'where_' . str_replace(['.', '-'], '_', $field); $bindName = $bindName ?: 'where_' . $rule . '_' . str_replace(['.', '-'], '_', $field);
if (preg_match('/\W/', $bindName)) { if (preg_match('/\W/', $bindName)) {
// 处理带非单词字符的字段名 // 处理带非单词字符的字段名
$bindName = md5($bindName); $bindName = md5($bindName);
} }
if ($value instanceof Expression) {
} elseif (is_object($value) && method_exists($value, '__toString')) {
// 对象数据写入
$value = $value->__toString();
}
$bindType = isset($binds[$field]) ? $binds[$field] : PDO::PARAM_STR; $bindType = isset($binds[$field]) ? $binds[$field] : PDO::PARAM_STR;
if (is_scalar($value) && array_key_exists($field, $binds) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) { if (is_scalar($value) && array_key_exists($field, $binds) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) {
if (strpos($value, ':') !== 0 || !$this->query->isBind(substr($value, 1))) { if (strpos($value, ':') !== 0 || !$this->query->isBind(substr($value, 1))) {
@ -367,7 +392,11 @@ abstract class Builder
} }
} elseif ('EXP' == $exp) { } elseif ('EXP' == $exp) {
// 表达式查询 // 表达式查询
$whereStr .= '( ' . $key . ' ' . $value . ' )'; if ($value instanceof Expression) {
$whereStr .= '( ' . $key . ' ' . $value->getValue() . ' )';
} else {
throw new Exception('where express error:' . $exp);
}
} elseif (in_array($exp, ['NOT NULL', 'NULL'])) { } elseif (in_array($exp, ['NOT NULL', 'NULL'])) {
// NULL 查询 // NULL 查询
$whereStr .= $key . ' IS ' . $exp; $whereStr .= $key . ' IS ' . $exp;
@ -485,6 +514,11 @@ abstract class Builder
} }
} }
$bindName = $bindName ?: $key; $bindName = $bindName ?: $key;
if ($this->query->isBind($bindName)) {
$bindName .= '_' . str_replace('.', '_', uniqid('', true));
}
$this->query->bind($bindName, $value, $bindType); $this->query->bind($bindName, $value, $bindType);
return ':' . $bindName; return ':' . $bindName;
} }
@ -492,7 +526,7 @@ abstract class Builder
/** /**
* limit分析 * limit分析
* @access protected * @access protected
* @param mixed $lmit * @param mixed $limit
* @return string * @return string
*/ */
protected function parseLimit($limit) protected function parseLimit($limit)
@ -515,7 +549,9 @@ abstract class Builder
list($table, $type, $on) = $item; list($table, $type, $on) = $item;
$condition = []; $condition = [];
foreach ((array) $on as $val) { foreach ((array) $on as $val) {
if (strpos($val, '=')) { if ($val instanceof Expression) {
$condition[] = $val->getValue();
} elseif (strpos($val, '=')) {
list($val1, $val2) = explode('=', $val, 2); list($val1, $val2) = explode('=', $val, 2);
$condition[] = $this->parseKey($val1, $options) . '=' . $this->parseKey($val2, $options); $condition[] = $this->parseKey($val1, $options) . '=' . $this->parseKey($val2, $options);
} else { } else {
@ -539,24 +575,29 @@ abstract class Builder
*/ */
protected function parseOrder($order, $options = []) protected function parseOrder($order, $options = [])
{ {
if (is_array($order)) { if (empty($order)) {
$array = []; return '';
foreach ($order as $key => $val) {
if (is_numeric($key)) {
if ('[rand]' == $val) {
$array[] = $this->parseRand();
} elseif (false === strpos($val, '(')) {
$array[] = $this->parseKey($val, $options);
} else {
$array[] = $val;
}
} else {
$sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : '';
$array[] = $this->parseKey($key, $options) . ' ' . $sort;
}
}
$order = implode(',', $array);
} }
$array = [];
foreach ($order as $key => $val) {
if ($val instanceof Expression) {
$array[] = $val->getValue();
} elseif ('[rand]' == $val) {
$array[] = $this->parseRand();
} else {
if (is_numeric($key)) {
list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' ');
} else {
$sort = $val;
}
$sort = strtoupper($sort);
$sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';
$array[] = $this->parseKey($key, $options, true) . $sort;
}
}
$order = implode(',', $array);
return !empty($order) ? ' ORDER BY ' . $order : ''; return !empty($order) ? ' ORDER BY ' . $order : '';
} }
@ -568,7 +609,7 @@ abstract class Builder
*/ */
protected function parseGroup($group) protected function parseGroup($group)
{ {
return !empty($group) ? ' GROUP BY ' . $group : ''; return !empty($group) ? ' GROUP BY ' . $this->parseKey($group) : '';
} }
/** /**
@ -590,6 +631,9 @@ abstract class Builder
*/ */
protected function parseComment($comment) protected function parseComment($comment)
{ {
if (false !== strpos($comment, '*/')) {
$comment = strstr($comment, '*/', true);
}
return !empty($comment) ? ' /* ' . $comment . ' */' : ''; return !empty($comment) ? ' /* ' . $comment . ' */' : '';
} }
@ -619,12 +663,12 @@ abstract class Builder
unset($union['type']); unset($union['type']);
foreach ($union as $u) { foreach ($union as $u) {
if ($u instanceof \Closure) { if ($u instanceof \Closure) {
$sql[] = $type . ' ' . $this->parseClosure($u, false); $sql[] = $type . ' ' . $this->parseClosure($u);
} elseif (is_string($u)) { } elseif (is_string($u)) {
$sql[] = $type . ' ' . $this->parseSqlTable($u); $sql[] = $type . ' ( ' . $this->parseSqlTable($u) . ' )';
} }
} }
return implode(' ', $sql); return ' ' . implode(' ', $sql);
} }
/** /**
@ -639,22 +683,22 @@ abstract class Builder
return ''; return '';
} }
if (is_array($index)) { return sprintf(" FORCE INDEX ( %s ) ", is_array($index) ? implode(',', $index) : $index);
$index = join(",", $index);
}
return sprintf(" FORCE INDEX ( %s ) ", $index);
} }
/** /**
* 设置锁机制 * 设置锁机制
* @access protected * @access protected
* @param bool $locl * @param bool|string $lock
* @return string * @return string
*/ */
protected function parseLock($lock = false) protected function parseLock($lock = false)
{ {
return $lock ? ' FOR UPDATE ' : ''; if (is_bool($lock)) {
return $lock ? ' FOR UPDATE ' : '';
} elseif (is_string($lock)) {
return ' ' . trim($lock) . ' ';
}
} }
/** /**
@ -723,8 +767,9 @@ abstract class Builder
* @param array $options 表达式 * @param array $options 表达式
* @param bool $replace 是否replace * @param bool $replace 是否replace
* @return string * @return string
* @throws Exception
*/ */
public function insertAll($dataSet, $options, $replace = false) public function insertAll($dataSet, $options = [], $replace = false)
{ {
// 获取合法的字段 // 获取合法的字段
if ('*' == $options['field']) { if ('*' == $options['field']) {
@ -733,7 +778,7 @@ abstract class Builder
$fields = $options['field']; $fields = $options['field'];
} }
foreach ($dataSet as &$data) { foreach ($dataSet as $data) {
foreach ($data as $key => $val) { foreach ($data as $key => $val) {
if (!in_array($key, $fields, true)) { if (!in_array($key, $fields, true)) {
if ($options['strict']) { if ($options['strict']) {
@ -754,23 +799,29 @@ abstract class Builder
} }
$value = array_values($data); $value = array_values($data);
$values[] = 'SELECT ' . implode(',', $value); $values[] = 'SELECT ' . implode(',', $value);
if (!isset($insertFields)) {
$insertFields = array_keys($data);
}
} }
$fields = array_map([$this, 'parseKey'], array_keys(reset($dataSet)));
$sql = str_replace( foreach ($insertFields as $field) {
$fields[] = $this->parseKey($field, $options, true);
}
return str_replace(
['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
[ [
$replace ? 'REPLACE' : 'INSERT', $replace ? 'REPLACE' : 'INSERT',
$this->parseTable($options['table'], $options), $this->parseTable($options['table'], $options),
implode(' , ', $fields), implode(' , ', $insertFields),
implode(' UNION ALL ', $values), implode(' UNION ALL ', $values),
$this->parseComment($options['comment']), $this->parseComment($options['comment']),
], $this->insertAllSql); ], $this->insertAllSql);
return $sql;
} }
/** /**
* 生成slectinsert SQL * 生成select insert SQL
* @access public * @access public
* @param array $fields 数据 * @param array $fields 数据
* @param string $table 数据表 * @param string $table 数据表
@ -791,7 +842,7 @@ abstract class Builder
/** /**
* 生成update SQL * 生成update SQL
* @access public * @access public
* @param array $fields 数据 * @param array $data 数据
* @param array $options 表达式 * @param array $options 表达式
* @return string * @return string
*/ */

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -90,6 +90,8 @@ abstract class Connection
'master_num' => 1, 'master_num' => 1,
// 指定从服务器序号 // 指定从服务器序号
'slave_no' => '', 'slave_no' => '',
// 模型写入后自动读取主服务器
'read_master' => false,
// 是否严格检查字段是否存在 // 是否严格检查字段是否存在
'fields_strict' => true, 'fields_strict' => true,
// 数据返回类型 // 数据返回类型
@ -338,8 +340,8 @@ abstract class Connection
* @param bool $master 是否在主服务器读操作 * @param bool $master 是否在主服务器读操作
* @param bool $pdo 是否返回PDO对象 * @param bool $pdo 是否返回PDO对象
* @return mixed * @return mixed
* @throws BindParamException
* @throws PDOException * @throws PDOException
* @throws \Exception
*/ */
public function query($sql, $bind = [], $master = false, $pdo = false) public function query($sql, $bind = [], $master = false, $pdo = false)
{ {
@ -354,19 +356,14 @@ abstract class Connection
$this->bind = $bind; $this->bind = $bind;
} }
// 释放前次的查询结果
if (!empty($this->PDOStatement)) {
$this->free();
}
Db::$queryTimes++; Db::$queryTimes++;
try { try {
// 调试开始 // 调试开始
$this->debug(true); $this->debug(true);
// 预处理 // 预处理
if (empty($this->PDOStatement)) { $this->PDOStatement = $this->linkID->prepare($sql);
$this->PDOStatement = $this->linkID->prepare($sql);
}
// 是否为存储过程调用 // 是否为存储过程调用
$procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
// 参数绑定 // 参数绑定
@ -378,7 +375,7 @@ abstract class Connection
// 执行查询 // 执行查询
$this->PDOStatement->execute(); $this->PDOStatement->execute();
// 调试结束 // 调试结束
$this->debug(false); $this->debug(false, '', $master);
// 返回结果集 // 返回结果集
return $this->getResult($pdo, $procedure); return $this->getResult($pdo, $procedure);
} catch (\PDOException $e) { } catch (\PDOException $e) {
@ -386,6 +383,11 @@ abstract class Connection
return $this->close()->query($sql, $bind, $master, $pdo); return $this->close()->query($sql, $bind, $master, $pdo);
} }
throw new PDOException($e, $this->config, $this->getLastsql()); throw new PDOException($e, $this->config, $this->getLastsql());
} catch (\Throwable $e) {
if ($this->isBreak($e)) {
return $this->close()->query($sql, $bind, $master, $pdo);
}
throw $e;
} catch (\Exception $e) { } catch (\Exception $e) {
if ($this->isBreak($e)) { if ($this->isBreak($e)) {
return $this->close()->query($sql, $bind, $master, $pdo); return $this->close()->query($sql, $bind, $master, $pdo);
@ -397,13 +399,14 @@ abstract class Connection
/** /**
* 执行语句 * 执行语句
* @access public * @access public
* @param string $sql sql指令 * @param string $sql sql指令
* @param array $bind 参数绑定 * @param array $bind 参数绑定
* @param Query $query 查询对象
* @return int * @return int
* @throws BindParamException
* @throws PDOException * @throws PDOException
* @throws \Exception
*/ */
public function execute($sql, $bind = []) public function execute($sql, $bind = [], Query $query = null)
{ {
$this->initConnect(true); $this->initConnect(true);
if (!$this->linkID) { if (!$this->linkID) {
@ -416,19 +419,14 @@ abstract class Connection
$this->bind = $bind; $this->bind = $bind;
} }
//释放前次的查询结果
if (!empty($this->PDOStatement) && $this->PDOStatement->queryString != $sql) {
$this->free();
}
Db::$executeTimes++; Db::$executeTimes++;
try { try {
// 调试开始 // 调试开始
$this->debug(true); $this->debug(true);
// 预处理 // 预处理
if (empty($this->PDOStatement)) { $this->PDOStatement = $this->linkID->prepare($sql);
$this->PDOStatement = $this->linkID->prepare($sql);
}
// 是否为存储过程调用 // 是否为存储过程调用
$procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
// 参数绑定 // 参数绑定
@ -440,18 +438,27 @@ abstract class Connection
// 执行语句 // 执行语句
$this->PDOStatement->execute(); $this->PDOStatement->execute();
// 调试结束 // 调试结束
$this->debug(false); $this->debug(false, '', true);
if ($query && !empty($this->config['deploy']) && !empty($this->config['read_master'])) {
$query->readMaster();
}
$this->numRows = $this->PDOStatement->rowCount(); $this->numRows = $this->PDOStatement->rowCount();
return $this->numRows; return $this->numRows;
} catch (\PDOException $e) { } catch (\PDOException $e) {
if ($this->isBreak($e)) { if ($this->isBreak($e)) {
return $this->close()->execute($sql, $bind); return $this->close()->execute($sql, $bind, $query);
} }
throw new PDOException($e, $this->config, $this->getLastsql()); throw new PDOException($e, $this->config, $this->getLastsql());
} catch (\Throwable $e) {
if ($this->isBreak($e)) {
return $this->close()->execute($sql, $bind, $query);
}
throw $e;
} catch (\Exception $e) { } catch (\Exception $e) {
if ($this->isBreak($e)) { if ($this->isBreak($e)) {
return $this->close()->execute($sql, $bind); return $this->close()->execute($sql, $bind, $query);
} }
throw $e; throw $e;
} }
@ -466,6 +473,10 @@ abstract class Connection
*/ */
public function getRealSql($sql, array $bind = []) public function getRealSql($sql, array $bind = [])
{ {
if (is_array($sql)) {
$sql = implode(';', $sql);
}
foreach ($bind as $key => $val) { foreach ($bind as $key => $val) {
$value = is_array($val) ? $val[0] : $val; $value = is_array($val) ? $val[0] : $val;
$type = is_array($val) ? $val[1] : PDO::PARAM_STR; $type = is_array($val) ? $val[1] : PDO::PARAM_STR;
@ -478,8 +489,8 @@ abstract class Connection
$sql = is_numeric($key) ? $sql = is_numeric($key) ?
substr_replace($sql, $value, strpos($sql, '?'), 1) : substr_replace($sql, $value, strpos($sql, '?'), 1) :
str_replace( str_replace(
[':' . $key . ')', ':' . $key . ',', ':' . $key . ' '], [':' . $key . ')', ':' . $key . ',', ':' . $key . ' ', ':' . $key . PHP_EOL],
[$value . ')', $value . ',', $value . ' '], [$value . ')', $value . ',', $value . ' ', $value . PHP_EOL],
$sql . ' '); $sql . ' ');
} }
return rtrim($sql); return rtrim($sql);
@ -552,7 +563,7 @@ abstract class Connection
* @access protected * @access protected
* @param bool $pdo 是否返回PDOStatement * @param bool $pdo 是否返回PDOStatement
* @param bool $procedure 是否存储过程 * @param bool $procedure 是否存储过程
* @return array * @return PDOStatement|array
*/ */
protected function getResult($pdo = false, $procedure = false) protected function getResult($pdo = false, $procedure = false)
{ {
@ -618,7 +629,8 @@ abstract class Connection
/** /**
* 启动事务 * 启动事务
* @access public * @access public
* @return void * @return bool|mixed
* @throws \Exception
*/ */
public function startTrans() public function startTrans()
{ {
@ -637,13 +649,15 @@ abstract class Connection
); );
} }
} catch (\PDOException $e) { } catch (\Exception $e) {
if ($this->isBreak($e)) { if ($this->isBreak($e)) {
--$this->transTimes;
return $this->close()->startTrans(); return $this->close()->startTrans();
} }
throw $e; throw $e;
} catch (\ErrorException $e) { } catch (\Error $e) {
if ($this->isBreak($e)) { if ($this->isBreak($e)) {
--$this->transTimes;
return $this->close()->startTrans(); return $this->close()->startTrans();
} }
throw $e; throw $e;
@ -724,7 +738,7 @@ abstract class Connection
* @param array $sqlArray SQL批处理指令 * @param array $sqlArray SQL批处理指令
* @return boolean * @return boolean
*/ */
public function batchQuery($sqlArray = []) public function batchQuery($sqlArray = [], $bind = [], Query $query = null)
{ {
if (!is_array($sqlArray)) { if (!is_array($sqlArray)) {
return false; return false;
@ -733,7 +747,7 @@ abstract class Connection
$this->startTrans(); $this->startTrans();
try { try {
foreach ($sqlArray as $sql) { foreach ($sqlArray as $sql) {
$this->execute($sql); $this->execute($sql, $bind, $query);
} }
// 提交事务 // 提交事务
$this->commit(); $this->commit();
@ -741,6 +755,7 @@ abstract class Connection
$this->rollback(); $this->rollback();
throw $e; throw $e;
} }
return true; return true;
} }
@ -776,13 +791,15 @@ abstract class Connection
$this->linkWrite = null; $this->linkWrite = null;
$this->linkRead = null; $this->linkRead = null;
$this->links = []; $this->links = [];
// 释放查询
$this->free();
return $this; return $this;
} }
/** /**
* 是否断线 * 是否断线
* @access protected * @access protected
* @param \PDOException $e 异常对象 * @param \PDOException|\Exception $e 异常对象
* @return bool * @return bool
*/ */
protected function isBreak($e) protected function isBreak($e)
@ -802,6 +819,7 @@ abstract class Connection
'SSL connection has been closed unexpectedly', 'SSL connection has been closed unexpectedly',
'Error writing data to the connection', 'Error writing data to the connection',
'Resource deadlock avoided', 'Resource deadlock avoided',
'failed with errno',
]; ];
$error = $e->getMessage(); $error = $e->getMessage();
@ -882,9 +900,10 @@ abstract class Connection
* @access protected * @access protected
* @param boolean $start 调试开始标记 true 开始 false 结束 * @param boolean $start 调试开始标记 true 开始 false 结束
* @param string $sql 执行的SQL语句 留空自动获取 * @param string $sql 执行的SQL语句 留空自动获取
* @param boolean $master 主从标记
* @return void * @return void
*/ */
protected function debug($start, $sql = '') protected function debug($start, $sql = '', $master = false)
{ {
if (!empty($this->config['debug'])) { if (!empty($this->config['debug'])) {
// 开启数据库调试模式 // 开启数据库调试模式
@ -901,7 +920,7 @@ abstract class Connection
$result = $this->getExplain($sql); $result = $this->getExplain($sql);
} }
// SQL监听 // SQL监听
$this->trigger($sql, $runtime, $result); $this->trigger($sql, $runtime, $result, $master);
} }
} }
} }
@ -923,19 +942,27 @@ abstract class Connection
* @param string $sql SQL语句 * @param string $sql SQL语句
* @param float $runtime SQL运行时间 * @param float $runtime SQL运行时间
* @param mixed $explain SQL分析 * @param mixed $explain SQL分析
* @return bool * @param bool $master 主从标记
* @return void
*/ */
protected function trigger($sql, $runtime, $explain = []) protected function trigger($sql, $runtime, $explain = [], $master = false)
{ {
if (!empty(self::$event)) { if (!empty(self::$event)) {
foreach (self::$event as $callback) { foreach (self::$event as $callback) {
if (is_callable($callback)) { if (is_callable($callback)) {
call_user_func_array($callback, [$sql, $runtime, $explain]); call_user_func_array($callback, [$sql, $runtime, $explain, $master]);
} }
} }
} else { } else {
// 未注册监听则记录到日志中 // 未注册监听则记录到日志中
Log::record('[ SQL ] ' . $sql . ' [ RunTime:' . $runtime . 's ]', 'sql'); if ($this->config['deploy']) {
// 分布式记录当前操作的主从
$master = $master ? 'master|' : 'slave|';
} else {
$master = '';
}
Log::record('[ SQL ] ' . $sql . ' [ ' . $master . 'RunTime:' . $runtime . 's ]', 'sql');
if (!empty($explain)) { if (!empty($explain)) {
Log::record('[ EXPLAIN : ' . var_export($explain, true) . ' ]', 'sql'); Log::record('[ EXPLAIN : ' . var_export($explain, true) . ' ]', 'sql');
} }

View File

@ -0,0 +1,48 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db;
class Expression
{
/**
* 查询表达式
*
* @var string
*/
protected $value;
/**
* 创建一个查询表达式
*
* @param string $value
* @return void
*/
public function __construct($value)
{
$this->value = $value;
}
/**
* 获取表达式
*
* @return string
*/
public function getValue()
{
return $this->value;
}
public function __toString()
{
return (string) $this->value;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -12,28 +12,93 @@
namespace think\db\builder; namespace think\db\builder;
use think\db\Builder; use think\db\Builder;
use think\Exception;
/** /**
* mysql数据库驱动 * mysql数据库驱动
*/ */
class Mysql extends Builder class Mysql extends Builder
{ {
protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES %DATA% %COMMENT%';
protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
/**
* 生成insertall SQL
* @access public
* @param array $dataSet 数据集
* @param array $options 表达式
* @param bool $replace 是否replace
* @return string
* @throws Exception
*/
public function insertAll($dataSet, $options = [], $replace = false)
{
// 获取合法的字段
if ('*' == $options['field']) {
$fields = array_keys($this->query->getFieldsType($options['table']));
} else {
$fields = $options['field'];
}
foreach ($dataSet as $data) {
foreach ($data as $key => $val) {
if (!in_array($key, $fields, true)) {
if ($options['strict']) {
throw new Exception('fields not exists:[' . $key . ']');
}
unset($data[$key]);
} elseif (is_null($val)) {
$data[$key] = 'NULL';
} elseif (is_scalar($val)) {
$data[$key] = $this->parseValue($val, $key);
} elseif (is_object($val) && method_exists($val, '__toString')) {
// 对象数据写入
$data[$key] = $val->__toString();
} else {
// 过滤掉非标量数据
unset($data[$key]);
}
}
$value = array_values($data);
$values[] = '( ' . implode(',', $value) . ' )';
if (!isset($insertFields)) {
$insertFields = array_map([$this, 'parseKey'], array_keys($data));
}
}
return str_replace(
['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
[
$replace ? 'REPLACE' : 'INSERT',
$this->parseTable($options['table'], $options),
implode(' , ', $insertFields),
implode(' , ', $values),
$this->parseComment($options['comment']),
], $this->insertAllSql);
}
/** /**
* 字段和表名处理 * 字段和表名处理
* @access protected * @access protected
* @param string $key * @param mixed $key
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function parseKey($key, $options = []) protected function parseKey($key, $options = [], $strict = false)
{ {
if (is_numeric($key)) {
return $key;
} elseif ($key instanceof Expression) {
return $key->getValue();
}
$key = trim($key); $key = trim($key);
if (strpos($key, '$.') && false === strpos($key, '(')) { if (strpos($key, '$.') && false === strpos($key, '(')) {
// JSON字段支持 // JSON字段支持
list($field, $name) = explode('$.', $key); list($field, $name) = explode('$.', $key);
$key = 'json_extract(' . $field . ', \'$.' . $name . '\')'; return 'json_extract(' . $field . ', \'$.' . $name . '\')';
} elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) { } elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) {
list($table, $key) = explode('.', $key, 2); list($table, $key) = explode('.', $key, 2);
if ('__TABLE__' == $table) { if ('__TABLE__' == $table) {
@ -43,7 +108,11 @@ class Mysql extends Builder
$table = $options['alias'][$table]; $table = $options['alias'][$table];
} }
} }
if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
throw new Exception('not support data:' . $key);
}
if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) {
$key = '`' . $key . '`'; $key = '`' . $key . '`';
} }
if (isset($table)) { if (isset($table)) {

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -18,6 +18,8 @@ use think\db\Builder;
*/ */
class Pgsql extends Builder class Pgsql extends Builder
{ {
protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
/** /**
* limit分析 * limit分析
@ -42,12 +44,18 @@ class Pgsql extends Builder
/** /**
* 字段和表名处理 * 字段和表名处理
* @access protected * @access protected
* @param string $key * @param mixed $key
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function parseKey($key, $options = []) protected function parseKey($key, $options = [], $strict = false)
{ {
if (is_numeric($key)) {
return $key;
} elseif ($key instanceof Expression) {
return $key->getValue();
}
$key = trim($key); $key = trim($key);
if (strpos($key, '$.') && false === strpos($key, '(')) { if (strpos($key, '$.') && false === strpos($key, '(')) {
// JSON字段支持 // JSON字段支持

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -22,6 +22,7 @@ class Sqlite extends Builder
/** /**
* limit * limit
* @access public * @access public
* @param string $limit
* @return string * @return string
*/ */
public function parseLimit($limit) public function parseLimit($limit)
@ -51,12 +52,18 @@ class Sqlite extends Builder
/** /**
* 字段和表名处理 * 字段和表名处理
* @access protected * @access protected
* @param string $key * @param mixed $key
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function parseKey($key, $options = []) protected function parseKey($key, $options = [], $strict = false)
{ {
if (is_numeric($key)) {
return $key;
} elseif ($key instanceof Expression) {
return $key->getValue();
}
$key = trim($key); $key = trim($key);
if (strpos($key, '.')) { if (strpos($key, '.')) {
list($table, $key) = explode('.', $key, 2); list($table, $key) = explode('.', $key, 2);

View File

@ -12,6 +12,7 @@
namespace think\db\builder; namespace think\db\builder;
use think\db\Builder; use think\db\Builder;
use think\db\Expression;
/** /**
* Sqlsrv数据库驱动 * Sqlsrv数据库驱动
@ -22,6 +23,8 @@ class Sqlsrv extends Builder
protected $selectInsertSql = 'SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%'; protected $selectInsertSql = 'SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%';
protected $updateSql = 'UPDATE %TABLE% SET %SET% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; protected $updateSql = 'UPDATE %TABLE% SET %SET% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
protected $deleteSql = 'DELETE FROM %TABLE% %USING% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; protected $deleteSql = 'DELETE FROM %TABLE% %USING% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
/** /**
* order分析 * order分析
@ -32,23 +35,29 @@ class Sqlsrv extends Builder
*/ */
protected function parseOrder($order, $options = []) protected function parseOrder($order, $options = [])
{ {
if (is_array($order)) { if (empty($order)) {
$array = []; return ' ORDER BY rand()';
foreach ($order as $key => $val) {
if (is_numeric($key)) {
if (false === strpos($val, '(')) {
$array[] = $this->parseKey($val, $options);
} elseif ('[rand]' == $val) {
$array[] = $this->parseRand();
}
} else {
$sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : '';
$array[] = $this->parseKey($key, $options) . ' ' . $sort;
}
}
$order = implode(',', $array);
} }
return !empty($order) ? ' ORDER BY ' . $order : ' ORDER BY rand()';
$array = [];
foreach ($order as $key => $val) {
if ($val instanceof Expression) {
$array[] = $val->getValue();
} elseif (is_numeric($key)) {
if (false === strpos($val, '(')) {
$array[] = $this->parseKey($val, $options);
} elseif ('[rand]' == $val) {
$array[] = $this->parseRand();
} else {
$array[] = $val;
}
} else {
$sort = in_array(strtolower(trim($val)), ['asc', 'desc'], true) ? ' ' . $val : '';
$array[] = $this->parseKey($key, $options, true) . ' ' . $sort;
}
}
return ' ORDER BY ' . implode(',', $array);
} }
/** /**
@ -64,12 +73,17 @@ class Sqlsrv extends Builder
/** /**
* 字段和表名处理 * 字段和表名处理
* @access protected * @access protected
* @param string $key * @param mixed $key
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function parseKey($key, $options = []) protected function parseKey($key, $options = [], $strict = false)
{ {
if (is_numeric($key)) {
return $key;
} elseif ($key instanceof Expression) {
return $key->getValue();
}
$key = trim($key); $key = trim($key);
if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) { if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) {
list($table, $key) = explode('.', $key, 2); list($table, $key) = explode('.', $key, 2);
@ -80,7 +94,11 @@ class Sqlsrv extends Builder
$table = $options['alias'][$table]; $table = $options['alias'][$table];
} }
} }
if (!is_numeric($key) && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) {
if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
throw new Exception('not support data:' . $key);
}
if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)\[.\s]/', $key))) {
$key = '[' . $key . ']'; $key = '[' . $key . ']';
} }
if (isset($table)) { if (isset($table)) {

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -31,12 +31,15 @@ class Mysql extends Connection
*/ */
protected function parseDsn($config) protected function parseDsn($config)
{ {
$dsn = 'mysql:dbname=' . $config['database'] . ';host=' . $config['hostname']; if (!empty($config['socket'])) {
if (!empty($config['hostport'])) { $dsn = 'mysql:unix_socket=' . $config['socket'];
$dsn .= ';port=' . $config['hostport']; } elseif (!empty($config['hostport'])) {
} elseif (!empty($config['socket'])) { $dsn = 'mysql:host=' . $config['hostname'] . ';port=' . $config['hostport'];
$dsn .= ';unix_socket=' . $config['socket']; } else {
$dsn = 'mysql:host=' . $config['hostname'];
} }
$dsn .= ';dbname=' . $config['database'];
if (!empty($config['charset'])) { if (!empty($config['charset'])) {
$dsn .= ';charset=' . $config['charset']; $dsn .= ';charset=' . $config['charset'];
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -50,7 +50,10 @@ class Sqlsrv extends Connection
public function getFields($tableName) public function getFields($tableName)
{ {
list($tableName) = explode(' ', $tableName); list($tableName) = explode(' ', $tableName);
$sql = "SELECT column_name, data_type, column_default, is_nullable $tableNames = explode('.', $tableName);
$tableName = isset($tableNames[1]) ? $tableNames[1] : $tableNames[0];
$sql = "SELECT column_name, data_type, column_default, is_nullable
FROM information_schema.tables AS t FROM information_schema.tables AS t
JOIN information_schema.columns AS c JOIN information_schema.columns AS c
ON t.table_catalog = c.table_catalog ON t.table_catalog = c.table_catalog

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -137,7 +137,7 @@ JS;
} }
break; break;
case '错误': case '错误':
$msg = str_replace("\n", '\n', $m); $msg = str_replace("\n", '\n', json_encode($m));
$style = 'color:#F4006B;font-size:14px;'; $style = 'color:#F4006B;font-size:14px;';
$line[] = "console.error(\"%c{$msg}\", \"{$style}\");"; $line[] = "console.error(\"%c{$msg}\", \"{$style}\");";
break; break;

View File

@ -53,7 +53,7 @@ class Html
return false; return false;
} }
// 获取基本信息 // 获取基本信息
$runtime = number_format(microtime(true) - THINK_START_TIME, 10); $runtime = number_format(microtime(true) - THINK_START_TIME, 10, '.', '');
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
$mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); $mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -21,11 +21,16 @@ use think\Response;
class Handle class Handle
{ {
protected $render;
protected $ignoreReport = [ protected $ignoreReport = [
'\\think\\exception\\HttpException', '\\think\\exception\\HttpException',
]; ];
public function setRender($render)
{
$this->render = $render;
}
/** /**
* Report or log an exception. * Report or log an exception.
* *
@ -78,6 +83,13 @@ class Handle
*/ */
public function render(Exception $e) public function render(Exception $e)
{ {
if ($this->render && $this->render instanceof \Closure) {
$result = call_user_func_array($this->render, [$e]);
if ($result) {
return $result;
}
}
if ($e instanceof HttpException) { if ($e instanceof HttpException) {
return $this->renderHttpException($e); return $this->renderHttpException($e);
} else { } else {

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -16,7 +16,7 @@ class RouteNotFoundException extends HttpException
public function __construct() public function __construct()
{ {
parent::__construct(404); parent::__construct(404, 'Route Not Found');
} }
} }

View File

@ -12,6 +12,7 @@
namespace think\log\driver; namespace think\log\driver;
use think\App; use think\App;
use think\Request;
/** /**
* 本地化调试输出到文件 * 本地化调试输出到文件
@ -20,13 +21,14 @@ class File
{ {
protected $config = [ protected $config = [
'time_format' => ' c ', 'time_format' => ' c ',
'single' => false,
'file_size' => 2097152, 'file_size' => 2097152,
'path' => LOG_PATH, 'path' => LOG_PATH,
'apart_level' => [], 'apart_level' => [],
'max_files' => 0,
'json' => false,
]; ];
protected $writed = [];
// 实例化并传入参数 // 实例化并传入参数
public function __construct($config = []) public function __construct($config = [])
{ {
@ -38,82 +40,231 @@ class File
/** /**
* 日志写入接口 * 日志写入接口
* @access public * @access public
* @param array $log 日志信息 * @param array $log 日志信息
* @param bool $append 是否追加请求信息
* @return bool * @return bool
*/ */
public function save(array $log = []) public function save(array $log = [], $append = false)
{ {
$cli = IS_CLI ? '_cli' : ''; $destination = $this->getMasterLogFile();
$destination = $this->config['path'] . date('Ym') . DS . date('d') . $cli . '.log';
$path = dirname($destination); $path = dirname($destination);
!is_dir($path) && mkdir($path, 0755, true); !is_dir($path) && mkdir($path, 0755, true);
$info = ''; $info = [];
foreach ($log as $type => $val) { foreach ($log as $type => $val) {
$level = '';
foreach ($val as $msg) { foreach ($val as $msg) {
if (!is_string($msg)) { if (!is_string($msg)) {
$msg = var_export($msg, true); $msg = var_export($msg, true);
} }
$level .= '[ ' . $type . ' ] ' . $msg . "\r\n";
$info[$type][] = $this->config['json'] ? $msg : '[ ' . $type . ' ] ' . $msg;
} }
if (in_array($type, $this->config['apart_level'])) {
if (!$this->config['json'] && (true === $this->config['apart_level'] || in_array($type, $this->config['apart_level']))) {
// 独立记录的日志级别 // 独立记录的日志级别
$filename = $path . DS . date('d') . '_' . $type . $cli . '.log'; $filename = $this->getApartLevelFile($path, $type);
$this->write($level, $filename, true);
} else { $this->write($info[$type], $filename, true, $append);
$info .= $level; unset($info[$type]);
} }
} }
if ($info) { if ($info) {
return $this->write($info, $destination); return $this->write($info, $destination, false, $append);
} }
return true; return true;
} }
protected function write($message, $destination, $apart = false) /**
* 获取主日志文件名
* @access public
* @return string
*/
protected function getMasterLogFile()
{ {
//检测日志文件大小,超过配置大小则备份日志文件重新生成 if ($this->config['single']) {
if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) { $name = is_string($this->config['single']) ? $this->config['single'] : 'single';
rename($destination, dirname($destination) . DS . time() . '-' . basename($destination));
$this->writed[$destination] = false;
}
if (empty($this->writed[$destination]) && !IS_CLI) { $destination = $this->config['path'] . $name . '.log';
if (App::$debug && !$apart) { } else {
// 获取基本信息 $cli = PHP_SAPI == 'cli' ? '_cli' : '';
if (isset($_SERVER['HTTP_HOST'])) {
$current_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; if ($this->config['max_files']) {
} else { $filename = date('Ymd') . $cli . '.log';
$current_uri = "cmd:" . implode(' ', $_SERVER['argv']); $files = glob($this->config['path'] . '*.log');
try {
if (count($files) > $this->config['max_files']) {
unlink($files[0]);
}
} catch (\Exception $e) {
} }
} else {
$runtime = round(microtime(true) - THINK_START_TIME, 10); $filename = date('Ym') . DIRECTORY_SEPARATOR . date('d') . $cli . '.log';
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
$time_str = ' [运行时间:' . number_format($runtime, 6) . 's][吞吐率:' . $reqs . 'req/s]';
$memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
$memory_str = ' [内存消耗:' . $memory_use . 'kb]';
$file_load = ' [文件加载:' . count(get_included_files()) . ']';
$message = '[ info ] ' . $current_uri . $time_str . $memory_str . $file_load . "\r\n" . $message;
} }
$now = date($this->config['time_format']);
$server = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '0.0.0.0';
$remote = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0';
$method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'CLI';
$uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
$message = "---------------------------------------------------------------\r\n[{$now}] {$server} {$remote} {$method} {$uri}\r\n" . $message;
$this->writed[$destination] = true; $destination = $this->config['path'] . $filename;
} }
if (IS_CLI) { return $destination;
$now = date($this->config['time_format']); }
$message = "[{$now}]" . $message;
/**
* 获取独立日志文件名
* @access public
* @param string $path 日志目录
* @param string $type 日志类型
* @return string
*/
protected function getApartLevelFile($path, $type)
{
$cli = PHP_SAPI == 'cli' ? '_cli' : '';
if ($this->config['single']) {
$name = is_string($this->config['single']) ? $this->config['single'] : 'single';
$name .= '_' . $type;
} elseif ($this->config['max_files']) {
$name = date('Ymd') . '_' . $type . $cli;
} else {
$name = date('d') . '_' . $type . $cli;
}
return $path . DIRECTORY_SEPARATOR . $name . '.log';
}
/**
* 日志写入
* @access protected
* @param array $message 日志信息
* @param string $destination 日志文件
* @param bool $apart 是否独立文件写入
* @param bool $append 是否追加请求信息
* @return bool
*/
protected function write($message, $destination, $apart = false, $append = false)
{
// 检测日志文件大小,超过配置大小则备份日志文件重新生成
$this->checkLogSize($destination);
// 日志信息封装
$info['timestamp'] = date($this->config['time_format']);
foreach ($message as $type => $msg) {
$info[$type] = is_array($msg) ? implode("\r\n", $msg) : $msg;
}
if (PHP_SAPI == 'cli') {
$message = $this->parseCliLog($info);
} else {
// 添加调试日志
$this->getDebugLog($info, $append, $apart);
$message = $this->parseLog($info);
} }
return error_log($message, 3, $destination); return error_log($message, 3, $destination);
} }
/**
* 检查日志文件大小并自动生成备份文件
* @access protected
* @param string $destination 日志文件
* @return void
*/
protected function checkLogSize($destination)
{
if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) {
try {
rename($destination, dirname($destination) . DIRECTORY_SEPARATOR . time() . '-' . basename($destination));
} catch (\Exception $e) {
}
}
}
/**
* CLI日志解析
* @access protected
* @param array $info 日志信息
* @return string
*/
protected function parseCliLog($info)
{
if ($this->config['json']) {
$message = json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n";
} else {
$now = $info['timestamp'];
unset($info['timestamp']);
$message = implode("\r\n", $info);
$message = "[{$now}]" . $message . "\r\n";
}
return $message;
}
/**
* 解析日志
* @access protected
* @param array $info 日志信息
* @return string
*/
protected function parseLog($info)
{
$request = Request::instance();
$requestInfo = [
'ip' => $request->ip(),
'method' => $request->method(),
'host' => $request->host(),
'uri' => $request->url(),
];
if ($this->config['json']) {
$info = $requestInfo + $info;
return json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n";
}
array_unshift($info, "---------------------------------------------------------------\r\n[{$info['timestamp']}] {$requestInfo['ip']} {$requestInfo['method']} {$requestInfo['host']}{$requestInfo['uri']}");
unset($info['timestamp']);
return implode("\r\n", $info) . "\r\n";
}
protected function getDebugLog(&$info, $append, $apart)
{
if (App::$debug && $append) {
if ($this->config['json']) {
// 获取基本信息
$runtime = round(microtime(true) - THINK_START_TIME, 10);
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
$memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
$info = [
'runtime' => number_format($runtime, 6) . 's',
'reqs' => $reqs . 'req/s',
'memory' => $memory_use . 'kb',
'file' => count(get_included_files()),
] + $info;
} elseif (!$apart) {
// 增加额外的调试信息
$runtime = round(microtime(true) - THINK_START_TIME, 10);
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
$memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
$time_str = '[运行时间:' . number_format($runtime, 6) . 's] [吞吐率:' . $reqs . 'req/s]';
$memory_str = ' [内存消耗:' . $memory_use . 'kb]';
$file_load = ' [文件加载:' . count(get_included_files()) . ']';
array_unshift($info, $time_str . $memory_str . $file_load);
}
}
}
} }

View File

@ -60,7 +60,7 @@ class Socket
* @param array $log 日志信息 * @param array $log 日志信息
* @return bool * @return bool
*/ */
public function save(array $log = []) public function save(array $log = [], $append = false)
{ {
if (!$this->check()) { if (!$this->check()) {
return false; return false;

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -16,20 +16,6 @@ use think\Model;
class Collection extends BaseCollection class Collection extends BaseCollection
{ {
/**
* 返回数组中指定的一列
* @param string $column_key
* @param string|null $index_key
* @return array
*/
public function column($column_key, $index_key = null)
{
if (function_exists('array_column')) {
return array_column($this->toArray(), $column_key, $index_key);
}
return parent::column($column_key, $index_key);
}
/** /**
* 延迟预载入关联查询 * 延迟预载入关联查询
* @access public * @access public
@ -85,7 +71,7 @@ class Collection extends BaseCollection
{ {
$this->each(function ($model) use ($append, $override) { $this->each(function ($model) use ($append, $override) {
/** @var Model $model */ /** @var Model $model */
$model->append($append, $override); $model && $model->append($append, $override);
}); });
return $this; return $this;
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -24,11 +24,11 @@ class Pivot extends Model
/** /**
* 架构函数 * 架构函数
* @access public * @access public
* @param Model $parent 上级模型
* @param array|object $data 数据 * @param array|object $data 数据
* @param Model $parent 上级模型
* @param string $table 中间数据表名 * @param string $table 中间数据表名
*/ */
public function __construct(Model $parent, $data = [], $table = '') public function __construct($data = [], Model $parent = null, $table = '')
{ {
$this->parent = $parent; $this->parent = $parent;
@ -37,8 +37,6 @@ class Pivot extends Model
} }
parent::__construct($data); parent::__construct($data);
$this->class = $this->name;
} }
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -35,6 +35,8 @@ abstract class Relation
protected $localKey; protected $localKey;
// 基础查询 // 基础查询
protected $baseQuery; protected $baseQuery;
// 是否为自关联
protected $selfRelation;
/** /**
* 获取关联的所属模型 * 获取关联的所属模型
@ -47,13 +49,13 @@ abstract class Relation
} }
/** /**
* 获取当前的关联模型 * 获取当前的关联模型对象实例
* @access public * @access public
* @return string * @return Model
*/ */
public function getModel() public function getModel()
{ {
return $this->model; return $this->query->getModel();
} }
/** /**
@ -66,6 +68,28 @@ abstract class Relation
return $this->query; return $this->query;
} }
/**
* 设置当前关联为自关联
* @access public
* @param bool $self 是否自关联
* @return $this
*/
public function selfRelation($self = true)
{
$this->selfRelation = $self;
return $this;
}
/**
* 当前关联是否为自关联
* @access public
* @return bool
*/
public function isSelfRelation()
{
return $this->selfRelation;
}
/** /**
* 封装关联数据集 * 封装关联数据集
* @access public * @access public
@ -108,7 +132,8 @@ abstract class Relation
* @access protected * @access protected
* @return void * @return void
*/ */
abstract protected function baseQuery(); protected function baseQuery()
{}
public function __call($method, $args) public function __call($method, $args)
{ {

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -11,6 +11,7 @@
namespace think\model\relation; namespace think\model\relation;
use think\db\Query;
use think\Loader; use think\Loader;
use think\Model; use think\Model;
@ -51,6 +52,7 @@ class BelongsTo extends OneToOne
call_user_func_array($closure, [ & $this->query]); call_user_func_array($closure, [ & $this->query]);
} }
$relationModel = $this->query $relationModel = $this->query
->removeWhereField($this->localKey)
->where($this->localKey, $this->parent->$foreignKey) ->where($this->localKey, $this->parent->$foreignKey)
->relation($subRelation) ->relation($subRelation)
->find(); ->find();
@ -68,7 +70,6 @@ class BelongsTo extends OneToOne
* @param string $operator 比较操作符 * @param string $operator 比较操作符
* @param integer $count 个数 * @param integer $count 个数
* @param string $id 关联表的统计字段 * @param string $id 关联表的统计字段
* @param string $joinType JOIN类型
* @return Query * @return Query
*/ */
public function has($operator = '>=', $count = 1, $id = '*') public function has($operator = '>=', $count = 1, $id = '*')
@ -79,14 +80,16 @@ class BelongsTo extends OneToOne
/** /**
* 根据关联条件查询当前模型 * 根据关联条件查询当前模型
* @access public * @access public
* @param mixed $where 查询条件(数组或者闭包) * @param mixed $where 查询条件(数组或者闭包)
* @param mixed $fields 字段
* @return Query * @return Query
*/ */
public function hasWhere($where = []) public function hasWhere($where = [], $fields = null)
{ {
$table = $this->query->getTable(); $table = $this->query->getTable();
$model = basename(str_replace('\\', '/', get_class($this->parent))); $model = basename(str_replace('\\', '/', get_class($this->parent)));
$relation = basename(str_replace('\\', '/', $this->model)); $relation = basename(str_replace('\\', '/', $this->model));
if (is_array($where)) { if (is_array($where)) {
foreach ($where as $key => $val) { foreach ($where as $key => $val) {
if (false === strpos($key, '.')) { if (false === strpos($key, '.')) {
@ -95,9 +98,11 @@ class BelongsTo extends OneToOne
} }
} }
} }
$fields = $this->getRelationQueryFields($fields, $model);
return $this->parent->db()->alias($model) return $this->parent->db()->alias($model)
->field($model . '.*') ->field($fields)
->join($table . ' ' . $relation, $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $this->joinType) ->join([$table => $relation], $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $this->joinType)
->where($where); ->where($where);
} }
@ -124,7 +129,8 @@ class BelongsTo extends OneToOne
} }
if (!empty($range)) { if (!empty($range)) {
$data = $this->eagerlyWhere($this, [ $this->query->removeWhereField($localKey);
$data = $this->eagerlyWhere($this->query, [
$localKey => [ $localKey => [
'in', 'in',
$range, $range,
@ -143,12 +149,13 @@ class BelongsTo extends OneToOne
$relationModel->isUpdate(true); $relationModel->isUpdate(true);
} }
if ($relationModel && !empty($this->bindAttr)) { if (!empty($this->bindAttr)) {
// 绑定关联属性 // 绑定关联属性
$this->bindAttr($relationModel, $result, $this->bindAttr); $this->bindAttr($relationModel, $result, $this->bindAttr);
} else {
// 设置关联属性
$result->setRelation($attr, $relationModel);
} }
// 设置关联属性
$result->setRelation($attr, $relationModel);
} }
} }
} }
@ -166,7 +173,8 @@ class BelongsTo extends OneToOne
{ {
$localKey = $this->localKey; $localKey = $this->localKey;
$foreignKey = $this->foreignKey; $foreignKey = $this->foreignKey;
$data = $this->eagerlyWhere($this, [$localKey => $result->$foreignKey], $localKey, $relation, $subRelation, $closure); $this->query->removeWhereField($localKey);
$data = $this->eagerlyWhere($this->query, [$localKey => $result->$foreignKey], $localKey, $relation, $subRelation, $closure);
// 关联模型 // 关联模型
if (!isset($data[$result->$foreignKey])) { if (!isset($data[$result->$foreignKey])) {
$relationModel = null; $relationModel = null;
@ -175,12 +183,13 @@ class BelongsTo extends OneToOne
$relationModel->setParent(clone $result); $relationModel->setParent(clone $result);
$relationModel->isUpdate(true); $relationModel->isUpdate(true);
} }
if ($relationModel && !empty($this->bindAttr)) { if (!empty($this->bindAttr)) {
// 绑定关联属性 // 绑定关联属性
$this->bindAttr($relationModel, $result, $this->bindAttr); $this->bindAttr($relationModel, $result, $this->bindAttr);
} else {
// 设置关联属性
$result->setRelation(Loader::parseName($relation), $relationModel);
} }
// 设置关联属性
$result->setRelation(Loader::parseName($relation), $relationModel);
} }
/** /**
@ -214,4 +223,21 @@ class BelongsTo extends OneToOne
return $this->parent->setRelation($this->relation, null); return $this->parent->setRelation($this->relation, null);
} }
/**
* 执行基础查询(仅执行一次)
* @access protected
* @return void
*/
protected function baseQuery()
{
if (empty($this->baseQuery)) {
if (isset($this->parent->{$this->foreignKey})) {
// 关联查询带入关联条件
$this->query->where($this->localKey, '=', $this->parent->{$this->foreignKey});
}
$this->baseQuery = true;
}
}
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -12,6 +12,7 @@
namespace think\model\relation; namespace think\model\relation;
use think\Collection; use think\Collection;
use think\Db;
use think\db\Query; use think\db\Query;
use think\Exception; use think\Exception;
use think\Loader; use think\Loader;
@ -28,6 +29,8 @@ class BelongsToMany extends Relation
protected $pivotName; protected $pivotName;
// 中间表模型对象 // 中间表模型对象
protected $pivot; protected $pivot;
// 中间表数据名称
protected $pivotDataName = 'pivot';
/** /**
* 构造函数 * 构造函数
@ -52,6 +55,10 @@ class BelongsToMany extends Relation
} }
$this->query = (new $model)->db(); $this->query = (new $model)->db();
$this->pivot = $this->newPivot(); $this->pivot = $this->newPivot();
if ('think\model\Pivot' == get_class($this->pivot)) {
$this->pivot->name($this->middle);
}
} }
/** /**
@ -66,14 +73,46 @@ class BelongsToMany extends Relation
} }
/** /**
* 实例化中间表模型 * 设置中间表数据名称
* @param $data * @access public
* @return mixed * @param string $name
* @return $this
*/ */
protected function newPivot($data = []) public function pivotDataName($name)
{ {
$pivot = $this->pivotName ?: '\\think\\model\\Pivot'; $this->pivotDataName = $name;
return new $pivot($this->parent, $data, $this->middle); return $this;
}
/**
* 获取中间表更新条件
* @param $data
* @return array
*/
protected function getUpdateWhere($data)
{
return [
$this->localKey => $data[$this->localKey],
$this->foreignKey => $data[$this->foreignKey],
];
}
/**
* 实例化中间表模型
* @param array $data
* @param bool $isUpdate
* @return Pivot
* @throws Exception
*/
protected function newPivot($data = [], $isUpdate = false)
{
$class = $this->pivotName ?: '\\think\\model\\Pivot';
$pivot = new $class($data, $this->parent, $this->middle);
if ($pivot instanceof Pivot) {
return $isUpdate ? $pivot->isUpdate(true, $this->getUpdateWhere($data)) : $pivot;
} else {
throw new Exception('pivot model must extends: \think\model\Pivot');
}
} }
/** /**
@ -93,7 +132,7 @@ class BelongsToMany extends Relation
} }
} }
} }
$model->setRelation('pivot', $this->newPivot($pivot)); $model->setRelation($this->pivotDataName, $this->newPivot($pivot, true));
} }
} }
@ -206,11 +245,12 @@ class BelongsToMany extends Relation
/** /**
* 根据关联条件查询当前模型 * 根据关联条件查询当前模型
* @access public * @access public
* @param mixed $where 查询条件(数组或者闭包) * @param mixed $where 查询条件(数组或者闭包)
* @param mixed $fields 字段
* @return Query * @return Query
* @throws Exception * @throws Exception
*/ */
public function hasWhere($where = []) public function hasWhere($where = [], $fields = null)
{ {
throw new Exception('relation not support: hasWhere'); throw new Exception('relation not support: hasWhere');
} }
@ -320,14 +360,22 @@ class BelongsToMany extends Relation
* 获取关联统计子查询 * 获取关联统计子查询
* @access public * @access public
* @param \Closure $closure 闭包 * @param \Closure $closure 闭包
* @param string $name 统计数据别名
* @return string * @return string
*/ */
public function getRelationCountQuery($closure) public function getRelationCountQuery($closure, &$name = null)
{ {
if ($closure) {
$return = call_user_func_array($closure, [ & $this->query]);
if ($return && is_string($return)) {
$name = $return;
}
}
return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
'pivot.' . $this->localKey => [ 'pivot.' . $this->localKey => [
'exp', 'exp',
'=' . $this->parent->getTable() . '.' . $this->parent->getPk(), Db::raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()),
], ],
])->fetchSql()->count(); ])->fetchSql()->count();
} }
@ -358,7 +406,7 @@ class BelongsToMany extends Relation
} }
} }
} }
$set->setRelation('pivot', $this->newPivot($pivot)); $set->setRelation($this->pivotDataName, $this->newPivot($pivot, true));
$data[$pivot[$this->localKey]][] = $set; $data[$pivot[$this->localKey]][] = $set;
} }
return $data; return $data;
@ -384,7 +432,7 @@ class BelongsToMany extends Relation
if (empty($this->baseQuery)) { if (empty($this->baseQuery)) {
$relationFk = $this->query->getPk(); $relationFk = $this->query->getPk();
$query->join($table . ' pivot', 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk) $query->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk)
->where($condition); ->where($condition);
} }
return $query; return $query;
@ -461,7 +509,7 @@ class BelongsToMany extends Relation
foreach ($ids as $id) { foreach ($ids as $id) {
$pivot[$this->foreignKey] = $id; $pivot[$this->foreignKey] = $id;
$this->pivot->insert($pivot, true); $this->pivot->insert($pivot, true);
$result[] = $this->newPivot($pivot); $result[] = $this->newPivot($pivot, true);
} }
if (count($result) == 1) { if (count($result) == 1) {
// 返回中间表模型对象 // 返回中间表模型对象
@ -473,6 +521,29 @@ class BelongsToMany extends Relation
} }
} }
/**
* 判断是否存在关联数据
* @access public
* @param mixed $data 数据 可以使用关联模型对象 或者 关联对象的主键
* @return Pivot
* @throws Exception
*/
public function attached($data)
{
if ($data instanceof Model) {
$relationFk = $data->getPk();
$id = $data->$relationFk;
} else {
$id = $data;
}
$pk = $this->parent->getPk();
$pivot = $this->pivot->where($this->localKey, $this->parent->$pk)->where($this->foreignKey, $id)->find();
return $pivot ?: false;
}
/** /**
* 解除关联的一个中间表数据 * 解除关联的一个中间表数据
* @access public * @access public
@ -565,7 +636,7 @@ class BelongsToMany extends Relation
if (empty($this->baseQuery) && $this->parent->getData()) { if (empty($this->baseQuery) && $this->parent->getData()) {
$pk = $this->parent->getPk(); $pk = $this->parent->getPk();
$table = $this->pivot->getTable(); $table = $this->pivot->getTable();
$this->query->join($table . ' pivot', 'pivot.' . $this->foreignKey . '=' . $this->query->getTable() . '.' . $this->query->getPk())->where('pivot.' . $this->localKey, $this->parent->$pk); $this->query->join([$table => 'pivot'], 'pivot.' . $this->foreignKey . '=' . $this->query->getTable() . '.' . $this->query->getPk())->where('pivot.' . $this->localKey, $this->parent->$pk);
$this->baseQuery = true; $this->baseQuery = true;
} }
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -24,7 +24,7 @@ class HasMany extends Relation
* @param Model $parent 上级模型对象 * @param Model $parent 上级模型对象
* @param string $model 模型名 * @param string $model 模型名
* @param string $foreignKey 关联外键 * @param string $foreignKey 关联外键
* @param string $localKey 关联主键 * @param string $localKey 当前模型主键
*/ */
public function __construct(Model $parent, $model, $foreignKey, $localKey) public function __construct(Model $parent, $model, $foreignKey, $localKey)
{ {
@ -77,7 +77,7 @@ class HasMany extends Relation
} }
if (!empty($range)) { if (!empty($range)) {
$data = $this->eagerlyOneToMany($this, [ $data = $this->eagerlyOneToMany($this->query, [
$this->foreignKey => [ $this->foreignKey => [
'in', 'in',
$range, $range,
@ -114,7 +114,7 @@ class HasMany extends Relation
$localKey = $this->localKey; $localKey = $this->localKey;
if (isset($result->$localKey)) { if (isset($result->$localKey)) {
$data = $this->eagerlyOneToMany($this, [$this->foreignKey => $result->$localKey], $relation, $subRelation, $closure); $data = $this->eagerlyOneToMany($this->query, [$this->foreignKey => $result->$localKey], $relation, $subRelation, $closure);
// 关联数据封装 // 关联数据封装
if (!isset($data[$result->$localKey])) { if (!isset($data[$result->$localKey])) {
$data[$result->$localKey] = []; $data[$result->$localKey] = [];
@ -143,7 +143,7 @@ class HasMany extends Relation
if ($closure) { if ($closure) {
call_user_func_array($closure, [ & $this->query]); call_user_func_array($closure, [ & $this->query]);
} }
$count = $this->query->where([$this->foreignKey => $result->$localKey])->count(); $count = $this->query->where($this->foreignKey, $result->$localKey)->count();
} }
return $count; return $count;
} }
@ -152,20 +152,19 @@ class HasMany extends Relation
* 创建关联统计子查询 * 创建关联统计子查询
* @access public * @access public
* @param \Closure $closure 闭包 * @param \Closure $closure 闭包
* @param string $name 统计数据别名
* @return string * @return string
*/ */
public function getRelationCountQuery($closure) public function getRelationCountQuery($closure, &$name = null)
{ {
if ($closure) { if ($closure) {
call_user_func_array($closure, [ & $this->query]); $return = call_user_func_array($closure, [ & $this->query]);
if ($return && is_string($return)) {
$name = $return;
}
} }
$localKey = $this->localKey ?: $this->parent->getPk();
return $this->query->where([ return $this->query->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $localKey)->fetchSql()->count();
$this->foreignKey => [
'exp',
'=' . $this->parent->getTable() . '.' . $this->parent->getPk(),
],
])->fetchSql()->count();
} }
/** /**
@ -185,7 +184,7 @@ class HasMany extends Relation
if ($closure) { if ($closure) {
call_user_func_array($closure, [ & $model]); call_user_func_array($closure, [ & $model]);
} }
$list = $model->where($where)->with($subRelation)->select(); $list = $model->removeWhereField($foreignKey)->where($where)->with($subRelation)->select();
// 组装模型数据 // 组装模型数据
$data = []; $data = [];
@ -206,12 +205,31 @@ class HasMany extends Relation
if ($data instanceof Model) { if ($data instanceof Model) {
$data = $data->getData(); $data = $data->getData();
} }
// 保存关联表数据 // 保存关联表数据
$model = new $this->model;
$data[$this->foreignKey] = $this->parent->{$this->localKey}; $data[$this->foreignKey] = $this->parent->{$this->localKey};
$model = new $this->model();
return $model->save($data) ? $model : false; return $model->save($data) ? $model : false;
} }
/**
* 创建关联对象实例
* @param array $data
* @return Model
*/
public function make($data = [])
{
if ($data instanceof Model) {
$data = $data->getData();
}
// 保存关联表数据
$data[$this->foreignKey] = $this->parent->{$this->localKey};
return new $this->model($data);
}
/** /**
* 批量保存当前关联数据对象 * 批量保存当前关联数据对象
* @access public * @access public
@ -238,24 +256,31 @@ class HasMany extends Relation
*/ */
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
{ {
$table = $this->query->getTable(); $table = $this->query->getTable();
return $this->parent->db()->alias('a') $model = basename(str_replace('\\', '/', get_class($this->parent)));
->join($table . ' b', 'a.' . $this->localKey . '=b.' . $this->foreignKey, $joinType) $relation = basename(str_replace('\\', '/', $this->model));
->group('b.' . $this->foreignKey)
return $this->parent->db()
->alias($model)
->field($model . '.*')
->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType)
->group($relation . '.' . $this->foreignKey)
->having('count(' . $id . ')' . $operator . $count); ->having('count(' . $id . ')' . $operator . $count);
} }
/** /**
* 根据关联条件查询当前模型 * 根据关联条件查询当前模型
* @access public * @access public
* @param mixed $where 查询条件(数组或者闭包) * @param mixed $where 查询条件(数组或者闭包)
* @param mixed $fields 字段
* @return Query * @return Query
*/ */
public function hasWhere($where = []) public function hasWhere($where = [], $fields = null)
{ {
$table = $this->query->getTable(); $table = $this->query->getTable();
$model = basename(str_replace('\\', '/', get_class($this->parent))); $model = basename(str_replace('\\', '/', get_class($this->parent)));
$relation = basename(str_replace('\\', '/', $this->model)); $relation = basename(str_replace('\\', '/', $this->model));
if (is_array($where)) { if (is_array($where)) {
foreach ($where as $key => $val) { foreach ($where as $key => $val) {
if (false === strpos($key, '.')) { if (false === strpos($key, '.')) {
@ -264,9 +289,13 @@ class HasMany extends Relation
} }
} }
} }
$fields = $this->getRelationQueryFields($fields, $model);
return $this->parent->db()->alias($model) return $this->parent->db()->alias($model)
->field($model . '.*') ->field($fields)
->join($table . ' ' . $relation, $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey) ->group($model . '.' . $this->localKey)
->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey)
->where($where); ->where($where);
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -77,10 +77,11 @@ class HasManyThrough extends Relation
/** /**
* 根据关联条件查询当前模型 * 根据关联条件查询当前模型
* @access public * @access public
* @param mixed $where 查询条件(数组或者闭包) * @param mixed $where 查询条件(数组或者闭包)
* @param mixed $fields 字段
* @return Query * @return Query
*/ */
public function hasWhere($where = []) public function hasWhere($where = [], $fields = null)
{ {
throw new Exception('relation not support: hasWhere'); throw new Exception('relation not support: hasWhere');
} }
@ -92,10 +93,9 @@ class HasManyThrough extends Relation
* @param string $relation 当前关联名 * @param string $relation 当前关联名
* @param string $subRelation 子关联名 * @param string $subRelation 子关联名
* @param \Closure $closure 闭包 * @param \Closure $closure 闭包
* @param string $class 数据集对象名 为空表示数组
* @return void * @return void
*/ */
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class) public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
{} {}
/** /**
@ -105,10 +105,9 @@ class HasManyThrough extends Relation
* @param string $relation 当前关联名 * @param string $relation 当前关联名
* @param string $subRelation 子关联名 * @param string $subRelation 子关联名
* @param \Closure $closure 闭包 * @param \Closure $closure 闭包
* @param string $class 数据集对象名 为空表示数组
* @return void * @return void
*/ */
public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class) public function eagerlyResult(&$result, $relation, $subRelation, $closure)
{} {}
/** /**
@ -121,6 +120,18 @@ class HasManyThrough extends Relation
public function relationCount($result, $closure) public function relationCount($result, $closure)
{} {}
/**
* 创建关联统计子查询
* @access public
* @param \Closure $closure 闭包
* @param string $name 统计数据别名
* @return string
*/
public function getRelationCountQuery($closure, &$name = null)
{
throw new Exception('relation not support: withCount');
}
/** /**
* 执行基础查询(进执行一次) * 执行基础查询(进执行一次)
* @access protected * @access protected

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -23,7 +23,7 @@ class HasOne extends OneToOne
* @param Model $parent 上级模型对象 * @param Model $parent 上级模型对象
* @param string $model 模型名 * @param string $model 模型名
* @param string $foreignKey 关联外键 * @param string $foreignKey 关联外键
* @param string $localKey 关联主键 * @param string $localKey 当前模型主键
* @param string $joinType JOIN类型 * @param string $joinType JOIN类型
*/ */
public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER') public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER')
@ -50,7 +50,11 @@ class HasOne extends OneToOne
call_user_func_array($closure, [ & $this->query]); call_user_func_array($closure, [ & $this->query]);
} }
// 判断关联类型执行查询 // 判断关联类型执行查询
$relationModel = $this->query->where($this->foreignKey, $this->parent->$localKey)->relation($subRelation)->find(); $relationModel = $this->query
->removeWhereField($this->foreignKey)
->where($this->foreignKey, $this->parent->$localKey)
->relation($subRelation)
->find();
if ($relationModel) { if ($relationModel) {
$relationModel->setParent(clone $this->parent); $relationModel->setParent(clone $this->parent);
@ -67,25 +71,30 @@ class HasOne extends OneToOne
public function has() public function has()
{ {
$table = $this->query->getTable(); $table = $this->query->getTable();
$model = basename(str_replace('\\', '/', get_class($this->parent)));
$relation = basename(str_replace('\\', '/', $this->model));
$localKey = $this->localKey; $localKey = $this->localKey;
$foreignKey = $this->foreignKey; $foreignKey = $this->foreignKey;
return $this->parent->db()->alias('a') return $this->parent->db()
->whereExists(function ($query) use ($table, $localKey, $foreignKey) { ->alias($model)
$query->table([$table => 'b'])->field('b.' . $foreignKey)->whereExp('a.' . $localKey, '=b.' . $foreignKey); ->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey) {
$query->table([$table => $relation])->field($relation . '.' . $foreignKey)->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey);
}); });
} }
/** /**
* 根据关联条件查询当前模型 * 根据关联条件查询当前模型
* @access public * @access public
* @param mixed $where 查询条件(数组或者闭包) * @param mixed $where 查询条件(数组或者闭包)
* @param mixed $fields 字段
* @return Query * @return Query
*/ */
public function hasWhere($where = []) public function hasWhere($where = [], $fields = null)
{ {
$table = $this->query->getTable(); $table = $this->query->getTable();
$model = basename(str_replace('\\', '/', get_class($this->parent))); $model = basename(str_replace('\\', '/', get_class($this->parent)));
$relation = basename(str_replace('\\', '/', $this->model)); $relation = basename(str_replace('\\', '/', $this->model));
if (is_array($where)) { if (is_array($where)) {
foreach ($where as $key => $val) { foreach ($where as $key => $val) {
if (false === strpos($key, '.')) { if (false === strpos($key, '.')) {
@ -94,9 +103,11 @@ class HasOne extends OneToOne
} }
} }
} }
$fields = $this->getRelationQueryFields($fields, $model);
return $this->parent->db()->alias($model) return $this->parent->db()->alias($model)
->field($model . '.*') ->field($fields)
->join($table . ' ' . $relation, $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $this->joinType) ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $this->joinType)
->where($where); ->where($where);
} }
@ -123,7 +134,8 @@ class HasOne extends OneToOne
} }
if (!empty($range)) { if (!empty($range)) {
$data = $this->eagerlyWhere($this, [ $this->query->removeWhereField($foreignKey);
$data = $this->eagerlyWhere($this->query, [
$foreignKey => [ $foreignKey => [
'in', 'in',
$range, $range,
@ -140,13 +152,14 @@ class HasOne extends OneToOne
$relationModel = $data[$result->$localKey]; $relationModel = $data[$result->$localKey];
$relationModel->setParent(clone $result); $relationModel->setParent(clone $result);
$relationModel->isUpdate(true); $relationModel->isUpdate(true);
if (!empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($relationModel, $result, $this->bindAttr);
}
} }
// 设置关联属性 if (!empty($this->bindAttr)) {
$result->setRelation($attr, $relationModel); // 绑定关联属性
$this->bindAttr($relationModel, $result, $this->bindAttr);
} else {
// 设置关联属性
$result->setRelation($attr, $relationModel);
}
} }
} }
} }
@ -164,7 +177,8 @@ class HasOne extends OneToOne
{ {
$localKey = $this->localKey; $localKey = $this->localKey;
$foreignKey = $this->foreignKey; $foreignKey = $this->foreignKey;
$data = $this->eagerlyWhere($this, [$foreignKey => $result->$localKey], $foreignKey, $relation, $subRelation, $closure); $this->query->removeWhereField($foreignKey);
$data = $this->eagerlyWhere($this->query, [$foreignKey => $result->$localKey], $foreignKey, $relation, $subRelation, $closure);
// 关联模型 // 关联模型
if (!isset($data[$result->$localKey])) { if (!isset($data[$result->$localKey])) {
@ -173,13 +187,29 @@ class HasOne extends OneToOne
$relationModel = $data[$result->$localKey]; $relationModel = $data[$result->$localKey];
$relationModel->setParent(clone $result); $relationModel->setParent(clone $result);
$relationModel->isUpdate(true); $relationModel->isUpdate(true);
if (!empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($relationModel, $result, $this->bindAttr);
}
} }
if (!empty($this->bindAttr)) {
$result->setRelation(Loader::parseName($relation), $relationModel); // 绑定关联属性
$this->bindAttr($relationModel, $result, $this->bindAttr);
} else {
$result->setRelation(Loader::parseName($relation), $relationModel);
}
} }
/**
* 执行基础查询(仅执行一次)
* @access protected
* @return void
*/
protected function baseQuery()
{
if (empty($this->baseQuery)) {
if (isset($this->parent->{$this->localKey})) {
// 关联查询带入关联条件
$this->query->where($this->foreignKey, '=', $this->parent->{$this->localKey});
}
$this->baseQuery = true;
}
}
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -11,6 +11,7 @@
namespace think\model\relation; namespace think\model\relation;
use think\Db;
use think\db\Query; use think\db\Query;
use think\Exception; use think\Exception;
use think\Loader; use think\Loader;
@ -82,10 +83,11 @@ class MorphMany extends Relation
/** /**
* 根据关联条件查询当前模型 * 根据关联条件查询当前模型
* @access public * @access public
* @param mixed $where 查询条件(数组或者闭包) * @param mixed $where 查询条件(数组或者闭包)
* @param mixed $fields 字段
* @return Query * @return Query
*/ */
public function hasWhere($where = []) public function hasWhere($where = [], $fields = null)
{ {
throw new Exception('relation not support: hasWhere'); throw new Exception('relation not support: hasWhere');
} }
@ -186,21 +188,25 @@ class MorphMany extends Relation
} }
/** /**
* 获取关联统计子查询 * 创建关联统计子查询
* @access public * @access public
* @param \Closure $closure 闭包 * @param \Closure $closure 闭包
* @param string $name 统计数据别名
* @return string * @return string
*/ */
public function getRelationCountQuery($closure) public function getRelationCountQuery($closure, &$name = null)
{ {
if ($closure) { if ($closure) {
call_user_func_array($closure, [ & $this->query]); $return = call_user_func_array($closure, [ & $this->query]);
if ($return && is_string($return)) {
$name = $return;
}
} }
return $this->query->where([ return $this->query->where([
$this->morphKey => [ $this->morphKey => [
'exp', 'exp',
'=' . $this->parent->getTable() . '.' . $this->parent->getPk(), Db::raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()),
], ],
$this->morphType => $this->type, $this->morphType => $this->type,
])->fetchSql()->count(); ])->fetchSql()->count();
@ -242,13 +248,36 @@ class MorphMany extends Relation
if ($data instanceof Model) { if ($data instanceof Model) {
$data = $data->getData(); $data = $data->getData();
} }
// 保存关联表数据 // 保存关联表数据
$pk = $this->parent->getPk(); $pk = $this->parent->getPk();
$model = new $this->model;
$data[$this->morphKey] = $this->parent->$pk; $data[$this->morphKey] = $this->parent->$pk;
$data[$this->morphType] = $this->type; $data[$this->morphType] = $this->type;
return $model->save($data) ? $model : false;
$model = new $this->model();
return $model->save() ? $model : false;
}
/**
* 创建关联对象实例
* @param array $data
* @return Model
*/
public function make($data = [])
{
if ($data instanceof Model) {
$data = $data->getData();
}
// 保存关联表数据
$pk = $this->parent->getPk();
$data[$this->morphKey] = $this->parent->$pk;
$data[$this->morphType] = $this->type;
return new $this->model($data);
} }
/** /**

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -81,10 +81,11 @@ class MorphOne extends Relation
/** /**
* 根据关联条件查询当前模型 * 根据关联条件查询当前模型
* @access public * @access public
* @param mixed $where 查询条件(数组或者闭包) * @param mixed $where 查询条件(数组或者闭包)
* @param mixed $fields 字段
* @return Query * @return Query
*/ */
public function hasWhere($where = []) public function hasWhere($where = [], $fields = null)
{ {
throw new Exception('relation not support: hasWhere'); throw new Exception('relation not support: hasWhere');
} }
@ -201,13 +202,35 @@ class MorphOne extends Relation
if ($data instanceof Model) { if ($data instanceof Model) {
$data = $data->getData(); $data = $data->getData();
} }
// 保存关联表数据 // 保存关联表数据
$pk = $this->parent->getPk(); $pk = $this->parent->getPk();
$model = new $this->model;
$data[$this->morphKey] = $this->parent->$pk; $data[$this->morphKey] = $this->parent->$pk;
$data[$this->morphType] = $this->type; $data[$this->morphType] = $this->type;
return $model->save($data) ? $model : false;
$model = new $this->model();
return $model->save() ? $model : false;
}
/**
* 创建关联对象实例
* @param array $data
* @return Model
*/
public function make($data = [])
{
if ($data instanceof Model) {
$data = $data->getData();
}
// 保存关联表数据
$pk = $this->parent->getPk();
$data[$this->morphKey] = $this->parent->$pk;
$data[$this->morphType] = $this->type;
return new $this->model($data);
} }
/** /**
@ -226,4 +249,15 @@ class MorphOne extends Relation
} }
} }
/**
* 创建关联统计子查询
* @access public
* @param \Closure $closure 闭包
* @param string $name 统计数据别名
* @return string
*/
public function getRelationCountQuery($closure, &$name = null)
{
throw new Exception('relation not support: withCount');
}
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -43,6 +43,18 @@ class MorphTo extends Relation
$this->relation = $relation; $this->relation = $relation;
} }
/**
* 获取当前的关联模型类的实例
* @access public
* @return Model
*/
public function getModel()
{
$morphType = $this->morphType;
$model = $this->parseModel($this->parent->$morphType);
return (new $model);
}
/** /**
* 延迟获取关联数据 * 延迟获取关联数据
* @param string $subRelation 子关联名 * @param string $subRelation 子关联名
@ -82,17 +94,18 @@ class MorphTo extends Relation
/** /**
* 根据关联条件查询当前模型 * 根据关联条件查询当前模型
* @access public * @access public
* @param mixed $where 查询条件(数组或者闭包) * @param mixed $where 查询条件(数组或者闭包)
* @param mixed $fields 字段
* @return Query * @return Query
*/ */
public function hasWhere($where = []) public function hasWhere($where = [], $fields = null)
{ {
throw new Exception('relation not support: hasWhere'); throw new Exception('relation not support: hasWhere');
} }
/** /**
* 解析模型的完整命名空间 * 解析模型的完整命名空间
* @access public * @access protected
* @param string $model 模型名(或者完整类名) * @param string $model 模型名(或者完整类名)
* @return string * @return string
*/ */
@ -238,17 +251,18 @@ class MorphTo extends Relation
/** /**
* 添加关联数据 * 添加关联数据
* @access public * @access public
* @param Model $model 关联模型对象 * @param Model $model 关联模型对象
* @param string $type 多态类型
* @return Model * @return Model
*/ */
public function associate($model) public function associate($model, $type = '')
{ {
$morphKey = $this->morphKey; $morphKey = $this->morphKey;
$morphType = $this->morphType; $morphType = $this->morphType;
$pk = $model->getPk(); $pk = $model->getPk();
$this->parent->setAttr($morphKey, $model->$pk); $this->parent->setAttr($morphKey, $model->$pk);
$this->parent->setAttr($morphType, get_class($model)); $this->parent->setAttr($morphType, $type ?: get_class($model));
$this->parent->save(); $this->parent->save();
return $this->parent->setRelation($this->relation, $model); return $this->parent->setRelation($this->relation, $model);
@ -272,10 +286,14 @@ class MorphTo extends Relation
} }
/** /**
* 执行基础查询(进执行一次) * 创建关联统计子查询
* @access protected * @access public
* @return void * @param \Closure $closure 闭包
* @param string $name 统计数据别名
* @return string
*/ */
protected function baseQuery() public function getRelationCountQuery($closure, &$name = null)
{} {
throw new Exception('relation not support: withCount');
}
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -57,18 +57,18 @@ abstract class OneToOne extends Relation
*/ */
public function eagerly(Query $query, $relation, $subRelation, $closure, $first) public function eagerly(Query $query, $relation, $subRelation, $closure, $first)
{ {
$name = Loader::parseName(basename(str_replace('\\', '/', $query->getModel()))); $name = Loader::parseName(basename(str_replace('\\', '/', get_class($query->getModel()))));
$alias = $name;
if ($first) { if ($first) {
$table = $query->getTable(); $table = $query->getTable();
$query->table([$table => $alias]); $query->table([$table => $name]);
if ($query->getOptions('field')) { if ($query->getOptions('field')) {
$field = $query->getOptions('field'); $field = $query->getOptions('field');
$query->removeOption('field'); $query->removeOption('field');
} else { } else {
$field = true; $field = true;
} }
$query->field($field, false, $table, $alias); $query->field($field, false, $table, $name);
$field = null; $field = null;
} }
@ -78,9 +78,9 @@ abstract class OneToOne extends Relation
$query->via($joinAlias); $query->via($joinAlias);
if ($this instanceof BelongsTo) { if ($this instanceof BelongsTo) {
$query->join($joinTable . ' ' . $joinAlias, $alias . '.' . $this->foreignKey . '=' . $joinAlias . '.' . $this->localKey, $this->joinType); $query->join([$joinTable => $joinAlias], $name . '.' . $this->foreignKey . '=' . $joinAlias . '.' . $this->localKey, $this->joinType);
} else { } else {
$query->join($joinTable . ' ' . $joinAlias, $alias . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey, $this->joinType); $query->join([$joinTable => $joinAlias], $name . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey, $this->joinType);
} }
if ($closure) { if ($closure) {
@ -214,6 +214,16 @@ abstract class OneToOne extends Relation
return $this; return $this;
} }
/**
* 获取绑定属性
* @access public
* @return array
*/
public function getBindAttr()
{
return $this->bindAttr;
}
/** /**
* 关联统计 * 关联统计
* @access public * @access public
@ -276,7 +286,7 @@ abstract class OneToOne extends Relation
if (isset($result->$key)) { if (isset($result->$key)) {
throw new Exception('bind attr has exists:' . $key); throw new Exception('bind attr has exists:' . $key);
} else { } else {
$result->setAttr($key, $model->$attr); $result->setAttr($key, $model ? $model->$attr : null);
} }
} }
} }
@ -294,6 +304,8 @@ abstract class OneToOne extends Relation
*/ */
protected function eagerlyWhere($model, $where, $key, $relation, $subRelation = '', $closure = false) protected function eagerlyWhere($model, $where, $key, $relation, $subRelation = '', $closure = false)
{ {
$this->baseQuery = true;
// 预载入关联查询 支持嵌套预载入 // 预载入关联查询 支持嵌套预载入
if ($closure) { if ($closure) {
call_user_func_array($closure, [ & $model]); call_user_func_array($closure, [ & $model]);
@ -312,10 +324,14 @@ abstract class OneToOne extends Relation
} }
/** /**
* 执行基础查询(进执行一次) * 创建关联统计子查询
* @access protected * @access public
* @return void * @param \Closure $closure 闭包
* @param string $name 统计数据别名
* @return string
*/ */
protected function baseQuery() public function getRelationCountQuery($closure, &$name = null)
{} {
throw new Exception('relation not support: withCount');
}
} }

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -67,7 +67,11 @@ class Redirect extends Response
*/ */
public function getTargetUrl() public function getTargetUrl()
{ {
return strpos($this->data, '://') ? $this->data : Url::build($this->data, $this->params); if (strpos($this->data, '://') || (0 === strpos($this->data, '/') && empty($this->params))) {
return $this->data;
} else {
return Url::build($this->data, $this->params);
}
} }
public function params($params = []) public function params($params = [])

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -11,6 +11,8 @@
namespace think\response; namespace think\response;
use think\Collection;
use think\Model;
use think\Response; use think\Response;
class Xml extends Response class Xml extends Response
@ -81,6 +83,11 @@ class Xml extends Response
protected function dataToXml($data, $item, $id) protected function dataToXml($data, $item, $id)
{ {
$xml = $attr = ''; $xml = $attr = '';
if ($data instanceof Collection || $data instanceof Model) {
$data = $data->toArray();
}
foreach ($data as $key => $val) { foreach ($data as $key => $val) {
if (is_numeric($key)) { if (is_numeric($key)) {
$id && $attr = " {$id}=\"{$key}\""; $id && $attr = " {$id}=\"{$key}\"";

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ] // | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

Some files were not shown because too many files have changed in this diff Show More