更新thinkphp 框架

This commit is contained in:
sugar1569 2018-12-22 16:28:06 +08:00
parent 535f3823d9
commit cbc9236d76
102 changed files with 3535 additions and 2032 deletions

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.11'); define('THINK_VERSION', '5.0.21');
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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -48,6 +48,7 @@ return [
'KVDB init error' => '没有初始化KVDB请在SAE管理平台初始化KVDB服务', 'KVDB init error' => '没有初始化KVDB请在SAE管理平台初始化KVDB服务',
'fields not exists' => '数据表字段不存在', 'fields not exists' => '数据表字段不存在',
'where express error' => '查询表达式错误', 'where express error' => '查询表达式错误',
'not support data' => '不支持的数据表达式',
'no data to update' => '没有任何数据需要更新', 'no data to update' => '没有任何数据需要更新',
'miss data to insert' => '缺少需要写入的数据', 'miss data to insert' => '缺少需要写入的数据',
'miss complex primary data' => '缺少复合主键数据', 'miss complex primary data' => '缺少复合主键数据',
@ -66,4 +67,70 @@ return [
'relation data not exists' => '关联数据不存在', 'relation data not exists' => '关联数据不存在',
'relation not support' => '关联不支持', 'relation not support' => '关联不支持',
'chunk not support order' => 'Chunk不支持调用order方法', '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

@ -552,6 +552,7 @@ class App
// 获取控制器名 // 获取控制器名
$controller = strip_tags($result[1] ?: $config['default_controller']); $controller = strip_tags($result[1] ?: $config['default_controller']);
$controller = $convert ? strtolower($controller) : $controller; $controller = $convert ? strtolower($controller) : $controller;
if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) { if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) {
throw new HttpException(404, 'controller not exists:' . $controller); throw new HttpException(404, 'controller not exists:' . $controller);
} }

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,21 +15,27 @@ 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(
'应用目录[' . APP_PATH . ']不可写,目录无法自动生成!<BR>请手动生成项目目录~',
10006
);
} }
foreach ($build as $module => $list) { foreach ($build as $module => $list) {
if ('__dir__' == $module) { if ('__dir__' == $module) {
// 创建目录列表 // 创建目录列表
@ -42,8 +48,10 @@ class Build
self::module($module, $list, $namespace, $suffix); self::module($module, $list, $namespace, $suffix);
} }
} }
// 解除锁定 // 解除锁定
unlink($lockfile); unlink($lock);
}
} }
/** /**
@ -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" : ''
);
} }
} }
} }
@ -92,27 +103,29 @@ class Build
*/ */
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,7 +135,10 @@ 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 {
@ -132,6 +148,7 @@ class Build
$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,7 +176,7 @@ class Build
/** /**
* 创建模块的欢迎页面 * 创建模块的欢迎页面
* @access public * @access protected
* @param string $module 模块名 * @param string $module 模块名
* @param string $namespace 应用类库命名空间 * @param string $namespace 应用类库命名空间
* @param bool $suffix 类库后缀 * @param bool $suffix 类库后缀
@ -167,10 +184,19 @@ class Build
*/ */
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,19 +15,28 @@ use think\cache\Driver;
class Cache class Cache
{ {
protected static $instance = []; /**
* @var array 缓存的实例
*/
public static $instance = [];
/**
* @var int 缓存读取次数
*/
public static $readTimes = 0; public static $readTimes = 0;
/**
* @var int 缓存写入次数
*/
public static $writeTimes = 0; public static $writeTimes = 0;
/** /**
* 操作句柄 * @var object 操作句柄
* @var object
* @access protected
*/ */
protected static $handler; public static $handler;
/** /**
* 连接缓存 * 连接缓存驱动
* @access public * @access public
* @param array $options 配置数组 * @param array $options 配置数组
* @param bool|string $name 缓存连接标识 true 强制重新连接 * @param bool|string $name 缓存连接标识 true 强制重新连接
@ -36,21 +45,26 @@ class Cache
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];
} }
@ -63,16 +77,17 @@ class Cache
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;
} }
@ -87,6 +102,7 @@ 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();
} }
@ -99,6 +115,7 @@ class Cache
public static function has($name) public static function has($name)
{ {
self::$readTimes++; self::$readTimes++;
return self::init()->has($name); return self::init()->has($name);
} }
@ -112,6 +129,7 @@ class Cache
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);
} }
@ -126,6 +144,7 @@ class Cache
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);
} }
@ -139,6 +158,7 @@ class Cache
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);
} }
@ -152,6 +172,7 @@ class Cache
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);
} }
@ -164,6 +185,7 @@ class Cache
public static function rm($name) public static function rm($name)
{ {
self::$writeTimes++; self::$writeTimes++;
return self::init()->rm($name); return self::init()->rm($name);
} }
@ -176,6 +198,7 @@ class Cache
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);
} }
@ -189,6 +212,7 @@ class Cache
{ {
self::$readTimes++; self::$readTimes++;
self::$writeTimes++; self::$writeTimes++;
return self::init()->pull($name); return self::init()->pull($name);
} }
@ -203,6 +227,7 @@ class Cache
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);
} }

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,41 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
} }
/** /**
* 删除数组的最后一个元素(出栈) * 合并数组并返回一个新的 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 +143,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 +153,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,33 +225,21 @@ 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) {
$result = $callback($item, $key); $result = $callback($item, $key);
if (false === $result) { if (false === $result) {
break; break;
} elseif (!is_object($item)) { }
if (!is_object($item)) {
$this->items[$key] = $result; $this->items[$key] = $result;
} }
} }
@ -201,46 +248,47 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
} }
/** /**
* 用回调函数过滤数组中的元素 * 用回调函数过滤数据中的元素
* @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;
@ -249,34 +297,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) {
return $a == $b ? 0 : (($a < $b) ? -1 : 1);
};
$callback ? uasort($items, $callback) : uasort($items, function ($a, $b) { uasort($items, $callback);
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
});
return new static($items); return new static($items);
} }
/** /**
* 将数组打乱 * 将数据打乱后组成新的 Collection 实例
* * @access public
* @return static * @return static
*/ */
public function shuffle() public function shuffle()
@ -284,16 +328,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)
@ -301,17 +344,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)) {
@ -321,24 +382,42 @@ 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();
@ -355,22 +434,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,22 +13,32 @@ 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] = [];
}
} }
/** /**
* 解析配置文件或内容 * 解析配置文件或内容
* @access public
* @param string $config 配置文件路径或内容 * @param string $config 配置文件路径或内容
* @param string $type 配置解析类型 * @param string $type 配置解析类型
* @param string $name 配置名(如设置即表示二级配置) * @param string $name 配置名(如设置即表示二级配置)
@ -38,15 +48,19 @@ class Config
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格式
* @access public
* @param string $file 配置文件名 * @param string $file 配置文件名
* @param string $name 配置名(如设置即表示二级配置) * @param string $name 配置名(如设置即表示二级配置)
* @param string $range 作用域 * @param string $range 作用域
@ -55,26 +69,30 @@ class Config
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')) { }
if ('yaml' == $type && function_exists('yaml_parse_file')) {
return self::set(yaml_parse_file($file), $name, $range); return self::set(yaml_parse_file($file), $name, $range);
} else { }
return self::parse($file, $type, $name, $range); return self::parse($file, $type, $name, $range);
} }
} else {
return self::$config[$range]; return self::$config[$range];
} }
}
/** /**
* 检测配置是否存在 * 检测配置是否存在
* @access public
* @param string $name 配置参数名(支持二级配置 . 号分割) * @param string $name 配置参数名(支持二级配置 . 号分割)
* @param string $range 作用域 * @param string $range 作用域
* @return bool * @return bool
@ -85,15 +103,16 @@ 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); $name = explode('.', $name, 2);
return isset(self::$config[$range][strtolower($name[0])][$name[1]]); return isset(self::$config[$range][strtolower($name[0])][$name[1]]);
} }
}
/** /**
* 获取配置参数 为空则获取所有配置 * 获取配置参数 为空则获取所有配置
* @access public
* @param string $name 配置参数名(支持二级配置 . 号分割) * @param string $name 配置参数名(支持二级配置 . 号分割)
* @param string $range 作用域 * @param string $range 作用域
* @return mixed * @return mixed
@ -101,15 +120,18 @@ class Config
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 = explode('.', $name, 2);
$name[0] = strtolower($name[0]); $name[0] = strtolower($name[0]);
@ -122,12 +144,14 @@ class Config
is_file($file) && self::load($file, $name[0]); 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; return isset(self::$config[$range][$name[0]][$name[1]]) ?
} self::$config[$range][$name[0]][$name[1]] :
null;
} }
/** /**
* 设置配置参数 name 为数组则为批量设置 * 设置配置参数 name 为数组则为批量设置
* @access public
* @param string|array $name 配置参数名(支持二级配置 . 号分割) * @param string|array $name 配置参数名(支持二级配置 . 号分割)
* @param mixed $value 配置值 * @param mixed $value 配置值
* @param string $range 作用域 * @param string $range 作用域
@ -136,39 +160,51 @@ class Config
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) : $name; array_merge(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(
self::$config[$range], array_change_key_case($name)
);
}
// 为空直接返回已有配置 // 为空直接返回已有配置
return self::$config[$range]; 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;
/**
* @var bool 是否自动退出执行
*/
private $autoExit = true; 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
* @access public
* @param bool $run 是否运行 Console
* @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 array $parameters
* @param string $driver * @param string $driver
* @return Output|Buffer * @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,6 +421,7 @@ class Console
/** /**
* 获取指令 * 获取指令
* @access public
* @param string $name 指令名称 * @param string $name 指令名称
* @return Command * @return Command
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
@ -343,7 +429,9 @@ class Console
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,6 +451,7 @@ class Console
/** /**
* 某个指令是否存在 * 某个指令是否存在
* @access public
* @param string $name 指令名称 * @param string $name 指令名称
* @return bool * @return bool
*/ */
@ -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);
$allNamespaces = $this->getNamespaces();
$namespaces = preg_grep('{^' . $expr . '}', $allNamespaces); $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
/** /**
* 查找指令 * 查找指令
* @access public
* @param string $name 名称或者别名 * @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);
$allCommands = array_keys($this->commands);
$commands = preg_grep('{^' . $expr . '}', $allCommands); $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
/** /**
* 获取所有的指令 * 获取所有的指令
* @access public
* @param string $namespace 命名空间 * @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
} }
/** /**
* 配置基于用户的参数和选项的输入和输出实例。 * 配置基于用户的参数和选项的输入和输出实例
* @access protected
* @param Input $input 输入实例 * @param Input $input 输入实例
* @param Output $output 输出实例 * @param Output $output 输出实例
* @return void
*/ */
protected function configureIO(Input $input, Output $output) protected function configureIO(Input $input, Output $output)
{ {
@ -551,6 +662,7 @@ class Console
/** /**
* 执行指令 * 执行指令
* @access protected
* @param Command $command 指令实例 * @param Command $command 指令实例
* @param Input $input 输入实例 * @param Input $input 输入实例
* @param Output $output 输出实例 * @param Output $output 输出实例
@ -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,40 +705,54 @@ 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);
} }
/** /**
* 获取可能的建议 * 获取可能的建议
* @access private
* @param array $abbrevs * @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 $name 指令名称
* @param string $limit 部分的命名空间的最大数量 * @param string $limit 部分的命名空间的最大数量
* @return string * @return string
*/ */
@ -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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -24,34 +24,36 @@ class Controller
* @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();
@ -66,7 +68,10 @@ class Controller
} }
} }
// 初始化 /**
* 初始化操作
* @access protected
*/
protected function _initialize() protected function _initialize()
{ {
} }
@ -76,6 +81,7 @@ 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 = [])
{ {
@ -83,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;
} }
@ -90,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;
} }
@ -131,22 +139,26 @@ 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;
} }
/** /**
@ -158,6 +170,7 @@ class Controller
protected function validateFailException($fail = true) protected function validateFailException($fail = true)
{ {
$this->failException = $fail; $this->failException = $fail;
return $this; return $this;
} }
@ -178,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]);
} }
@ -203,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(); return $v->getError();
} }
} else {
return true; 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,25 +86,37 @@ 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 数据
* @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
@ -111,12 +127,15 @@ 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 数据
* @access public
* @param string $name cookie 名称 * @param string $name cookie 名称
* @param string|null $prefix cookie 前缀 * @param string|null $prefix cookie 前缀
* @return bool * @return bool
@ -124,13 +143,15 @@ class Cookie
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 的值
* @access public
* @param string $name cookie 名称 * @param string $name cookie 名称
* @param string|null $prefix cookie 前缀 * @param string|null $prefix cookie 前缀
* @return mixed * @return mixed
@ -138,6 +159,7 @@ class Cookie
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
* @access public
* @param string $name cookie 名称 * @param string $name cookie 名称
* @param string|null $prefix cookie 前缀 * @param string|null $prefix cookie 前缀
* @return mixed * @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
* @access public
* @param string|null $prefix cookie 前缀 * @param string|null $prefix cookie 前缀
* @return mixed * @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,16 +49,23 @@ 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 强制重新连接
@ -70,34 +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) {
$name = md5(serialize($config)); $name = md5(serialize($config));
} }
self::$instance[$name] = new $class($options); self::$instance[$name] = new $class($options);
} }
return self::$instance[$name]; return self::$instance[$name];
} }
public static function clear() { /**
self::$instance = null; * 清除连接实例
* @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)
@ -105,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'] : '',
@ -144,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 = [];
/** /**
* 记录时间(微秒)和内存使用情况 * 记录时间(微秒)和内存使用情况
* @access public
* @param string $name 标记位置 * @param string $name 标记位置
* @param mixed $value 标记值 留空则取当前 time 表示仅记录时间 否则同时记录时间和内存 * @param mixed $value 标记值(留空则取当前 time 表示仅记录时间 否则同时记录时间和内存)
* @return mixed * @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
/** /**
* 统计某个区间的时间(微秒)使用情况 返回值以秒为单位 * 统计某个区间的时间(微秒)使用情况 返回值以秒为单位
* @access public
* @param string $start 开始标签 * @param string $start 开始标签
* @param string $end 结束标签 * @param string $end 结束标签
* @param integer|string $dec 小数位 * @param integer $dec 小数位
* @return integer * @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
/** /**
* 记录区间的内存使用情况 * 记录区间的内存使用情况
* @access public
* @param string $start 开始标签 * @param string $start 开始标签
* @param string $end 结束标签 * @param string $end 结束标签
* @param integer|string $dec 小数位 * @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,101 +118,126 @@ 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];
} }
/** /**
* 统计区间的内存峰值情况 * 统计区间的内存峰值情况
* @access public
* @param string $start 开始标签 * @param string $start 开始标签
* @param string $end 结束标签 * @param string $end 结束标签
* @param integer|string $dec 小数位 * @param integer $dec 小数位
* @return mixed * @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];
} }
/** /**
* 获取文件加载信息 * 获取文件加载信息
* @access public
* @param bool $detail 是否显示详细 * @param bool $detail 是否显示详细
* @return integer|array * @return integer|array
*/ */
public static function getFile($detail = false) public static function getFile($detail = false)
{ {
if ($detail) {
$files = get_included_files(); $files = get_included_files();
if ($detail) {
$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);
} }
/** /**
* 浏览器友好的变量输出 * 浏览器友好的变量输出
* @access public
* @param mixed $var 变量 * @param mixed $var 变量
* @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串 * @param boolean $echo 是否输出(默认为 true,为 false 则返回输出字符串)
* @param string $label 标签 默认为空 * @param string|null $label 标签(默认为空)
* @param integer $flags htmlspecialchars flags * @param integer $flags htmlspecialchars 的标志
* @return void|string * @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>');

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,6 +15,7 @@ class Env
{ {
/** /**
* 获取环境变量值 * 获取环境变量值
* @access public
* @param string $name 环境变量名(支持二级 . 号分割) * @param string $name 环境变量名(支持二级 . 号分割)
* @param string $default 默认值 * @param string $default 默认值
* @return mixed * @return mixed
@ -22,15 +23,17 @@ class Env
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 * 错误处理
* @access public
* @param integer $errno 错误编号 * @param integer $errno 错误编号
* @param integer $errstr 详细错误信息 * @param integer $errstr 详细错误信息
* @param string $errfile 出错的文件 * @param string $errfile 出错的文件
* @param integer $errline 出错行号 * @param integer $errline 出错行号
* @param array $errcontext * @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()
{ {
if (!is_null($error = error_get_last()) && self::isFatal($error['type'])) {
// 将错误信息托管至 think\ErrorException // 将错误信息托管至 think\ErrorException
$exception = new ErrorException($error['type'], $error['message'], $error['file'], $error['line']); if (!is_null($error = error_get_last()) && self::isFatal($error['type'])) {
self::appException(new ErrorException(
self::appException($exception); $error['type'], $error['message'], $error['file'], $error['line']
));
} }
// 写入日志 // 写入日志
@ -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,25 +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) { if ($class instanceof \Closure) {
$handle->setRender($class); $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,10 +13,8 @@ namespace think;
class Exception extends \Exception class Exception extends \Exception
{ {
/** /**
* 保存异常页面显示的额外Debug数据 * @var array 保存异常页面显示的额外 Debug 数据
* @var array
*/ */
protected $data = []; protected $data = [];
@ -33,8 +31,10 @@ class Exception extends \Exception
* key1 value1 * key1 value1
* key2 value2 * key2 value2
* *
* @access protected
* @param string $label 数据分类,用于异常页面显示 * @param string $label 数据分类,用于异常页面显示
* @param array $data 需要显示的数据,必须为关联数组 * @param array $data 需要显示的数据,必须为关联数组
* @return void
*/ */
final protected function setData($label, array $data) final protected function setData($label, array $data)
{ {
@ -44,7 +44,8 @@ class Exception extends \Exception
/** /**
* 获取异常额外 Debug 数据 * 获取异常额外 Debug 数据
* 主要用于输出到异常页面便于调试 * 主要用于输出到异常页面便于调试
* @return array 由setData设置的Debug数据 * @access public
* @return array
*/ */
final public function getData() final public function getData()
{ {

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;
} }
/** /**
* 设置上传信息 * 设置上传信息
* @access public
* @param array $info 上传文件信息 * @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,97 +116,100 @@ 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;
} }
/** /**
* 获取文件的哈希散列值 * 获取文件的哈希散列值
* @param string $type * @access public
* @return mixed $string * @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];
} }
/** /**
* 检查目录是否可写 * 检查目录是否可写
* @access protected
* @param string $path 目录 * @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 {
$this->error = "目录 {$path} 创建失败!";
return false; return false;
} }
}
/** /**
* 获取文件类型信息 * 获取文件类型信息
* @access public
* @return string * @return string
*/ */
public function getMime() public function getMime()
{ {
if(function_exists('finfo_open')) {
$finfo = finfo_open(FILEINFO_MIME_TYPE); $finfo = finfo_open(FILEINFO_MIME_TYPE);
return finfo_file($finfo, $this->filename); return finfo_file($finfo, $this->filename);
}else{
return $this->info['type'];
}
} }
/** /**
* 设置文件的命名规则 * 设置文件的命名规则
* @access public
* @param string $rule 文件命名规则 * @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;
} }
/** /**
* 设置上传文件的验证规则 * 设置上传文件的验证规则
* @access public
* @param array $rule 验证规则 * @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);
} }
/** /**
* 检测上传文件 * 检测上传文件
* @access public
* @param array $rule 验证规则 * @param array $rule 验证规则
* @return bool * @return bool
*/ */
@ -184,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;
} }
@ -211,6 +246,7 @@ class File extends SplFileObject
/** /**
* 检测上传文件后缀 * 检测上传文件后缀
* @access public
* @param array|string $ext 允许后缀 * @param array|string $ext 允许后缀
* @return bool * @return bool
*/ */
@ -219,33 +255,37 @@ 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, 13])) { // 如果上传的不是图片,或者是图片而且后缀确实符合图片类型则返回 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 { try {
$info = getimagesize($image); $info = getimagesize($image);
return $info ? $info[2] : false; return $info ? $info[2] : false;
@ -253,43 +293,38 @@ class File extends SplFileObject
return false; return false;
} }
} }
}
/** /**
* 检测上传文件大小 * 检测上传文件大小
* @access public
* @param integer $size 最大大小 * @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;
} }
/** /**
* 检测上传文件类型 * 检测上传文件类型
* @access public
* @param array|string $mime 允许类型 * @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;
} }
/** /**
* 移动文件 * 移动文件
* @access public
* @param string $path 保存路径 * @param string $path 保存路径
* @param string|bool $savename 保存的文件名 默认自动生成 * @param string|bool $savename 保存的文件名 默认自动生成
* @param boolean $replace 同名文件是否覆盖 * @param boolean $replace 同名文件是否覆盖
* @return false|File false-失败 否则返回File实例 * @return false|File
*/ */
public function move($path, $savename = true, $replace = true) public function move($path, $savename = true, $replace = true)
{ {
@ -301,7 +336,7 @@ class File extends SplFileObject
// 检测合法性 // 检测合法性
if (!$this->isValid()) { if (!$this->isValid()) {
$this->error = '非法上传文件'; $this->error = 'upload illegal files';
return false; return false;
} }
@ -309,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);
@ -319,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;
} }
@ -329,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;
} }
/** /**
* 获取保存文件名 * 获取保存文件名
* @access protected
* @param string|bool $savename 保存的文件名 默认自动生成 * @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 {
@ -366,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;
} }
/** /**
* 获取错误代码信息 * 获取错误代码信息
* @access private
* @param int $errorNo 错误号 * @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,11 +13,14 @@ namespace think;
class Hook class Hook
{ {
/**
* @var array 标签
*/
private static $tags = []; private static $tags = [];
/** /**
* 动态添加行为扩展到某个标签 * 动态添加行为扩展到某个标签
* @access public
* @param string $tag 标签名称 * @param string $tag 标签名称
* @param mixed $behavior 行为名称 * @param mixed $behavior 行为名称
* @param bool $first 是否放到开头执行 * @param bool $first 是否放到开头执行
@ -26,6 +29,7 @@ class Hook
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
/** /**
* 批量导入插件 * 批量导入插件
* @access public
* @param array $tags 插件信息 * @param array $tags 插件信息
* @param boolean $recursive 是否递归合并 * @param boolean $recursive 是否递归合并
* @return void
*/ */
public static function import(array $tags, $recursive = true) public static function import(array $tags, $recursive = true)
{ {
@ -59,21 +65,22 @@ 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] : [];
} }
/** /**
* 监听标签的行为 * 监听标签的行为
* @access public
* @param string $tag 标签名称 * @param string $tag 标签名称
* @param mixed $params 传入参数 * @param mixed $params 传入参数
* @param mixed $extra 额外参数 * @param mixed $extra 额外参数
@ -83,31 +90,34 @@ class Hook
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;
} }
/** /**
* 执行某个行为 * 执行某个行为
* @access public
* @param mixed $class 要执行的行为 * @param mixed $class 要执行的行为
* @param string $tag 方法名(标签名) * @param string $tag 方法名(标签名)
* @param Mixed $params 传人的参数 * @param mixed $params 传人的参数
* @param mixed $extra 额外参数 * @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,36 +13,59 @@ 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;
} }
/** /**
* 设置语言定义(不区分大小写) * 设置语言定义(不区分大小写)
* @access public
* @param string|array $name 语言变量 * @param string|array $name 语言变量
* @param string $value 语言值 * @param string $value 语言值
* @param string $range 语言作用域 * @param string $range 语言作用域
@ -51,19 +74,21 @@ class Lang
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;
} }
/** /**
* 加载语言定义(不区分大小写) * 加载语言定义(不区分大小写)
* @access public
* @param array|string $file 语言文件 * @param array|string $file 语言文件
* @param string $range 语言作用域 * @param string $range 语言作用域
* @return mixed * @return mixed
@ -71,32 +96,37 @@ class Lang
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];
} }
/** /**
* 获取语言定义(不区分大小写) * 获取语言定义(不区分大小写)
* @access public
* @param string|null $name 语言变量 * @param string|null $name 语言变量
* @param string $range 语言作用域 * @param string $range 语言作用域
* @return mixed * @return mixed
@ -104,11 +134,13 @@ class Lang
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)]);
} }
/** /**
* 获取语言定义(不区分大小写) * 获取语言定义(不区分大小写)
* @access public
* @param string|null $name 语言变量 * @param string|null $name 语言变量
* @param array $vars 变量替换 * @param array $vars 变量替换
* @param string $range 语言作用域 * @param string $range 语言作用域
@ -117,10 +149,12 @@ class Lang
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,16 +179,17 @@ 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])) {
@ -168,21 +203,25 @@ class Lang
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;
} }
/** /**
* 设置语言自动侦测的变量 * 设置语言自动侦测的变量
* @access public
* @param string $var 变量名称 * @param string $var 变量名称
* @return void * @return void
*/ */
@ -193,6 +232,7 @@ class Lang
/** /**
* 设置语言的 cookie 保存变量 * 设置语言的 cookie 保存变量
* @access public
* @param string $var 变量名称 * @param string $var 变量名称
* @return void * @return void
*/ */
@ -203,6 +243,7 @@ class Lang
/** /**
* 设置语言的 cookie 的过期时间 * 设置语言的 cookie 的过期时间
* @access public
* @param string $expire 过期时间 * @param string $expire 过期时间
* @return void * @return void
*/ */
@ -213,6 +254,7 @@ class Lang
/** /**
* 设置允许的语言列表 * 设置允许的语言列表
* @access public
* @param array $list 语言列表 * @param array $list 语言列表
* @return void * @return void
*/ */

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 = [];
/**
* @var array PSR-4 的加载目录
*/
private static $prefixDirsPsr4 = []; private static $prefixDirsPsr4 = [];
/**
* @var array PSR-4 加载失败的回退目录
*/
private static $fallbackDirsPsr4 = []; private static $fallbackDirsPsr4 = [];
// PSR-0 /**
* @var array PSR-0 命名空间前缀映射
*/
private static $prefixesPsr0 = []; 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)) {
return false;
}
__include_file($file); __include_file($file);
return true; 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])) {
// 类库映射 // 类库映射
return self::$map[$class]; if (!empty(self::$classMap[$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 { } else {
self::$fallbackDirsPsr0 = array_merge(
self::$fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0]; $first = $prefix[0];
if (!isset(self::$prefixesPsr0[$first][$prefix])) { if (!isset(self::$prefixesPsr0[$first][$prefix])) {
self::$prefixesPsr0[$first][$prefix] = (array) $paths; 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( self::$prefixesPsr0[$first][$prefix] = $prepend ?
self::$prefixesPsr0[$first][$prefix], array_merge((array) $paths, self::$prefixesPsr0[$first][$prefix]) :
(array) $paths 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 本函数有缓存功能
* @access public
* @param string $class 类库命名空间字符串 * @param string $class 类库命名空间字符串
* @param string $baseUrl 起始路径 * @param string $baseUrl 起始路径
* @param string $ext 导入的文件扩展名 * @param string $ext 导入的文件扩展名
* @return boolean * @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;
} }
@ -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,20 +413,22 @@ 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;
} }
/** /**
* 实例化(分层)模型 * 实例化(分层)模型
* @access public
* @param string $name Model名称 * @param string $name Model名称
* @param string $layer 业务层名称 * @param string $layer 业务层名称
* @param bool $appendSuffix 是否添加类名后缀 * @param bool $appendSuffix 是否添加类名后缀
@ -365,37 +438,32 @@ class Loader
*/ */
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;
} }
/** /**
* 实例化(分层)控制器 格式:[模块名/]控制器名 * 实例化(分层)控制器 格式:[模块名/]控制器名
* @access public
* @param string $name 资源地址 * @param string $name 资源地址
* @param string $layer 控制层名称 * @param string $layer 控制层名称
* @param bool $appendSuffix 是否添加类名后缀 * @param bool $appendSuffix 是否添加类名后缀
@ -405,28 +473,26 @@ class Loader
*/ */
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());
} else {
throw new ClassNotFoundException('class not exists:' . $class, $class);
} }
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);
} }
/** /**
* 实例化验证类 格式:[模块名/]验证器名 * 实例化验证类 格式:[模块名/]验证器名
* @access public
* @param string $name 资源地址 * @param string $name 资源地址
* @param string $layer 验证层名称 * @param string $layer 验证层名称
* @param bool $appendSuffix 是否添加类名后缀 * @param bool $appendSuffix 是否添加类名后缀
@ -437,40 +503,62 @@ class Loader
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];
} }
/** /**
* 数据库初始化 并取得数据库类实例 * 数据库初始化 并取得数据库类实例
* @access public
* @param mixed $config 数据库配置 * @param mixed $config 数据库配置
* @param bool|string $name 连接标识 true 强制重新连接 * @param bool|string $name 连接标识 true 强制重新连接
* @return \think\db\Connection * @return \think\db\Connection
@ -482,6 +570,7 @@ class Loader
/** /**
* 远程调用模块的操作方法 参数格式 [模块/控制器/]操作 * 远程调用模块的操作方法 参数格式 [模块/控制器/]操作
* @access public
* @param string $url 调用地址 * @param string $url 调用地址
* @param string|array $vars 调用参数 支持字符串和数组 * @param string|array $vars 调用参数 支持字符串和数组
* @param string $layer 要调用的控制层名称 * @param string $layer 要调用的控制层名称
@ -494,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, '=')) {
@ -502,13 +592,17 @@ 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 的风格
* @access public
* @param string $name 字符串 * @param string $name 字符串
* @param integer $type 转换类型 * @param integer $type 转换类型
* @param bool $ucfirst 首字母是否大写(驼峰规则) * @param bool $ucfirst 首字母是否大写(驼峰规则)
@ -520,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), "_"));
} }
/** /**
* 解析应用类的类名 * 解析应用类的类名
* @access public
* @param string $module 模块名 * @param string $module 模块名
* @param string $layer 层名 controller model ... * @param string $layer 层名 controller model ...
* @param string $name 类名 * @param string $name 类名
* @param bool $appendSuffix * @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()
@ -553,10 +654,11 @@ class Loader
} }
} }
// 作用范围隔离
/** /**
* 作用范围隔离 * include
* * @param string $file 文件路径
* @param $file
* @return mixed * @return mixed
*/ */
function __include_file($file) function __include_file($file)
@ -564,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');
} }
/** /**
* 获取日志信息 * 获取日志信息
* @access public
* @param string $type 信息类型 * @param string $type 信息类型
* @return array * @return array|string
*/ */
public static function getLog($type = '') public static function getLog($type = '')
{ {
@ -77,6 +96,7 @@ class Log
/** /**
* 记录调试信息 * 记录调试信息
* @access public
* @param mixed $msg 调试信息 * @param mixed $msg 调试信息
* @param string $type 信息类型 * @param string $type 信息类型
* @return void * @return void
@ -84,14 +104,14 @@ class Log
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,7 +120,8 @@ class Log
} }
/** /**
* 当前日志记录的授权key * 设置当前日志记录的授权 key
* @access public
* @param string $key 授权 key * @param string $key 授权 key
* @return void * @return void
*/ */
@ -111,30 +132,31 @@ class Log
/** /**
* 检查日志写入权限 * 检查日志写入权限
* @access public
* @param array $config 当前日志配置参数 * @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)) { is_null(self::$driver) && self::init(Config::get('log'));
// 检测日志写入权限 // 检测日志写入权限
if (!self::check(self::$config)) {
return false; return false;
} }
@ -154,18 +176,18 @@ class Log
} }
} }
$result = self::$driver->save($log); if ($result = self::$driver->save($log)) {
if ($result) {
self::$log = []; self::$log = [];
} }
Hook::listen('log_write_done', $log); Hook::listen('log_write_done', $log);
return $result; return $result;
} }
return true;
}
/** /**
* 实时写入日志信息 并支持行为 * 实时写入日志信息 并支持行为
* @access public
* @param mixed $msg 调试信息 * @param mixed $msg 调试信息
* @param string $type 信息类型 * @param string $type 信息类型
* @param bool $force 是否强制写入 * @param bool $force 是否强制写入
@ -174,39 +196,41 @@ class Log
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[$type][] = $msg;
// 监听 log_write // 监听 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)) {
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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -57,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 = [];
// 显示属性 // 显示属性
@ -90,6 +94,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
protected $type = []; protected $type = [];
// 是否为更新数据 // 是否为更新数据
protected $isUpdate = false; protected $isUpdate = false;
// 是否强制更新所有数据
protected $force = false;
// 更新条件 // 更新条件
protected $updateWhere; protected $updateWhere;
// 验证失败是否抛出异常 // 验证失败是否抛出异常
@ -110,6 +116,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
*/ */
protected static $initialized = []; protected static $initialized = [];
/**
* 是否从主库读取(主从分布式有效)
* @var array
*/
protected static $readMaster;
/** /**
* 构造方法 * 构造方法
* @access public * @access public
@ -122,6 +134,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;
@ -155,6 +177,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
@ -176,7 +212,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)) {
@ -192,6 +232,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
@ -262,7 +315,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;
} }
@ -337,6 +389,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
@ -572,7 +636,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
*/ */
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 {
// 首先获取关联数据 // 首先获取关联数据
@ -639,7 +703,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':
try {
$value = unserialize($value); $value = unserialize($value);
} catch (\Exception $e) {
$value = null;
}
break; break;
default: default:
if (false !== strpos($type, '\\')) { if (false !== strpos($type, '\\')) {
@ -692,7 +760,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);
} }
} }
} }
@ -825,11 +893,33 @@ 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 {
$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 { } else {
$item[$name] = $this->getAttr($name); $item[$name] = $this->getAttr($name);
} }
} }
} }
}
return !empty($item) ? $item : []; return !empty($item) ? $item : [];
} }
@ -1021,12 +1111,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;
} }
// 检测字段 // 检测字段
@ -1068,16 +1163,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, false, false, $sequence);
} else { } else {
$result = $this->getQuery()->insert($this->data); $result = $this->getQuery()->insert($this->data, false, 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;
}
} }
} }
@ -1114,10 +1210,18 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
if ($this->autoWriteTimestamp) { if ($this->autoWriteTimestamp) {
array_push($field, $this->createTime, $this->updateTime); 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;
} }
@ -1143,12 +1247,16 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
*/ */
public function getChangedData() public function getChangedData()
{ {
if ($this->force) {
$data = $this->data;
} else {
$data = array_udiff_assoc($this->data, $this->origin, function ($a, $b) { $data = array_udiff_assoc($this->data, $this->origin, function ($a, $b) {
if ((empty($a) || empty($b)) && $a !== $b) { if ((empty($a) || empty($b)) && $a !== $b) {
return 1; return 1;
} }
return is_object($a) || $a != $b ? 1 : 0; return is_object($a) || $a != $b ? 1 : 0;
}); });
}
if (!empty($this->readonly)) { if (!empty($this->readonly)) {
// 只读字段不允许更新 // 只读字段不允许更新
@ -1254,14 +1362,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;
@ -1271,7 +1379,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)
@ -1283,6 +1391,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
@ -1621,14 +1744,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;
@ -1706,11 +1829,12 @@ 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);
} }
/** /**
@ -1976,7 +2100,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 {
@ -2002,7 +2126,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 {
@ -2040,7 +2164,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;
@ -2056,7 +2179,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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -128,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();
} }
@ -395,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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -121,6 +121,11 @@ class Request
protected $cache; protected $cache;
// 缓存是否检查 // 缓存是否检查
protected $isCheckCache; protected $isCheckCache;
/**
* 是否合并Param
* @var bool
*/
protected $mergeParam = false;
/** /**
* 构造函数 * 构造函数
@ -182,6 +187,18 @@ 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
@ -232,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'];
@ -502,7 +519,7 @@ class Request
{ {
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')]); $this->method = strtoupper($_POST[Config::get('var_method')]);
@ -510,7 +527,7 @@ class Request
} 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;
@ -616,7 +633,7 @@ class Request
*/ */
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) {
@ -632,7 +649,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) {
// 获取包含文件上传信息的数组 // 获取包含文件上传信息的数组
@ -1093,7 +1111,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 LIKE|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 其他安全过滤
@ -1239,7 +1257,9 @@ 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;
} }
} }
@ -1255,7 +1275,9 @@ 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;
} }
} }
@ -1265,7 +1287,7 @@ class Request
* @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;
@ -1273,7 +1295,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);
@ -1338,11 +1364,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;
} }
/** /**
@ -1463,11 +1496,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);
} }
} }

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 {

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,12 +35,11 @@ class Route
private static $rest = [ private static $rest = [
'index' => ['get', '', 'index'], 'index' => ['get', '', 'index'],
'create' => ['get', '/create', 'create'], 'create' => ['get', '/create', 'create'],
'rules' => ['post','/rules','rules'],
'edit' => ['get', '/:id/edit', 'edit'], 'edit' => ['get', '/:id/edit', 'edit'],
'read' => ['get', '/:id', 'read'], 'read' => ['get', '/:id', 'read'],
'save' => ['post', '', 'save'], 'save' => ['post', '', 'save'],
'update' => ['put', '/:id', 'update'], 'update' => ['put', '/:id', 'update'],
'delete' => ['delete', '/:id', 'delete'] 'delete' => ['delete', '/:id', 'delete'],
]; ];
// 不同请求类型的方法前缀 // 不同请求类型的方法前缀
@ -223,7 +222,7 @@ 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 路由参数
@ -271,7 +270,7 @@ 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 路由参数
@ -336,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;
} }
} }
@ -488,7 +487,7 @@ 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 变量规则
@ -502,7 +501,7 @@ 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 变量规则
@ -516,7 +515,7 @@ 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 变量规则
@ -530,7 +529,7 @@ 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 变量规则
@ -544,7 +543,7 @@ 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 变量规则
@ -558,7 +557,7 @@ 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 变量规则
@ -572,7 +571,7 @@ 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 变量规则
@ -598,7 +597,6 @@ class Route
} }
$rule = implode('/', $item) . '/' . $last; $rule = implode('/', $item) . '/' . $last;
} }
// 注册资源路由 // 注册资源路由
foreach (self::$rest as $key => $val) { foreach (self::$rest as $key => $val) {
if ((isset($option['only']) && !in_array($key, $option['only'])) if ((isset($option['only']) && !in_array($key, $option['only']))
@ -669,7 +667,7 @@ 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
*/ */
@ -739,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];
@ -837,6 +835,15 @@ class Route
*/ */
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);
@ -1377,11 +1384,23 @@ class 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);
@ -1508,7 +1527,7 @@ 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'])) {
@ -1529,9 +1548,10 @@ 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);
@ -1545,7 +1565,7 @@ 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];
} }
/** /**
@ -1602,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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

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分离出来的模板引擎
@ -65,10 +66,14 @@ class Template
{ {
$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';
@ -76,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
@ -121,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)
{ {
@ -184,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);
@ -235,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 = '')
{ {
@ -689,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;
@ -1071,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;
} }
@ -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,55 +36,54 @@ class Validate
// 验证规则默认提示信息 // 验证规则默认提示信息
protected static $typeMsg = [ protected static $typeMsg = [
'require' => ':attribute不能为空', 'require' => ':attribute require',
'number' => ':attribute必须是数字', 'number' => ':attribute must be numeric',
'integer' => ':attribute必须是整数', 'integer' => ':attribute must be integer',
'float' => ':attribute必须是浮点数', 'float' => ':attribute must be float',
'boolean' => ':attribute必须是布尔值', 'boolean' => ':attribute must be bool',
'email' => ':attribute格式不符', 'email' => ':attribute not a valid email address',
'array' => ':attribute必须是数组', 'array' => ':attribute must be a array',
'accepted' => ':attribute必须是yes、on或者1', 'accepted' => ':attribute must be yes,on or 1',
'date' => ':attribute格式不符合', 'date' => ':attribute not a valid datetime',
'file' => ':attribute不是有效的上传文件', 'file' => ':attribute not a valid file',
'image' => ':attribute不是有效的图像文件', 'image' => ':attribute not a valid image',
'alpha' => ':attribute只能是字母', 'alpha' => ':attribute must be alpha',
'alphaNum' => ':attribute只能是字母和数字', 'alphaNum' => ':attribute must be alpha-numeric',
'alphaDash' => ':attribute只能是字母、数字和下划线_及破折号-', 'alphaDash' => ':attribute must be alpha-numeric, dash, underscore',
'activeUrl' => ':attribute不是有效的域名或者IP', 'activeUrl' => ':attribute not a valid domain or ip',
'chs' => ':attribute只能是汉字', 'chs' => ':attribute must be chinese',
'chsAlpha' => ':attribute只能是汉字、字母', 'chsAlpha' => ':attribute must be chinese or alpha',
'chsAlphaNum' => ':attribute只能是汉字、字母和数字', 'chsAlphaNum' => ':attribute must be chinese,alpha-numeric',
'chsDash' => ':attribute只能是汉字、字母、数字和下划线_及破折号-', 'chsDash' => ':attribute must be chinese,alpha-numeric,underscore, dash',
'url' => ':attribute不是有效的URL地址', 'url' => ':attribute not a valid url',
'ip' => ':attribute不是有效的IP地址', 'ip' => ':attribute not a valid ip',
'dateFormat' => ':attribute必须使用日期格式 :rule', 'dateFormat' => ':attribute must be dateFormat of :rule',
'in' => ':attribute必须在 :rule 范围内', 'in' => ':attribute must be in :rule',
'notIn' => ':attribute不能在 :rule 范围内', 'notIn' => ':attribute be notin :rule',
'between' => ':attribute只能在 :1 - :2 之间', 'between' => ':attribute must between :1 - :2',
'notBetween' => ':attribute不能在 :1 - :2 之间', 'notBetween' => ':attribute not between :1 - :2',
'length' => ':attribute长度不符合要求 :rule', 'length' => 'size of :attribute must be :rule',
'max' => ':attribute长度不能超过 :rule', 'max' => 'max size of :attribute must be :rule',
'min' => ':attribute长度不能小于 :rule', 'min' => 'min size of :attribute must be :rule',
'after' => ':attribute日期不能小于 :rule', 'after' => ':attribute cannot be less than :rule',
'before' => ':attribute日期不能超过 :rule', 'before' => ':attribute cannot exceed :rule',
'expire' => '不在有效期内 :rule', 'expire' => ':attribute not within :rule',
'allowIp' => '不允许的IP访问', 'allowIp' => 'access IP is not allowed',
'denyIp' => '禁止的IP访问', 'denyIp' => 'access IP denied',
'confirm' => ':attribute和确认字段:2不一致', 'confirm' => ':attribute out of accord with :2',
'different' => ':attribute和比较字段:2不能相同', 'different' => ':attribute cannot be same with :2',
'egt' => ':attribute必须大于等于 :rule', 'egt' => ':attribute must greater than or equal :rule',
'gt' => ':attribute必须大于 :rule', 'gt' => ':attribute must greater than :rule',
'elt' => ':attribute必须小于等于 :rule', 'elt' => ':attribute must less than or equal :rule',
'lt' => ':attribute必须小于 :rule', 'lt' => ':attribute must less than :rule',
'eq' => ':attribute必须等于 :rule', 'eq' => ':attribute must equal :rule',
'unique' => ':attribute已存在', 'unique' => ':attribute has exists',
'regex' => ':attribute不符合指定规则', 'regex' => ':attribute not conform to the rules',
'method' => '无效的请求类型', 'method' => 'invalid Request method',
'token' => '令牌数据无效', 'token' => 'invalid token',
'fileSize' => '上传文件大小不符', 'fileSize' => 'filesize not match',
'fileExt' => '上传文件后缀不符', 'fileExt' => 'extensions to upload is not allowed',
'fileMime' => '上传文件类型不符', 'fileMime' => 'mimetype to upload is not allowed',
]; ];
// 当前验证场景 // 当前验证场景
@ -340,6 +339,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
@ -364,25 +398,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)) {
@ -418,6 +434,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
@ -681,21 +730,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;
} }
} }
@ -708,21 +753,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;
} }
} }
@ -735,18 +776,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;
} }
} }
@ -846,13 +886,14 @@ class Validate
$map[$key] = $data[$field]; $map[$key] = $data[$field];
} }
$pk = strval(isset($rule[3]) ? $rule[3] : $db->getPk()); $pk = isset($rule[3]) ? $rule[3] : $db->getPk();
if (is_string($pk)) {
if (isset($rule[2])) { if (isset($rule[2])) {
$map[$pk] = ['neq', $rule[2]]; $map[$pk] = ['neq', $rule[2]];
} elseif (isset($data[$pk])) { } elseif (isset($data[$pk])) {
$map[$pk] = ['neq', $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;
} }
@ -1154,7 +1195,7 @@ class Validate
// 不是正则表达式则两端补上/ // 不是正则表达式则两端补上/
$rule = '/^' . $rule . '$/'; $rule = '/^' . $rule . '$/';
} }
return 1 === preg_match($rule, (string) $value); return is_scalar($value) && 1 === preg_match($rule, (string) $value);
} }
/** /**
@ -1233,11 +1274,13 @@ class Validate
} elseif (0 === strpos($type, 'require')) { } elseif (0 === strpos($type, 'require')) {
$msg = self::$typeMsg['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();
@ -147,11 +147,6 @@ class View
*/ */
public function fetch($template = '', $vars = [], $replace = [], $config = [], $renderContent = false) public function fetch($template = '', $vars = [], $replace = [], $config = [], $renderContent = false)
{ {
// 网页ResponseHeaders 设置
header('Author:'.base64_decode('Q1JN').'EB');
header('Content-Type:text/html; charset=utf-8');
header('Tel:'.base64_decode('MDI5LTY1NjEwMzg=').'0');
header('X-Powered-By:http://'.base64_decode('d3d3LmNybWViLmNv').'m');
// 模板变量 // 模板变量
$vars = array_merge(self::$var, $this->data, $vars); $vars = array_merge(self::$var, $this->data, $vars);
@ -160,18 +155,21 @@ class View
ob_implicit_flush(0); ob_implicit_flush(0);
// 渲染输出 // 渲染输出
try {
$method = $renderContent ? 'display' : 'fetch'; $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); $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,8 +120,10 @@ 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)) {
while ($this->has($name . '_lock')) { $time = time();
while ($time + 5 > time() && $this->has($name . '_lock')) {
// 存在锁定则等待 // 存在锁定则等待
usleep(200000);
} }
try { try {
@ -136,6 +138,10 @@ abstract class Driver
} catch (\Exception $e) { } catch (\Exception $e) {
// 解锁 // 解锁
$this->rm($name . '_lock'); $this->rm($name . '_lock');
throw $e;
} catch (\throwable $e) {
$this->rm($name . '_lock');
throw $e;
} }
} else { } else {
$value = $this->get($name); $value = $this->get($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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -27,6 +27,8 @@ class File extends Driver
'data_compress' => false, 'data_compress' => false,
]; ];
protected $expire;
/** /**
* 构造函数 * 构造函数
* @param array $options * @param array $options
@ -62,9 +64,10 @@ 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;
@ -107,11 +111,13 @@ class File extends Driver
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) {
return $default; return $default;
} }
$this->expire = $expire;
$content = substr($content, 32); $content = substr($content, 32);
if ($this->options['data_compress'] && function_exists('gzcompress')) { if ($this->options['data_compress'] && function_exists('gzcompress')) {
//启用数据压缩 //启用数据压缩
@ -140,7 +146,7 @@ class File extends Driver
if ($expire instanceof \DateTime) { if ($expire instanceof \DateTime) {
$expire = $expire->getTimestamp() - time(); $expire = $expire->getTimestamp() - time();
} }
$filename = $this->getCacheKey($name); $filename = $this->getCacheKey($name, true);
if ($this->tag && !is_file($filename)) { if ($this->tag && !is_file($filename)) {
$first = true; $first = true;
} }
@ -171,10 +177,13 @@ class File extends Driver
{ {
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;
} }
/** /**
@ -188,10 +197,13 @@ class File extends Driver
{ {
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;
} }
/** /**
@ -203,7 +215,10 @@ class File extends Driver
public function rm($name) public function rm($name)
{ {
$filename = $this->getCacheKey($name); $filename = $this->getCacheKey($name);
try {
return $this->unlink($filename); return $this->unlink($filename);
} catch (\Exception $e) {
}
} }
/** /**
@ -226,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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -149,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);
} }
/** /**

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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -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,12 +83,17 @@ 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;
} }
/** /**
@ -108,9 +116,8 @@ class Redis extends Driver
$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);
@ -129,6 +136,7 @@ class Redis extends Driver
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);
} }
@ -142,6 +150,7 @@ class Redis extends Driver
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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -159,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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

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,9 +33,15 @@ class Clear extends Command
{ {
$path = $input->getOption('path') ?: RUNTIME_PATH; $path = $input->getOption('path') ?: RUNTIME_PATH;
$type = $input->getArgument('type');
if ($type == 'route') {
Cache::clear('route_check');
} else {
if (is_dir($path)) { if (is_dir($path)) {
$this->clearPath($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

@ -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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -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,
// 数据返回类型 // 数据返回类型
@ -354,15 +356,15 @@ 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->free();
}
// 预处理 // 预处理
if (empty($this->PDOStatement)) { if (empty($this->PDOStatement)) {
$this->PDOStatement = $this->linkID->prepare($sql); $this->PDOStatement = $this->linkID->prepare($sql);
@ -378,7 +380,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 +388,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);
@ -399,11 +406,12 @@ 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 PDOException * @throws PDOException
* @throws \Exception * @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,15 +424,15 @@ 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->queryString != $sql) {
$this->free();
}
// 预处理 // 预处理
if (empty($this->PDOStatement)) { if (empty($this->PDOStatement)) {
$this->PDOStatement = $this->linkID->prepare($sql); $this->PDOStatement = $this->linkID->prepare($sql);
@ -440,18 +448,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 +483,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 +499,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);
@ -648,6 +669,11 @@ abstract class Connection
return $this->close()->startTrans(); return $this->close()->startTrans();
} }
throw $e; throw $e;
} catch (\Error $e) {
if ($this->isBreak($e)) {
return $this->close()->startTrans();
}
throw $e;
} }
} }
@ -725,7 +751,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;
@ -734,7 +760,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();
@ -742,6 +768,7 @@ abstract class Connection
$this->rollback(); $this->rollback();
throw $e; throw $e;
} }
return true; return true;
} }
@ -803,6 +830,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();
@ -883,9 +911,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'])) {
// 开启数据库调试模式 // 开启数据库调试模式
@ -902,7 +931,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);
} }
} }
} }
@ -924,19 +953,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

@ -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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -53,14 +53,16 @@ class Query
protected static $info = []; protected static $info = [];
// 回调事件 // 回调事件
private static $event = []; private static $event = [];
// 读取主库
private static $readMaster = [];
/** /**
* 构造函数 * 构造函数
* @access public * @access public
* @param Connection $connection 数据库对象实例 * @param Connection $connection 数据库对象实例
* @param string $model 模型名 * @param Model $model 模型对象
*/ */
public function __construct(Connection $connection = null, $model = '') public function __construct(Connection $connection = null, $model = null)
{ {
$this->connection = $connection ?: Db::connect([], true); $this->connection = $connection ?: Db::connect([], true);
$this->prefix = $this->connection->getConfig('prefix'); $this->prefix = $this->connection->getConfig('prefix');
@ -131,15 +133,34 @@ class Query
} }
/** /**
* 获取当前的模型对象 * 获取当前的模型对象实例
* @access public * @access public
* @return string * @return Model|null
*/ */
public function getModel() public function getModel()
{ {
return $this->model; return $this->model;
} }
/**
* 设置后续从主库读取数据
* @access public
* @param bool $allTable
* @return void
*/
public function readMaster($allTable = false)
{
if ($allTable) {
$table = '*';
} else {
$table = isset($this->options['table']) ? $this->options['table'] : $this->getTable();
}
static::$readMaster[$table] = true;
return $this;
}
/** /**
* 获取当前的builder实例对象 * 获取当前的builder实例对象
* @access public * @access public
@ -238,7 +259,7 @@ class Query
*/ */
public function execute($sql, $bind = []) public function execute($sql, $bind = [])
{ {
return $this->connection->execute($sql, $bind); return $this->connection->execute($sql, $bind, $this);
} }
/** /**
@ -312,9 +333,9 @@ class Query
* @param array $sql SQL批处理指令 * @param array $sql SQL批处理指令
* @return boolean * @return boolean
*/ */
public function batchQuery($sql = []) public function batchQuery($sql = [], $bind = [])
{ {
return $this->connection->batchQuery($sql); return $this->connection->batchQuery($sql, $bind);
} }
/** /**
@ -403,7 +424,7 @@ class Query
if (empty($this->options['table'])) { if (empty($this->options['table'])) {
$this->options['table'] = $this->getTable(); $this->options['table'] = $this->getTable();
} }
$key = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options) . serialize($this->bind)); $key = is_string($cache['key']) ? $cache['key'] : md5($this->connection->getConfig('database') . '.' . $field . serialize($this->options) . serialize($this->bind));
$result = Cache::get($key); $result = Cache::get($key);
} }
if (false === $result) { if (false === $result) {
@ -420,7 +441,7 @@ class Query
$result += 0; $result += 0;
} }
if (isset($cache)) { if (isset($cache) && false !== $result) {
// 缓存数据 // 缓存数据
$this->cacheData($key, $result, $cache); $this->cacheData($key, $result, $cache);
} }
@ -447,7 +468,7 @@ class Query
if (empty($this->options['table'])) { if (empty($this->options['table'])) {
$this->options['table'] = $this->getTable(); $this->options['table'] = $this->getTable();
} }
$guid = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options) . serialize($this->bind)); $guid = is_string($cache['key']) ? $cache['key'] : md5($this->connection->getConfig('database') . '.' . $field . serialize($this->options) . serialize($this->bind));
$result = Cache::get($guid); $result = Cache::get($guid);
} }
if (false === $result) { if (false === $result) {
@ -534,22 +555,24 @@ class Query
* MIN查询 * MIN查询
* @access public * @access public
* @param string $field 字段名 * @param string $field 字段名
* @param bool $force 强制转为数字类型
* @return mixed * @return mixed
*/ */
public function min($field) public function min($field, $force = true)
{ {
return $this->value('MIN(' . $field . ') AS tp_min', 0, true); return $this->value('MIN(' . $field . ') AS tp_min', 0, $force);
} }
/** /**
* MAX查询 * MAX查询
* @access public * @access public
* @param string $field 字段名 * @param string $field 字段名
* @param bool $force 强制转为数字类型
* @return mixed * @return mixed
*/ */
public function max($field) public function max($field, $force = true)
{ {
return $this->value('MAX(' . $field . ') AS tp_max', 0, true); return $this->value('MAX(' . $field . ') AS tp_max', 0, $force);
} }
/** /**
@ -607,7 +630,7 @@ class Query
return true; return true;
} }
} }
return $this->setField($field, ['exp', $field . '+' . $step]); return $this->setField($field, ['inc', $step]);
} }
/** /**
@ -635,8 +658,9 @@ class Query
$this->options = []; $this->options = [];
return true; return true;
} }
return $this->setField($field, ['inc', $step]);
} }
return $this->setField($field, ['exp', $field . '-' . $step]); return $this->setField($field, ['dec', $step]);
} }
/** /**
@ -704,7 +728,8 @@ class Query
{ {
// 传入的表名为数组 // 传入的表名为数组
if (is_array($join)) { if (is_array($join)) {
list($table, $alias) = each($join); $table = $join;
$alias = array_shift($join);
} else { } else {
$join = trim($join); $join = trim($join);
if (false !== strpos($join, '(')) { if (false !== strpos($join, '(')) {
@ -725,13 +750,9 @@ class Query
$table = $this->getTable($table); $table = $this->getTable($table);
} }
} }
} if (isset($alias) && $table != $alias) {
if (isset($alias)) {
if (isset($this->options['alias'][$table])) {
$table = $table . '@think' . uniqid();
}
$table = [$table => $alias]; $table = [$table => $alias];
$this->alias($table); }
} }
return $table; return $table;
} }
@ -769,8 +790,15 @@ class Query
{ {
if (empty($field)) { if (empty($field)) {
return $this; return $this;
} elseif ($field instanceof Expression) {
$this->options['field'][] = $field;
return $this;
} }
if (is_string($field)) { if (is_string($field)) {
if (preg_match('/[\<\'\"\(]/', $field)) {
return $this->fieldRaw($field);
}
$field = array_map('trim', explode(',', $field)); $field = array_map('trim', explode(',', $field));
} }
if (true === $field) { if (true === $field) {
@ -794,12 +822,30 @@ class Query
} }
if (isset($this->options['field'])) { if (isset($this->options['field'])) {
$field = array_merge($this->options['field'], $field); $field = array_merge((array) $this->options['field'], $field);
} }
$this->options['field'] = array_unique($field); $this->options['field'] = array_unique($field);
return $this; return $this;
} }
/**
* 表达式方式指定查询字段
* @access public
* @param string $field 字段名
* @param array $bind 参数绑定
* @return $this
*/
public function fieldRaw($field, array $bind = [])
{
$this->options['field'][] = $this->raw($field);
if ($bind) {
$this->bind($bind);
}
return $this;
}
/** /**
* 设置数据 * 设置数据
* @access public * @access public
@ -828,7 +874,7 @@ class Query
{ {
$fields = is_string($field) ? explode(',', $field) : $field; $fields = is_string($field) ? explode(',', $field) : $field;
foreach ($fields as $field) { foreach ($fields as $field) {
$this->data($field, ['exp', $field . '+' . $step]); $this->data($field, ['inc', $step]);
} }
return $this; return $this;
} }
@ -844,7 +890,7 @@ class Query
{ {
$fields = is_string($field) ? explode(',', $field) : $field; $fields = is_string($field) ? explode(',', $field) : $field;
foreach ($fields as $field) { foreach ($fields as $field) {
$this->data($field, ['exp', $field . '-' . $step]); $this->data($field, ['dec', $step]);
} }
return $this; return $this;
} }
@ -858,25 +904,36 @@ class Query
*/ */
public function exp($field, $value) public function exp($field, $value)
{ {
$this->data($field, ['exp', $value]); $this->data($field, $this->raw($value));
return $this; return $this;
} }
/**
* 使用表达式设置数据
* @access public
* @param mixed $value 表达式
* @return Expression
*/
public function raw($value)
{
return new Expression($value);
}
/** /**
* 指定JOIN查询字段 * 指定JOIN查询字段
* @access public * @access public
* @param string|array $table 数据表 * @param string|array $table 数据表
* @param string|array $field 查询字段 * @param string|array $field 查询字段
* @param string|array $on JOIN条件 * @param mixed $on JOIN条件
* @param string $type JOIN类型 * @param string $type JOIN类型
* @return $this * @return $this
*/ */
public function view($join, $field = true, $on = null, $type = 'INNER') public function view($join, $field = true, $on = null, $type = 'INNER')
{ {
$this->options['view'] = true; $this->options['view'] = true;
if (is_array($join) && key($join) !== 0) { if (is_array($join) && key($join) === 0) {
foreach ($join as $key => $val) { foreach ($join as $key => $val) {
$this->view($key, $val[0], isset($val[1]) ? $val[1] : null, isset($val[2]) ? $val[2] : 'INNER'); $this->view($val[0], $val[1], isset($val[2]) ? $val[2] : null, isset($val[3]) ? $val[3] : 'INNER');
} }
} else { } else {
$fields = []; $fields = [];
@ -975,6 +1032,37 @@ class Query
return $this; return $this;
} }
/**
* 指定表达式查询条件
* @access public
* @param string $where 查询条件
* @param array $bind 参数绑定
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function whereRaw($where, $bind = [], $logic = 'AND')
{
$this->options['where'][$logic][] = $this->raw($where);
if ($bind) {
$this->bind($bind);
}
return $this;
}
/**
* 指定表达式查询条件 OR
* @access public
* @param string $where 查询条件
* @param array $bind 参数绑定
* @return $this
*/
public function whereOrRaw($where, $bind = [])
{
return $this->whereRaw($where, $bind, 'OR');
}
/** /**
* 指定Null查询条件 * 指定Null查询条件
* @access public * @access public
@ -984,7 +1072,7 @@ class Query
*/ */
public function whereNull($field, $logic = 'AND') public function whereNull($field, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'null', null); $this->parseWhereExp($logic, $field, 'null', null, [], true);
return $this; return $this;
} }
@ -997,7 +1085,7 @@ class Query
*/ */
public function whereNotNull($field, $logic = 'AND') public function whereNotNull($field, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'notnull', null); $this->parseWhereExp($logic, $field, 'notnull', null, [], true);
return $this; return $this;
} }
@ -1037,7 +1125,7 @@ class Query
*/ */
public function whereIn($field, $condition, $logic = 'AND') public function whereIn($field, $condition, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'in', $condition); $this->parseWhereExp($logic, $field, 'in', $condition, [], true);
return $this; return $this;
} }
@ -1051,7 +1139,7 @@ class Query
*/ */
public function whereNotIn($field, $condition, $logic = 'AND') public function whereNotIn($field, $condition, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'not in', $condition); $this->parseWhereExp($logic, $field, 'not in', $condition, [], true);
return $this; return $this;
} }
@ -1065,7 +1153,7 @@ class Query
*/ */
public function whereLike($field, $condition, $logic = 'AND') public function whereLike($field, $condition, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'like', $condition); $this->parseWhereExp($logic, $field, 'like', $condition, [], true);
return $this; return $this;
} }
@ -1079,7 +1167,7 @@ class Query
*/ */
public function whereNotLike($field, $condition, $logic = 'AND') public function whereNotLike($field, $condition, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'not like', $condition); $this->parseWhereExp($logic, $field, 'not like', $condition, [], true);
return $this; return $this;
} }
@ -1093,7 +1181,7 @@ class Query
*/ */
public function whereBetween($field, $condition, $logic = 'AND') public function whereBetween($field, $condition, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'between', $condition); $this->parseWhereExp($logic, $field, 'between', $condition, [], true);
return $this; return $this;
} }
@ -1107,7 +1195,7 @@ class Query
*/ */
public function whereNotBetween($field, $condition, $logic = 'AND') public function whereNotBetween($field, $condition, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'not between', $condition); $this->parseWhereExp($logic, $field, 'not between', $condition, [], true);
return $this; return $this;
} }
@ -1121,7 +1209,7 @@ class Query
*/ */
public function whereExp($field, $condition, $logic = 'AND') public function whereExp($field, $condition, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'exp', $condition); $this->parseWhereExp($logic, $field, 'exp', $this->raw($condition), [], true);
return $this; return $this;
} }
@ -1148,9 +1236,10 @@ class Query
* @param mixed $op 查询表达式 * @param mixed $op 查询表达式
* @param mixed $condition 查询条件 * @param mixed $condition 查询条件
* @param array $param 查询参数 * @param array $param 查询参数
* @param bool $strict 严格模式
* @return void * @return void
*/ */
protected function parseWhereExp($logic, $field, $op, $condition, $param = []) protected function parseWhereExp($logic, $field, $op, $condition, $param = [], $strict = false)
{ {
$logic = strtoupper($logic); $logic = strtoupper($logic);
if ($field instanceof \Closure) { if ($field instanceof \Closure) {
@ -1161,8 +1250,17 @@ class Query
if (is_string($field) && !empty($this->options['via']) && !strpos($field, '.')) { if (is_string($field) && !empty($this->options['via']) && !strpos($field, '.')) {
$field = $this->options['via'] . '.' . $field; $field = $this->options['via'] . '.' . $field;
} }
if (is_string($field) && preg_match('/[,=\>\<\'\"\(\s]/', $field)) {
$where[] = ['exp', $field]; if ($field instanceof Expression) {
return $this->whereRaw($field, is_array($op) ? $op : []);
} elseif ($strict) {
// 使用严格模式查询
$where[$field] = [$op, $condition];
// 记录一个字段多次查询条件
$this->options['multi'][$logic][$field][] = $where[$field];
} elseif (is_string($field) && preg_match('/[,=\>\<\'\"\(\s]/', $field)) {
$where[] = ['exp', $this->raw($field)];
if (is_array($op)) { if (is_array($op)) {
// 参数绑定 // 参数绑定
$this->bind($op); $this->bind($op);
@ -1184,20 +1282,27 @@ class Query
} elseif (in_array(strtolower($op), ['null', 'notnull', 'not null'])) { } elseif (in_array(strtolower($op), ['null', 'notnull', 'not null'])) {
// null查询 // null查询
$where[$field] = [$op, '']; $where[$field] = [$op, ''];
$this->options['multi'][$logic][$field][] = $where[$field]; $this->options['multi'][$logic][$field][] = $where[$field];
} elseif (is_null($condition)) { } elseif (is_null($condition)) {
// 字段相等查询 // 字段相等查询
$where[$field] = ['eq', $op]; $where[$field] = ['eq', $op];
$this->options['multi'][$logic][$field][] = $where[$field]; $this->options['multi'][$logic][$field][] = $where[$field];
} else { } else {
$where[$field] = [$op, $condition, isset($param[2]) ? $param[2] : null]; if ('exp' == strtolower($op)) {
if ('exp' == strtolower($op) && isset($param[2]) && is_array($param[2])) { $where[$field] = ['exp', $this->raw($condition)];
// 参数绑定 // 参数绑定
if (isset($param[2]) && is_array($param[2])) {
$this->bind($param[2]); $this->bind($param[2]);
} }
} else {
$where[$field] = [$op, $condition];
}
// 记录一个字段多次查询条件 // 记录一个字段多次查询条件
$this->options['multi'][$logic][$field][] = $where[$field]; $this->options['multi'][$logic][$field][] = $where[$field];
} }
if (!empty($where)) { if (!empty($where)) {
if (!isset($this->options['where'][$logic])) { if (!isset($this->options['where'][$logic])) {
$this->options['where'][$logic] = []; $this->options['where'][$logic] = [];
@ -1239,6 +1344,7 @@ class Query
$logic = strtoupper($logic); $logic = strtoupper($logic);
if (isset($this->options['where'][$logic][$field])) { if (isset($this->options['where'][$logic][$field])) {
unset($this->options['where'][$logic][$field]); unset($this->options['where'][$logic][$field]);
unset($this->options['multi'][$logic][$field]);
} }
return $this; return $this;
} }
@ -1414,12 +1520,22 @@ class Query
*/ */
public function order($field, $order = null) public function order($field, $order = null)
{ {
if (!empty($field)) { if (empty($field)) {
return $this;
} elseif ($field instanceof Expression) {
$this->options['order'][] = $field;
return $this;
}
if (is_string($field)) { if (is_string($field)) {
if (!empty($this->options['via'])) { if (!empty($this->options['via'])) {
$field = $this->options['via'] . '.' . $field; $field = $this->options['via'] . '.' . $field;
} }
if (strpos($field, ',')) {
$field = array_map('trim', explode(',', $field));
} else {
$field = empty($order) ? $field : [$field => $order]; $field = empty($order) ? $field : [$field => $order];
}
} elseif (!empty($this->options['via'])) { } elseif (!empty($this->options['via'])) {
foreach ($field as $key => $val) { foreach ($field as $key => $val) {
if (is_numeric($key)) { if (is_numeric($key)) {
@ -1438,7 +1554,25 @@ class Query
} else { } else {
$this->options['order'][] = $field; $this->options['order'][] = $field;
} }
return $this;
} }
/**
* 表达式方式指定Field排序
* @access public
* @param string $field 排序字段
* @param array $bind 参数绑定
* @return $this
*/
public function orderRaw($field, array $bind = [])
{
$this->options['order'][] = $this->raw($field);
if ($bind) {
$this->bind($bind);
}
return $this; return $this;
} }
@ -1523,7 +1657,12 @@ class Query
{ {
if (is_array($alias)) { if (is_array($alias)) {
foreach ($alias as $key => $val) { foreach ($alias as $key => $val) {
$this->options['alias'][$key] = $val; if (false !== strpos($key, '__')) {
$table = $this->parseSqlTable($key);
} else {
$table = $key;
}
$this->options['alias'][$table] = $val;
} }
} else { } else {
if (isset($this->options['table'])) { if (isset($this->options['table'])) {
@ -1651,15 +1790,17 @@ class Query
* 查询日期或者时间 * 查询日期或者时间
* @access public * @access public
* @param string $field 日期字段名 * @param string $field 日期字段名
* @param string $op 比较运算符或者表达式 * @param string|array $op 比较运算符或者表达式
* @param string|array $range 比较范围 * @param string|array $range 比较范围
* @return $this * @return $this
*/ */
public function whereTime($field, $op, $range = null) public function whereTime($field, $op, $range = null)
{ {
if (is_null($range)) { if (is_null($range)) {
if (is_array($op)) {
$range = $op;
} else {
// 使用日期表达式 // 使用日期表达式
$date = getdate();
switch (strtolower($op)) { switch (strtolower($op)) {
case 'today': case 'today':
case 'd': case 'd':
@ -1667,15 +1808,15 @@ class Query
break; break;
case 'week': case 'week':
case 'w': case 'w':
$range = 'this week 00:00:00'; $range = ['this week 00:00:00', 'next week 00:00:00'];
break; break;
case 'month': case 'month':
case 'm': case 'm':
$range = mktime(0, 0, 0, $date['mon'], 1, $date['year']); $range = ['first Day of this month 00:00:00', 'first Day of next month 00:00:00'];
break; break;
case 'year': case 'year':
case 'y': case 'y':
$range = mktime(0, 0, 0, 1, 1, $date['year']); $range = ['this year 1/1', 'next year 1/1'];
break; break;
case 'yesterday': case 'yesterday':
$range = ['yesterday', 'today']; $range = ['yesterday', 'today'];
@ -1684,14 +1825,15 @@ class Query
$range = ['last week 00:00:00', 'this week 00:00:00']; $range = ['last week 00:00:00', 'this week 00:00:00'];
break; break;
case 'last month': case 'last month':
$range = [date('y-m-01', strtotime('-1 month')), mktime(0, 0, 0, $date['mon'], 1, $date['year'])]; $range = ['first Day of last month 00:00:00', 'first Day of this month 00:00:00'];
break; break;
case 'last year': case 'last year':
$range = [mktime(0, 0, 0, 1, 1, $date['year'] - 1), mktime(0, 0, 0, 1, 1, $date['year'])]; $range = ['last year 1/1', 'this year 1/1'];
break; break;
default: default:
$range = $op; $range = $op;
} }
}
$op = is_array($range) ? 'between' : '>'; $op = is_array($range) ? 'between' : '>';
} }
$this->where($field, strtolower($op) . ' time', $range); $this->where($field, strtolower($op) . ' time', $range);
@ -1735,7 +1877,7 @@ class Query
$schema = $guid; $schema = $guid;
} }
// 读取缓存 // 读取缓存
if (is_file(RUNTIME_PATH . 'schema/' . $schema . '.php')) { if (!App::$debug && is_file(RUNTIME_PATH . 'schema/' . $schema . '.php')) {
$info = include RUNTIME_PATH . 'schema/' . $schema . '.php'; $info = include RUNTIME_PATH . 'schema/' . $schema . '.php';
} else { } else {
$info = $this->connection->getFields($guid); $info = $this->connection->getFields($guid);
@ -1810,7 +1952,9 @@ class Query
*/ */
protected function getFieldBindType($type) protected function getFieldBindType($type)
{ {
if (preg_match('/(int|double|float|decimal|real|numeric|serial|bit)/is', $type)) { if (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) {
$bind = PDO::PARAM_STR;
} elseif (preg_match('/(int|double|float|decimal|real|numeric|serial|bit)/is', $type)) {
$bind = PDO::PARAM_INT; $bind = PDO::PARAM_INT;
} elseif (preg_match('/bool/is', $type)) { } elseif (preg_match('/bool/is', $type)) {
$bind = PDO::PARAM_BOOL; $bind = PDO::PARAM_BOOL;
@ -1893,10 +2037,9 @@ class Query
} }
$first = true; $first = true;
$currentModel = $this->model;
/** @var Model $class */ /** @var Model $class */
$class = new $currentModel; $class = $this->model;
foreach ($with as $key => $relation) { foreach ($with as $key => $relation) {
$subRelation = ''; $subRelation = '';
$closure = false; $closure = false;
@ -1955,7 +2098,7 @@ class Query
$relation = $key; $relation = $key;
} }
$relation = Loader::parseName($relation, 1, false); $relation = Loader::parseName($relation, 1, false);
$count = '(' . (new $this->model)->$relation()->getRelationCountQuery($closure) . ')'; $count = '(' . $this->model->$relation()->getRelationCountQuery($closure) . ')';
$this->field([$count => Loader::parseName($relation) . '_count']); $this->field([$count => Loader::parseName($relation) . '_count']);
} }
} }
@ -2082,7 +2225,7 @@ class Query
} }
// 执行操作 // 执行操作
$result = 0 === $sql ? 0 : $this->execute($sql, $bind); $result = 0 === $sql ? 0 : $this->execute($sql, $bind, $this);
if ($result) { if ($result) {
$sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null); $sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null);
$lastInsId = $this->getLastInsID($sequence); $lastInsId = $this->getLastInsID($sequence);
@ -2120,25 +2263,38 @@ class Query
* @access public * @access public
* @param mixed $dataSet 数据集 * @param mixed $dataSet 数据集
* @param boolean $replace 是否replace * @param boolean $replace 是否replace
* @param integer $limit 每次写入数据限制
* @return integer|string * @return integer|string
*/ */
public function insertAll(array $dataSet, $replace = false) public function insertAll(array $dataSet, $replace = false, $limit = null)
{ {
// 分析查询表达式 // 分析查询表达式
$options = $this->parseExpress(); $options = $this->parseExpress();
if (!is_array(reset($dataSet))) { if (!is_array(reset($dataSet))) {
return false; return false;
} }
// 生成SQL语句 // 生成SQL语句
if (is_null($limit)) {
$sql = $this->builder->insertAll($dataSet, $options, $replace); $sql = $this->builder->insertAll($dataSet, $options, $replace);
} else {
$array = array_chunk($dataSet, $limit, true);
foreach ($array as $item) {
$sql[] = $this->builder->insertAll($item, $options, $replace);
}
}
// 获取参数绑定 // 获取参数绑定
$bind = $this->getBind(); $bind = $this->getBind();
if ($options['fetch_sql']) { if ($options['fetch_sql']) {
// 获取实际执行的SQL语句 // 获取实际执行的SQL语句
return $this->connection->getRealSql($sql, $bind); return $this->connection->getRealSql($sql, $bind);
} elseif (is_array($sql)) {
// 执行操作
return $this->batchQuery($sql, $bind, $this);
} else { } else {
// 执行操作 // 执行操作
return $this->execute($sql, $bind); return $this->execute($sql, $bind, $this);
} }
} }
@ -2164,7 +2320,7 @@ class Query
return $this->connection->getRealSql($sql, $bind); return $this->connection->getRealSql($sql, $bind);
} else { } else {
// 执行操作 // 执行操作
return $this->execute($sql, $bind); return $this->execute($sql, $bind, $this);
} }
} }
@ -2231,7 +2387,7 @@ class Query
Cache::clear($options['cache']['tag']); Cache::clear($options['cache']['tag']);
} }
// 执行操作 // 执行操作
$result = '' == $sql ? 0 : $this->execute($sql, $bind); $result = '' == $sql ? 0 : $this->execute($sql, $bind, $this);
if ($result) { if ($result) {
if (is_string($pk) && isset($where[$pk])) { if (is_string($pk) && isset($where[$pk])) {
$data[$pk] = $where[$pk]; $data[$pk] = $where[$pk];
@ -2300,7 +2456,7 @@ class Query
// 判断查询缓存 // 判断查询缓存
$cache = $options['cache']; $cache = $options['cache'];
unset($options['cache']); unset($options['cache']);
$key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options) . serialize($this->bind)); $key = is_string($cache['key']) ? $cache['key'] : md5($this->connection->getConfig('database') . '.' . serialize($options) . serialize($this->bind));
$resultSet = Cache::get($key); $resultSet = Cache::get($key);
} }
if (false === $resultSet) { if (false === $resultSet) {
@ -2334,11 +2490,10 @@ class Query
// 数据列表读取后的处理 // 数据列表读取后的处理
if (!empty($this->model)) { if (!empty($this->model)) {
// 生成模型对象 // 生成模型对象
$modelName = $this->model;
if (count($resultSet) > 0) { if (count($resultSet) > 0) {
foreach ($resultSet as $key => $result) { foreach ($resultSet as $key => $result) {
/** @var Model $model */ /** @var Model $model */
$model = new $modelName($result); $model = $this->model->newInstance($result);
$model->isUpdate(true); $model->isUpdate(true);
// 关联查询 // 关联查询
@ -2358,7 +2513,7 @@ class Query
// 模型数据集转换 // 模型数据集转换
$resultSet = $model->toCollection($resultSet); $resultSet = $model->toCollection($resultSet);
} else { } else {
$resultSet = (new $modelName)->toCollection($resultSet); $resultSet = $this->model->toCollection($resultSet);
} }
} elseif ('collection' == $this->connection->getConfig('resultset_type')) { } elseif ('collection' == $this->connection->getConfig('resultset_type')) {
// 返回Collection对象 // 返回Collection对象
@ -2402,10 +2557,16 @@ class Query
} elseif (is_array($value) && is_string($value[0]) && 'eq' == strtolower($value[0])) { } elseif (is_array($value) && is_string($value[0]) && 'eq' == strtolower($value[0])) {
$data = $value[1]; $data = $value[1];
} }
$prefix = $this->connection->getConfig('database') . '.';
if (isset($data)) { if (isset($data)) {
return 'think:' . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data; return 'think:' . $prefix . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data;
} else { }
return md5(serialize($options) . serialize($bind));
try {
return md5($prefix . serialize($options) . serialize($bind));
} catch (\Exception $e) {
throw new Exception('closure not support cache(true)');
} }
} }
@ -2442,11 +2603,11 @@ class Query
// 判断查询缓存 // 判断查询缓存
$cache = $options['cache']; $cache = $options['cache'];
if (true === $cache['key'] && !is_null($data) && !is_array($data)) { if (true === $cache['key'] && !is_null($data) && !is_array($data)) {
$key = 'think:' . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data; $key = 'think:' . $this->connection->getConfig('database') . '.' . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data;
} elseif (is_string($cache['key'])) { } elseif (is_string($cache['key'])) {
$key = $cache['key']; $key = $cache['key'];
} elseif (!isset($key)) { } elseif (!isset($key)) {
$key = md5(serialize($options) . serialize($this->bind)); $key = md5($this->connection->getConfig('database') . '.' . serialize($options) . serialize($this->bind));
} }
$result = Cache::get($key); $result = Cache::get($key);
} }
@ -2484,7 +2645,7 @@ class Query
$result = isset($resultSet[0]) ? $resultSet[0] : null; $result = isset($resultSet[0]) ? $resultSet[0] : null;
} }
if (isset($cache) && false !== $result) { if (isset($cache) && $result) {
// 缓存数据 // 缓存数据
$this->cacheData($key, $result, $cache); $this->cacheData($key, $result, $cache);
} }
@ -2494,8 +2655,7 @@ class Query
if (!empty($result)) { if (!empty($result)) {
if (!empty($this->model)) { if (!empty($this->model)) {
// 返回模型对象 // 返回模型对象
$model = $this->model; $result = $this->model->newInstance($result);
$result = new $model($result);
$result->isUpdate(true, isset($options['where']['AND']) ? $options['where']['AND'] : null); $result->isUpdate(true, isset($options['where']['AND']) ? $options['where']['AND'] : null);
// 关联查询 // 关联查询
if (!empty($options['relation'])) { if (!empty($options['relation'])) {
@ -2526,7 +2686,8 @@ class Query
protected function throwNotFound($options = []) protected function throwNotFound($options = [])
{ {
if (!empty($this->model)) { if (!empty($this->model)) {
throw new ModelNotFoundException('model data Not Found:' . $this->model, $this->model, $options); $class = get_class($this->model);
throw new ModelNotFoundException('model data Not Found:' . $class, $class, $options);
} else { } else {
$table = is_array($options['table']) ? key($options['table']) : $options['table']; $table = is_array($options['table']) ? key($options['table']) : $options['table'];
throw new DataNotFoundException('table data not Found:' . $table, $table, $options); throw new DataNotFoundException('table data not Found:' . $table, $table, $options);
@ -2574,15 +2735,11 @@ class Query
public function chunk($count, $callback, $column = null, $order = 'asc') public function chunk($count, $callback, $column = null, $order = 'asc')
{ {
$options = $this->getOptions(); $options = $this->getOptions();
if (isset($options['table'])) { if (empty($options['table'])) {
$table = is_array($options['table']) ? key($options['table']) : $options['table']; $options['table'] = $this->getTable();
} else {
$table = '';
}
$column = $column ?: $this->getPk($table);
if (is_array($column)) {
$column = $column[0];
} }
$column = $column ?: $this->getPk($options);
if (isset($options['order'])) { if (isset($options['order'])) {
if (App::$debug) { if (App::$debug) {
throw new \LogicException('chunk not support call order'); throw new \LogicException('chunk not support call order');
@ -2590,32 +2747,42 @@ class Query
unset($options['order']); unset($options['order']);
} }
$bind = $this->bind; $bind = $this->bind;
$resultSet = $this->options($options)->limit($count)->order($column, $order)->select(); if (is_array($column)) {
$times = 1;
$query = $this->options($options)->page($times, $count);
} else {
if (strpos($column, '.')) { if (strpos($column, '.')) {
list($alias, $key) = explode('.', $column); list($alias, $key) = explode('.', $column);
} else { } else {
$key = $column; $key = $column;
} }
$query = $this->options($options)->limit($count);
}
$resultSet = $query->order($column, $order)->select();
while (count($resultSet) > 0) {
if ($resultSet instanceof Collection) { if ($resultSet instanceof Collection) {
$resultSet = $resultSet->all(); $resultSet = $resultSet->all();
} }
while (!empty($resultSet)) {
if (false === call_user_func($callback, $resultSet)) { if (false === call_user_func($callback, $resultSet)) {
return false; return false;
} }
if (is_array($column)) {
$times++;
$query = $this->options($options)->page($times, $count);
} else {
$end = end($resultSet); $end = end($resultSet);
$lastId = is_array($end) ? $end[$key] : $end->$key; $lastId = is_array($end) ? $end[$key] : $end->getData($key);
$resultSet = $this->options($options) $query = $this->options($options)
->limit($count) ->limit($count)
->bind($bind) ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId);
->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId)
->order($column, $order)
->select();
if ($resultSet instanceof Collection) {
$resultSet = $resultSet->all();
} }
$resultSet = $query->bind($bind)->order($column, $order)->select();
} }
return true; return true;
} }
@ -2692,7 +2859,7 @@ class Query
Cache::clear($options['cache']['tag']); Cache::clear($options['cache']['tag']);
} }
// 执行操作 // 执行操作
$result = $this->execute($sql, $bind); $result = $this->execute($sql, $bind, $this);
if ($result) { if ($result) {
if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) { if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) {
list($a, $val) = explode('|', $key); list($a, $val) = explode('|', $key);
@ -2777,6 +2944,10 @@ class Query
} }
} }
if (isset(static::$readMaster['*']) || (is_string($options['table']) && isset(static::$readMaster[$options['table']]))) {
$options['master'] = true;
}
foreach (['join', 'union', 'group', 'having', 'limit', 'order', 'force', 'comment'] as $name) { foreach (['join', 'union', 'group', 'having', 'limit', 'order', 'force', 'comment'] as $name) {
if (!isset($options[$name])) { if (!isset($options[$name])) {
$options[$name] = ''; $options[$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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -44,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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -52,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数据库驱动
@ -34,10 +35,15 @@ class Sqlsrv extends Builder
*/ */
protected function parseOrder($order, $options = []) protected function parseOrder($order, $options = [])
{ {
if (is_array($order)) { if (empty($order)) {
return ' ORDER BY rand()';
}
$array = []; $array = [];
foreach ($order as $key => $val) { foreach ($order as $key => $val) {
if (is_numeric($key)) { if ($val instanceof Expression) {
$array[] = $val->getValue();
} elseif (is_numeric($key)) {
if (false === strpos($val, '(')) { if (false === strpos($val, '(')) {
$array[] = $this->parseKey($val, $options); $array[] = $this->parseKey($val, $options);
} elseif ('[rand]' == $val) { } elseif ('[rand]' == $val) {
@ -46,13 +52,12 @@ class Sqlsrv extends Builder
$array[] = $val; $array[] = $val;
} }
} else { } else {
$sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : ''; $sort = in_array(strtolower(trim($val)), ['asc', 'desc'], true) ? ' ' . $val : '';
$array[] = $this->parseKey($key, $options) . ' ' . $sort; $array[] = $this->parseKey($key, $options, true) . ' ' . $sort;
} }
} }
$order = implode(',', $array);
} return ' ORDER BY ' . implode(',', $array);
return !empty($order) ? ' ORDER BY ' . $order : ' ORDER BY rand()';
} }
/** /**
@ -68,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);
@ -84,7 +94,7 @@ class Sqlsrv extends Builder
$table = $options['alias'][$table]; $table = $options['alias'][$table];
} }
} }
if (!is_numeric($key) && !preg_match('/[,\'\"\*\(\)\[.\s]/', $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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

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

@ -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

@ -12,6 +12,7 @@
namespace think\log\driver; namespace think\log\driver;
use think\App; use think\App;
use think\Request;
/** /**
* 本地化调试输出到文件 * 本地化调试输出到文件
@ -20,9 +21,11 @@ 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,
]; ];
protected $writed = []; protected $writed = [];
@ -43,8 +46,24 @@ class File
*/ */
public function save(array $log = []) public function save(array $log = [])
{ {
if ($this->config['single']) {
$destination = $this->config['path'] . 'single.log';
} else {
$cli = IS_CLI ? '_cli' : ''; $cli = IS_CLI ? '_cli' : '';
$destination = $this->config['path'] . date('Ym') . DS . date('d') . $cli . '.log';
if ($this->config['max_files']) {
$filename = date('Ymd') . $cli . '.log';
$files = glob($this->config['path'] . '*.log');
if (count($files) > $this->config['max_files']) {
unlink($files[0]);
}
} else {
$filename = date('Ym') . '/' . date('d') . $cli . '.log';
}
$destination = $this->config['path'] . $filename;
}
$path = dirname($destination); $path = dirname($destination);
!is_dir($path) && mkdir($path, 0755, true); !is_dir($path) && mkdir($path, 0755, true);
@ -60,7 +79,13 @@ class File
} }
if (in_array($type, $this->config['apart_level'])) { if (in_array($type, $this->config['apart_level'])) {
// 独立记录的日志级别 // 独立记录的日志级别
if ($this->config['single']) {
$filename = $path . DS . $type . '.log';
} elseif ($this->config['max_files']) {
$filename = $path . DS . date('Ymd') . '_' . $type . $cli . '.log';
} else {
$filename = $path . DS . date('d') . '_' . $type . $cli . '.log'; $filename = $path . DS . date('d') . '_' . $type . $cli . '.log';
}
$this->write($level, $filename, true); $this->write($level, $filename, true);
} else { } else {
$info .= $level; $info .= $level;
@ -76,7 +101,10 @@ class File
{ {
//检测日志文件大小,超过配置大小则备份日志文件重新生成 //检测日志文件大小,超过配置大小则备份日志文件重新生成
if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) { if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) {
try {
rename($destination, dirname($destination) . DS . time() . '-' . basename($destination)); rename($destination, dirname($destination) . DS . time() . '-' . basename($destination));
} catch (\Exception $e) {
}
$this->writed[$destination] = false; $this->writed[$destination] = false;
} }
@ -99,11 +127,10 @@ class File
$message = '[ info ] ' . $current_uri . $time_str . $memory_str . $file_load . "\r\n" . $message; $message = '[ info ] ' . $current_uri . $time_str . $memory_str . $file_load . "\r\n" . $message;
} }
$now = date($this->config['time_format']); $now = date($this->config['time_format']);
$server = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '0.0.0.0'; $ip = Request::instance()->ip();
$remote = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0';
$method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'CLI'; $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'CLI';
$uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
$message = "---------------------------------------------------------------\r\n[{$now}] {$server} {$remote} {$method} {$uri}\r\n" . $message; $message = "---------------------------------------------------------------\r\n[{$now}] {$ip} {$method} {$uri}\r\n" . $message;
$this->writed[$destination] = true; $this->writed[$destination] = 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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -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

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 = null, $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

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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -52,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();
@ -80,13 +81,15 @@ 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,
@ -146,13 +152,13 @@ class BelongsTo extends OneToOne
if (!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);
} }
} }
} }
}
/** /**
* 预载入关联查询(数据) * 预载入关联查询(数据)
@ -167,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;
@ -179,10 +186,11 @@ class BelongsTo extends OneToOne
if (!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);
} }
}
/** /**
* 添加关联数据 * 添加关联数据
@ -215,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;
@ -52,6 +53,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 +71,34 @@ class BelongsToMany extends Relation
} }
/** /**
* 实例化中间表模型 * 获取中间表更新条件
* @param $data * @param $data
* @return mixed * @return array
*/ */
protected function newPivot($data = []) protected function getUpdateWhere($data)
{ {
$pivot = $this->pivotName ?: '\\think\\model\\Pivot'; return [
return new $pivot($this->parent, $data, $this->middle); $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 +118,7 @@ class BelongsToMany extends Relation
} }
} }
} }
$model->setRelation('pivot', $this->newPivot($pivot)); $model->setRelation('pivot', $this->newPivot($pivot, true));
} }
} }
@ -207,10 +232,11 @@ 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');
} }
@ -327,7 +353,7 @@ class BelongsToMany extends Relation
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 +384,7 @@ class BelongsToMany extends Relation
} }
} }
} }
$set->setRelation('pivot', $this->newPivot($pivot)); $set->setRelation('pivot', $this->newPivot($pivot, true));
$data[$pivot[$this->localKey]][] = $set; $data[$pivot[$this->localKey]][] = $set;
} }
return $data; return $data;
@ -384,7 +410,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 +487,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) {
// 返回中间表模型对象 // 返回中间表模型对象
@ -565,7 +591,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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -77,7 +77,7 @@ class HasMany extends Relation
} }
if (!empty($range)) { if (!empty($range)) {
$data = $this->eagerlyOneToMany(new $this->model, [ $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(new $this->model, [$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;
} }
@ -160,12 +160,7 @@ class HasMany extends Relation
call_user_func_array($closure, [ & $this->query]); call_user_func_array($closure, [ & $this->query]);
} }
$localKey = $this->localKey ?: $this->parent->getPk(); $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() . '.' . $localKey,
],
])->fetchSql()->count();
} }
/** /**
@ -185,7 +180,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 = [];
@ -245,7 +240,7 @@ class HasMany extends Relation
return $this->parent->db() return $this->parent->db()
->alias($model) ->alias($model)
->field($model . '.*') ->field($model . '.*')
->join($table . ' ' . $relation, $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType) ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType)
->group($relation . '.' . $this->foreignKey) ->group($relation . '.' . $this->foreignKey)
->having('count(' . $id . ')' . $operator . $count); ->having('count(' . $id . ')' . $operator . $count);
} }
@ -254,13 +249,15 @@ class HasMany 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)
{ {
$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, '.')) {
@ -269,9 +266,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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -78,9 +78,10 @@ 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)
{} {}
/** /**

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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -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);
@ -74,7 +78,7 @@ class HasOne extends OneToOne
return $this->parent->db() return $this->parent->db()
->alias($model) ->alias($model)
->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey) { ->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey) {
$query->table([$table => $relation])->field($relation . '.' . $foreignKey)->whereExp($model . '.' . $localKey, '=' . $relatoin . '.' . $foreignKey); $query->table([$table => $relation])->field($relation . '.' . $foreignKey)->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey);
}); });
} }
@ -82,13 +86,15 @@ class HasOne 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, '.')) {
@ -97,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);
} }
@ -126,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,
@ -147,12 +156,13 @@ class HasOne extends OneToOne
if (!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);
} }
} }
} }
}
/** /**
* 预载入关联查询(数据) * 预载入关联查询(数据)
@ -167,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])) {
@ -180,9 +191,25 @@ class HasOne extends OneToOne
if (!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);
} }
}
/**
* 执行基础查询(仅执行一次)
* @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;
@ -83,9 +84,10 @@ 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');
} }
@ -200,7 +202,7 @@ class MorphMany extends Relation
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();

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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -82,9 +82,10 @@ 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');
} }

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 子关联名
@ -83,16 +95,17 @@ 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
*/ */
@ -239,16 +252,17 @@ 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);

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
@ -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]);

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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -264,8 +264,8 @@ class TagLib
if (!empty($this->tags[$name]['expression'])) { if (!empty($this->tags[$name]['expression'])) {
static $_taglibs; static $_taglibs;
if (!isset($_taglibs[$name])) { if (!isset($_taglibs[$name])) {
$_taglibs[$name][0] = strlen(ltrim($this->tpl->config('taglib_begin'), '\\') . $name); $_taglibs[$name][0] = strlen($this->tpl->config('taglib_begin_origin') . $name);
$_taglibs[$name][1] = strlen(ltrim($this->tpl->config('taglib_end'), '\\')); $_taglibs[$name][1] = strlen($this->tpl->config('taglib_end_origin'));
} }
$result['expression'] = substr($str, $_taglibs[$name][0], -$_taglibs[$name][1]); $result['expression'] = substr($str, $_taglibs[$name][0], -$_taglibs[$name][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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -15,6 +15,8 @@ use think\Exception;
class File class File
{ {
protected $cacheFile;
/** /**
* 写入编译缓存 * 写入编译缓存
* @param string $cacheFile 缓存的文件名 * @param string $cacheFile 缓存的文件名
@ -42,12 +44,13 @@ class File
*/ */
public function read($cacheFile, $vars = []) public function read($cacheFile, $vars = [])
{ {
$this->cacheFile = $cacheFile;
if (!empty($vars) && is_array($vars)) { if (!empty($vars) && is_array($vars)) {
// 模板阵列变量分解成为独立变量 // 模板阵列变量分解成为独立变量
extract($vars, EXTR_OVERWRITE); extract($vars, EXTR_OVERWRITE);
} }
//载入模版缓存文件 //载入模版缓存文件
include $cacheFile; include $this->cacheFile;
} }
/** /**

View File

@ -278,7 +278,7 @@ class Cx extends Taglib
*/ */
public function tagCase($tag, $content) public function tagCase($tag, $content)
{ {
$value = !empty($tag['expression']) ? $tag['expression'] : $tag['value']; $value = isset($tag['expression']) ? $tag['expression'] : $tag['value'];
$flag = substr($value, 0, 1); $flag = substr($value, 0, 1);
if ('$' == $flag || ':' == $flag) { if ('$' == $flag || ':' == $flag) {
$value = $this->autoBuildVar($value); $value = $this->autoBuildVar($value);

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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -29,7 +29,11 @@ class Php
'view_suffix' => 'php', 'view_suffix' => 'php',
// 模板文件名分隔符 // 模板文件名分隔符
'view_depr' => DS, 'view_depr' => DS,
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写
'auto_rule' => 1,
]; ];
protected $template;
protected $content;
public function __construct($config = []) public function __construct($config = [])
{ {
@ -68,16 +72,12 @@ class Php
if (!is_file($template)) { if (!is_file($template)) {
throw new TemplateNotFoundException('template not exists:' . $template, $template); throw new TemplateNotFoundException('template not exists:' . $template, $template);
} }
$this->template = $template;
// 记录视图信息 // 记录视图信息
App::$debug && Log::record('[ VIEW ] ' . $template . ' [ ' . var_export(array_keys($data), true) . ' ]', 'info'); App::$debug && Log::record('[ VIEW ] ' . $template . ' [ ' . var_export(array_keys($data), true) . ' ]', 'info');
if (isset($data['template'])) {
$__template__ = $template;
extract($data, EXTR_OVERWRITE); extract($data, EXTR_OVERWRITE);
include $__template__; include $this->template;
} else {
extract($data, EXTR_OVERWRITE);
include $template;
}
} }
/** /**
@ -89,14 +89,10 @@ class Php
*/ */
public function display($content, $data = []) public function display($content, $data = [])
{ {
if (isset($data['content'])) { $this->content = $content;
$__content__ = $content;
extract($data, EXTR_OVERWRITE); extract($data, EXTR_OVERWRITE);
eval('?>' . $__content__); eval('?>' . $this->content);
} else {
extract($data, EXTR_OVERWRITE);
eval('?>' . $content);
}
} }
/** /**
@ -132,7 +128,7 @@ class Php
if ($controller) { if ($controller) {
if ('' == $template) { if ('' == $template) {
// 如果模板文件名为空 按照默认规则定位 // 如果模板文件名为空 按照默认规则定位
$template = str_replace('.', DS, $controller) . $depr . $request->action(); $template = str_replace('.', DS, $controller) . $depr . (1 == $this->config['auto_rule'] ? Loader::parseName($request->action(true)) : $request->action());
} elseif (false === strpos($template, $depr)) { } elseif (false === strpos($template, $depr)) {
$template = str_replace('.', DS, $controller) . $depr . $template; $template = str_replace('.', DS, $controller) . $depr . $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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -34,6 +34,8 @@ class Think
'view_depr' => DS, 'view_depr' => DS,
// 是否开启模板编译缓存,设为false则每次都会重新编译 // 是否开启模板编译缓存,设为false则每次都会重新编译
'tpl_cache' => true, 'tpl_cache' => true,
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写
'auto_rule' => 1,
]; ];
public function __construct($config = []) public function __construct($config = [])
@ -127,7 +129,7 @@ class Think
if ($controller) { if ($controller) {
if ('' == $template) { if ('' == $template) {
// 如果模板文件名为空 按照默认规则定位 // 如果模板文件名为空 按照默认规则定位
$template = str_replace('.', DS, $controller) . $depr . $request->action(); $template = str_replace('.', DS, $controller) . $depr . (1 == $this->config['auto_rule'] ? Loader::parseName($request->action(true)) : $request->action());
} elseif (false === strpos($template, $depr)) { } elseif (false === strpos($template, $depr)) {
$template = str_replace('.', DS, $controller) . $depr . $template; $template = str_replace('.', DS, $controller) . $depr . $template;
} }

View File

@ -1,5 +1,4 @@
<?php <?php
/** /**
* 用法: * 用法:
* load_trait('controller/Jump'); * load_trait('controller/Jump');
@ -30,17 +29,20 @@ trait Jump
* @param mixed $msg 提示信息 * @param mixed $msg 提示信息
* @param string $url 跳转的 URL 地址 * @param string $url 跳转的 URL 地址
* @param mixed $data 返回的数据 * @param mixed $data 返回的数据
* @param integer $wait 跳转等待时间 * @param int $wait 跳转等待时间
* @param array $header 发送的 Header 信息 * @param array $header 发送的 Header 信息
* @return void * @return void
* @throws HttpResponseException
*/ */
protected function success($msg = '', $url = null, $data = '', $wait = 3, array $header = []) protected function success($msg = '', $url = null, $data = '', $wait = 3, array $header = [])
{ {
if (is_null($url) && !is_null(Request::instance()->server('HTTP_REFERER'))) { if (is_null($url) && !is_null(Request::instance()->server('HTTP_REFERER'))) {
$url = Request::instance()->server('HTTP_REFERER'); $url = Request::instance()->server('HTTP_REFERER');
} elseif ('' !== $url) { } elseif ('' !== $url && !strpos($url, '://') && 0 !== strpos($url, '/')) {
$url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : Url::build($url); $url = Url::build($url);
} }
$type = $this->getResponseType();
$result = [ $result = [
'code' => 1, 'code' => 1,
'msg' => $msg, 'msg' => $msg,
@ -49,12 +51,16 @@ trait Jump
'wait' => $wait, 'wait' => $wait,
]; ];
$type = $this->getResponseType();
if ('html' == strtolower($type)) { if ('html' == strtolower($type)) {
$result = ViewTemplate::instance(Config::get('template'), Config::get('view_replace_str')) $template = Config::get('template');
$view = Config::get('view_replace_str');
$result = ViewTemplate::instance($template, $view)
->fetch(Config::get('dispatch_success_tmpl'), $result); ->fetch(Config::get('dispatch_success_tmpl'), $result);
} }
$response = Response::create($result, $type)->header($header); $response = Response::create($result, $type)->header($header);
throw new HttpResponseException($response); throw new HttpResponseException($response);
} }
@ -64,17 +70,20 @@ trait Jump
* @param mixed $msg 提示信息 * @param mixed $msg 提示信息
* @param string $url 跳转的 URL 地址 * @param string $url 跳转的 URL 地址
* @param mixed $data 返回的数据 * @param mixed $data 返回的数据
* @param integer $wait 跳转等待时间 * @param int $wait 跳转等待时间
* @param array $header 发送的 Header 信息 * @param array $header 发送的 Header 信息
* @return void * @return void
* @throws HttpResponseException
*/ */
protected function error($msg = '', $url = null, $data = '', $wait = 3, array $header = []) protected function error($msg = '', $url = null, $data = '', $wait = 3, array $header = [])
{ {
if (is_null($url)) { if (is_null($url)) {
$url = Request::instance()->isAjax() ? '' : 'javascript:history.back(-1);'; $url = Request::instance()->isAjax() ? '' : 'javascript:history.back(-1);';
} elseif ('' !== $url) { } elseif ('' !== $url && !strpos($url, '://') && 0 !== strpos($url, '/')) {
$url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : Url::build($url); $url = Url::build($url);
} }
$type = $this->getResponseType();
$result = [ $result = [
'code' => 0, 'code' => 0,
'msg' => $msg, 'msg' => $msg,
@ -83,12 +92,16 @@ trait Jump
'wait' => $wait, 'wait' => $wait,
]; ];
$type = $this->getResponseType();
if ('html' == strtolower($type)) { if ('html' == strtolower($type)) {
$result = ViewTemplate::instance(Config::get('template'), Config::get('view_replace_str')) $template = Config::get('template');
$view = Config::get('view_replace_str');
$result = ViewTemplate::instance($template, $view)
->fetch(Config::get('dispatch_error_tmpl'), $result); ->fetch(Config::get('dispatch_error_tmpl'), $result);
} }
$response = Response::create($result, $type)->header($header); $response = Response::create($result, $type)->header($header);
throw new HttpResponseException($response); throw new HttpResponseException($response);
} }
@ -96,11 +109,12 @@ trait Jump
* 返回封装后的 API 数据到客户端 * 返回封装后的 API 数据到客户端
* @access protected * @access protected
* @param mixed $data 要返回的数据 * @param mixed $data 要返回的数据
* @param integer $code 返回的code * @param int $code 返回的 code
* @param mixed $msg 提示信息 * @param mixed $msg 提示信息
* @param string $type 返回数据格式 * @param string $type 返回数据格式
* @param array $header 发送的 Header 信息 * @param array $header 发送的 Header 信息
* @return void * @return void
* @throws HttpResponseException
*/ */
protected function result($data, $code = 0, $msg = '', $type = '', array $header = []) protected function result($data, $code = 0, $msg = '', $type = '', array $header = [])
{ {
@ -112,6 +126,7 @@ trait Jump
]; ];
$type = $type ?: $this->getResponseType(); $type = $type ?: $this->getResponseType();
$response = Response::create($result, $type)->header($header); $response = Response::create($result, $type)->header($header);
throw new HttpResponseException($response); throw new HttpResponseException($response);
} }
@ -119,19 +134,22 @@ trait Jump
* URL 重定向 * URL 重定向
* @access protected * @access protected
* @param string $url 跳转的 URL 表达式 * @param string $url 跳转的 URL 表达式
* @param array|integer $params 其它URL参数 * @param array|int $params 其它 URL 参数
* @param integer $code http code * @param int $code http code
* @param array $with 隐式传参 * @param array $with 隐式传参
* @return void * @return void
* @throws HttpResponseException
*/ */
protected function redirect($url, $params = [], $code = 302, $with = []) protected function redirect($url, $params = [], $code = 302, $with = [])
{ {
$response = new Redirect($url);
if (is_integer($params)) { if (is_integer($params)) {
$code = $params; $code = $params;
$params = []; $params = [];
} }
$response = new Redirect($url);
$response->code($code)->params($params)->with($with); $response->code($code)->params($params)->with($with);
throw new HttpResponseException($response); throw new HttpResponseException($response);
} }
@ -142,7 +160,8 @@ trait Jump
*/ */
protected function getResponseType() protected function getResponseType()
{ {
$isAjax = Request::instance()->isAjax(); return Request::instance()->isAjax()
return $isAjax ? Config::get('default_ajax_return') : Config::get('default_return_type'); ? Config::get('default_ajax_return')
: Config::get('default_return_type');
} }
} }

View File

@ -3,10 +3,13 @@
namespace traits\model; namespace traits\model;
use think\db\Query; use think\db\Query;
use think\Model;
/**
* @mixin \Think\Model
*/
trait SoftDelete trait SoftDelete
{ {
/** /**
* 判断当前实例是否被软删除 * 判断当前实例是否被软删除
* @access public * @access public
@ -15,22 +18,21 @@ trait SoftDelete
public function trashed() public function trashed()
{ {
$field = $this->getDeleteTimeField(); $field = $this->getDeleteTimeField();
if (!empty($this->data[$field])) {
if ($field && !empty($this->data[$field])) {
return true; return true;
} }
return false; return false;
} }
/** /**
* 查询软删除数据 * 查询包含软删除数据
* @access public * @access public
* @return Query * @return Query
*/ */
public static function withTrashed() public static function withTrashed()
{ {
$model = new static(); return (new static )->getQuery();
$field = $model->getDeleteTimeField(true);
return $model->getQuery();
} }
/** /**
@ -42,8 +44,12 @@ trait SoftDelete
{ {
$model = new static(); $model = new static();
$field = $model->getDeleteTimeField(true); $field = $model->getDeleteTimeField(true);
return $model->getQuery()
->useSoftDelete($field, ['not null', '']); if ($field) {
return $model->getQuery()->useSoftDelete($field, ['not null', '']);
} else {
return $model->getQuery();
}
} }
/** /**
@ -57,44 +63,53 @@ trait SoftDelete
if (false === $this->trigger('before_delete', $this)) { if (false === $this->trigger('before_delete', $this)) {
return false; return false;
} }
$name = $this->getDeleteTimeField(); $name = $this->getDeleteTimeField();
if (!$force) { if ($name && !$force) {
// 软删除 // 软删除
$this->data[$name] = $this->autoWriteTimestamp($name); $this->data[$name] = $this->autoWriteTimestamp($name);
$result = $this->isUpdate()->save(); $result = $this->isUpdate()->save();
} else { } else {
// 删除条件 // 强制删除当前模型数据
$where = $this->getWhere(); $result = $this->getQuery()->where($this->getWhere())->delete();
// 删除当前模型数据
$result = $this->getQuery()->where($where)->delete();
} }
// 关联删除 // 关联删除
if (!empty($this->relationWrite)) { if (!empty($this->relationWrite)) {
foreach ($this->relationWrite as $key => $name) { foreach ($this->relationWrite as $key => $name) {
$name = is_numeric($key) ? $name : $key; $name = is_numeric($key) ? $name : $key;
$model = $this->getAttr($name); $result = $this->getRelation($name);
if ($model instanceof Model) { if ($result instanceof Model) {
$model->delete($force); $result->delete();
} elseif ($result instanceof Collection || is_array($result)) {
foreach ($result as $model) {
$model->delete();
}
} }
} }
} }
$this->trigger('after_delete', $this); $this->trigger('after_delete', $this);
// 清空原始数据 // 清空原始数据
$this->origin = []; $this->origin = [];
return $result; return $result;
} }
/** /**
* 删除记录 * 删除记录
* @access public * @access public
* @param mixed $data 主键列表 支持闭包查询条件 * @param mixed $data 主键列表(支持闭包查询条件)
* @param bool $force 是否强制删除 * @param bool $force 是否强制删除
* @return integer 成功删除的记录数 * @return integer 成功删除的记录数
*/ */
public static function destroy($data, $force = false) public static function destroy($data, $force = false)
{ {
if (is_null($data)) {
return 0;
}
// 包含软删除数据 // 包含软删除数据
$query = self::withTrashed(); $query = self::withTrashed();
if (is_array($data) && key($data) !== 0) { if (is_array($data) && key($data) !== 0) {
@ -103,18 +118,16 @@ trait SoftDelete
} 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 (is_null($data)) {
return 0;
} }
$resultSet = $query->select($data);
$count = 0; $count = 0;
if ($resultSet) { if ($resultSet = $query->select($data)) {
foreach ($resultSet as $data) { foreach ($resultSet as $data) {
$result = $data->delete($force); $result = $data->delete($force);
$count += $result; $count += $result;
} }
} }
return $count; return $count;
} }
@ -126,46 +139,61 @@ trait SoftDelete
*/ */
public function restore($where = []) public function restore($where = [])
{ {
$name = $this->getDeleteTimeField();
if (empty($where)) { if (empty($where)) {
$pk = $this->getPk(); $pk = $this->getPk();
$where[$pk] = $this->getData($pk); $where[$pk] = $this->getData($pk);
} }
$name = $this->getDeleteTimeField();
if ($name) {
// 恢复删除 // 恢复删除
return $this->getQuery() return $this->getQuery()
->useSoftDelete($name, ['not null', '']) ->useSoftDelete($name, ['not null', ''])
->where($where) ->where($where)
->update([$name => null]); ->update([$name => null]);
} else {
return 0;
}
} }
/** /**
* 查询默认不包含软删除数据 * 查询默认不包含软删除数据
* @access protected * @access protected
* @param Query $query 查询对象 * @param Query $query 查询对象
* @return void * @return Query
*/ */
protected function base($query) protected function base($query)
{ {
$field = $this->getDeleteTimeField(true); $field = $this->getDeleteTimeField(true);
$query->useSoftDelete($field); return $field ? $query->useSoftDelete($field) : $query;
} }
/** /**
* 获取软删除字段 * 获取软删除字段
* @access public * @access public
* @param bool $read 是否查询操作 写操作的时候会自动去掉表别名 * @param bool $read 是否查询操作(写操作的时候会自动去掉表别名)
* @return string * @return string
*/ */
protected function getDeleteTimeField($read = false) protected function getDeleteTimeField($read = false)
{ {
$field = property_exists($this, 'deleteTime') && isset($this->deleteTime) ? $this->deleteTime : 'delete_time'; $field = property_exists($this, 'deleteTime') && isset($this->deleteTime) ?
$this->deleteTime :
'delete_time';
if (false === $field) {
return false;
}
if (!strpos($field, '.')) { if (!strpos($field, '.')) {
$field = '__TABLE__.' . $field; $field = '__TABLE__.' . $field;
} }
if (!$read && strpos($field, '.')) { if (!$read && strpos($field, '.')) {
$array = explode('.', $field); $array = explode('.', $field);
$field = array_pop($array); $field = array_pop($array);
} }
return $field; return $field;
} }
} }

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,31 +15,40 @@ use think\Exception;
trait Instance trait Instance
{ {
/**
* @var null|static 实例对象
*/
protected static $instance = null; protected static $instance = null;
/** /**
* @param array $options * 获取示例
* @param array $options 实例配置
* @return static * @return static
*/ */
public static function instance($options = []) public static function instance($options = [])
{ {
if (is_null(self::$instance)) { if (is_null(self::$instance)) self::$instance = new self($options);
self::$instance = new self($options);
}
return self::$instance; return self::$instance;
} }
// 静态调用 /**
public static function __callStatic($method, $params) * 静态调用
* @param string $method 调用方法
* @param array $params 调用参数
* @return mixed
* @throws Exception
*/
public static function __callStatic($method, array $params)
{ {
if (is_null(self::$instance)) { if (is_null(self::$instance)) self::$instance = new self();
self::$instance = new self();
}
$call = substr($method, 1); $call = substr($method, 1);
if (0 === strpos($method, '_') && is_callable([self::$instance, $call])) {
return call_user_func_array([self::$instance, $call], $params); if (0 !== strpos($method, '_') || !is_callable([self::$instance, $call])) {
} else {
throw new Exception("method not exists:" . $method); throw new Exception("method not exists:" . $method);
} }
return call_user_func_array([self::$instance, $call], $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 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -12,7 +12,8 @@
namespace think; namespace think;
// ThinkPHP 引导文件 // ThinkPHP 引导文件
// 加载基础文件 // 1. 加载基础文件
require __DIR__ . '/base.php'; require __DIR__ . '/base.php';
// 执行应用
// 2. 执行应用
App::run()->send(); App::run()->send();

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