diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6cefcb38cb..dc8e91cd53 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ 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 分支; 7. `push` 你的本地仓库到 GitHub; 8. 提交 `pull request`; -9. 等待 CI 验证(若不通过则重复 5~7,GitHub 会自动更新你的 `pull request`); +9. 等待 CI 验证(若不通过则重复 5~7,不需要重新提交 `pull request`,GitHub 会自动更新你的 `pull request`); 10. 等待管理员处理,并及时 `rebase` 你的分支到上游 master 分支(若上游 master 分支有修改)。 *若有必要,可以 `git push -f` 强行推送 rebase 后的分支到自己的 `fork`* diff --git a/LICENSE.txt b/LICENSE.txt index 574a39c401..2cb9a8a9f7 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ ThinkPHP遵循Apache2开源协议发布,并提供免费使用。 -版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn) +版权所有Copyright © 2006-2017 by ThinkPHP (http://thinkphp.cn) All rights reserved。 ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。 diff --git a/README.md b/README.md index 4b0eb838f1..f01fd2b961 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ www WEB部署目录(或者子目录) ## 命名规范 -ThinkPHP5的命名规范遵循PSR-2规范以及PSR-4自动加载规范。 +ThinkPHP5的命名规范遵循`PSR-2`规范以及`PSR-4`自动加载规范。 ## 参与开发 注册并登录 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。 diff --git a/base.php b/base.php index 796d349704..92c4fa5576 100644 --- a/base.php +++ b/base.php @@ -2,14 +2,14 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- -define('THINK_VERSION', '5.0.11'); +define('THINK_VERSION', '5.0.24'); define('THINK_START_TIME', microtime(true)); define('THINK_START_MEM', memory_get_usage()); define('EXT', '.php'); @@ -40,8 +40,10 @@ // 加载环境变量配置文件 if (is_file(ROOT_PATH . '.env')) { $env = parse_ini_file(ROOT_PATH . '.env', true); + foreach ($env as $key => $val) { $name = ENV_PREFIX . strtoupper($key); + if (is_array($val)) { foreach ($val as $k => $v) { $item = $name . '_' . strtoupper($k); diff --git a/composer.json b/composer.json index c546e11423..ccc81ca258 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "require-dev": { "phpunit/phpunit": "4.8.*", "johnkary/phpunit-speedtrap": "^1.0", - "mikey179/vfsStream": "~1.6", + "mikey179/vfsstream": "~1.6", "phploc/phploc": "2.*", "sebastian/phpcpd": "2.*", "phpdocumentor/reflection-docblock": "^2.0" diff --git a/convention.php b/convention.php index 4ca03c06e8..e3ce7200c6 100644 --- a/convention.php +++ b/convention.php @@ -5,62 +5,62 @@ // | 应用设置 // +---------------------------------------------------------------------- // 默认Host地址 - 'app_host' => '', + 'app_host' => '', // 应用调试模式 - 'app_debug' => false, + 'app_debug' => false, // 应用Trace - 'app_trace' => false, + 'app_trace' => false, // 应用模式状态 - 'app_status' => '', + 'app_status' => '', // 是否支持多模块 - 'app_multi_module' => true, + 'app_multi_module' => true, // 入口自动绑定模块 - 'auto_bind_module' => false, + 'auto_bind_module' => false, // 注册的根命名空间 - 'root_namespace' => [], + 'root_namespace' => [], // 扩展函数文件 - 'extra_file_list' => [THINK_PATH . 'helper' . EXT], + 'extra_file_list' => [THINK_PATH . 'helper' . EXT], // 默认输出类型 - 'default_return_type' => 'html', + 'default_return_type' => 'html', // 默认AJAX 数据返回格式,可选json xml ... - 'default_ajax_return' => 'json', + 'default_ajax_return' => 'json', // 默认JSONP格式返回的处理方法 - 'default_jsonp_handler' => 'jsonpReturn', + 'default_jsonp_handler' => 'jsonpReturn', // 默认JSONP处理方法 - 'var_jsonp_handler' => 'callback', + 'var_jsonp_handler' => 'callback', // 默认时区 - 'default_timezone' => 'PRC', + 'default_timezone' => 'PRC', // 是否开启多语言 - 'lang_switch_on' => false, + 'lang_switch_on' => false, // 默认全局过滤方法 用逗号分隔多个 - 'default_filter' => '', + 'default_filter' => '', // 默认语言 - 'default_lang' => 'zh-cn', + 'default_lang' => 'zh-cn', // 应用类库后缀 - 'class_suffix' => false, + 'class_suffix' => false, // 控制器类后缀 - 'controller_suffix' => false, + 'controller_suffix' => false, // +---------------------------------------------------------------------- // | 模块设置 // +---------------------------------------------------------------------- // 默认模块名 - 'default_module' => 'index', + 'default_module' => 'index', // 禁止访问模块 - 'deny_module_list' => ['common'], + 'deny_module_list' => ['common'], // 默认控制器名 - 'default_controller' => 'Index', + 'default_controller' => 'Index', // 默认操作名 - 'default_action' => 'index', + 'default_action' => 'index', // 默认验证器 - 'default_validate' => '', + 'default_validate' => '', // 默认的空控制器名 - 'empty_controller' => 'Error', + 'empty_controller' => 'Error', // 操作方法前缀 - 'use_action_prefix' => false, + 'use_action_prefix' => false, // 操作方法后缀 - 'action_suffix' => '', + 'action_suffix' => '', // 自动搜索控制器 'controller_auto_search' => false, @@ -69,104 +69,106 @@ // +---------------------------------------------------------------------- // PATHINFO变量名 用于兼容模式 - 'var_pathinfo' => 's', + 'var_pathinfo' => 's', // 兼容PATH_INFO获取 - 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'], + 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'], // pathinfo分隔符 - 'pathinfo_depr' => '/', + 'pathinfo_depr' => '/', // HTTPS代理标识 - 'https_agent_name' => '', + 'https_agent_name' => '', // URL伪静态后缀 - 'url_html_suffix' => 'html', + 'url_html_suffix' => 'html', // URL普通方式参数 用于自动生成 - 'url_common_param' => false, + 'url_common_param' => false, // URL参数方式 0 按名称成对解析 1 按顺序解析 - 'url_param_type' => 0, + 'url_param_type' => 0, // 是否开启路由 - 'url_route_on' => true, + 'url_route_on' => true, // 路由配置文件(支持配置多个) - 'route_config_file' => ['route'], + 'route_config_file' => ['route'], // 路由使用完整匹配 - 'route_complete_match' => false, + 'route_complete_match' => false, // 是否强制使用路由 - 'url_route_must' => false, + 'url_route_must' => false, // 域名部署 - 'url_domain_deploy' => false, + 'url_domain_deploy' => false, // 域名根,如thinkphp.cn - 'url_domain_root' => '', + 'url_domain_root' => '', // 是否自动转换URL中的控制器和操作名 - 'url_convert' => true, + 'url_convert' => true, // 默认的访问控制器层 - 'url_controller_layer' => 'controller', + 'url_controller_layer' => 'controller', // 表单请求类型伪装变量 - 'var_method' => '_method', + 'var_method' => '_method', // 表单ajax伪装变量 - 'var_ajax' => '_ajax', + 'var_ajax' => '_ajax', // 表单pjax伪装变量 - 'var_pjax' => '_pjax', + 'var_pjax' => '_pjax', // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则 - 'request_cache' => false, + 'request_cache' => false, // 请求缓存有效期 - 'request_cache_expire' => null, + 'request_cache_expire' => null, // 全局请求缓存排除规则 - 'request_cache_except' => [], + 'request_cache_except' => [], // +---------------------------------------------------------------------- // | 模板设置 // +---------------------------------------------------------------------- - 'template' => [ + 'template' => [ + // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 + 'auto_rule' => 1, // 模板引擎类型 支持 php think 支持扩展 - 'type' => 'Think', + 'type' => 'Think', // 视图基础目录,配置目录为所有模块的视图起始目录 - 'view_base' => '', + 'view_base' => '', // 当前模板的视图目录 留空为自动获取 - 'view_path' => '', + 'view_path' => '', // 模板后缀 - 'view_suffix' => 'html', + 'view_suffix' => 'html', // 模板文件名分隔符 - 'view_depr' => DS, + 'view_depr' => DS, // 模板引擎普通标签开始标记 - 'tpl_begin' => '{', + 'tpl_begin' => '{', // 模板引擎普通标签结束标记 - 'tpl_end' => '}', + 'tpl_end' => '}', // 标签库标签开始标记 'taglib_begin' => '{', // 标签库标签结束标记 - 'taglib_end' => '}', + 'taglib_end' => '}', ], // 视图输出字符串内容替换 - 'view_replace_str' => [], + 'view_replace_str' => [], // 默认跳转页面对应的模板文件 - 'dispatch_success_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl', - 'dispatch_error_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl', + 'dispatch_success_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl', + 'dispatch_error_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl', // +---------------------------------------------------------------------- // | 异常及错误设置 // +---------------------------------------------------------------------- // 异常页面的模板文件 - 'exception_tmpl' => THINK_PATH . 'tpl' . DS . 'think_exception.tpl', + 'exception_tmpl' => THINK_PATH . 'tpl' . DS . 'think_exception.tpl', // 错误显示信息,非调试模式有效 - 'error_message' => '页面错误!请稍后再试~', + 'error_message' => '页面错误!请稍后再试~', // 显示错误信息 - 'show_error_msg' => false, + 'show_error_msg' => false, // 异常处理handle类 留空使用 \think\exception\Handle - 'exception_handle' => '', + 'exception_handle' => '', // 是否记录trace信息到日志 - 'record_trace' => false, + 'record_trace' => false, // +---------------------------------------------------------------------- // | 日志设置 // +---------------------------------------------------------------------- - 'log' => [ + 'log' => [ // 日志记录方式,内置 file socket 支持扩展 - 'type' => 'File', + 'type' => 'File', // 日志保存目录 - 'path' => LOG_PATH, + 'path' => LOG_PATH, // 日志记录级别 'level' => [], ], @@ -174,7 +176,7 @@ // +---------------------------------------------------------------------- // | Trace设置 开启 app_trace 后 有效 // +---------------------------------------------------------------------- - 'trace' => [ + 'trace' => [ // 内置Html Console 支持扩展 'type' => 'Html', ], @@ -183,11 +185,11 @@ // | 缓存设置 // +---------------------------------------------------------------------- - 'cache' => [ + 'cache' => [ // 驱动方式 - 'type' => 'File', + 'type' => 'File', // 缓存保存目录 - 'path' => CACHE_PATH, + 'path' => CACHE_PATH, // 缓存前缀 'prefix' => '', // 缓存有效期 0表示永久缓存 @@ -198,36 +200,36 @@ // | 会话设置 // +---------------------------------------------------------------------- - 'session' => [ - 'id' => '', + 'session' => [ + 'id' => '', // SESSION_ID的提交变量,解决flash上传跨域 'var_session_id' => '', // SESSION 前缀 - 'prefix' => 'think', + 'prefix' => 'think', // 驱动方式 支持redis memcache memcached - 'type' => '', + 'type' => '', // 是否自动开启 SESSION - 'auto_start' => true, - 'httponly' => true, - 'secure' => false, + 'auto_start' => true, + 'httponly' => true, + 'secure' => false, ], // +---------------------------------------------------------------------- // | Cookie设置 // +---------------------------------------------------------------------- - 'cookie' => [ + 'cookie' => [ // cookie 名称前缀 - 'prefix' => '', + 'prefix' => '', // cookie 保存时间 - 'expire' => 0, + 'expire' => 0, // cookie 保存路径 - 'path' => '/', + 'path' => '/', // cookie 有效域名 - 'domain' => '', + 'domain' => '', // cookie 启用安全传输 - 'secure' => false, + 'secure' => false, // httponly设置 - 'httponly' => '', + 'httponly' => '', // 是否使用 setcookie 'setcookie' => true, ], @@ -236,54 +238,61 @@ // | 数据库设置 // +---------------------------------------------------------------------- - 'database' => [ + 'database' => [ // 数据库类型 - 'type' => 'mysql', + 'type' => 'mysql', // 数据库连接DSN配置 - 'dsn' => '', + 'dsn' => '', // 服务器地址 - 'hostname' => '127.0.0.1', + 'hostname' => '127.0.0.1', // 数据库名 - 'database' => '', + 'database' => '', // 数据库用户名 - 'username' => 'root', + 'username' => 'root', // 数据库密码 - 'password' => '', + 'password' => '', // 数据库连接端口 - 'hostport' => '', + 'hostport' => '', // 数据库连接参数 - 'params' => [], + 'params' => [], // 数据库编码默认采用utf8 - 'charset' => 'utf8', + 'charset' => 'utf8', // 数据库表前缀 - 'prefix' => '', + 'prefix' => '', // 数据库调试模式 - 'debug' => false, + 'debug' => false, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, + 'deploy' => 0, // 数据库读写是否分离 主从式有效 - 'rw_separate' => false, + 'rw_separate' => false, // 读写分离后 主服务器数量 - 'master_num' => 1, + 'master_num' => 1, // 指定从服务器序号 - 'slave_no' => '', + 'slave_no' => '', // 是否严格检查字段是否存在 - 'fields_strict' => true, + 'fields_strict' => true, // 数据集返回类型 - 'resultset_type' => 'array', + 'resultset_type' => 'array', // 自动写入时间戳字段 - 'auto_timestamp' => false, + 'auto_timestamp' => false, // 时间字段取出后的默认时间格式 'datetime_format' => 'Y-m-d H:i:s', // 是否需要进行SQL性能分析 - 'sql_explain' => false, + 'sql_explain' => false, ], //分页配置 - 'paginate' => [ - 'type' => 'bootstrap', - 'var_page' => 'page', + 'paginate' => [ + 'type' => 'bootstrap', + 'var_page' => 'page', 'list_rows' => 15, ], + //控制台配置 + 'console' => [ + 'name' => 'Think Console', + 'version' => '0.1', + 'user' => null, + ], + ]; diff --git a/helper.php b/helper.php index b6320b2e3b..1c1dcb9b65 100644 --- a/helper.php +++ b/helper.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -28,7 +28,6 @@ use think\Response; use think\Session; use think\Url; -use think\View; if (!function_exists('load_trait')) { /** @@ -128,7 +127,7 @@ function input($key = '', $default = null, $filter = '') // 指定参数来源 list($method, $key) = explode('.', $key, 2); if (!in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'route', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) { - $key = $method . '.' . $key; + $key = $method . '.' . $key; $method = 'param'; } } else { @@ -504,7 +503,7 @@ function xml($data = [], $code = 200, $header = [], $options = []) function redirect($url = [], $params = [], $code = 302, $with = []) { if (is_integer($params)) { - $code = $params; + $code = $params; $params = []; } return Response::create($url, 'redirect', $code)->params($params)->with($with); diff --git a/lang/zh-cn.php b/lang/zh-cn.php index 911c1e750c..3e7413d598 100644 --- a/lang/zh-cn.php +++ b/lang/zh-cn.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -12,58 +12,125 @@ // 核心中文语言包 return [ // 系统错误提示 - 'Undefined variable' => '未定义变量', - 'Undefined index' => '未定义数组索引', - 'Undefined offset' => '未定义数组下标', - 'Parse error' => '语法解析错误', - 'Type error' => '类型错误', - 'Fatal error' => '致命错误', - 'syntax error' => '语法错误', + 'Undefined variable' => '未定义变量', + 'Undefined index' => '未定义数组索引', + 'Undefined offset' => '未定义数组下标', + 'Parse error' => '语法解析错误', + 'Type error' => '类型错误', + 'Fatal error' => '致命错误', + 'syntax error' => '语法错误', // 框架核心错误提示 'dispatch type not support' => '不支持的调度类型', - 'method param miss' => '方法参数错误', - 'method not exists' => '方法不存在', - 'module not exists' => '模块不存在', - 'controller not exists' => '控制器不存在', - 'class not exists' => '类不存在', - 'property not exists' => '类的属性不存在', - 'template not exists' => '模板文件不存在', - 'illegal controller name' => '非法的控制器名称', - 'illegal action name' => '非法的操作名称', - 'url suffix deny' => '禁止的URL后缀访问', - 'Route Not Found' => '当前访问路由未定义', - 'Undefined db type' => '未定义数据库类型', - 'variable type error' => '变量类型错误', - 'PSR-4 error' => 'PSR-4 规范错误', - 'not support total' => '简洁模式下不能获取数据总数', - 'not support last' => '简洁模式下不能获取最后一页', - 'error session handler' => '错误的SESSION处理器类', - 'not allow php tag' => '模板不允许使用PHP语法', - 'not support' => '不支持', - 'redisd master' => 'Redisd 主服务器错误', - 'redisd slave' => 'Redisd 从服务器错误', - 'must run at sae' => '必须在SAE运行', - 'memcache init error' => '未开通Memcache服务,请在SAE管理平台初始化Memcache服务', - 'KVDB init error' => '没有初始化KVDB,请在SAE管理平台初始化KVDB服务', - 'fields not exists' => '数据表字段不存在', - 'where express error' => '查询表达式错误', - 'no data to update' => '没有任何数据需要更新', - 'miss data to insert' => '缺少需要写入的数据', + 'method param miss' => '方法参数错误', + 'method not exists' => '方法不存在', + 'module not exists' => '模块不存在', + 'controller not exists' => '控制器不存在', + 'class not exists' => '类不存在', + 'property not exists' => '类的属性不存在', + 'template not exists' => '模板文件不存在', + 'illegal controller name' => '非法的控制器名称', + 'illegal action name' => '非法的操作名称', + 'url suffix deny' => '禁止的URL后缀访问', + 'Route Not Found' => '当前访问路由未定义', + 'Undefined db type' => '未定义数据库类型', + 'variable type error' => '变量类型错误', + 'PSR-4 error' => 'PSR-4 规范错误', + 'not support total' => '简洁模式下不能获取数据总数', + 'not support last' => '简洁模式下不能获取最后一页', + 'error session handler' => '错误的SESSION处理器类', + 'not allow php tag' => '模板不允许使用PHP语法', + 'not support' => '不支持', + 'redisd master' => 'Redisd 主服务器错误', + 'redisd slave' => 'Redisd 从服务器错误', + 'must run at sae' => '必须在SAE运行', + 'memcache init error' => '未开通Memcache服务,请在SAE管理平台初始化Memcache服务', + 'KVDB init error' => '没有初始化KVDB,请在SAE管理平台初始化KVDB服务', + 'fields not exists' => '数据表字段不存在', + 'where express error' => '查询表达式错误', + 'not support data' => '不支持的数据表达式', + 'no data to update' => '没有任何数据需要更新', + 'miss data to insert' => '缺少需要写入的数据', 'miss complex primary data' => '缺少复合主键数据', - 'miss update condition' => '缺少更新条件', - 'model data Not Found' => '模型数据不存在', - 'table data not Found' => '表数据不存在', - 'delete without condition' => '没有条件不会执行删除操作', - 'miss relation data' => '缺少关联表数据', - 'tag attr must' => '模板标签属性必须', - 'tag error' => '模板标签错误', - 'cache write error' => '缓存写入失败', - 'sae mc write error' => 'SAE mc 写入错误', - 'route name not exists' => '路由标识不存在(或参数不够)', - 'invalid request' => '非法请求', - 'bind attr has exists' => '模型的属性已经存在', - 'relation data not exists' => '关联数据不存在', - 'relation not support' => '关联不支持', - 'chunk not support order' => 'Chunk不支持调用order方法', + 'miss update condition' => '缺少更新条件', + 'model data Not Found' => '模型数据不存在', + 'table data not Found' => '表数据不存在', + 'delete without condition' => '没有条件不会执行删除操作', + 'miss relation data' => '缺少关联表数据', + 'tag attr must' => '模板标签属性必须', + 'tag error' => '模板标签错误', + 'cache write error' => '缓存写入失败', + 'sae mc write error' => 'SAE mc 写入错误', + 'route name not exists' => '路由标识不存在(或参数不够)', + 'invalid request' => '非法请求', + 'bind attr has exists' => '模型的属性已经存在', + 'relation data not exists' => '关联数据不存在', + 'relation not support' => '关联不支持', + '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' => '规则错误', ]; diff --git a/library/think/App.php b/library/think/App.php index 615be241b6..3f11bcbd07 100644 --- a/library/think/App.php +++ b/library/think/App.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -18,7 +18,7 @@ /** * App 应用管理 - * @author liu21st + * @author liu21st */ class App { @@ -57,24 +57,32 @@ class App */ protected static $routeMust; + /** + * @var array 请求调度分发 + */ protected static $dispatch; + + /** + * @var array 额外加载文件 + */ protected static $file = []; /** * 执行应用程序 * @access public - * @param Request $request Request对象 + * @param Request $request 请求对象 * @return Response * @throws Exception */ public static function run(Request $request = null) { - is_null($request) && $request = Request::instance(); + $request = is_null($request) ? Request::instance() : $request; try { $config = self::initCommon(); + + // 模块/控制器绑定 if (defined('BIND_MODULE')) { - // 模块/控制器绑定 BIND_MODULE && Route::bind(BIND_MODULE); } elseif ($config['auto_bind_module']) { // 入口自动绑定 @@ -88,10 +96,8 @@ public static function run(Request $request = null) // 默认语言 Lang::range($config['default_lang']); - if ($config['lang_switch_on']) { - // 开启多语言机制 检测当前语言 - Lang::detect(); - } + // 开启多语言机制 检测当前语言 + $config['lang_switch_on'] && Lang::detect(); $request->langset(Lang::range()); // 加载系统语言包 @@ -100,12 +106,16 @@ public static function run(Request $request = null) APP_PATH . 'lang' . DS . $request->langset() . EXT, ]); + // 监听 app_dispatch + Hook::listen('app_dispatch', self::$dispatch); // 获取应用调度信息 $dispatch = self::$dispatch; + + // 未设置调度信息则进行 URL 路由检测 if (empty($dispatch)) { - // 进行URL路由检测 $dispatch = self::routeCheck($request, $config); } + // 记录当前调度信息 $request->dispatch($dispatch); @@ -116,10 +126,15 @@ public static function run(Request $request = null) Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info'); } - // 监听app_begin + // 监听 app_begin Hook::listen('app_begin', $dispatch); + // 请求缓存检查 - $request->cache($config['request_cache'], $config['request_cache_expire'], $config['request_cache_except']); + $request->cache( + $config['request_cache'], + $config['request_cache_expire'], + $config['request_cache_except'] + ); $data = self::exec($dispatch, $config); } catch (HttpResponseException $exception) { @@ -134,24 +149,151 @@ public static function run(Request $request = null) $response = $data; } elseif (!is_null($data)) { // 默认自动识别响应输出类型 - $isAjax = $request->isAjax(); - $type = $isAjax ? Config::get('default_ajax_return') : Config::get('default_return_type'); + $type = $request->isAjax() ? + Config::get('default_ajax_return') : + Config::get('default_return_type'); + $response = Response::create($data, $type); } else { $response = Response::create(); } - // 监听app_end + // 监听 app_end Hook::listen('app_end', $response); return $response; } + /** + * 初始化应用,并返回配置信息 + * @access public + * @return array + */ + public static function initCommon() + { + if (empty(self::$init)) { + if (defined('APP_NAMESPACE')) { + self::$namespace = APP_NAMESPACE; + } + + Loader::addNamespace(self::$namespace, APP_PATH); + + // 初始化应用 + $config = self::init(); + self::$suffix = $config['class_suffix']; + + // 应用调试模式 + self::$debug = Env::get('app_debug', Config::get('app_debug')); + + if (!self::$debug) { + ini_set('display_errors', 'Off'); + } elseif (!IS_CLI) { + // 重新申请一块比较大的 buffer + if (ob_get_level() > 0) { + $output = ob_get_clean(); + } + + ob_start(); + + if (!empty($output)) { + echo $output; + } + + } + + if (!empty($config['root_namespace'])) { + Loader::addNamespace($config['root_namespace']); + } + + // 加载额外文件 + if (!empty($config['extra_file_list'])) { + foreach ($config['extra_file_list'] as $file) { + $file = strpos($file, '.') ? $file : APP_PATH . $file . EXT; + if (is_file($file) && !isset(self::$file[$file])) { + include $file; + self::$file[$file] = true; + } + } + } + + // 设置系统时区 + date_default_timezone_set($config['default_timezone']); + + // 监听 app_init + Hook::listen('app_init'); + + self::$init = true; + } + + return Config::get(); + } + + /** + * 初始化应用或模块 + * @access public + * @param string $module 模块名 + * @return array + */ + private static function init($module = '') + { + // 定位模块目录 + $module = $module ? $module . DS : ''; + + // 加载初始化文件 + if (is_file(APP_PATH . $module . 'init' . EXT)) { + include APP_PATH . $module . 'init' . EXT; + } elseif (is_file(RUNTIME_PATH . $module . 'init' . EXT)) { + include RUNTIME_PATH . $module . 'init' . EXT; + } else { + // 加载模块配置 + $config = Config::load(CONF_PATH . $module . 'config' . CONF_EXT); + + // 读取数据库配置文件 + $filename = CONF_PATH . $module . 'database' . CONF_EXT; + Config::load($filename, 'database'); + + // 读取扩展配置文件 + if (is_dir(CONF_PATH . $module . 'extra')) { + $dir = CONF_PATH . $module . 'extra'; + $files = scandir($dir); + foreach ($files as $file) { + if ('.' . pathinfo($file, PATHINFO_EXTENSION) === CONF_EXT) { + $filename = $dir . DS . $file; + Config::load($filename, pathinfo($file, PATHINFO_FILENAME)); + } + } + } + + // 加载应用状态配置 + if ($config['app_status']) { + Config::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT); + } + + // 加载行为扩展文件 + if (is_file(CONF_PATH . $module . 'tags' . EXT)) { + Hook::import(include CONF_PATH . $module . 'tags' . EXT); + } + + // 加载公共文件 + $path = APP_PATH . $module; + if (is_file($path . 'common' . EXT)) { + include $path . 'common' . EXT; + } + + // 加载当前模块语言包 + if ($module) { + Lang::load($path . 'lang' . DS . Request::instance()->langset() . EXT); + } + } + + return Config::get(); + } + /** * 设置当前请求的调度信息 * @access public * @param array|string $dispatch 调度信息 - * @param string $type 调度类型 + * @param string $type 调度类型 * @return void */ public static function dispatch($dispatch, $type = 'module') @@ -169,9 +311,11 @@ public static function dispatch($dispatch, $type = 'module') public static function invokeFunction($function, $vars = []) { $reflect = new \ReflectionFunction($function); - $args = self::bindParams($reflect, $vars); + $args = self::bindParams($reflect, $vars); + // 记录执行信息 self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info'); + return $reflect->invokeArgs($args); } @@ -185,34 +329,33 @@ public static function invokeFunction($function, $vars = []) public static function invokeMethod($method, $vars = []) { if (is_array($method)) { - $class = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]); + $class = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]); $reflect = new \ReflectionMethod($class, $method[1]); } else { // 静态方法 $reflect = new \ReflectionMethod($method); } + $args = self::bindParams($reflect, $vars); self::$debug && Log::record('[ RUN ] ' . $reflect->class . '->' . $reflect->name . '[ ' . $reflect->getFileName() . ' ]', 'info'); + return $reflect->invokeArgs(isset($class) ? $class : null, $args); } /** * 调用反射执行类的实例化 支持依赖注入 * @access public - * @param string $class 类名 - * @param array $vars 变量 + * @param string $class 类名 + * @param array $vars 变量 * @return mixed */ public static function invokeClass($class, $vars = []) { - $reflect = new \ReflectionClass($class); + $reflect = new \ReflectionClass($class); $constructor = $reflect->getConstructor(); - if ($constructor) { - $args = self::bindParams($constructor, $vars); - } else { - $args = []; - } + $args = $constructor ? self::bindParams($constructor, $vars) : []; + return $reflect->newInstanceArgs($args); } @@ -225,52 +368,58 @@ public static function invokeClass($class, $vars = []) */ private static function bindParams($reflect, $vars = []) { + // 自动获取请求变量 if (empty($vars)) { - // 自动获取请求变量 - if (Config::get('url_param_type')) { - $vars = Request::instance()->route(); - } else { - $vars = Request::instance()->param(); - } + $vars = Config::get('url_param_type') ? + Request::instance()->route() : + Request::instance()->param(); } + $args = []; if ($reflect->getNumberOfParameters() > 0) { // 判断数组类型 数字数组时按顺序绑定参数 reset($vars); - $type = key($vars) === 0 ? 1 : 0; - $params = $reflect->getParameters(); - foreach ($params as $param) { + $type = key($vars) === 0 ? 1 : 0; + + foreach ($reflect->getParameters() as $param) { $args[] = self::getParamValue($param, $vars, $type); } } + return $args; } /** * 获取参数值 * @access private - * @param \ReflectionParameter $param - * @param array $vars 变量 - * @param string $type + * @param \ReflectionParameter $param 参数 + * @param array $vars 变量 + * @param string $type 类别 * @return array */ private static function getParamValue($param, &$vars, $type) { - $name = $param->getName(); + $name = $param->getName(); $class = $param->getClass(); + if ($class) { $className = $class->getName(); - $bind = Request::instance()->$name; + $bind = Request::instance()->$name; + if ($bind instanceof $className) { $result = $bind; } else { if (method_exists($className, 'invoke')) { $method = new \ReflectionMethod($className, 'invoke'); + if ($method->isPublic() && $method->isStatic()) { return $className::invoke(Request::instance()); } } - $result = method_exists($className, 'instance') ? $className::instance() : new $className; + + $result = method_exists($className, 'instance') ? + $className::instance() : + new $className; } } elseif (1 == $type && !empty($vars)) { $result = array_shift($vars); @@ -281,67 +430,87 @@ private static function getParamValue($param, &$vars, $type) } else { throw new \InvalidArgumentException('method param miss:' . $name); } + return $result; } + /** + * 执行调用分发 + * @access protected + * @param array $dispatch 调用信息 + * @param array $config 配置信息 + * @return Response|mixed + * @throws \InvalidArgumentException + */ protected static function exec($dispatch, $config) { switch ($dispatch['type']) { - case 'redirect': - // 执行重定向跳转 - $data = Response::create($dispatch['url'], 'redirect')->code($dispatch['status']); + case 'redirect': // 重定向跳转 + $data = Response::create($dispatch['url'], 'redirect') + ->code($dispatch['status']); break; - case 'module': - // 模块/控制器/操作 - $data = self::module($dispatch['module'], $config, isset($dispatch['convert']) ? $dispatch['convert'] : null); + case 'module': // 模块/控制器/操作 + $data = self::module( + $dispatch['module'], + $config, + isset($dispatch['convert']) ? $dispatch['convert'] : null + ); break; - case 'controller': - // 执行控制器操作 + case 'controller': // 执行控制器操作 $vars = array_merge(Request::instance()->param(), $dispatch['var']); - $data = Loader::action($dispatch['controller'], $vars, $config['url_controller_layer'], $config['controller_suffix']); + $data = Loader::action( + $dispatch['controller'], + $vars, + $config['url_controller_layer'], + $config['controller_suffix'] + ); break; - case 'method': - // 执行回调方法 + case 'method': // 回调方法 $vars = array_merge(Request::instance()->param(), $dispatch['var']); $data = self::invokeMethod($dispatch['method'], $vars); break; - case 'function': - // 执行闭包 + case 'function': // 闭包 $data = self::invokeFunction($dispatch['function']); break; - case 'response': + case 'response': // Response 实例 $data = $dispatch['response']; break; default: throw new \InvalidArgumentException('dispatch type not support'); } + return $data; } /** * 执行模块 * @access public - * @param array $result 模块/控制器/操作 - * @param array $config 配置参数 + * @param array $result 模块/控制器/操作 + * @param array $config 配置参数 * @param bool $convert 是否自动转换控制器和操作名 * @return mixed + * @throws HttpException */ public static function module($result, $config, $convert = null) { if (is_string($result)) { $result = explode('/', $result); } + $request = Request::instance(); + if ($config['app_multi_module']) { // 多模块部署 - $module = strip_tags(strtolower($result[0] ?: $config['default_module'])); - $bind = Route::getBind('module'); + $module = strip_tags(strtolower($result[0] ?: $config['default_module'])); + $bind = Route::getBind('module'); $available = false; + if ($bind) { // 绑定模块 list($bindModule) = explode('/', $bind); + if (empty($result[0])) { - $module = $bindModule; + $module = $bindModule; $available = true; } elseif ($module == $bindModule) { $available = true; @@ -355,8 +524,13 @@ public static function module($result, $config, $convert = null) // 初始化模块 $request->module($module); $config = self::init($module); + // 模块请求缓存检查 - $request->cache($config['request_cache'], $config['request_cache_expire'], $config['request_cache_except']); + $request->cache( + $config['request_cache'], + $config['request_cache_expire'], + $config['request_cache_except'] + ); } else { throw new HttpException(404, 'module not exists:' . $module); } @@ -365,18 +539,32 @@ public static function module($result, $config, $convert = null) $module = ''; $request->module($module); } + + // 设置默认过滤机制 + $request->filter($config['default_filter']); + // 当前模块路径 App::$modulePath = APP_PATH . ($module ? $module . DS : ''); // 是否自动转换控制器和操作名 $convert = is_bool($convert) ? $convert : $config['url_convert']; + // 获取控制器名 $controller = strip_tags($result[1] ?: $config['default_controller']); + + if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) { + throw new HttpException(404, 'controller not exists:' . $controller); + } + $controller = $convert ? strtolower($controller) : $controller; // 获取操作名 $actionName = strip_tags($result[2] ?: $config['default_action']); - $actionName = $convert ? strtolower($actionName) : $actionName; + if (!empty($config['action_convert'])) { + $actionName = Loader::parseName($actionName, 1); + } else { + $actionName = $convert ? strtolower($actionName) : $actionName; + } // 设置当前请求的控制器、操作 $request->controller(Loader::parseName($controller, 1))->action($actionName); @@ -385,7 +573,12 @@ public static function module($result, $config, $convert = null) Hook::listen('module_init', $request); try { - $instance = Loader::controller($controller, $config['url_controller_layer'], $config['controller_suffix'], $config['empty_controller']); + $instance = Loader::controller( + $controller, + $config['url_controller_layer'], + $config['controller_suffix'], + $config['empty_controller'] + ); } catch (ClassNotFoundException $e) { throw new HttpException(404, 'controller not exists:' . $e->getClass()); } @@ -397,6 +590,13 @@ public static function module($result, $config, $convert = null) if (is_callable([$instance, $action])) { // 执行操作方法 $call = [$instance, $action]; + // 严格获取当前操作方法名 + $reflect = new \ReflectionMethod($instance, $action); + $methodName = $reflect->getName(); + $suffix = $config['action_suffix']; + $actionName = $suffix ? substr($methodName, 0, -strlen($suffix)) : $methodName; + $request->action($actionName); + } elseif (is_callable([$instance, '_empty'])) { // 空操作 $call = [$instance, '_empty']; @@ -411,133 +611,20 @@ public static function module($result, $config, $convert = null) return self::invokeMethod($call, $vars); } - /** - * 初始化应用 - */ - public static function initCommon() - { - if (empty(self::$init)) { - if (defined('APP_NAMESPACE')) { - self::$namespace = APP_NAMESPACE; - } - Loader::addNamespace(self::$namespace, APP_PATH); - - // 初始化应用 - $config = self::init(); - self::$suffix = $config['class_suffix']; - - // 应用调试模式 - self::$debug = Env::get('app_debug', Config::get('app_debug')); - if (!self::$debug) { - ini_set('display_errors', 'Off'); - } elseif (!IS_CLI) { - //重新申请一块比较大的buffer - if (ob_get_level() > 0) { - $output = ob_get_clean(); - } - ob_start(); - if (!empty($output)) { - echo $output; - } - } - - if (!empty($config['root_namespace'])) { - Loader::addNamespace($config['root_namespace']); - } - - // 加载额外文件 - if (!empty($config['extra_file_list'])) { - foreach ($config['extra_file_list'] as $file) { - $file = strpos($file, '.') ? $file : APP_PATH . $file . EXT; - if (is_file($file) && !isset(self::$file[$file])) { - include $file; - self::$file[$file] = true; - } - } - } - - // 设置系统时区 - date_default_timezone_set($config['default_timezone']); - - // 监听app_init - Hook::listen('app_init'); - - self::$init = true; - } - return Config::get(); - } - - /** - * 初始化应用或模块 - * @access public - * @param string $module 模块名 - * @return array - */ - private static function init($module = '') - { - // 定位模块目录 - $module = $module ? $module . DS : ''; - - // 加载初始化文件 - if (is_file(APP_PATH . $module . 'init' . EXT)) { - include APP_PATH . $module . 'init' . EXT; - } elseif (is_file(RUNTIME_PATH . $module . 'init' . EXT)) { - include RUNTIME_PATH . $module . 'init' . EXT; - } else { - $path = APP_PATH . $module; - // 加载模块配置 - $config = Config::load(CONF_PATH . $module . 'config' . CONF_EXT); - // 读取数据库配置文件 - $filename = CONF_PATH . $module . 'database' . CONF_EXT; - Config::load($filename, 'database'); - // 读取扩展配置文件 - if (is_dir(CONF_PATH . $module . 'extra')) { - $dir = CONF_PATH . $module . 'extra'; - $files = scandir($dir); - foreach ($files as $file) { - if ('.' . pathinfo($file, PATHINFO_EXTENSION) === CONF_EXT) { - $filename = $dir . DS . $file; - Config::load($filename, pathinfo($file, PATHINFO_FILENAME)); - } - } - } - - // 加载应用状态配置 - if ($config['app_status']) { - $config = Config::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT); - } - - // 加载行为扩展文件 - if (is_file(CONF_PATH . $module . 'tags' . EXT)) { - Hook::import(include CONF_PATH . $module . 'tags' . EXT); - } - - // 加载公共文件 - if (is_file($path . 'common' . EXT)) { - include $path . 'common' . EXT; - } - - // 加载当前模块语言包 - if ($module) { - Lang::load($path . 'lang' . DS . Request::instance()->langset() . EXT); - } - } - return Config::get(); - } - /** * URL路由检测(根据PATH_INFO) * @access public - * @param \think\Request $request - * @param array $config + * @param \think\Request $request 请求实例 + * @param array $config 配置信息 * @return array * @throws \think\Exception */ public static function routeCheck($request, array $config) { - $path = $request->path(); - $depr = $config['pathinfo_depr']; + $path = $request->path(); + $depr = $config['pathinfo_depr']; $result = false; + // 路由检测 $check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on']; if ($check) { @@ -545,34 +632,33 @@ public static function routeCheck($request, array $config) if (is_file(RUNTIME_PATH . 'route.php')) { // 读取路由缓存 $rules = include RUNTIME_PATH . 'route.php'; - if (is_array($rules)) { - Route::rules($rules); - } + is_array($rules) && Route::rules($rules); } else { $files = $config['route_config_file']; foreach ($files as $file) { if (is_file(CONF_PATH . $file . CONF_EXT)) { // 导入路由配置 $rules = include CONF_PATH . $file . CONF_EXT; - if (is_array($rules)) { - Route::import($rules); - } + is_array($rules) && Route::import($rules); } } } // 路由检测(根据路由定义返回不同的URL调度) $result = Route::check($request, $path, $depr, $config['url_domain_deploy']); - $must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must']; + $must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must']; + if ($must && false === $result) { // 路由无效 throw new RouteNotFoundException(); } } + + // 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索 if (false === $result) { - // 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索 $result = Route::parseUrl($path, $depr, $config['controller_auto_search']); } + return $result; } @@ -586,6 +672,6 @@ public static function routeCheck($request, array $config) public static function route($route, $must = false) { self::$routeCheck = $route; - self::$routeMust = $must; + self::$routeMust = $must; } } diff --git a/library/think/Build.php b/library/think/Build.php index 6e055c92ac..1b9da98dae 100644 --- a/library/think/Build.php +++ b/library/think/Build.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -14,36 +14,44 @@ class Build { /** - * 根据传入的build资料创建目录和文件 - * @access protected - * @param array $build build列表 + * 根据传入的 build 资料创建目录和文件 + * @access public + * @param array $build build 列表 * @param string $namespace 应用类库命名空间 - * @param bool $suffix 类库后缀 + * @param bool $suffix 类库后缀 * @return void + * @throws Exception */ public static function run(array $build = [], $namespace = 'app', $suffix = false) { // 锁定 - $lockfile = APP_PATH . 'build.lock'; - if (is_writable($lockfile)) { - return; - } elseif (!touch($lockfile)) { - throw new Exception('应用目录[' . APP_PATH . ']不可写,目录无法自动生成!
请手动生成项目目录~', 10006); - } - foreach ($build as $module => $list) { - if ('__dir__' == $module) { - // 创建目录列表 - self::buildDir($list); - } elseif ('__file__' == $module) { - // 创建文件列表 - self::buildFile($list); - } else { - // 创建模块 - self::module($module, $list, $namespace, $suffix); + $lock = APP_PATH . 'build.lock'; + + // 如果锁定文件不可写(不存在)则进行处理,否则表示已经有程序在处理了 + if (!is_writable($lock)) { + if (!touch($lock)) { + throw new Exception( + '应用目录[' . APP_PATH . ']不可写,目录无法自动生成!
请手动生成项目目录~', + 10006 + ); } + + foreach ($build as $module => $list) { + if ('__dir__' == $module) { + // 创建目录列表 + self::buildDir($list); + } elseif ('__file__' == $module) { + // 创建文件列表 + self::buildFile($list); + } else { + // 创建模块 + self::module($module, $list, $namespace, $suffix); + } + } + + // 解除锁定 + unlink($lock); } - // 解除锁定 - unlink($lockfile); } /** @@ -55,10 +63,8 @@ public static function run(array $build = [], $namespace = 'app', $suffix = fals protected static function buildDir($list) { foreach ($list as $dir) { - if (!is_dir(APP_PATH . $dir)) { - // 创建目录 - mkdir(APP_PATH . $dir, 0755, true); - } + // 目录不存在则创建目录 + !is_dir(APP_PATH . $dir) && mkdir(APP_PATH . $dir, 0755, true); } } @@ -71,12 +77,17 @@ protected static function buildDir($list) protected static function buildFile($list) { foreach ($list as $file) { + // 先创建目录 if (!is_dir(APP_PATH . dirname($file))) { - // 创建目录 mkdir(APP_PATH . dirname($file), 0755, true); } + + // 再创建文件 if (!is_file(APP_PATH . $file)) { - file_put_contents(APP_PATH . $file, 'php' == pathinfo($file, PATHINFO_EXTENSION) ? " ['config.php', 'common.php'], - '__dir__' => ['controller', 'model', 'view'], + '__dir__' => ['controller', 'model', 'view'], ]; } + // 创建子目录和文件 foreach ($list as $path => $file) { $modulePath = APP_PATH . $module . DS; + if ('__dir__' == $path) { // 生成子目录 foreach ($file as $dir) { @@ -122,16 +135,20 @@ public static function module($module = '', $list = [], $namespace = 'app', $suf // 生成(空白)文件 foreach ($file as $name) { if (!is_file($modulePath . $name)) { - file_put_contents($modulePath . $name, 'php' == pathinfo($name, PATHINFO_EXTENSION) ? "has($name); } /** * 读取缓存 * @access public - * @param string $name 缓存标识 - * @param mixed $default 默认值 + * @param string $name 缓存标识 + * @param mixed $default 默认值 * @return mixed */ public static function get($name, $default = false) { self::$readTimes++; + return self::init()->get($name, $default); } /** * 写入缓存 * @access public - * @param string $name 缓存标识 - * @param mixed $value 存储数据 - * @param int|null $expire 有效时间 0为永久 + * @param string $name 缓存标识 + * @param mixed $value 存储数据 + * @param int|null $expire 有效时间 0为永久 * @return boolean */ public static function set($name, $value, $expire = null) { self::$writeTimes++; + return self::init()->set($name, $value, $expire); } /** * 自增缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public static function inc($name, $step = 1) { self::$writeTimes++; + return self::init()->inc($name, $step); } /** * 自减缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public static function dec($name, $step = 1) { self::$writeTimes++; + return self::init()->dec($name, $step); } /** * 删除缓存 * @access public - * @param string $name 缓存标识 + * @param string $name 缓存标识 * @return boolean */ public static function rm($name) { self::$writeTimes++; + return self::init()->rm($name); } /** * 清除缓存 * @access public - * @param string $tag 标签名 + * @param string $tag 标签名 * @return boolean */ public static function clear($tag = null) { self::$writeTimes++; + return self::init()->clear($tag); } /** * 读取缓存并删除 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return mixed */ public static function pull($name) { self::$readTimes++; self::$writeTimes++; + return self::init()->pull($name); } /** * 如果不存在则写入缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $value 存储数据 - * @param int $expire 有效时间 0为永久 + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param int $expire 有效时间 0为永久 * @return mixed */ public static function remember($name, $value, $expire = null) { self::$readTimes++; + return self::init()->remember($name, $value, $expire); } /** * 缓存标签 * @access public - * @param string $name 标签名 - * @param string|array $keys 缓存标识 - * @param bool $overlay 是否覆盖 + * @param string $name 标签名 + * @param string|array $keys 缓存标识 + * @param bool $overlay 是否覆盖 * @return Driver */ public static function tag($name, $keys = null, $overlay = false) diff --git a/library/think/Collection.php b/library/think/Collection.php index 3fcba5cdb5..78e56d9379 100644 --- a/library/think/Collection.php +++ b/library/think/Collection.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -19,20 +19,35 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable { + /** + * @var array 数据 + */ protected $items = []; + /** + * Collection constructor. + * @access public + * @param array $items 数据 + */ public function __construct($items = []) { $this->items = $this->convertToArray($items); } + /** + * 创建 Collection 实例 + * @access public + * @param array $items 数据 + * @return static + */ public static function make($items = []) { return new static($items); } /** - * 是否为空 + * 判断数据是否为空 + * @access public * @return bool */ public function isEmpty() @@ -40,106 +55,106 @@ public function isEmpty() return empty($this->items); } + /** + * 将数据转成数组 + * @access public + * @return array + */ public function toArray() { 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); } + /** + * 获取全部的数据 + * @access public + * @return array + */ public function all() { return $this->items; } /** - * 合并数组 - * - * @param mixed $items + * 交换数组中的键和值 + * @access public * @return static */ - public function merge($items) + public function flip() { - return new static(array_merge($this->items, $this->convertToArray($items))); + return new static(array_flip($this->items)); } /** - * 比较数组,返回差集 - * - * @param mixed $items + * 返回数组中所有的键名组成的新 Collection 实例 + * @access public * @return static */ - public function diff($items) + public function keys() { - return new static(array_diff($this->items, $this->convertToArray($items))); + return new static(array_keys($this->items)); } /** - * 交换数组中的键和值 - * + * 返回数组中所有的值组成的新 Collection 实例 + * @access public * @return static */ - public function flip() + public function values() { - return new static(array_flip($this->items)); + return new static(array_values($this->items)); } /** - * 比较数组,返回交集 - * - * @param mixed $items + * 合并数组并返回一个新的 Collection 实例 + * @access public + * @param mixed $items 新的数据 * @return static */ - public function intersect($items) + public function merge($items) { - return new static(array_intersect($this->items, $this->convertToArray($items))); + return new static(array_merge($this->items, $this->convertToArray($items))); } /** - * 返回数组中所有的键名 - * + * 比较数组,返回差集生成的新 Collection 实例 + * @access public + * @param mixed $items 做比较的数据 * @return static */ - public function keys() + public function diff($items) { - return new static(array_keys($this->items)); + return new static(array_diff($this->items, $this->convertToArray($items))); } /** - * 删除数组的最后一个元素(出栈) - * - * @return mixed + * 比较数组,返回交集组成的 Collection 新实例 + * @access public + * @param mixed $items 比较数据 + * @return static */ - public function pop() + public function intersect($items) { - return array_pop($this->items); + return new static(array_intersect($this->items, $this->convertToArray($items))); } /** - * 通过使用用户自定义函数,以字符串返回数组 - * - * @param callable $callback - * @param mixed $initial + * 返回并删除数据中的的最后一个元素(出栈) + * @access public * @return mixed */ - public function reduce(callable $callback, $initial = null) - { - return array_reduce($this->items, $callback, $initial); - } - - /** - * 以相反的顺序返回数组。 - * - * @return static - */ - public function reverse() + public function pop() { - return new static(array_reverse($this->items)); + return array_pop($this->items); } /** - * 删除数组中首个元素,并返回被删除元素的值 - * + * 返回并删除数据中首个元素 + * @access public * @return mixed */ public function shift() @@ -147,28 +162,11 @@ public function shift() return array_shift($this->items); } - /** - * 把一个数组分割为新的数组块. - * - * @param int $size - * @param bool $preserveKeys - * @return static - */ - public function chunk($size, $preserveKeys = false) - { - $chunks = []; - - foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) { - $chunks[] = new static($chunk); - } - - return new static($chunks); - } - /** * 在数组开头插入一个元素 - * @param mixed $value - * @param miexed $key + * @access public + * @param mixed $value 值 + * @param mixed $key 键名 * @return void */ public function unshift($value, $key = null) @@ -182,8 +180,9 @@ public function unshift($value, $key = null) /** * 在数组结尾插入一个元素 - * @param mixed $value - * @param mixed $key + * @access public + * @param mixed $value 值 + * @param mixed $key 键名 * @return void */ public function push($value, $key = null) @@ -196,18 +195,61 @@ public function push($value, $key = null) } /** - * 给每个元素执行个回调 - * - * @param callable $callback + * 通过使用用户自定义函数,以字符串返回数组 + * @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 + */ + public function chunk($size, $preserveKeys = false) + { + $chunks = []; + + foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) { + $chunks[] = new static($chunk); + } + + return new static($chunks); + } + + /** + * 给数据中的每个元素执行回调 + * @access public + * @param callable $callback 回调函数 * @return $this */ public function each(callable $callback) { foreach ($this->items as $key => $item) { $result = $callback($item, $key); + if (false === $result) { break; - } elseif (!is_object($item)) { + } + + if (!is_object($item)) { $this->items[$key] = $result; } } @@ -216,46 +258,47 @@ public function each(callable $callback) } /** - * 用回调函数过滤数组中的元素 - * @param callable|null $callback + * 用回调函数过滤数据中的元素 + * @access public + * @param callable|null $callback 回调函数 * @return static */ public function filter(callable $callback = null) { - if ($callback) { - return new static(array_filter($this->items, $callback)); - } - - return new static(array_filter($this->items)); + return new static(array_filter($this->items, $callback ?: null)); } /** - * 返回数组中指定的一列 - * @param $column_key - * @param null $index_key + * 返回数据中指定的一列 + * @access public + * @param mixed $columnKey 键名 + * @param null $indexKey 作为索引值的列 * @return array */ - public function column($column_key, $index_key = null) + public function column($columnKey, $indexKey = null) { if (function_exists('array_column')) { - return array_column($this->items, $column_key, $index_key); + return array_column($this->items, $columnKey, $indexKey); } $result = []; foreach ($this->items as $row) { - $key = $value = null; + $key = $value = null; $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; - $key = (string) $row[$index_key]; } - if (null === $column_key) { + + if (null === $columnKey) { $valueSet = true; - $value = $row; - } elseif (is_array($row) && array_key_exists($column_key, $row)) { + $value = $row; + } elseif (is_array($row) && array_key_exists($columnKey, $row)) { $valueSet = true; - $value = $row[$column_key]; + $value = $row[$columnKey]; } + if ($valueSet) { if ($keySet) { $result[$key] = $value; @@ -264,34 +307,30 @@ public function column($column_key, $index_key = null) } } } + return $result; } /** - * 对数组排序 - * - * @param callable|null $callback + * 对数据排序,并返回排序后的数据组成的新 Collection 实例 + * @access public + * @param callable|null $callback 回调函数 * @return static */ public function sort(callable $callback = null) { $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) { - - if ($a == $b) { - return 0; - } - - return ($a < $b) ? -1 : 1; - }); - + uasort($items, $callback); return new static($items); } /** - * 将数组打乱 - * + * 将数据打乱后组成新的 Collection 实例 + * @access public * @return static */ public function shuffle() @@ -299,16 +338,15 @@ public function shuffle() $items = $this->items; shuffle($items); - return new static($items); } /** - * 截取数组 - * - * @param int $offset - * @param int $length - * @param bool $preserveKeys + * 截取数据并返回新的 Collection 实例 + * @access public + * @param int $offset 起始位置 + * @param int $length 截取长度 + * @param bool $preserveKeys 是否保持原先的键名 * @return static */ public function slice($offset, $length = null, $preserveKeys = false) @@ -316,17 +354,35 @@ public function slice($offset, $length = null, $preserveKeys = false) return new static(array_slice($this->items, $offset, $length, $preserveKeys)); } - // ArrayAccess + /** + * 指定的键是否存在 + * @access public + * @param mixed $offset 键名 + * @return bool + */ public function offsetExists($offset) { return array_key_exists($offset, $this->items); } + /** + * 获取指定键对应的值 + * @access public + * @param mixed $offset 键名 + * @return mixed + */ public function offsetGet($offset) { return $this->items[$offset]; } + /** + * 设置键值 + * @access public + * @param mixed $offset 键名 + * @param mixed $value 值 + * @return void + */ public function offsetSet($offset, $value) { if (is_null($offset)) { @@ -336,33 +392,51 @@ public function offsetSet($offset, $value) } } + /** + * 删除指定键值 + * @access public + * @param mixed $offset 键名 + * @return void + */ public function offsetUnset($offset) { unset($this->items[$offset]); } - //Countable + /** + * 统计数据的个数 + * @access public + * @return int + */ public function count() { return count($this->items); } - //IteratorAggregate + /** + * 获取数据的迭代器 + * @access public + * @return ArrayIterator + */ public function getIterator() { return new ArrayIterator($this->items); } - //JsonSerializable + /** + * 将数据反序列化成数组 + * @access public + * @return array + */ public function jsonSerialize() { return $this->toArray(); } /** - * 转换当前数据集为JSON字符串 + * 转换当前数据集为 JSON 字符串 * @access public - * @param integer $options json参数 + * @param integer $options json 参数 * @return string */ public function toJson($options = JSON_UNESCAPED_UNICODE) @@ -370,22 +444,24 @@ public function toJson($options = JSON_UNESCAPED_UNICODE) return json_encode($this->toArray(), $options); } + /** + * 将数据转换成字符串 + * @access public + * @return string + */ public function __toString() { return $this->toJson(); } /** - * 转换成数组 - * - * @param mixed $items + * 将数据转换成数组 + * @access protected + * @param mixed $items 数据 * @return array */ protected function convertToArray($items) { - if ($items instanceof self) { - return $items->all(); - } - return (array) $items; + return $items instanceof self ? $items->all() : (array) $items; } } diff --git a/library/think/Config.php b/library/think/Config.php index 1fa5d4afd3..81b0f04fdc 100644 --- a/library/think/Config.php +++ b/library/think/Config.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -13,70 +13,88 @@ class Config { - // 配置参数 + /** + * @var array 配置参数 + */ private static $config = []; - // 参数作用域 + + /** + * @var string 参数作用域 + */ private static $range = '_sys_'; - // 设定配置参数的作用域 + /** + * 设定配置参数的作用域 + * @access public + * @param string $range 作用域 + * @return void + */ public static function range($range) { self::$range = $range; - if (!isset(self::$config[$range])) { - self::$config[$range] = []; - } + + if (!isset(self::$config[$range])) self::$config[$range] = []; } /** * 解析配置文件或内容 - * @param string $config 配置文件路径或内容 - * @param string $type 配置解析类型 - * @param string $name 配置名(如设置即表示二级配置) - * @param string $range 作用域 + * @access public + * @param string $config 配置文件路径或内容 + * @param string $type 配置解析类型 + * @param string $name 配置名(如设置即表示二级配置) + * @param string $range 作用域 * @return mixed */ public static function parse($config, $type = '', $name = '', $range = '') { $range = $range ?: self::$range; - if (empty($type)) { - $type = pathinfo($config, PATHINFO_EXTENSION); - } - $class = false !== strpos($type, '\\') ? $type : '\\think\\config\\driver\\' . ucwords($type); + + if (empty($type)) $type = pathinfo($config, PATHINFO_EXTENSION); + + $class = false !== strpos($type, '\\') ? + $type : + '\\think\\config\\driver\\' . ucwords($type); + return self::set((new $class())->parse($config), $name, $range); } /** * 加载配置文件(PHP格式) - * @param string $file 配置文件名 - * @param string $name 配置名(如设置即表示二级配置) - * @param string $range 作用域 + * @access public + * @param string $file 配置文件名 + * @param string $name 配置名(如设置即表示二级配置) + * @param string $range 作用域 * @return mixed */ public static function load($file, $name = '', $range = '') { $range = $range ?: self::$range; - if (!isset(self::$config[$range])) { - self::$config[$range] = []; - } + + if (!isset(self::$config[$range])) self::$config[$range] = []; + if (is_file($file)) { $name = strtolower($name); $type = pathinfo($file, PATHINFO_EXTENSION); + if ('php' == $type) { return self::set(include $file, $name, $range); - } elseif ('yaml' == $type && function_exists('yaml_parse_file')) { + } + + if ('yaml' == $type && function_exists('yaml_parse_file')) { return self::set(yaml_parse_file($file), $name, $range); - } else { - return self::parse($file, $type, $name, $range); } - } else { - return self::$config[$range]; + + return self::parse($file, $type, $name, $range); } + + return self::$config[$range]; } /** * 检测配置是否存在 - * @param string $name 配置参数名(支持二级配置 .号分割) - * @param string $range 作用域 + * @access public + * @param string $name 配置参数名(支持二级配置 . 号分割) + * @param string $range 作用域 * @return bool */ public static function has($name, $range = '') @@ -85,90 +103,109 @@ public static function has($name, $range = '') if (!strpos($name, '.')) { return isset(self::$config[$range][strtolower($name)]); - } else { - // 二维数组设置和获取支持 - $name = explode('.', $name, 2); - return isset(self::$config[$range][strtolower($name[0])][$name[1]]); } + + // 二维数组设置和获取支持 + $name = explode('.', $name, 2); + return isset(self::$config[$range][strtolower($name[0])][$name[1]]); } /** * 获取配置参数 为空则获取所有配置 - * @param string $name 配置参数名(支持二级配置 .号分割) - * @param string $range 作用域 + * @access public + * @param string $name 配置参数名(支持二级配置 . 号分割) + * @param string $range 作用域 * @return mixed */ public static function get($name = null, $range = '') { $range = $range ?: self::$range; + // 无参数时获取所有 if (empty($name) && isset(self::$config[$range])) { return self::$config[$range]; } + // 非二级配置时直接返回 if (!strpos($name, '.')) { $name = strtolower($name); return isset(self::$config[$range][$name]) ? self::$config[$range][$name] : null; - } else { - // 二维数组设置和获取支持 - $name = explode('.', $name, 2); - $name[0] = strtolower($name[0]); + } - if (!isset(self::$config[$range][$name[0]])) { - // 动态载入额外配置 - $module = Request::instance()->module(); - $file = CONF_PATH . ($module ? $module . DS : '') . 'extra' . DS . $name[0] . CONF_EXT; + // 二维数组设置和获取支持 + $name = explode('.', $name, 2); + $name[0] = strtolower($name[0]); - is_file($file) && self::load($file, $name[0]); - } + if (!isset(self::$config[$range][$name[0]])) { + // 动态载入额外配置 + $module = Request::instance()->module(); + $file = CONF_PATH . ($module ? $module . DS : '') . 'extra' . DS . $name[0] . CONF_EXT; - return isset(self::$config[$range][$name[0]][$name[1]]) ? self::$config[$range][$name[0]][$name[1]] : null; + is_file($file) && self::load($file, $name[0]); } + + return isset(self::$config[$range][$name[0]][$name[1]]) ? + self::$config[$range][$name[0]][$name[1]] : + null; } /** - * 设置配置参数 name为数组则为批量设置 - * @param string|array $name 配置参数名(支持二级配置 .号分割) - * @param mixed $value 配置值 - * @param string $range 作用域 + * 设置配置参数 name 为数组则为批量设置 + * @access public + * @param string|array $name 配置参数名(支持二级配置 . 号分割) + * @param mixed $value 配置值 + * @param string $range 作用域 * @return mixed */ public static function set($name, $value = null, $range = '') { $range = $range ?: self::$range; - if (!isset(self::$config[$range])) { - self::$config[$range] = []; - } + + if (!isset(self::$config[$range])) self::$config[$range] = []; + + // 字符串则表示单个配置设置 if (is_string($name)) { if (!strpos($name, '.')) { self::$config[$range][strtolower($name)] = $value; } else { - // 二维数组设置和获取支持 - $name = explode('.', $name, 2); + // 二维数组 + $name = explode('.', $name, 2); self::$config[$range][strtolower($name[0])][$name[1]] = $value; } - return; - } elseif (is_array($name)) { - // 批量设置 + + return $value; + } + + // 数组则表示批量设置 + if (is_array($name)) { if (!empty($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]; - } else { - return self::$config[$range] = array_merge(self::$config[$range], array_change_key_case($name)); } - } else { - // 为空直接返回 已有配置 - return self::$config[$range]; + + return self::$config[$range] = array_merge( + self::$config[$range], + array_change_key_case($name) + ); } + + // 为空直接返回已有配置 + return self::$config[$range]; } /** * 重置配置参数 + * @access public + * @param string $range 作用域 + * @return void */ public static function reset($range = '') { $range = $range ?: self::$range; + if (true === $range) { self::$config = []; } else { diff --git a/library/think/Console.php b/library/think/Console.php index 1d97ab2b7e..369c6f550c 100644 --- a/library/think/Console.php +++ b/library/think/Console.php @@ -16,24 +16,52 @@ use think\console\input\Definition as InputDefinition; use think\console\input\Option as InputOption; use think\console\Output; -use think\console\output\driver\Buffer; class Console { - + /** + * @var string 命令名称 + */ private $name; + + /** + * @var string 命令版本 + */ private $version; - /** @var Command[] */ + /** + * @var Command[] 命令 + */ private $commands = []; + /** + * @var bool 是否需要帮助信息 + */ private $wantHelps = false; + /** + * @var bool 是否捕获异常 + */ private $catchExceptions = true; - private $autoExit = true; + + /** + * @var bool 是否自动退出执行 + */ + private $autoExit = true; + + /** + * @var InputDefinition 输入定义 + */ private $definition; + + /** + * @var string 默认执行的命令 + */ private $defaultCommand; + /** + * @var array 默认提供的命令 + */ private static $defaultCommands = [ "think\\console\\command\\Help", "think\\console\\command\\Lists", @@ -47,51 +75,82 @@ class Console "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; + if ($user) { + $this->setUser($user); + } + $this->defaultCommand = 'list'; - $this->definition = $this->getDefaultInputDefinition(); + $this->definition = $this->getDefaultInputDefinition(); foreach ($this->getDefaultCommands() as $command) { $this->add($command); } } + /** + * 设置执行用户 + * @param $user + */ + public function setUser($user) + { + $user = posix_getpwnam($user); + if ($user) { + posix_setuid($user['uid']); + posix_setgid($user['gid']); + } + } + + /** + * 初始化 Console + * @access public + * @param bool $run 是否运行 Console + * @return int|Console + */ public static function init($run = true) { static $console; + if (!$console) { - // 实例化console - $console = new self('Think Console', '0.1'); + $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) { - if (class_exists($command) && is_subclass_of($command, "\\think\\console\\Command")) { - // 注册指令 - $console->add(new $command()); - } + class_exists($command) && + is_subclass_of($command, "\\think\\console\\Command") && + $console->add(new $command()); // 注册指令 } } } } - if ($run) { - // 运行 - return $console->run(); - } else { - return $console; - } + + return $run ? $console->run() : $console; } /** - * @param $command - * @param array $parameters - * @param string $driver - * @return Output|Buffer + * 调用命令 + * @access public + * @param string $command + * @param array $parameters + * @param string $driver + * @return Output */ public static function call($command, array $parameters = [], $driver = 'buffer') { @@ -99,7 +158,7 @@ public static function call($command, array $parameters = [], $driver = 'buffer' array_unshift($parameters, $command); - $input = new Input($parameters); + $input = new Input($parameters); $output = new Output($driver); $console->setCatchExceptions(false); @@ -110,13 +169,13 @@ public static function call($command, array $parameters = [], $driver = 'buffer' /** * 执行当前的指令 + * @access public * @return int * @throws \Exception - * @api */ public function run() { - $input = new Input(); + $input = new Input(); $output = new Output(); $this->configureIO($input, $output); @@ -124,27 +183,21 @@ public function run() try { $exitCode = $this->doRun($input, $output); } catch (\Exception $e) { - if (!$this->catchExceptions) { - throw $e; - } + if (!$this->catchExceptions) throw $e; $output->renderException($e); $exitCode = $e->getCode(); + if (is_numeric($exitCode)) { - $exitCode = (int) $exitCode; - if (0 === $exitCode) { - $exitCode = 1; - } + $exitCode = ((int) $exitCode) ?: 1; } else { $exitCode = 1; } } if ($this->autoExit) { - if ($exitCode > 255) { - $exitCode = 255; - } + if ($exitCode > 255) $exitCode = 255; exit($exitCode); } @@ -154,12 +207,14 @@ public function run() /** * 执行指令 - * @param Input $input - * @param Output $output + * @access public + * @param Input $input 输入 + * @param Output $output 输出 * @return int */ public function doRun(Input $input, Output $output) { + // 获取版本信息 if (true === $input->hasParameterOption(['--version', '-V'])) { $output->writeln($this->getLongVersion()); @@ -168,9 +223,10 @@ public function doRun(Input $input, Output $output) $name = $this->getCommandName($input); + // 获取帮助信息 if (true === $input->hasParameterOption(['--help', '-h'])) { if (!$name) { - $name = 'help'; + $name = 'help'; $input = new Input(['help']); } else { $this->wantHelps = true; @@ -178,29 +234,30 @@ public function doRun(Input $input, Output $output) } if (!$name) { - $name = $this->defaultCommand; + $name = $this->defaultCommand; $input = new Input([$this->defaultCommand]); } - $command = $this->find($name); - - $exitCode = $this->doRunCommand($command, $input, $output); - - return $exitCode; + return $this->doRunCommand($this->find($name), $input, $output); } /** * 设置输入参数定义 - * @param InputDefinition $definition + * @access public + * @param InputDefinition $definition 输入定义 + * @return $this; */ public function setDefinition(InputDefinition $definition) { $this->definition = $definition; + + return $this; } /** * 获取输入参数定义 - * @return InputDefinition The InputDefinition instance + * @access public + * @return InputDefinition */ public function getDefinition() { @@ -208,8 +265,9 @@ public function getDefinition() } /** - * Gets the help message. - * @return string A help message. + * 获取帮助信息 + * @access public + * @return string */ public function getHelp() { @@ -217,27 +275,34 @@ public function getHelp() } /** - * 是否捕获异常 - * @param bool $boolean - * @api + * 设置是否捕获异常 + * @access public + * @param bool $boolean 是否捕获 + * @return $this */ public function setCatchExceptions($boolean) { $this->catchExceptions = (bool) $boolean; + + return $this; } /** - * 是否自动退出 - * @param bool $boolean - * @api + * 设置是否自动退出 + * @access public + * @param bool $boolean 是否自动退出 + * @return $this */ public function setAutoExit($boolean) { $this->autoExit = (bool) $boolean; + + return $this; } /** * 获取名称 + * @access public * @return string */ public function getName() @@ -247,17 +312,21 @@ public function getName() /** * 设置名称 - * @param string $name + * @access public + * @param string $name 名称 + * @return $this */ public function setName($name) { $this->name = $name; + + return $this; } /** * 获取版本 + * @access public * @return string - * @api */ public function getVersion() { @@ -266,21 +335,30 @@ public function getVersion() /** * 设置版本 - * @param string $version + * @access public + * @param string $version 版本信息 + * @return $this */ public function setVersion($version) { $this->version = $version; + + return $this; } /** * 获取完整的版本号 + * @access public * @return string */ public function getLongVersion() { if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) { - return sprintf('%s version %s', $this->getName(), $this->getVersion()); + return sprintf( + '%s version %s', + $this->getName(), + $this->getVersion() + ); } return 'Console Tool'; @@ -288,7 +366,8 @@ public function getLongVersion() /** * 注册一个指令 - * @param string $name + * @access public + * @param string $name 指令名称 * @return Command */ public function register($name) @@ -297,32 +376,37 @@ public function register($name) } /** - * 添加指令 - * @param Command[] $commands + * 批量添加指令 + * @access public + * @param Command[] $commands 指令实例 + * @return $this */ public function addCommands(array $commands) { - foreach ($commands as $command) { - $this->add($command); - } + foreach ($commands as $command) $this->add($command); + + return $this; } /** * 添加一个指令 - * @param Command $command - * @return Command + * @access public + * @param Command $command 命令实例 + * @return Command|bool */ public function add(Command $command) { - $command->setConsole($this); - if (!$command->isEnabled()) { $command->setConsole(null); - return; + return false; } + $command->setConsole($this); + 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; @@ -336,14 +420,17 @@ public function add(Command $command) /** * 获取指令 - * @param string $name 指令名称 + * @access public + * @param string $name 指令名称 * @return Command * @throws \InvalidArgumentException */ public function get($name) { if (!isset($this->commands[$name])) { - throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name)); + throw new \InvalidArgumentException( + sprintf('The command "%s" does not exist.', $name) + ); } $command = $this->commands[$name]; @@ -363,7 +450,8 @@ public function get($name) /** * 某个指令是否存在 - * @param string $name 指令名称 + * @access public + * @param string $name 指令名称 * @return bool */ public function has($name) @@ -373,16 +461,24 @@ public function has($name) /** * 获取所有的命名空间 + * @access public * @return array */ public function getNamespaces() { $namespaces = []; + 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) { - $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias)); + $namespaces = array_merge( + $namespaces, + $this->extractAllNamespaces($alias) + ); } } @@ -390,21 +486,26 @@ public function getNamespaces() } /** - * 查找注册命名空间中的名称或缩写。 + * 查找注册命名空间中的名称或缩写 + * @access public * @param string $namespace * @return string * @throws \InvalidArgumentException */ public function findNamespace($namespace) { - $allNamespaces = $this->getNamespaces(); - $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]) . '[^:]*'; }, $namespace); + + $allNamespaces = $this->getNamespaces(); $namespaces = preg_grep('{^' . $expr . '}', $allNamespaces); 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 (1 == count($alternatives)) { @@ -420,8 +521,15 @@ public function findNamespace($namespace) } $exact = in_array($namespace, $namespaces, true); + if (count($namespaces) > 1 && !$exact) { - throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces)))); + throw new \InvalidArgumentException( + sprintf( + 'The namespace "%s" is ambiguous (%s).', + $namespace, + $this->getAbbreviationSuggestions(array_values($namespaces)) + ) + ); } return $exact ? $namespace : reset($namespaces); @@ -429,20 +537,22 @@ public function findNamespace($namespace) /** * 查找指令 - * @param string $name 名称或者别名 + * @access public + * @param string $name 名称或者别名 * @return Command * @throws \InvalidArgumentException */ 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]) . '[^:]*'; }, $name); + + $allCommands = array_keys($this->commands); $commands = preg_grep('{^' . $expr . '}', $allCommands); 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)); } @@ -462,7 +572,7 @@ public function find($name) if (count($commands) > 1) { $commandList = $this->commands; - $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) { + $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) { $commandName = $commandList[$nameOrAlias]->getName(); return $commandName === $nameOrAlias || !in_array($commandName, $commands); @@ -473,7 +583,9 @@ public function find($name) if (count($commands) > 1 && !$exact) { $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)); @@ -481,21 +593,20 @@ public function find($name) /** * 获取所有的指令 - * @param string $namespace 命名空间 + * @access public + * @param string $namespace 命名空间 * @return Command[] - * @api */ public function all($namespace = null) { - if (null === $namespace) { - return $this->commands; - } + if (null === $namespace) return $this->commands; $commands = []; + foreach ($this->commands as $name => $command) { - if ($this->extractNamespace($name, substr_count($namespace, ':') + 1) === $namespace) { - $commands[$name] = $command; - } + $ext = $this->extractNamespace($name, substr_count($namespace, ':') + 1); + + if ($ext === $namespace) $commands[$name] = $command; } return $commands; @@ -503,7 +614,8 @@ public function all($namespace = null) /** * 获取可能的指令名 - * @param array $names + * @access public + * @param array $names 指令名 * @return array */ public static function getAbbreviations($names) @@ -511,7 +623,7 @@ public static function getAbbreviations($names) $abbrevs = []; foreach ($names as $name) { for ($len = strlen($name); $len > 0; --$len) { - $abbrev = substr($name, 0, $len); + $abbrev = substr($name, 0, $len); $abbrevs[$abbrev][] = $name; } } @@ -520,9 +632,11 @@ public static function getAbbreviations($names) } /** - * 配置基于用户的参数和选项的输入和输出实例。 - * @param Input $input 输入实例 - * @param Output $output 输出实例 + * 配置基于用户的参数和选项的输入和输出实例 + * @access protected + * @param Input $input 输入实例 + * @param Output $output 输出实例 + * @return void */ protected function configureIO(Input $input, Output $output) { @@ -551,9 +665,10 @@ protected function configureIO(Input $input, Output $output) /** * 执行指令 - * @param Command $command 指令实例 - * @param Input $input 输入实例 - * @param Output $output 输出实例 + * @access protected + * @param Command $command 指令实例 + * @param Input $input 输入实例 + * @param Output $output 输出实例 * @return int * @throws \Exception */ @@ -563,8 +678,9 @@ protected function doRunCommand(Command $command, Input $input, Output $output) } /** - * 获取指令的基础名称 - * @param Input $input + * 获取指令的名称 + * @access protected + * @param Input $input 输入实例 * @return string */ protected function getCommandName(Input $input) @@ -574,6 +690,7 @@ protected function getCommandName(Input $input) /** * 获取默认输入定义 + * @access protected * @return InputDefinition */ protected function getDefaultInputDefinition() @@ -591,41 +708,55 @@ protected function getDefaultInputDefinition() } /** - * 设置默认命令 - * @return Command[] An array of default Command instances + * 获取默认命令 + * @access protected + * @return Command[] */ protected function getDefaultCommands() { $defaultCommands = []; - foreach (self::$defaultCommands as $classname) { - if (class_exists($classname) && is_subclass_of($classname, "think\\console\\Command")) { - $defaultCommands[] = new $classname(); + foreach (self::$defaultCommands as $class) { + if (class_exists($class) && is_subclass_of($class, "think\\console\\Command")) { + $defaultCommands[] = new $class(); } } return $defaultCommands; } - public static function addDefaultCommands(array $classnames) + /** + * 添加默认指令 + * @access public + * @param array $classes 指令 + * @return void + */ + public static function addDefaultCommands(array $classes) { - self::$defaultCommands = array_merge(self::$defaultCommands, $classnames); + self::$defaultCommands = array_merge(self::$defaultCommands, $classes); } /** * 获取可能的建议 - * @param array $abbrevs + * @access private + * @param array $abbrevs * @return string */ 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 指令 - * @param string $limit 部分的命名空间的最大数量 + * 返回指令的命名空间部分 + * @access public + * @param string $name 指令名称 + * @param string $limit 部分的命名空间的最大数量 * @return string */ public function extractNamespace($name, $limit = null) @@ -638,16 +769,17 @@ public function extractNamespace($name, $limit = null) /** * 查找可替代的建议 - * @param string $name - * @param array|\Traversable $collection + * @access private + * @param string $name 指令名称 + * @param array|\Traversable $collection 建议集合 * @return array */ private function findAlternatives($name, $collection) { - $threshold = 1e3; + $threshold = 1e3; $alternatives = []; - $collectionParts = []; + foreach ($collection as $item) { $collectionParts[$item] = explode(':', $item); } @@ -655,6 +787,7 @@ private function findAlternatives($name, $collection) foreach (explode(':', $name) as $i => $subname) { foreach ($collectionParts as $collectionName => $parts) { $exists = isset($alternatives[$collectionName]); + if (!isset($parts[$i]) && $exists) { $alternatives[$collectionName] += $threshold; continue; @@ -663,8 +796,14 @@ private function findAlternatives($name, $collection) } $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) { $alternatives[$collectionName] += $threshold; } @@ -673,14 +812,18 @@ private function findAlternatives($name, $collection) foreach ($collection as $item) { $lev = levenshtein($name, $item); + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { - $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; + $alternatives[$item] = isset($alternatives[$item]) ? + $alternatives[$item] - $lev : + $lev; } } $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; }); + asort($alternatives); return array_keys($alternatives); @@ -688,24 +831,28 @@ private function findAlternatives($name, $collection) /** * 设置默认的指令 - * @param string $commandName The Command name + * @access public + * @param string $commandName 指令名称 + * @return $this */ public function setDefaultCommand($commandName) { $this->defaultCommand = $commandName; + + return $this; } /** * 返回所有的命名空间 - * @param string $name + * @access private + * @param string $name 指令名称 * @return array */ private function extractAllNamespaces($name) { - $parts = explode(':', $name, -1); $namespaces = []; - foreach ($parts as $part) { + foreach (explode(':', $name, -1) as $part) { if (count($namespaces)) { $namespaces[] = end($namespaces) . ':' . $part; } else { diff --git a/library/think/Controller.php b/library/think/Controller.php index e91f2d42c8..57f3dff68b 100644 --- a/library/think/Controller.php +++ b/library/think/Controller.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -24,34 +24,36 @@ class Controller * @var \think\View 视图类实例 */ protected $view; + /** - * @var \think\Request Request实例 + * @var \think\Request Request 实例 */ protected $request; - // 验证失败是否抛出异常 + + /** + * @var bool 验证失败是否抛出异常 + */ protected $failException = false; - // 是否批量验证 + + /** + * @var bool 是否批量验证 + */ protected $batchValidate = false; /** - * 前置操作方法列表 - * @var array $beforeActionList - * @access protected + * @var array 前置操作方法列表 */ protected $beforeActionList = []; /** * 构造方法 - * @param Request $request Request对象 * @access public + * @param Request $request Request 对象 */ public function __construct(Request $request = null) { - if (is_null($request)) { - $request = Request::instance(); - } - $this->view = View::instance(Config::get('template'), Config::get('view_replace_str')); - $this->request = $request; + $this->view = View::instance(Config::get('template'), Config::get('view_replace_str')); + $this->request = is_null($request) ? Request::instance() : $request; // 控制器初始化 $this->_initialize(); @@ -66,7 +68,10 @@ public function __construct(Request $request = null) } } - // 初始化 + /** + * 初始化操作 + * @access protected + */ protected function _initialize() { } @@ -74,8 +79,9 @@ protected function _initialize() /** * 前置操作 * @access protected - * @param string $method 前置操作方法名 - * @param array $options 调用参数 ['only'=>[...]] 或者['except'=>[...]] + * @param string $method 前置操作方法名 + * @param array $options 调用参数 ['only'=>[...]] 或者 ['except'=>[...]] + * @return void */ protected function beforeAction($method, $options = []) { @@ -83,6 +89,7 @@ protected function beforeAction($method, $options = []) if (is_string($options['only'])) { $options['only'] = explode(',', $options['only']); } + if (!in_array($this->request->action(), $options['only'])) { return; } @@ -90,6 +97,7 @@ protected function beforeAction($method, $options = []) if (is_string($options['except'])) { $options['except'] = explode(',', $options['except']); } + if (in_array($this->request->action(), $options['except'])) { return; } @@ -101,10 +109,10 @@ protected function beforeAction($method, $options = []) /** * 加载模板输出 * @access protected - * @param string $template 模板文件名 - * @param array $vars 模板输出变量 - * @param array $replace 模板替换 - * @param array $config 模板参数 + * @param string $template 模板文件名 + * @param array $vars 模板输出变量 + * @param array $replace 模板替换 + * @param array $config 模板参数 * @return mixed */ protected function fetch($template = '', $vars = [], $replace = [], $config = []) @@ -115,10 +123,10 @@ protected function fetch($template = '', $vars = [], $replace = [], $config = [] /** * 渲染内容输出 * @access protected - * @param string $content 模板内容 - * @param array $vars 模板输出变量 - * @param array $replace 替换内容 - * @param array $config 模板参数 + * @param string $content 模板内容 + * @param array $vars 模板输出变量 + * @param array $replace 替换内容 + * @param array $config 模板参数 * @return mixed */ protected function display($content = '', $vars = [], $replace = [], $config = []) @@ -129,24 +137,28 @@ protected function display($content = '', $vars = [], $replace = [], $config = [ /** * 模板变量赋值 * @access protected - * @param mixed $name 要显示的模板变量 - * @param mixed $value 变量的值 - * @return void + * @param mixed $name 要显示的模板变量 + * @param mixed $value 变量的值 + * @return $this */ protected function assign($name, $value = '') { $this->view->assign($name, $value); + + return $this; } /** * 初始化模板引擎 * @access protected * @param array|string $engine 引擎参数 - * @return void + * @return $this */ protected function engine($engine) { $this->view->engine($engine); + + return $this; } /** @@ -158,17 +170,18 @@ protected function engine($engine) protected function validateFailException($fail = true) { $this->failException = $fail; + return $this; } /** * 验证数据 * @access protected - * @param array $data 数据 - * @param string|array $validate 验证器名或者验证规则数组 - * @param array $message 提示信息 - * @param bool $batch 是否批量验证 - * @param mixed $callback 回调方法(闭包) + * @param array $data 数据 + * @param string|array $validate 验证器名或者验证规则数组 + * @param array $message 提示信息 + * @param bool $batch 是否批量验证 + * @param mixed $callback 回调方法(闭包) * @return array|string|true * @throws ValidateException */ @@ -178,24 +191,27 @@ protected function validate($data, $validate, $message = [], $batch = false, $ca $v = Loader::validate(); $v->rule($validate); } else { + // 支持场景 if (strpos($validate, '.')) { - // 支持场景 list($validate, $scene) = explode('.', $validate); } + $v = Loader::validate($validate); - if (!empty($scene)) { - $v->scene($scene); - } + + !empty($scene) && $v->scene($scene); } - // 是否批量验证 + + // 批量验证 if ($batch || $this->batchValidate) { $v->batch(true); } + // 设置错误信息 if (is_array($message)) { $v->message($message); } + // 使用回调验证 if ($callback && is_callable($callback)) { call_user_func_array($callback, [$v, &$data]); } @@ -203,11 +219,11 @@ protected function validate($data, $validate, $message = [], $batch = false, $ca if (!$v->check($data)) { if ($this->failException) { throw new ValidateException($v->getError()); - } else { - return $v->getError(); } - } else { - return true; + + return $v->getError(); } + + return true; } } diff --git a/library/think/Cookie.php b/library/think/Cookie.php index 3205fcd9ae..8f6307a894 100644 --- a/library/think/Cookie.php +++ b/library/think/Cookie.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -13,28 +13,28 @@ class Cookie { + /** + * @var array cookie 设置参数 + */ protected static $config = [ - // cookie 名称前缀 - 'prefix' => '', - // cookie 保存时间 - 'expire' => 0, - // cookie 保存路径 - 'path' => '/', - // cookie 有效域名 - 'domain' => '', - // cookie 启用安全传输 - 'secure' => false, - // httponly设置 - 'httponly' => '', - // 是否使用 setcookie - 'setcookie' => true, + 'prefix' => '', // cookie 名称前缀 + 'expire' => 0, // cookie 保存时间 + 'path' => '/', // cookie 保存路径 + 'domain' => '', // cookie 有效域名 + 'secure' => false, // cookie 启用安全传输 + 'httponly' => false, // httponly 设置 + 'setcookie' => true, // 是否使用 setcookie ]; + /** + * @var bool 是否完成初始化了 + */ protected static $init; /** * Cookie初始化 - * @param array $config + * @access public + * @param array $config 配置参数 * @return void */ public static function init(array $config = []) @@ -42,39 +42,43 @@ public static function init(array $config = []) if (empty($config)) { $config = Config::get('cookie'); } + self::$config = array_merge(self::$config, array_change_key_case($config)); + if (!empty(self::$config['httponly'])) { ini_set('session.cookie_httponly', 1); } + self::$init = true; } /** - * 设置或者获取cookie作用域(前缀) - * @param string $prefix - * @return string|void + * 设置或者获取 cookie 作用域(前缀) + * @access public + * @param string $prefix 前缀 + * @return string| */ public static function prefix($prefix = '') { if (empty($prefix)) { return self::$config['prefix']; } - self::$config['prefix'] = $prefix; + + return self::$config['prefix'] = $prefix; } /** * Cookie 设置、获取、删除 - * - * @param string $name cookie名称 - * @param mixed $value cookie值 - * @param mixed $option 可选参数 可能会是 null|integer|string - * - * @return mixed - * @internal param mixed $options cookie参数 + * @access public + * @param string $name cookie 名称 + * @param mixed $value cookie 值 + * @param mixed $option 可选参数 可能会是 null|integer|string + * @return void */ public static function set($name, $value = '', $option = null) { !isset(self::$init) && self::init(); + // 参数设置(会覆盖黙认设置) if (!is_null($option)) { if (is_numeric($option)) { @@ -82,28 +86,45 @@ public static function set($name, $value = '', $option = null) } elseif (is_string($option)) { parse_str($option, $option); } + $config = array_merge(self::$config, array_change_key_case($option)); } else { $config = self::$config; } + $name = $config['prefix'] . $name; - // 设置cookie + + // 设置 cookie if (is_array($value)) { array_walk_recursive($value, 'self::jsonFormatProtect', 'encode'); $value = 'think:' . json_encode($value); } - $expire = !empty($config['expire']) ? $_SERVER['REQUEST_TIME'] + intval($config['expire']) : 0; + + $expire = !empty($config['expire']) ? + $_SERVER['REQUEST_TIME'] + intval($config['expire']) : + 0; + if ($config['setcookie']) { - setcookie($name, $value, $expire, $config['path'], $config['domain'], $config['secure'], $config['httponly']); + setcookie( + $name, + $value, + $expire, + $config['path'], + $config['domain'], + $config['secure'], + $config['httponly'] + ); } + $_COOKIE[$name] = $value; } /** - * 永久保存Cookie数据 - * @param string $name cookie名称 - * @param mixed $value cookie值 - * @param mixed $option 可选参数 可能会是 null|integer|string + * 永久保存 Cookie 数据 + * @access public + * @param string $name cookie 名称 + * @param mixed $value cookie 值 + * @param mixed $option 可选参数 可能会是 null|integer|string * @return void */ public static function forever($name, $value = '', $option = null) @@ -111,114 +132,152 @@ public static function forever($name, $value = '', $option = null) if (is_null($option) || is_numeric($option)) { $option = []; } + $option['expire'] = 315360000; + self::set($name, $value, $option); } /** - * 判断Cookie数据 - * @param string $name cookie名称 - * @param string|null $prefix cookie前缀 + * 判断是否有 Cookie 数据 + * @access public + * @param string $name cookie 名称 + * @param string|null $prefix cookie 前缀 * @return bool */ public static function has($name, $prefix = null) { !isset(self::$init) && self::init(); + $prefix = !is_null($prefix) ? $prefix : self::$config['prefix']; - $name = $prefix . $name; - return isset($_COOKIE[$name]); + + return isset($_COOKIE[$prefix . $name]); } /** - * Cookie获取 - * @param string $name cookie名称 - * @param string|null $prefix cookie前缀 + * 获取 Cookie 的值 + * @access public + * @param string $name cookie 名称 + * @param string|null $prefix cookie 前缀 * @return mixed */ public static function get($name = '', $prefix = null) { !isset(self::$init) && self::init(); + $prefix = !is_null($prefix) ? $prefix : self::$config['prefix']; - $key = $prefix . $name; + $key = $prefix . $name; if ('' == $name) { // 获取全部 if ($prefix) { $value = []; + foreach ($_COOKIE as $k => $val) { if (0 === strpos($k, $prefix)) { $value[$k] = $val; } + } } else { $value = $_COOKIE; } } elseif (isset($_COOKIE[$key])) { $value = $_COOKIE[$key]; + if (0 === strpos($value, 'think:')) { - $value = substr($value, 6); - $value = json_decode($value, true); + $value = json_decode(substr($value, 6), true); array_walk_recursive($value, 'self::jsonFormatProtect', 'decode'); } } else { $value = null; } + return $value; } /** - * Cookie删除 - * @param string $name cookie名称 - * @param string|null $prefix cookie前缀 - * @return mixed + * 删除 Cookie + * @access public + * @param string $name cookie 名称 + * @param string|null $prefix cookie 前缀 + * @return void */ public static function delete($name, $prefix = null) { !isset(self::$init) && self::init(); + $config = self::$config; $prefix = !is_null($prefix) ? $prefix : $config['prefix']; - $name = $prefix . $name; + $name = $prefix . $name; + 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]); } /** - * Cookie清空 - * @param string|null $prefix cookie前缀 - * @return mixed + * 清除指定前缀的所有 cookie + * @access public + * @param string|null $prefix cookie 前缀 + * @return void */ public static function clear($prefix = null) { - // 清除指定前缀的所有cookie if (empty($_COOKIE)) { return; } + !isset(self::$init) && self::init(); - // 要删除的cookie前缀,不指定则删除config设置的指定前缀 + + // 要删除的 cookie 前缀,不指定则删除 config 设置的指定前缀 $config = self::$config; $prefix = !is_null($prefix) ? $prefix : $config['prefix']; + if ($prefix) { - // 如果前缀为空字符串将不作处理直接返回 foreach ($_COOKIE as $key => $val) { if (0 === strpos($key, $prefix)) { if ($config['setcookie']) { - setcookie($key, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'], $config['domain'], $config['secure'], $config['httponly']); + setcookie( + $key, + '', + $_SERVER['REQUEST_TIME'] - 3600, + $config['path'], + $config['domain'], + $config['secure'], + $config['httponly'] + ); } + unset($_COOKIE[$key]); } } } - return; } - private static function jsonFormatProtect(&$val, $key, $type = 'encode') + /** + * 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) { $val = 'decode' == $type ? urldecode($val) : urlencode($val); } } - } diff --git a/library/think/Db.php b/library/think/Db.php index ea0071a8fd..9f8945b34c 100644 --- a/library/think/Db.php +++ b/library/think/Db.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -49,19 +49,26 @@ */ class Db { - // 数据库连接实例 + /** + * @var Connection[] 数据库连接实例 + */ private static $instance = []; - // 查询次数 + + /** + * @var int 查询次数 + */ public static $queryTimes = 0; - // 执行次数 + + /** + * @var int 执行次数 + */ public static $executeTimes = 0; /** - * 数据库初始化 并取得数据库类实例 - * @static + * 数据库初始化,并取得数据库类实例 * @access public - * @param mixed $config 连接配置 - * @param bool|string $name 连接标识 true 强制重新连接 + * @param mixed $config 连接配置 + * @param bool|string $name 连接标识 true 强制重新连接 * @return Connection * @throws Exception */ @@ -70,34 +77,48 @@ public static function connect($config = [], $name = false) if (false === $name) { $name = md5(serialize($config)); } + if (true === $name || !isset(self::$instance[$name])) { // 解析连接参数 支持数组和字符串 $options = self::parseConfig($config); + if (empty($options['type'])) { throw new \InvalidArgumentException('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) { Log::record('[ DB ] INIT ' . $options['type'], 'info'); } + if (true === $name) { $name = md5(serialize($config)); } + self::$instance[$name] = new $class($options); } + return self::$instance[$name]; } - - public static function clear() { - self::$instance = null; + + /** + * 清除连接实例 + * @access public + * @return void + */ + public static function clear() + { + self::$instance = []; } /** * 数据库连接参数解析 - * @static * @access private - * @param mixed $config + * @param mixed $config 连接参数 * @return array */ private static function parseConfig($config) @@ -105,38 +126,35 @@ private static function parseConfig($config) if (empty($config)) { $config = Config::get('database'); } elseif (is_string($config) && false === strpos($config, '/')) { - // 支持读取配置参数 - $config = Config::get($config); - } - if (is_string($config)) { - return self::parseDsn($config); - } else { - return $config; + $config = Config::get($config); // 支持读取配置参数 } + + return is_string($config) ? self::parseDsn($config) : $config; } /** - * DSN解析 + * DSN 解析 * 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1¶m2=val2#utf8 - * @static * @access private - * @param string $dsnStr + * @param string $dsnStr 数据库 DSN 字符串解析 * @return array */ private static function parseDsn($dsnStr) { $info = parse_url($dsnStr); + if (!$info) { return []; } + $dsn = [ - 'type' => $info['scheme'], + 'type' => $info['scheme'], 'username' => isset($info['user']) ? $info['user'] : '', 'password' => isset($info['pass']) ? $info['pass'] : '', 'hostname' => isset($info['host']) ? $info['host'] : '', 'hostport' => isset($info['port']) ? $info['port'] : '', 'database' => !empty($info['path']) ? ltrim($info['path'], '/') : '', - 'charset' => isset($info['fragment']) ? $info['fragment'] : 'utf8', + 'charset' => isset($info['fragment']) ? $info['fragment'] : 'utf8', ]; if (isset($info['query'])) { @@ -144,13 +162,19 @@ private static function parseDsn($dsnStr) } else { $dsn['params'] = []; } + return $dsn; } - // 调用驱动类的方法 + /** + * 调用驱动类的方法 + * @access public + * @param string $method 方法名 + * @param array $params 参数 + * @return mixed + */ public static function __callStatic($method, $params) { - // 自动初始化数据库 return call_user_func_array([self::connect(), $method], $params); } } diff --git a/library/think/Debug.php b/library/think/Debug.php index 9994e20c8f..0ae9c34aa1 100644 --- a/library/think/Debug.php +++ b/library/think/Debug.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -16,46 +16,55 @@ class Debug { - // 区间时间信息 + /** + * @var array 区间时间信息 + */ protected static $info = []; - // 区间内存信息 + + /** + * @var array 区间内存信息 + */ protected static $mem = []; /** * 记录时间(微秒)和内存使用情况 - * @param string $name 标记位置 - * @param mixed $value 标记值 留空则取当前 time 表示仅记录时间 否则同时记录时间和内存 - * @return mixed + * @access public + * @param string $name 标记位置 + * @param mixed $value 标记值(留空则取当前 time 表示仅记录时间 否则同时记录时间和内存) + * @return void */ public static function remark($name, $value = '') { - // 记录时间和内存使用 self::$info[$name] = is_float($value) ? $value : microtime(true); + if ('time' != $value) { - self::$mem['mem'][$name] = is_float($value) ? $value : memory_get_usage(); + self::$mem['mem'][$name] = is_float($value) ? $value : memory_get_usage(); self::$mem['peak'][$name] = memory_get_peak_usage(); } } /** * 统计某个区间的时间(微秒)使用情况 返回值以秒为单位 - * @param string $start 开始标签 - * @param string $end 结束标签 - * @param integer|string $dec 小数位 - * @return integer + * @access public + * @param string $start 开始标签 + * @param string $end 结束标签 + * @param integer $dec 小数位 + * @return string */ public static function getRangeTime($start, $end, $dec = 6) { if (!isset(self::$info[$end])) { self::$info[$end] = microtime(true); } + return number_format((self::$info[$end] - self::$info[$start]), $dec); } /** * 统计从开始到统计时的时间(微秒)使用情况 返回值以秒为单位 - * @param integer|string $dec 小数位 - * @return integer + * @access public + * @param integer $dec 小数位 + * @return string */ public static function getUseTime($dec = 6) { @@ -64,6 +73,7 @@ public static function getUseTime($dec = 6) /** * 获取当前访问的吞吐率情况 + * @access public * @return string */ public static function getThroughputRate() @@ -73,9 +83,10 @@ public static function getThroughputRate() /** * 记录区间的内存使用情况 - * @param string $start 开始标签 - * @param string $end 结束标签 - * @param integer|string $dec 小数位 + * @access public + * @param string $start 开始标签 + * @param string $end 结束标签 + * @param integer $dec 小数位 * @return string */ public static function getRangeMem($start, $end, $dec = 2) @@ -83,123 +94,152 @@ public static function getRangeMem($start, $end, $dec = 2) if (!isset(self::$mem['mem'][$end])) { self::$mem['mem'][$end] = memory_get_usage(); } + $size = self::$mem['mem'][$end] - self::$mem['mem'][$start]; - $a = ['B', 'KB', 'MB', 'GB', 'TB']; - $pos = 0; + $a = ['B', 'KB', 'MB', 'GB', 'TB']; + $pos = 0; + while ($size >= 1024) { $size /= 1024; $pos++; } + return round($size, $dec) . " " . $a[$pos]; } /** * 统计从开始到统计时的内存使用情况 - * @param integer|string $dec 小数位 + * @access public + * @param integer $dec 小数位 * @return string */ public static function getUseMem($dec = 2) { $size = memory_get_usage() - THINK_START_MEM; - $a = ['B', 'KB', 'MB', 'GB', 'TB']; - $pos = 0; + $a = ['B', 'KB', 'MB', 'GB', 'TB']; + $pos = 0; + while ($size >= 1024) { $size /= 1024; $pos++; } + return round($size, $dec) . " " . $a[$pos]; } /** * 统计区间的内存峰值情况 - * @param string $start 开始标签 - * @param string $end 结束标签 - * @param integer|string $dec 小数位 - * @return mixed + * @access public + * @param string $start 开始标签 + * @param string $end 结束标签 + * @param integer $dec 小数位 + * @return string */ public static function getMemPeak($start, $end, $dec = 2) { if (!isset(self::$mem['peak'][$end])) { self::$mem['peak'][$end] = memory_get_peak_usage(); } + $size = self::$mem['peak'][$end] - self::$mem['peak'][$start]; - $a = ['B', 'KB', 'MB', 'GB', 'TB']; - $pos = 0; + $a = ['B', 'KB', 'MB', 'GB', 'TB']; + $pos = 0; + while ($size >= 1024) { $size /= 1024; $pos++; } + return round($size, $dec) . " " . $a[$pos]; } /** * 获取文件加载信息 - * @param bool $detail 是否显示详细 + * @access public + * @param bool $detail 是否显示详细 * @return integer|array */ public static function getFile($detail = false) { + $files = get_included_files(); + if ($detail) { - $files = get_included_files(); - $info = []; - foreach ($files as $key => $file) { + $info = []; + + foreach ($files as $file) { $info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )'; } + return $info; } - return count(get_included_files()); + + return count($files); } /** * 浏览器友好的变量输出 - * @param mixed $var 变量 - * @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串 - * @param string $label 标签 默认为空 - * @param integer $flags htmlspecialchars flags - * @return void|string + * @access public + * @param mixed $var 变量 + * @param boolean $echo 是否输出(默认为 true,为 false 则返回输出字符串) + * @param string|null $label 标签(默认为空) + * @param integer $flags htmlspecialchars 的标志 + * @return null|string */ public static function dump($var, $echo = true, $label = null, $flags = ENT_SUBSTITUTE) { $label = (null === $label) ? '' : rtrim($label) . ':'; + ob_start(); var_dump($var); - $output = ob_get_clean(); - $output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output); + $output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', ob_get_clean()); + if (IS_CLI) { $output = PHP_EOL . $label . $output . PHP_EOL; } else { if (!extension_loaded('xdebug')) { $output = htmlspecialchars($output, $flags); } + $output = '
' . $label . $output . '
'; } + if ($echo) { echo($output); return; - } else { - return $output; } + + return $output; } + /** + * 调试信息注入到响应中 + * @access public + * @param Response $response 响应实例 + * @param string $content 返回的字符串 + * @return void + */ public static function inject(Response $response, &$content) { - $config = Config::get('trace'); - $type = isset($config['type']) ? $config['type'] : 'Html'; - $request = Request::instance(); - $class = false !== strpos($type, '\\') ? $type : '\\think\\debug\\' . ucwords($type); + $config = Config::get('trace'); + $type = isset($config['type']) ? $config['type'] : 'Html'; + $class = false !== strpos($type, '\\') ? $type : '\\think\\debug\\' . ucwords($type); + unset($config['type']); - if (class_exists($class)) { - $trace = new $class($config); - } else { + + if (!class_exists($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) { - //TODO 记录 + // TODO 记录 } else { $output = $trace->output($response, Log::getLog()); + if (is_string($output)) { - // trace调试信息注入 + // trace 调试信息注入 $pos = strripos($content, ''); if (false !== $pos) { $content = substr($content, 0, $pos) . $output . substr($content, $pos); diff --git a/library/think/Env.php b/library/think/Env.php index fa87897ca4..0a8b250974 100644 --- a/library/think/Env.php +++ b/library/think/Env.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -15,22 +15,25 @@ class Env { /** * 获取环境变量值 - * @param string $name 环境变量名(支持二级 .号分割) - * @param string $default 默认值 + * @access public + * @param string $name 环境变量名(支持二级 . 号分割) + * @param string $default 默认值 * @return mixed */ public static function get($name, $default = null) { $result = getenv(ENV_PREFIX . strtoupper(str_replace('.', '_', $name))); + if (false !== $result) { if ('false' === $result) { $result = false; } elseif ('true' === $result) { $result = true; } + return $result; - } else { - return $default; } + + return $default; } } diff --git a/library/think/Error.php b/library/think/Error.php index 9eda5442e5..fc63f41f4e 100644 --- a/library/think/Error.php +++ b/library/think/Error.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -20,6 +20,7 @@ class Error { /** * 注册异常处理 + * @access public * @return void */ public static function register() @@ -31,8 +32,10 @@ public static function register() } /** - * Exception Handler - * @param \Exception|\Throwable $e + * 异常处理 + * @access public + * @param \Exception|\Throwable $e 异常 + * @return void */ public static function appException($e) { @@ -40,44 +43,53 @@ public static function appException($e) $e = new ThrowableError($e); } - self::getExceptionHandler()->report($e); + $handler = self::getExceptionHandler(); + $handler->report($e); + if (IS_CLI) { - self::getExceptionHandler()->renderForConsole(new ConsoleOutput, $e); + $handler->renderForConsole(new ConsoleOutput, $e); } else { - self::getExceptionHandler()->render($e)->send(); + $handler->render($e)->send(); } } /** - * Error Handler - * @param integer $errno 错误编号 - * @param integer $errstr 详细错误信息 - * @param string $errfile 出错的文件 - * @param integer $errline 出错行号 - * @param array $errcontext + * 错误处理 + * @access public + * @param integer $errno 错误编号 + * @param integer $errstr 详细错误信息 + * @param string $errfile 出错的文件 + * @param integer $errline 出错行号 + * @return void * @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) { - // 将错误信息托管至 think\exception\ErrorException throw $exception; - } else { - self::getExceptionHandler()->report($exception); } + + self::getExceptionHandler()->report($exception); } /** - * Shutdown Handler + * 异常中止处理 + * @access public + * @return void */ public static function appShutdown() { + // 将错误信息托管至 think\ErrorException if (!is_null($error = error_get_last()) && self::isFatal($error['type'])) { - // 将错误信息托管至think\ErrorException - $exception = new ErrorException($error['type'], $error['message'], $error['file'], $error['line']); - - self::appException($exception); + self::appException(new ErrorException( + $error['type'], + $error['message'], + $error['file'], + $error['line'] + )); } // 写入日志 @@ -86,8 +98,8 @@ public static function appShutdown() /** * 确定错误类型是否致命 - * - * @param int $type + * @access protected + * @param int $type 错误类型 * @return bool */ protected static function isFatal($type) @@ -96,25 +108,32 @@ protected static function isFatal($type) } /** - * Get an instance of the exception handler. - * + * 获取异常处理的实例 + * @access public * @return Handle */ public static function getExceptionHandler() { static $handle; + if (!$handle) { - // 异常处理handle + // 异常处理 handle $class = Config::get('exception_handle'); - if ($class && is_string($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; } else { $handle = new Handle; + if ($class instanceof \Closure) { $handle->setRender($class); } + } } + return $handle; } } diff --git a/library/think/Exception.php b/library/think/Exception.php index 034c85b647..1ef06bdbd3 100644 --- a/library/think/Exception.php +++ b/library/think/Exception.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -13,15 +13,13 @@ class Exception extends \Exception { - /** - * 保存异常页面显示的额外Debug数据 - * @var array + * @var array 保存异常页面显示的额外 Debug 数据 */ protected $data = []; /** - * 设置异常额外的Debug数据 + * 设置异常额外的 Debug 数据 * 数据将会显示为下面的格式 * * Exception Data @@ -33,8 +31,10 @@ class Exception extends \Exception * key1 value1 * key2 value2 * - * @param string $label 数据分类,用于异常页面显示 - * @param array $data 需要显示的数据,必须为关联数组 + * @access protected + * @param string $label 数据分类,用于异常页面显示 + * @param array $data 需要显示的数据,必须为关联数组 + * @return void */ final protected function setData($label, array $data) { @@ -42,13 +42,14 @@ final protected function setData($label, array $data) } /** - * 获取异常额外Debug数据 + * 获取异常额外 Debug 数据 * 主要用于输出到异常页面便于调试 - * @return array 由setData设置的Debug数据 + * @access public + * @return array */ final public function getData() { return $this->data; } - + } diff --git a/library/think/File.php b/library/think/File.php index dc597701cb..78253bdbc4 100644 --- a/library/think/File.php +++ b/library/think/File.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -16,25 +16,51 @@ class File extends SplFileObject { /** - * 错误信息 - * @var string + * @var string 错误信息 */ private $error = ''; - // 当前完整文件名 + + /** + * @var string 当前完整文件名 + */ protected $filename; - // 上传文件名 + + /** + * @var string 上传文件名 + */ protected $saveName; - // 文件上传命名规则 + + /** + * @var string 文件上传命名规则 + */ protected $rule = 'date'; - // 文件上传验证规则 + + /** + * @var array 文件上传验证规则 + */ protected $validate = []; - // 单元测试 + + /** + * @var bool 单元测试 + */ protected $isTest; - // 上传文件信息 + + /** + * @var array 上传文件信息 + */ protected $info; - // 文件hash信息 + + /** + * @var array 文件 hash 信息 + */ protected $hash = []; + /** + * File constructor. + * @access public + * @param string $filename 文件名称 + * @param string $mode 访问模式 + */ public function __construct($filename, $mode = 'r') { parent::__construct($filename, $mode); @@ -42,30 +68,35 @@ public function __construct($filename, $mode = 'r') } /** - * 是否测试 - * @param bool $test 是否测试 + * 设置是否是单元测试 + * @access public + * @param bool $test 是否是测试 * @return $this */ public function isTest($test = false) { $this->isTest = $test; + return $this; } /** * 设置上传信息 - * @param array $info 上传文件信息 + * @access public + * @param array $info 上传文件信息 * @return $this */ public function setUploadInfo($info) { $this->info = $info; + return $this; } /** * 获取上传文件的信息 - * @param string $name + * @access public + * @param string $name 信息名称 * @return array|string */ public function getInfo($name = '') @@ -75,6 +106,7 @@ public function getInfo($name = '') /** * 获取上传文件的文件名 + * @access public * @return string */ public function getSaveName() @@ -84,94 +116,101 @@ public function getSaveName() /** * 设置上传文件的保存文件名 - * @param string $saveName + * @access public + * @param string $saveName 保存名称 * @return $this */ public function setSaveName($saveName) { $this->saveName = $saveName; + return $this; } /** * 获取文件的哈希散列值 - * @param string $type - * @return mixed $string + * @access public + * @param string $type 类型 + * @return string */ public function hash($type = 'sha1') { if (!isset($this->hash[$type])) { $this->hash[$type] = hash_file($type, $this->filename); } + return $this->hash[$type]; } /** * 检查目录是否可写 - * @param string $path 目录 + * @access protected + * @param string $path 目录 * @return boolean */ protected function checkPath($path) { - if (is_dir($path)) { + if (is_dir($path) || mkdir($path, 0755, true)) { return true; } - if (mkdir($path, 0755, true)) { - return true; - } else { - $this->error = "目录 {$path} 创建失败!"; - return false; - } + $this->error = ['directory {:path} creation failed', ['path' => $path]]; + + return false; } /** * 获取文件类型信息 + * @access public * @return string */ public function getMime() { $finfo = finfo_open(FILEINFO_MIME_TYPE); + return finfo_file($finfo, $this->filename); } /** * 设置文件的命名规则 - * @param string $rule 文件命名规则 + * @access public + * @param string $rule 文件命名规则 * @return $this */ public function rule($rule) { $this->rule = $rule; + return $this; } /** * 设置上传文件的验证规则 - * @param array $rule 验证规则 + * @access public + * @param array $rule 验证规则 * @return $this */ - public function validate($rule = []) + public function validate(array $rule = []) { $this->validate = $rule; + return $this; } /** * 检测是否合法的上传文件 + * @access public * @return bool */ public function isValid() { - if ($this->isTest) { - return is_file($this->filename); - } - return is_uploaded_file($this->filename); + return $this->isTest ? is_file($this->filename) : is_uploaded_file($this->filename); } /** * 检测上传文件 - * @param array $rule 验证规则 + * @access public + * @param array $rule 验证规则 * @return bool */ public function check($rule = []) @@ -180,25 +219,25 @@ public function check($rule = []) /* 检查文件大小 */ if (isset($rule['size']) && !$this->checkSize($rule['size'])) { - $this->error = '上传文件大小不符!'; + $this->error = 'filesize not match'; return false; } - /* 检查文件Mime类型 */ + /* 检查文件 Mime 类型 */ if (isset($rule['type']) && !$this->checkMime($rule['type'])) { - $this->error = '上传文件MIME类型不允许!'; + $this->error = 'mimetype to upload is not allowed'; return false; } /* 检查文件后缀 */ if (isset($rule['ext']) && !$this->checkExt($rule['ext'])) { - $this->error = '上传文件后缀不允许'; + $this->error = 'extensions to upload is not allowed'; return false; } /* 检查图像文件 */ if (!$this->checkImg()) { - $this->error = '非法图像文件!'; + $this->error = 'illegal image files'; return false; } @@ -207,7 +246,8 @@ public function check($rule = []) /** * 检测上传文件后缀 - * @param array|string $ext 允许后缀 + * @access public + * @param array|string $ext 允许后缀 * @return bool */ public function checkExt($ext) @@ -215,77 +255,76 @@ public function checkExt($ext) if (is_string($ext)) { $ext = explode(',', $ext); } + $extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION)); - if (!in_array($extension, $ext)) { - return false; - } - return true; + + return in_array($extension, $ext); } /** * 检测图像文件 + * @access public * @return bool */ public function checkImg() { $extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION)); - /* 对图像文件进行严格检测 */ - if (in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) && !in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6, 13])) { - return false; - } - return true; + + // 如果上传的不是图片,或者是图片而且后缀确实符合图片类型则返回 true + return !in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) || in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6, 13]); } - // 判断图像类型 + /** + * 判断图像类型 + * @access protected + * @param string $image 图片名称 + * @return bool|int + */ protected function getImageType($image) { if (function_exists('exif_imagetype')) { return exif_imagetype($image); - } else { - try { - $info = getimagesize($image); - return $info ? $info[2] : false; - } catch (\Exception $e) { - return false; - } + } + + try { + $info = getimagesize($image); + return $info ? $info[2] : false; + } catch (\Exception $e) { + return false; } } /** * 检测上传文件大小 - * @param integer $size 最大大小 + * @access public + * @param integer $size 最大大小 * @return bool */ public function checkSize($size) { - if ($this->getSize() > $size) { - return false; - } - return true; + return $this->getSize() <= $size; } /** * 检测上传文件类型 - * @param array|string $mime 允许类型 + * @access public + * @param array|string $mime 允许类型 * @return bool */ public function checkMime($mime) { - if (is_string($mime)) { - $mime = explode(',', $mime); - } - if (!in_array(strtolower($this->getMime()), $mime)) { - return false; - } - return true; + $mime = is_string($mime) ? explode(',', $mime) : $mime; + + return in_array(strtolower($this->getMime()), $mime); } /** * 移动文件 - * @param string $path 保存路径 - * @param string|bool $savename 保存的文件名 默认自动生成 - * @param boolean $replace 同名文件是否覆盖 - * @return false|File false-失败 否则返回File实例 + * @access public + * @param string $path 保存路径 + * @param string|bool $savename 保存的文件名 默认自动生成 + * @param boolean $replace 同名文件是否覆盖 + * @return false|File */ public function move($path, $savename = true, $replace = true) { @@ -297,7 +336,7 @@ public function move($path, $savename = true, $replace = true) // 检测合法性 if (!$this->isValid()) { - $this->error = '非法上传文件'; + $this->error = 'upload illegal files'; return false; } @@ -305,6 +344,7 @@ public function move($path, $savename = true, $replace = true) if (!$this->check()) { return false; } + $path = rtrim($path, DS) . DS; // 文件保存命名规则 $saveName = $this->buildSaveName($savename); @@ -315,9 +355,9 @@ public function move($path, $savename = true, $replace = true) return false; } - /* 不覆盖同名文件 */ + // 不覆盖同名文件 if (!$replace && is_file($filename)) { - $this->error = '存在同名文件' . $filename; + $this->error = ['has the same filename: {:filename}', ['filename' => $filename]]; return false; } @@ -325,25 +365,27 @@ public function move($path, $savename = true, $replace = true) if ($this->isTest) { rename($this->filename, $filename); } elseif (!move_uploaded_file($this->filename, $filename)) { - $this->error = '文件上传保存错误!'; + $this->error = 'upload write error'; return false; } - // 返回 File对象实例 + + // 返回 File 对象实例 $file = new self($filename); - $file->setSaveName($saveName); - $file->setUploadInfo($this->info); + $file->setSaveName($saveName)->setUploadInfo($this->info); + return $file; } /** * 获取保存文件名 - * @param string|bool $savename 保存的文件名 默认自动生成 + * @access protected + * @param string|bool $savename 保存的文件名 默认自动生成 * @return string */ protected function buildSaveName($savename) { + // 自动生成文件名 if (true === $savename) { - // 自动生成文件名 if ($this->rule instanceof \Closure) { $savename = call_user_func_array($this->rule, [$this]); } else { @@ -353,7 +395,7 @@ protected function buildSaveName($savename) break; default: if (in_array($this->rule, hash_algos())) { - $hash = $this->hash($this->rule); + $hash = $this->hash($this->rule); $savename = substr($hash, 0, 2) . DS . substr($hash, 2); } elseif (is_callable($this->rule)) { $savename = call_user_func($this->rule); @@ -362,52 +404,73 @@ protected function buildSaveName($savename) } } } - } elseif ('' === $savename) { + } elseif ('' === $savename || false === $savename) { $savename = $this->getInfo('name'); } + if (!strpos($savename, '.')) { $savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION); } + return $savename; } /** * 获取错误代码信息 - * @param int $errorNo 错误号 + * @access private + * @param int $errorNo 错误号 + * @return $this */ private function error($errorNo) { switch ($errorNo) { case 1: case 2: - $this->error = '上传文件大小超过了最大值!'; + $this->error = 'upload File size exceeds the maximum value'; break; case 3: - $this->error = '文件只有部分被上传!'; + $this->error = 'only the portion of file is uploaded'; break; case 4: - $this->error = '没有文件被上传!'; + $this->error = 'no file to uploaded'; break; case 6: - $this->error = '找不到临时文件夹!'; + $this->error = 'upload temp dir not found'; break; case 7: - $this->error = '文件写入失败!'; + $this->error = 'file write error'; break; default: - $this->error = '未知上传错误!'; + $this->error = 'unknown upload error'; } + + return $this; } /** - * 获取错误信息 - * @return mixed + * 获取错误信息(支持多语言) + * @access public + * @return string */ 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) { return $this->hash($method); diff --git a/library/think/Hook.php b/library/think/Hook.php index f06196e4de..64eb903f07 100644 --- a/library/think/Hook.php +++ b/library/think/Hook.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -13,19 +13,23 @@ class Hook { - + /** + * @var array 标签 + */ private static $tags = []; /** * 动态添加行为扩展到某个标签 - * @param string $tag 标签名称 - * @param mixed $behavior 行为名称 - * @param bool $first 是否放到开头执行 + * @access public + * @param string $tag 标签名称 + * @param mixed $behavior 行为名称 + * @param bool $first 是否放到开头执行 * @return void */ public static function add($tag, $behavior, $first = false) { isset(self::$tags[$tag]) || self::$tags[$tag] = []; + if (is_array($behavior) && !is_callable($behavior)) { if (!array_key_exists('_overlay', $behavior) || !$behavior['_overlay']) { unset($behavior['_overlay']); @@ -43,8 +47,10 @@ public static function add($tag, $behavior, $first = false) /** * 批量导入插件 - * @param array $tags 插件信息 - * @param boolean $recursive 是否递归合并 + * @access public + * @param array $tags 插件信息 + * @param boolean $recursive 是否递归合并 + * @return void */ public static function import(array $tags, $recursive = true) { @@ -59,77 +65,83 @@ public static function import(array $tags, $recursive = true) /** * 获取插件信息 - * @param string $tag 插件位置 留空获取全部 + * @access public + * @param string $tag 插件位置(留空获取全部) * @return array */ public static function get($tag = '') { if (empty($tag)) { - //获取全部的插件信息 return self::$tags; - } else { - return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : []; } + + return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : []; } /** * 监听标签的行为 - * @param string $tag 标签名称 - * @param mixed $params 传入参数 - * @param mixed $extra 额外参数 - * @param bool $once 只获取一个有效返回值 + * @access public + * @param string $tag 标签名称 + * @param mixed $params 传入参数 + * @param mixed $extra 额外参数 + * @param bool $once 只获取一个有效返回值 * @return mixed */ public static function listen($tag, &$params = null, $extra = null, $once = false) { $results = []; - $tags = static::get($tag); - foreach ($tags as $key => $name) { + + foreach (static::get($tag) as $key => $name) { $results[$key] = self::exec($name, $tag, $params, $extra); - if (false === $results[$key]) { - // 如果返回false 则中断行为执行 - break; - } elseif (!is_null($results[$key]) && $once) { + + // 如果返回 false,或者仅获取一个有效返回则中断行为执行 + if (false === $results[$key] || (!is_null($results[$key]) && $once)) { break; } } + return $once ? end($results) : $results; } /** * 执行某个行为 - * @param mixed $class 要执行的行为 - * @param string $tag 方法名(标签名) - * @param Mixed $params 传人的参数 - * @param mixed $extra 额外参数 + * @access public + * @param mixed $class 要执行的行为 + * @param string $tag 方法名(标签名) + * @param mixed $params 传人的参数 + * @param mixed $extra 额外参数 * @return mixed */ public static function exec($class, $tag = '', &$params = null, $extra = null) { App::$debug && Debug::remark('behavior_start', 'time'); + $method = Loader::parseName($tag, 1, false); + if ($class instanceof \Closure) { $result = call_user_func_array($class, [ & $params, $extra]); - $class = 'Closure'; + $class = 'Closure'; } elseif (is_array($class)) { list($class, $method) = $class; $result = (new $class())->$method($params, $extra); - $class = $class . '->' . $method; + $class = $class . '->' . $method; } elseif (is_object($class)) { $result = $class->$method($params, $extra); - $class = get_class($class); + $class = get_class($class); } elseif (strpos($class, '::')) { $result = call_user_func_array($class, [ & $params, $extra]); } else { - $obj = new $class(); + $obj = new $class(); $method = ($tag && is_callable([$obj, $method])) ? $method : 'run'; $result = $obj->$method($params, $extra); } + if (App::$debug) { Debug::remark('behavior_end', 'time'); Log::record('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info'); } + return $result; } diff --git a/library/think/Lang.php b/library/think/Lang.php index a4404f1e17..4a01a959e8 100644 --- a/library/think/Lang.php +++ b/library/think/Lang.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -13,115 +13,149 @@ class Lang { - // 语言数据 + /** + * @var array 语言数据 + */ private static $lang = []; - // 语言作用域 + + /** + * @var string 语言作用域 + */ private static $range = 'zh-cn'; - // 语言自动侦测的变量 + + /** + * @var string 语言自动侦测的变量 + */ protected static $langDetectVar = 'lang'; - // 语言Cookie变量 + + /** + * @var string 语言 Cookie 变量 + */ protected static $langCookieVar = 'think_var'; - // 语言Cookie的过期时间 + + /** + * @var int 语言 Cookie 的过期时间 + */ protected static $langCookieExpire = 3600; - // 允许语言列表 + + /** + * @var array 允许语言列表 + */ protected static $allowLangList = []; - // Accept-Language转义为对应语言包名称 系统默认配置 - protected static $acceptLanguage = [ - 'zh-hans-cn' => 'zh-cn', - ]; - // 设定当前的语言 + /** + * @var array Accept-Language 转义为对应语言包名称 系统默认配置 + */ + protected static $acceptLanguage = ['zh-hans-cn' => 'zh-cn']; + + /** + * 设定当前的语言 + * @access public + * @param string $range 语言作用域 + * @return string + */ public static function range($range = '') { - if ('' == $range) { - return self::$range; - } else { + if ($range) { self::$range = $range; } + return self::$range; } /** * 设置语言定义(不区分大小写) - * @param string|array $name 语言变量 - * @param string $value 语言值 - * @param string $range 语言作用域 + * @access public + * @param string|array $name 语言变量 + * @param string $value 语言值 + * @param string $range 语言作用域 * @return mixed */ public static function set($name, $value = null, $range = '') { $range = $range ?: self::$range; - // 批量定义 + if (!isset(self::$lang[$range])) { self::$lang[$range] = []; } + if (is_array($name)) { return self::$lang[$range] = array_change_key_case($name) + self::$lang[$range]; - } else { - return self::$lang[$range][strtolower($name)] = $value; } + + return self::$lang[$range][strtolower($name)] = $value; } /** * 加载语言定义(不区分大小写) - * @param array|string $file 语言文件 - * @param string $range 语言作用域 + * @access public + * @param array|string $file 语言文件 + * @param string $range 语言作用域 * @return mixed */ public static function load($file, $range = '') { $range = $range ?: self::$range; + $file = is_string($file) ? [$file] : $file; + if (!isset(self::$lang[$range])) { self::$lang[$range] = []; } - // 批量定义 - if (is_string($file)) { - $file = [$file]; - } + $lang = []; + foreach ($file as $_file) { if (is_file($_file)) { // 记录加载信息 App::$debug && Log::record('[ LANG ] ' . $_file, 'info'); + $_lang = include $_file; + if (is_array($_lang)) { $lang = array_change_key_case($_lang) + $lang; } } } + if (!empty($lang)) { self::$lang[$range] = $lang + self::$lang[$range]; } + return self::$lang[$range]; } /** * 获取语言定义(不区分大小写) - * @param string|null $name 语言变量 - * @param string $range 语言作用域 + * @access public + * @param string|null $name 语言变量 + * @param string $range 语言作用域 * @return mixed */ public static function has($name, $range = '') { $range = $range ?: self::$range; + return isset(self::$lang[$range][strtolower($name)]); } /** * 获取语言定义(不区分大小写) - * @param string|null $name 语言变量 - * @param array $vars 变量替换 - * @param string $range 语言作用域 + * @access public + * @param string|null $name 语言变量 + * @param array $vars 变量替换 + * @param string $range 语言作用域 * @return mixed */ public static function get($name = null, $vars = [], $range = '') { $range = $range ?: self::$range; + // 空参数返回所有定义 if (empty($name)) { return self::$lang[$range]; } - $key = strtolower($name); + + $key = strtolower($name); $value = isset(self::$lang[$range][$key]) ? self::$lang[$range][$key] : $name; // 变量解析 @@ -145,45 +179,55 @@ public static function get($name = null, $vars = [], $range = '') } } + return $value; } /** * 自动侦测设置获取语言选择 + * @access public * @return string */ public static function detect() { - // 自动侦测设置获取语言选择 $langSet = ''; if (isset($_GET[self::$langDetectVar])) { - // url中设置了语言变量 + // url 中设置了语言变量 $langSet = strtolower($_GET[self::$langDetectVar]); } elseif (isset($_COOKIE[self::$langCookieVar])) { - // Cookie中设置了语言变量 + // Cookie 中设置了语言变量 $langSet = strtolower($_COOKIE[self::$langCookieVar]); - } elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { + } elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) && preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches)) { // 自动侦测浏览器语言 - preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches); - $langSet = strtolower($matches[1]); + $langSet = strtolower($matches[1]); $acceptLangs = Config::get('header_accept_lang'); + if (isset($acceptLangs[$langSet])) { $langSet = $acceptLangs[$langSet]; } elseif (isset(self::$acceptLanguage[$langSet])) { $langSet = self::$acceptLanguage[$langSet]; } } + + if (preg_match('/^([a-z\d\-]+)/i', $langSet, $matches)) { + $langSet = strtolower($matches[1]); + } else { + $langSet = self::$range; + } + + // 合法的语言 if (empty(self::$allowLangList) || in_array($langSet, self::$allowLangList)) { - // 合法的语言 self::$range = $langSet ?: self::$range; } + return self::$range; } /** * 设置语言自动侦测的变量 - * @param string $var 变量名称 + * @access public + * @param string $var 变量名称 * @return void */ public static function setLangDetectVar($var) @@ -192,8 +236,9 @@ public static function setLangDetectVar($var) } /** - * 设置语言的cookie保存变量 - * @param string $var 变量名称 + * 设置语言的 cookie 保存变量 + * @access public + * @param string $var 变量名称 * @return void */ public static function setLangCookieVar($var) @@ -202,8 +247,9 @@ public static function setLangCookieVar($var) } /** - * 设置语言的cookie的过期时间 - * @param string $expire 过期时间 + * 设置语言的 cookie 的过期时间 + * @access public + * @param string $expire 过期时间 * @return void */ public static function setLangCookieExpire($expire) @@ -213,7 +259,8 @@ public static function setLangCookieExpire($expire) /** * 设置允许的语言列表 - * @param array $list 语言列表 + * @access public + * @param array $list 语言列表 * @return void */ public static function setAllowLangList($list) diff --git a/library/think/Loader.php b/library/think/Loader.php index 54885ddccc..e9a97ac5e4 100644 --- a/library/think/Loader.php +++ b/library/think/Loader.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -15,26 +15,57 @@ class Loader { + /** + * @var array 实例数组 + */ protected static $instance = []; - // 类名映射 - protected static $map = []; - // 命名空间别名 + /** + * @var array 类名映射 + */ + protected static $classMap = []; + + /** + * @var array 命名空间别名 + */ protected static $namespaceAlias = []; - // PSR-4 + /** + * @var array PSR-4 命名空间前缀长度映射 + */ private static $prefixLengthsPsr4 = []; - private static $prefixDirsPsr4 = []; - private static $fallbackDirsPsr4 = []; - // PSR-0 - private static $prefixesPsr0 = []; + /** + * @var array PSR-4 的加载目录 + */ + private static $prefixDirsPsr4 = []; + + /** + * @var array PSR-4 加载失败的回退目录 + */ + private static $fallbackDirsPsr4 = []; + + /** + * @var array PSR-0 命名空间前缀映射 + */ + private static $prefixesPsr0 = []; + + /** + * @var array PSR-0 加载失败的回退目录 + */ private static $fallbackDirsPsr0 = []; - // 自动加载的文件 - private static $autoloadFiles = []; + /** + * @var array 需要加载的文件 + */ + private static $files = []; - // 自动加载 + /** + * 自动加载 + * @access public + * @param string $class 类名 + * @return bool + */ public static function autoload($class) { // 检测命名空间别名 @@ -49,33 +80,33 @@ public static function autoload($class) } if ($file = self::findFile($class)) { - - // Win环境严格区分大小写 - if (IS_WIN && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) { - return false; + // 非 Win 环境不严格区分大小写 + if (!IS_WIN || pathinfo($file, PATHINFO_FILENAME) == pathinfo(realpath($file), PATHINFO_FILENAME)) { + __include_file($file); + return true; } - - __include_file($file); - return true; } + + return false; } /** * 查找文件 - * @param $class - * @return bool + * @access private + * @param string $class 类名 + * @return bool|string */ 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 $logicalPathPsr4 = strtr($class, '\\', DS) . EXT; - $first = $class[0]; + if (isset(self::$prefixLengthsPsr4[$first])) { foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) { if (0 === strpos($class, $prefix)) { @@ -97,7 +128,7 @@ private static function findFile($class) // 查找 PSR-0 if (false !== $pos = strrpos($class, '\\')) { - // namespaced class name + // namespace class name $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) . strtr(substr($logicalPathPsr4, $pos + 1), '_', DS); } else { @@ -124,20 +155,33 @@ private static function findFile($class) } } - 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 = '') { if (is_array($class)) { - self::$map = array_merge(self::$map, $class); + self::$classMap = array_merge(self::$classMap, $class); } 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 = '') { if (is_array($namespace)) { @@ -149,84 +193,77 @@ public static function addNamespace($namespace, $path = '') } } - // 添加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) { if (!$prefix) { - if ($prepend) { - self::$fallbackDirsPsr0 = array_merge( - (array) $paths, - self::$fallbackDirsPsr0 - ); + self::$fallbackDirsPsr0 = $prepend ? + array_merge((array) $paths, self::$fallbackDirsPsr0) : + array_merge(self::$fallbackDirsPsr0, (array) $paths); + } else { + $first = $prefix[0]; + + if (!isset(self::$prefixesPsr0[$first][$prefix])) { + self::$prefixesPsr0[$first][$prefix] = (array) $paths; } else { - self::$fallbackDirsPsr0 = array_merge( - self::$fallbackDirsPsr0, - (array) $paths - ); + self::$prefixesPsr0[$first][$prefix] = $prepend ? + array_merge((array) $paths, self::$prefixesPsr0[$first][$prefix]) : + array_merge(self::$prefixesPsr0[$first][$prefix], (array) $paths); } - - return; - } - - $first = $prefix[0]; - if (!isset(self::$prefixesPsr0[$first][$prefix])) { - self::$prefixesPsr0[$first][$prefix] = (array) $paths; - - return; - } - if ($prepend) { - self::$prefixesPsr0[$first][$prefix] = array_merge( - (array) $paths, - self::$prefixesPsr0[$first][$prefix] - ); - } else { - self::$prefixesPsr0[$first][$prefix] = array_merge( - self::$prefixesPsr0[$first][$prefix], - (array) $paths - ); } } - // 添加Psr4空间 + /** + * 添加 PSR-4 空间 + * @access private + * @param array|string $prefix 空间前缀 + * @param string $paths 路径 + * @param bool $prepend 预先设置的优先级更高 + * @return void + */ private static function addPsr4($prefix, $paths, $prepend = false) { if (!$prefix) { // Register directories for the root namespace. - if ($prepend) { - self::$fallbackDirsPsr4 = array_merge( - (array) $paths, - self::$fallbackDirsPsr4 - ); - } else { - self::$fallbackDirsPsr4 = array_merge( - self::$fallbackDirsPsr4, - (array) $paths - ); - } + self::$fallbackDirsPsr4 = $prepend ? + array_merge((array) $paths, self::$fallbackDirsPsr4) : + array_merge(self::$fallbackDirsPsr4, (array) $paths); + } elseif (!isset(self::$prefixDirsPsr4[$prefix])) { // Register directories for a new namespace. $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { - throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + throw new \InvalidArgumentException( + "A non-empty PSR-4 prefix must end with a namespace separator." + ); } + self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - self::$prefixDirsPsr4[$prefix] = (array) $paths; - } elseif ($prepend) { - // Prepend directories for an already registered namespace. - self::$prefixDirsPsr4[$prefix] = array_merge( - (array) $paths, - self::$prefixDirsPsr4[$prefix] - ); + self::$prefixDirsPsr4[$prefix] = (array) $paths; + } 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. - self::$prefixDirsPsr4[$prefix] = array_merge( - self::$prefixDirsPsr4[$prefix], - (array) $paths - ); + array_merge(self::$prefixDirsPsr4[$prefix], (array) $paths); } } - // 注册命名空间别名 + /** + * 注册命名空间别名 + * @access public + * @param array|string $namespace 命名空间 + * @param string $original 源文件 + * @return void + */ public static function addNamespaceAlias($namespace, $original = '') { if (is_array($namespace)) { @@ -236,32 +273,58 @@ public static function addNamespaceAlias($namespace, $original = '') } } - // 注册自动加载机制 - 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); + + // 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([ - 'think' => LIB_PATH . 'think' . DS, + 'think' => LIB_PATH . 'think' . DS, 'behavior' => LIB_PATH . 'behavior' . DS, - 'traits' => LIB_PATH . 'traits' . DS, + 'traits' => LIB_PATH . 'traits' . DS, ]); + // 加载类库映射文件 if (is_file(RUNTIME_PATH . 'classmap' . EXT)) { self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT)); } - // Composer自动加载支持 - if (is_dir(VENDOR_PATH . 'composer')) { - self::registerComposerLoader(); - } + self::loadComposerAutoloadFiles(); - // 自动加载extend目录 + // 自动加载 extend 目录 self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS); } - // 注册composer自动加载 + /** + * 注册 composer 自动加载 + * @access private + * @return void + */ private static function registerComposerLoader() { if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) { @@ -286,28 +349,36 @@ private static function registerComposerLoader() } if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) { - $includeFiles = require VENDOR_PATH . 'composer/autoload_files.php'; - foreach ($includeFiles as $fileIdentifier => $file) { - if (empty(self::$autoloadFiles[$fileIdentifier])) { - __require_file($file); - self::$autoloadFiles[$fileIdentifier] = true; - } + self::$files = require VENDOR_PATH . 'composer/autoload_files.php'; + } + } + + // 加载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 本函数有缓存功能 - * @param string $class 类库命名空间字符串 - * @param string $baseUrl 起始路径 - * @param string $ext 导入的文件扩展名 - * @return boolean + * 导入所需的类库 同 Java 的 Import 本函数有缓存功能 + * @access public + * @param string $class 类库命名空间字符串 + * @param string $baseUrl 起始路径 + * @param string $ext 导入的文件扩展名 + * @return bool */ public static function import($class, $baseUrl = '', $ext = EXT) { static $_file = []; - $key = $class . $baseUrl; - $class = str_replace(['.', '#'], [DS, '.'], $class); + $key = $class . $baseUrl; + $class = str_replace(['.', '#'], [DS, '.'], $class); + if (isset($_file[$key])) { return true; } @@ -319,7 +390,7 @@ public static function import($class, $baseUrl = '', $ext = EXT) // 注册的命名空间 $baseUrl = self::$prefixDirsPsr4[$name . '\\']; } elseif ('@' == $name) { - //加载当前模块应用类库 + // 加载当前模块应用类库 $baseUrl = App::$modulePath; } elseif (is_dir(EXTEND_PATH . $name)) { $baseUrl = EXTEND_PATH . $name . DS; @@ -330,11 +401,11 @@ public static function import($class, $baseUrl = '', $ext = EXT) } elseif (substr($baseUrl, -1) != DS) { $baseUrl .= DS; } - // 如果类存在 则导入类库文件 + + // 如果类存在则导入类库文件 if (is_array($baseUrl)) { foreach ($baseUrl as $path) { - $filename = $path . DS . $class . $ext; - if (is_file($filename)) { + if (is_file($filename = $path . DS . $class . $ext)) { break; } } @@ -342,137 +413,154 @@ public static function import($class, $baseUrl = '', $ext = EXT) $filename = $baseUrl . $class . $ext; } - if (!empty($filename) && is_file($filename)) { - // 开启调试模式Win环境严格区分大小写 - if (IS_WIN && pathinfo($filename, PATHINFO_FILENAME) != pathinfo(realpath($filename), PATHINFO_FILENAME)) { - return false; - } + if (!empty($filename) && + is_file($filename) && + (!IS_WIN || pathinfo($filename, PATHINFO_FILENAME) == pathinfo(realpath($filename), PATHINFO_FILENAME)) + ) { __include_file($filename); $_file[$key] = true; + return true; } + return false; } /** * 实例化(分层)模型 - * @param string $name Model名称 - * @param string $layer 业务层名称 - * @param bool $appendSuffix 是否添加类名后缀 - * @param string $common 公共模块名 + * @access public + * @param string $name Model名称 + * @param string $layer 业务层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @param string $common 公共模块名 * @return object * @throws ClassNotFoundException */ public static function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common') { - $guid = $name . $layer; - if (isset(self::$instance[$guid])) { - return self::$instance[$guid]; - } - if (false !== strpos($name, '\\')) { - $class = $name; - $module = Request::instance()->module(); - } else { - if (strpos($name, '/')) { - list($module, $name) = explode('/', $name, 2); - } else { - $module = Request::instance()->module(); - } - $class = self::parseClass($module, $layer, $name, $appendSuffix); + $uid = $name . $layer; + + if (isset(self::$instance[$uid])) { + return self::$instance[$uid]; } + + list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix); + if (class_exists($class)) { $model = new $class(); } else { $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class); + if (class_exists($class)) { $model = new $class(); } else { throw new ClassNotFoundException('class not exists:' . $class, $class); } } - self::$instance[$guid] = $model; - return $model; + + return self::$instance[$uid] = $model; } /** * 实例化(分层)控制器 格式:[模块名/]控制器名 - * @param string $name 资源地址 - * @param string $layer 控制层名称 - * @param bool $appendSuffix 是否添加类名后缀 - * @param string $empty 空控制器名称 + * @access public + * @param string $name 资源地址 + * @param string $layer 控制层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @param string $empty 空控制器名称 * @return object * @throws ClassNotFoundException */ public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '') { - if (false !== strpos($name, '\\')) { - $class = $name; - $module = Request::instance()->module(); - } else { - if (strpos($name, '/')) { - list($module, $name) = explode('/', $name); - } else { - $module = Request::instance()->module(); - } - $class = self::parseClass($module, $layer, $name, $appendSuffix); - } + list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix); + if (class_exists($class)) { return App::invokeClass($class); - } elseif ($empty && class_exists($emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix))) { - return new $emptyClass(Request::instance()); - } 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); } /** * 实例化验证类 格式:[模块名/]验证器名 - * @param string $name 资源地址 - * @param string $layer 验证层名称 - * @param bool $appendSuffix 是否添加类名后缀 - * @param string $common 公共模块名 + * @access public + * @param string $name 资源地址 + * @param string $layer 验证层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @param string $common 公共模块名 * @return object|false * @throws ClassNotFoundException */ public static function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common') { $name = $name ?: Config::get('default_validate'); + if (empty($name)) { return new Validate; } - $guid = $name . $layer; - if (isset(self::$instance[$guid])) { - return self::$instance[$guid]; - } - if (false !== strpos($name, '\\')) { - $class = $name; - $module = Request::instance()->module(); - } else { - if (strpos($name, '/')) { - list($module, $name) = explode('/', $name); - } else { - $module = Request::instance()->module(); - } - $class = self::parseClass($module, $layer, $name, $appendSuffix); + + $uid = $name . $layer; + if (isset(self::$instance[$uid])) { + return self::$instance[$uid]; } + + list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix); + if (class_exists($class)) { $validate = new $class; } else { $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class); + if (class_exists($class)) { $validate = new $class; } else { throw new ClassNotFoundException('class not exists:' . $class, $class); } } - self::$instance[$guid] = $validate; - return $validate; + + return self::$instance[$uid] = $validate; + } + + /** + * 解析模块和类名 + * @access protected + * @param string $name 资源地址 + * @param string $layer 验证层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @return array + */ + protected static function getModuleAndClass($name, $layer, $appendSuffix) + { + if (false !== strpos($name, '\\')) { + $module = Request::instance()->module(); + $class = $name; + } else { + if (strpos($name, '/')) { + list($module, $name) = explode('/', $name, 2); + } else { + $module = Request::instance()->module(); + } + + $class = self::parseClass($module, $layer, $name, $appendSuffix); + } + + return [$module, $class]; } /** * 数据库初始化 并取得数据库类实例 - * @param mixed $config 数据库配置 - * @param bool|string $name 连接标识 true 强制重新连接 + * @access public + * @param mixed $config 数据库配置 + * @param bool|string $name 连接标识 true 强制重新连接 * @return \think\db\Connection */ public static function db($config = [], $name = false) @@ -482,18 +570,20 @@ public static function db($config = [], $name = false) /** * 远程调用模块的操作方法 参数格式 [模块/控制器/]操作 - * @param string $url 调用地址 - * @param string|array $vars 调用参数 支持字符串和数组 - * @param string $layer 要调用的控制层名称 - * @param bool $appendSuffix 是否添加类名后缀 + * @access public + * @param string $url 调用地址 + * @param string|array $vars 调用参数 支持字符串和数组 + * @param string $layer 要调用的控制层名称 + * @param bool $appendSuffix 是否添加类名后缀 * @return mixed */ public static function action($url, $vars = [], $layer = 'controller', $appendSuffix = false) { - $info = pathinfo($url); + $info = pathinfo($url); $action = $info['basename']; $module = '.' != $info['dirname'] ? $info['dirname'] : Request::instance()->controller(); - $class = self::controller($module, $layer, $appendSuffix); + $class = self::controller($module, $layer, $appendSuffix); + if ($class) { if (is_scalar($vars)) { if (strpos($vars, '=')) { @@ -502,16 +592,20 @@ public static function action($url, $vars = [], $layer = 'controller', $appendSu $vars = [$vars]; } } + return App::invokeMethod([$class, $action . Config::get('action_suffix')], $vars); } + + return false; } /** * 字符串命名风格转换 - * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格 - * @param string $name 字符串 - * @param integer $type 转换类型 - * @param bool $ucfirst 首字母是否大写(驼峰规则) + * type 0 将 Java 风格转换为 C 的风格 1 将 C 风格转换为 Java 的风格 + * @access public + * @param string $name 字符串 + * @param integer $type 转换类型 + * @param bool $ucfirst 首字母是否大写(驼峰规则) * @return string */ public static function parseName($name, $type = 0, $ucfirst = true) @@ -520,31 +614,38 @@ public static function parseName($name, $type = 0, $ucfirst = true) $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) { return strtoupper($match[1]); }, $name); + return $ucfirst ? ucfirst($name) : lcfirst($name); - } else { - return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); } + + return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); } /** * 解析应用类的类名 - * @param string $module 模块名 - * @param string $layer 层名 controller model ... - * @param string $name 类名 - * @param bool $appendSuffix + * @access public + * @param string $module 模块名 + * @param string $layer 层名 controller model ... + * @param string $name 类名 + * @param bool $appendSuffix 是否添加类名后缀 * @return string */ public static function parseClass($module, $layer, $name, $appendSuffix = false) { - $name = str_replace(['/', '.'], '\\', $name); - $array = explode('\\', $name); - $class = self::parseName(array_pop($array), 1) . (App::$suffix || $appendSuffix ? ucfirst($layer) : ''); - $path = $array ? implode('\\', $array) . '\\' : ''; - return App::$namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $path . $class; + + $array = explode('\\', str_replace(['/', '.'], '\\', $name)); + $class = self::parseName(array_pop($array), 1); + $class = $class . (App::$suffix || $appendSuffix ? ucfirst($layer) : ''); + $path = $array ? implode('\\', $array) . '\\' : ''; + + return App::$namespace . '\\' . + ($module ? $module . '\\' : '') . + $layer . '\\' . $path . $class; } /** * 初始化类的实例 + * @access public * @return void */ public static function clearInstance() @@ -553,10 +654,11 @@ public static function clearInstance() } } +// 作用范围隔离 + /** - * 作用范围隔离 - * - * @param $file + * include + * @param string $file 文件路径 * @return mixed */ function __include_file($file) @@ -564,6 +666,11 @@ function __include_file($file) return include $file; } +/** + * require + * @param string $file 文件路径 + * @return mixed + */ function __require_file($file) { return require $file; diff --git a/library/think/Log.php b/library/think/Log.php index a20ab2629c..d711b9445f 100644 --- a/library/think/Log.php +++ b/library/think/Log.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -17,58 +17,77 @@ * Class Log * @package think * - * @method void log($msg) static - * @method void error($msg) static - * @method void info($msg) static - * @method void sql($msg) static - * @method void notice($msg) static - * @method void alert($msg) static + * @method void log($msg) static 记录一般日志 + * @method void error($msg) static 记录错误日志 + * @method void info($msg) static 记录一般信息日志 + * @method void sql($msg) static 记录 SQL 查询日志 + * @method void notice($msg) static 记录提示日志 + * @method void alert($msg) static 记录报警日志 */ class Log { - const LOG = 'log'; - const ERROR = 'error'; - const INFO = 'info'; - const SQL = 'sql'; + const LOG = 'log'; + const ERROR = 'error'; + const INFO = 'info'; + const SQL = 'sql'; const NOTICE = 'notice'; - const ALERT = 'alert'; - const DEBUG = 'debug'; + const ALERT = 'alert'; + const DEBUG = 'debug'; - // 日志信息 + /** + * @var array 日志信息 + */ protected static $log = []; - // 配置参数 + + /** + * @var array 配置参数 + */ protected static $config = []; - // 日志类型 + + /** + * @var array 日志类型 + */ protected static $type = ['log', 'error', 'info', 'sql', 'notice', 'alert', 'debug']; - // 日志写入驱动 + + /** + * @var log\driver\File|log\driver\Test|log\driver\Socket 日志写入驱动 + */ protected static $driver; - // 当前日志授权key + /** + * @var string 当前日志授权 key + */ protected static $key; /** * 日志初始化 - * @param array $config + * @access public + * @param array $config 配置参数 + * @return void */ public static function init($config = []) { - $type = isset($config['type']) ? $config['type'] : 'File'; - $class = false !== strpos($type, '\\') ? $type : '\\think\\log\\driver\\' . ucwords($type); + $type = isset($config['type']) ? $config['type'] : 'File'; + $class = false !== strpos($type, '\\') ? $type : '\\think\\log\\driver\\' . ucwords($type); + self::$config = $config; unset($config['type']); + if (class_exists($class)) { self::$driver = new $class($config); } else { throw new ClassNotFoundException('class not exists:' . $class, $class); } + // 记录初始化信息 App::$debug && Log::record('[ LOG ] INIT ' . $type, 'info'); } /** * 获取日志信息 - * @param string $type 信息类型 - * @return array + * @access public + * @param string $type 信息类型 + * @return array|string */ public static function getLog($type = '') { @@ -77,21 +96,22 @@ public static function getLog($type = '') /** * 记录调试信息 - * @param mixed $msg 调试信息 - * @param string $type 信息类型 + * @access public + * @param mixed $msg 调试信息 + * @param string $type 信息类型 * @return void */ public static function record($msg, $type = 'log') { self::$log[$type][] = $msg; - if (IS_CLI) { - // 命令行下面日志写入改进 - self::save(); - } + + // 命令行下面日志写入改进 + IS_CLI && self::save(); } /** * 清空日志信息 + * @access public * @return void */ public static function clear() @@ -100,8 +120,9 @@ public static function clear() } /** - * 当前日志记录的授权key - * @param string $key 授权key + * 设置当前日志记录的授权 key + * @access public + * @param string $key 授权 key * @return void */ public static function key($key) @@ -111,102 +132,105 @@ public static function key($key) /** * 检查日志写入权限 - * @param array $config 当前日志配置参数 + * @access public + * @param array $config 当前日志配置参数 * @return bool */ public static function check($config) { - if (self::$key && !empty($config['allow_key']) && !in_array(self::$key, $config['allow_key'])) { - return false; - } - return true; + return !self::$key || empty($config['allow_key']) || in_array(self::$key, $config['allow_key']); } /** * 保存调试信息 + * @access public * @return bool */ public static function save() { - if (!empty(self::$log)) { - if (is_null(self::$driver)) { - self::init(Config::get('log')); - } + // 没有需要保存的记录则直接返回 + if (empty(self::$log)) { + return true; + } - if (!self::check(self::$config)) { - // 检测日志写入权限 - return false; - } + is_null(self::$driver) && self::init(Config::get('log')); - if (empty(self::$config['level'])) { - // 获取全部日志 - $log = self::$log; - if (!App::$debug && isset($log['debug'])) { - unset($log['debug']); - } - } else { - // 记录允许级别 - $log = []; - foreach (self::$config['level'] as $level) { - if (isset(self::$log[$level])) { - $log[$level] = self::$log[$level]; - } + // 检测日志写入权限 + if (!self::check(self::$config)) { + return false; + } + + if (empty(self::$config['level'])) { + // 获取全部日志 + $log = self::$log; + if (!App::$debug && isset($log['debug'])) { + unset($log['debug']); + } + } else { + // 记录允许级别 + $log = []; + foreach (self::$config['level'] as $level) { + if (isset(self::$log[$level])) { + $log[$level] = self::$log[$level]; } } + } - $result = self::$driver->save($log); - if ($result) { - self::$log = []; - } - Hook::listen('log_write_done', $log); - return $result; + if ($result = self::$driver->save($log, true)) { + self::$log = []; } - return true; + + Hook::listen('log_write_done', $log); + + return $result; } /** * 实时写入日志信息 并支持行为 - * @param mixed $msg 调试信息 - * @param string $type 信息类型 - * @param bool $force 是否强制写入 + * @access public + * @param mixed $msg 调试信息 + * @param string $type 信息类型 + * @param bool $force 是否强制写入 * @return bool */ public static function write($msg, $type = 'log', $force = false) { $log = self::$log; - // 封装日志信息 - if (true === $force || empty(self::$config['level'])) { - $log[$type][] = $msg; - } elseif (in_array($type, self::$config['level'])) { - $log[$type][] = $msg; - } else { + + // 如果不是强制写入,而且信息类型不在可记录的类别中则直接返回 false 不做记录 + if (true !== $force && !empty(self::$config['level']) && !in_array($type, self::$config['level'])) { return false; } - // 监听log_write + // 封装日志信息 + $log[$type][] = $msg; + + // 监听 log_write 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) { + if ($result = self::$driver->save($log, false)) { self::$log = []; } + return $result; } /** - * 静态调用 - * @param $method - * @param $args - * @return mixed + * 静态方法调用 + * @access public + * @param string $method 调用方法 + * @param mixed $args 参数 + * @return void */ public static function __callStatic($method, $args) { if (in_array($method, self::$type)) { array_push($args, $method); - return call_user_func_array('\\think\\Log::record', $args); + + call_user_func_array('\\think\\Log::record', $args); } } diff --git a/library/think/Model.php b/library/think/Model.php index 6725c4c5b7..906af03a3d 100644 --- a/library/think/Model.php +++ b/library/think/Model.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -57,6 +57,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess protected $pk; // 数据表字段信息 留空则自动获取 protected $field = []; + // 数据排除字段 + protected $except = []; + // 数据废弃字段 + protected $disuse = []; // 只读字段 protected $readonly = []; // 显示属性 @@ -90,6 +94,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess protected $type = []; // 是否为更新数据 protected $isUpdate = false; + // 是否使用Replace + protected $replace = false; + // 是否强制更新所有数据 + protected $force = false; // 更新条件 protected $updateWhere; // 验证失败是否抛出异常 @@ -110,6 +118,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess */ protected static $initialized = []; + /** + * 是否从主库读取(主从分布式有效) + * @var array + */ + protected static $readMaster; + /** * 构造方法 * @access public @@ -122,6 +136,16 @@ public function __construct($data = []) } else { $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; @@ -130,10 +154,10 @@ public function __construct($data = []) if (empty($this->name)) { // 当前模型名 - $name = str_replace('\\', '/', $this->class); + $name = str_replace('\\', '/', $this->class); $this->name = basename($name); if (Config::get('class_suffix')) { - $suffix = basename(dirname($name)); + $suffix = basename(dirname($name)); $this->name = substr($this->name, 0, -strlen($suffix)); } } @@ -155,6 +179,20 @@ public function __construct($data = []) $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 @@ -176,7 +214,11 @@ protected function buildQuery() $con = Db::connect($connection); // 设置当前模型 确保查询返回模型对象 $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)) { @@ -192,6 +234,19 @@ protected function buildQuery() 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 @@ -262,7 +317,6 @@ protected static function init() public function setParent($model) { $this->parent = $model; - return $this; } @@ -337,6 +391,18 @@ public function isAutoWriteTimestamp($auto) return $this; } + /** + * 更新是否强制写入数据 而不做比较 + * @access public + * @param bool $force + * @return $this + */ + public function force($force = true) + { + $this->force = $force; + return $this; + } + /** * 修改器 设置数据对象值 * @access public @@ -413,7 +479,7 @@ protected function autoWriteTimestamp($name) case 'datetime': case 'date': $format = !empty($param) ? $param : $this->dateFormat; - $value = $this->formatDateTime(time(), $format); + $value = $this->formatDateTime(time(), $format); break; case 'timestamp': case 'integer': @@ -491,8 +557,8 @@ protected function writeTransform($value, $type) break; case 'datetime': $format = !empty($param) ? $param : $this->dateFormat; - $value = is_numeric($value) ? $value : strtotime($value); - $value = $this->formatDateTime($value, $format); + $value = is_numeric($value) ? $value : strtotime($value); + $value = $this->formatDateTime($value, $format); break; case 'object': if (is_object($value)) { @@ -501,9 +567,10 @@ protected function writeTransform($value, $type) break; case 'array': $value = (array) $value; + // no break case 'json': $option = !empty($param) ? (int) $param : JSON_UNESCAPED_UNICODE; - $value = json_encode($value, $option); + $value = json_encode($value, $option); break; case 'serialize': $value = serialize($value); @@ -524,10 +591,10 @@ public function getAttr($name) { try { $notFound = false; - $value = $this->getData($name); + $value = $this->getData($name); } catch (InvalidArgumentException $e) { $notFound = true; - $value = null; + $value = null; } // 检测属性获取器 @@ -572,7 +639,7 @@ public function getAttr($name) */ 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; } else { // 首先获取关联数据 @@ -620,13 +687,13 @@ protected function readTransform($value, $type) case 'timestamp': if (!is_null($value)) { $format = !empty($param) ? $param : $this->dateFormat; - $value = $this->formatDateTime($value, $format); + $value = $this->formatDateTime($value, $format); } break; case 'datetime': if (!is_null($value)) { $format = !empty($param) ? $param : $this->dateFormat; - $value = $this->formatDateTime(strtotime($value), $format); + $value = $this->formatDateTime(strtotime($value), $format); } break; case 'json': @@ -639,7 +706,11 @@ protected function readTransform($value, $type) $value = empty($value) ? new \stdClass() : json_decode($value); break; case 'serialize': - $value = unserialize($value); + try { + $value = unserialize($value); + } catch (\Exception $e) { + $value = null; + } break; default: if (false !== strpos($type, '\\')) { @@ -692,7 +763,7 @@ public function appendRelationAttr($relation, $append) if (isset($this->data[$key])) { throw new Exception('bind attr has exists:' . $key); } else { - $this->data[$key] = $model->$attr; + $this->data[$key] = $model->getAttr($attr); } } } @@ -782,19 +853,19 @@ protected function subToArray($model, $visible, $hidden, $key) */ public function toArray() { - $item = []; + $item = []; $visible = []; - $hidden = []; + $hidden = []; $data = array_merge($this->data, $this->relation); // 过滤属性 if (!empty($this->visible)) { $array = $this->parseAttr($this->visible, $visible); - $data = array_intersect_key($data, array_flip($array)); + $data = array_intersect_key($data, array_flip($array)); } elseif (!empty($this->hidden)) { $array = $this->parseAttr($this->hidden, $hidden, false); - $data = array_diff_key($data, array_flip($array)); + $data = array_diff_key($data, array_flip($array)); } foreach ($data as $key => $val) { @@ -818,18 +889,18 @@ public function toArray() foreach ($this->append as $key => $name) { if (is_array($name)) { // 追加关联对象属性 - $relation = $this->getAttr($key); + $relation = $this->getAttr($key); $item[$key] = $relation->append($name)->toArray(); } elseif (strpos($name, '.')) { list($key, $attr) = explode('.', $name); // 追加关联对象属性 - $relation = $this->getAttr($key); + $relation = $this->getAttr($key); $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); + $value = $this->getRelationData($modelRelation); if (method_exists($modelRelation, 'getBindAttr')) { $bindAttr = $modelRelation->getBindAttr(); @@ -839,7 +910,7 @@ public function toArray() if (isset($this->data[$key])) { throw new Exception('bind attr has exists:' . $key); } else { - $item[$key] = $value ? $value->$attr : null; + $item[$key] = $value ? $value->getAttr($attr) : null; } } continue; @@ -889,7 +960,7 @@ public function toCollection($collection) if ('collection' == $this->resultSetType) { $collection = new ModelCollection($collection); } elseif (false !== strpos($this->resultSetType, '\\')) { - $class = $this->resultSetType; + $class = $this->resultSetType; $collection = new $class($collection); } } @@ -945,6 +1016,18 @@ protected function isPk($key) return false; } + /** + * 新增数据是否使用Replace + * @access public + * @param bool $replace + * @return $this + */ + public function replace($replace = true) + { + $this->replace = $replace; + return $this; + } + /** * 保存当前数据对象 * @access public @@ -957,22 +1040,24 @@ public function save($data = [], $where = [], $sequence = null) { if (is_string($data)) { $sequence = $data; - $data = []; + $data = []; } + // 数据自动验证 if (!empty($data)) { - // 数据自动验证 if (!$this->validateData($data)) { return false; } + // 数据对象赋值 foreach ($data as $key => $value) { $this->setAttr($key, $value, $data); } - if (!empty($where)) { - $this->isUpdate = true; - $this->updateWhere = $where; - } + } + + if (!empty($where)) { + $this->isUpdate = true; + $this->updateWhere = $where; } // 自动关联写入 @@ -1028,7 +1113,7 @@ public function save($data = [], $where = [], $sequence = null) return 0; } elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) { // 自动写入更新时间 - $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); + $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); $this->data[$this->updateTime] = $data[$this->updateTime]; } @@ -1043,12 +1128,17 @@ public function save($data = [], $where = [], $sequence = null) } } - if (is_string($pk) && isset($data[$pk])) { - if (!isset($where[$pk])) { - unset($where); - $where[$pk] = $data[$pk]; + $array = []; + + foreach ((array) $pk as $key) { + if (isset($data[$key])) { + $array[$key] = $data[$key]; + unset($data[$key]); } - unset($data[$pk]); + } + + if (!empty($array)) { + $where = $array; } // 检测字段 @@ -1090,16 +1180,17 @@ public function save($data = [], $where = [], $sequence = null) // 检测字段 $allowFields = $this->checkAllowField(array_merge($this->auto, $this->insert)); if (!empty($allowFields)) { - $result = $this->getQuery()->strict(false)->field($allowFields)->insert($this->data); + $result = $this->getQuery()->strict(false)->field($allowFields)->insert($this->data, $this->replace, false, $sequence); } else { - $result = $this->getQuery()->insert($this->data); + $result = $this->getQuery()->insert($this->data, $this->replace, false, $sequence); } // 获取自动增长主键 - if ($result && is_string($pk) && (!isset($this->data[$pk]) || '' == $this->data[$pk])) { - $insertId = $this->getQuery()->getLastInsID($sequence); - if ($insertId) { - $this->data[$pk] = $insertId; + if ($result && $insertId = $this->getQuery()->getLastInsID($sequence)) { + foreach ((array) $pk as $key) { + if (!isset($this->data[$key]) || '' == $this->data[$key]) { + $this->data[$key] = $insertId; + } } } @@ -1130,16 +1221,24 @@ protected function checkAllowField($auto = []) { if (true === $this->field) { $this->field = $this->getQuery()->getTableInfo('', 'fields'); - $field = $this->field; + $field = $this->field; } elseif (!empty($this->field)) { $field = array_merge($this->field, $auto); if ($this->autoWriteTimestamp) { array_push($field, $this->createTime, $this->updateTime); } + } elseif (!empty($this->except)) { + $fields = $this->getQuery()->getTableInfo('', 'fields'); + $field = array_diff($fields, (array) $this->except); + $this->field = $field; } else { $field = []; } + if ($this->disuse) { + // 废弃字段 + $field = array_diff($field, (array) $this->disuse); + } return $field; } @@ -1165,12 +1264,16 @@ protected function autoRelationUpdate($relation) */ public function getChangedData() { - $data = array_udiff_assoc($this->data, $this->origin, function ($a, $b) { - if ((empty($a) || empty($b)) && $a !== $b) { - return 1; - } - return is_object($a) || $a != $b ? 1 : 0; - }); + if ($this->force) { + $data = $this->data; + } else { + $data = array_udiff_assoc($this->data, $this->origin, function ($a, $b) { + if ((empty($a) || empty($b)) && $a !== $b) { + return 1; + } + return is_object($a) || $a != $b ? 1 : 0; + }); + } if (!empty($this->readonly)) { // 只读字段不允许更新 @@ -1218,7 +1321,7 @@ public function setInc($field, $step = 1, $lazyTime = 0) public function setDec($field, $step = 1, $lazyTime = 0) { // 更新条件 - $where = $this->getWhere(); + $where = $this->getWhere(); $result = $this->getQuery()->where($where)->setDec($field, $step, $lazyTime); if (true !== $result) { $this->data[$field] -= $step; @@ -1268,7 +1371,7 @@ public function saveAll($dataSet, $replace = true) } $result = []; - $db = $this->getQuery(); + $db = $this->getQuery(); $db->startTrans(); try { $pk = $this->getPk(); @@ -1276,14 +1379,14 @@ public function saveAll($dataSet, $replace = true) $auto = true; } 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); } else { $result[$key] = self::create($data, $this->field); } } $db->commit(); - return $result; + return $this->toCollection($result); } catch (\Exception $e) { $db->rollback(); throw $e; @@ -1293,7 +1396,7 @@ public function saveAll($dataSet, $replace = true) /** * 设置允许写入的字段 * @access public - * @param mixed $field 允许写入的字段 如果为true只允许写入数据表字段 + * @param string|array $field 允许写入的字段 如果为true只允许写入数据表字段 * @return $this */ public function allowField($field) @@ -1305,6 +1408,21 @@ public function allowField($field) 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 @@ -1380,7 +1498,7 @@ public function delete() // 关联删除 if (!empty($this->relationWrite)) { foreach ($this->relationWrite as $key => $name) { - $name = is_numeric($key) ? $name : $key; + $name = is_numeric($key) ? $name : $key; $model = $this->getAttr($name); if ($model instanceof Model) { $model->delete(); @@ -1420,7 +1538,7 @@ public function validate($rule = true, $msg = [], $batch = false) if (is_array($rule)) { $this->validate = [ 'rule' => $rule, - 'msg' => $msg, + 'msg' => $msg, ]; } else { $this->validate = true === $rule ? $this->name : $rule; @@ -1584,7 +1702,7 @@ public static function get($data, $with = [], $cache = false) if (true === $with || is_int($with)) { $cache = $with; - $with = []; + $with = []; } $query = static::parseQuery($data, $with, $cache); return $query->find($data); @@ -1603,7 +1721,7 @@ public static function all($data = null, $with = [], $cache = false) { if (true === $with || is_int($with)) { $cache = $with; - $with = []; + $with = []; } $query = static::parseQuery($data, $with, $cache); return $query->select($data); @@ -1622,13 +1740,13 @@ protected static function parseQuery(&$data, $with, $cache) $result = self::with($with)->cache($cache); if (is_array($data) && key($data) !== 0) { $result = $result->where($data); - $data = null; + $data = null; } elseif ($data instanceof \Closure) { call_user_func_array($data, [ & $result]); $data = null; } elseif ($data instanceof Query) { $result = $data->with($with)->cache($cache); - $data = null; + $data = null; } return $result; } @@ -1653,7 +1771,7 @@ public static function destroy($data) $data = null; } $resultSet = $query->select($data); - $count = 0; + $count = 0; if ($resultSet) { foreach ($resultSet as $data) { $result = $data->delete(); @@ -1672,8 +1790,8 @@ public static function destroy($data) */ public static function scope($name) { - $model = new static(); - $query = $model->db(); + $model = new static(); + $query = $model->db(); $params = func_get_args(); array_shift($params); array_unshift($params, $query); @@ -1726,13 +1844,14 @@ public static function has($relation, $operator = '>=', $count = 1, $id = '*') /** * 根据关联条件查询当前模型 * @access public - * @param string $relation 关联方法名 - * @param mixed $where 查询条件(数组或者闭包) + * @param string $relation 关联方法名 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @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); } /** @@ -1766,19 +1885,19 @@ public function relationQuery($relations) foreach ($relations as $key => $relation) { $subRelation = ''; - $closure = null; + $closure = null; if ($relation instanceof \Closure) { // 支持闭包查询过滤关联条件 - $closure = $relation; + $closure = $relation; $relation = $key; } if (is_array($relation)) { $subRelation = $relation; - $relation = $key; + $relation = $key; } elseif (strpos($relation, '.')) { list($relation, $subRelation) = explode('.', $relation, 2); } - $method = Loader::parseName($relation, 1, false); + $method = Loader::parseName($relation, 1, false); $this->data[$relation] = $this->$method()->getRelation($subRelation, $closure); } return $this; @@ -1796,14 +1915,14 @@ public function eagerlyResultSet(&$resultSet, $relation) $relations = is_string($relation) ? explode(',', $relation) : $relation; foreach ($relations as $key => $relation) { $subRelation = ''; - $closure = false; + $closure = false; if ($relation instanceof \Closure) { - $closure = $relation; + $closure = $relation; $relation = $key; } if (is_array($relation)) { $subRelation = $relation; - $relation = $key; + $relation = $key; } elseif (strpos($relation, '.')) { list($relation, $subRelation) = explode('.', $relation, 2); } @@ -1825,14 +1944,14 @@ public function eagerlyResult(&$result, $relation) foreach ($relations as $key => $relation) { $subRelation = ''; - $closure = false; + $closure = false; if ($relation instanceof \Closure) { - $closure = $relation; + $closure = $relation; $relation = $key; } if (is_array($relation)) { $subRelation = $relation; - $relation = $key; + $relation = $key; } elseif (strpos($relation, '.')) { list($relation, $subRelation) = explode('.', $relation, 2); } @@ -1855,14 +1974,14 @@ public function relationCount(&$result, $relation) foreach ($relations as $key => $relation) { $closure = false; if ($relation instanceof \Closure) { - $closure = $relation; + $closure = $relation; $relation = $key; } elseif (is_string($key)) { - $name = $relation; + $name = $relation; $relation = $key; } $relation = Loader::parseName($relation, 1, false); - $count = $this->$relation()->relationCount($result, $closure); + $count = $this->$relation()->relationCount($result, $closure); if (!isset($name)) { $name = Loader::parseName($relation) . '_count'; } @@ -1897,8 +2016,8 @@ protected function getForeignKey($name) public function hasOne($model, $foreignKey = '', $localKey = '', $alias = [], $joinType = 'INNER') { // 记录当前关联信息 - $model = $this->parseModel($model); - $localKey = $localKey ?: $this->getPk(); + $model = $this->parseModel($model); + $localKey = $localKey ?: $this->getPk(); $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); return new HasOne($this, $model, $foreignKey, $localKey, $joinType); } @@ -1916,11 +2035,11 @@ public function hasOne($model, $foreignKey = '', $localKey = '', $alias = [], $j public function belongsTo($model, $foreignKey = '', $localKey = '', $alias = [], $joinType = 'INNER') { // 记录当前关联信息 - $model = $this->parseModel($model); + $model = $this->parseModel($model); $foreignKey = $foreignKey ?: $this->getForeignKey($model); - $localKey = $localKey ?: (new $model)->getPk(); - $trace = debug_backtrace(false, 2); - $relation = Loader::parseName($trace[1]['function']); + $localKey = $localKey ?: (new $model)->getPk(); + $trace = debug_backtrace(false, 2); + $relation = Loader::parseName($trace[1]['function']); return new BelongsTo($this, $model, $foreignKey, $localKey, $joinType, $relation); } @@ -1935,8 +2054,8 @@ public function belongsTo($model, $foreignKey = '', $localKey = '', $alias = [], public function hasMany($model, $foreignKey = '', $localKey = '') { // 记录当前关联信息 - $model = $this->parseModel($model); - $localKey = $localKey ?: $this->getPk(); + $model = $this->parseModel($model); + $localKey = $localKey ?: $this->getPk(); $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); return new HasMany($this, $model, $foreignKey, $localKey); } @@ -1954,9 +2073,9 @@ public function hasMany($model, $foreignKey = '', $localKey = '') public function hasManyThrough($model, $through, $foreignKey = '', $throughKey = '', $localKey = '') { // 记录当前关联信息 - $model = $this->parseModel($model); - $through = $this->parseModel($through); - $localKey = $localKey ?: $this->getPk(); + $model = $this->parseModel($model); + $through = $this->parseModel($through); + $localKey = $localKey ?: $this->getPk(); $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); $throughKey = $throughKey ?: $this->getForeignKey($through); return new HasManyThrough($this, $model, $through, $foreignKey, $throughKey, $localKey); @@ -1974,11 +2093,11 @@ public function hasManyThrough($model, $through, $foreignKey = '', $throughKey = public function belongsToMany($model, $table = '', $foreignKey = '', $localKey = '') { // 记录当前关联信息 - $model = $this->parseModel($model); - $name = Loader::parseName(basename(str_replace('\\', '/', $model))); - $table = $table ?: Loader::parseName($this->name) . '_' . $name; + $model = $this->parseModel($model); + $name = Loader::parseName(basename(str_replace('\\', '/', $model))); + $table = $table ?: Loader::parseName($this->name) . '_' . $name; $foreignKey = $foreignKey ?: $name . '_id'; - $localKey = $localKey ?: $this->getForeignKey($this->name); + $localKey = $localKey ?: $this->getForeignKey($this->name); return new BelongsToMany($this, $model, $table, $foreignKey, $localKey); } @@ -1998,11 +2117,11 @@ public function morphMany($model, $morph = null, $type = '') $trace = debug_backtrace(false, 2); $morph = Loader::parseName($trace[1]['function']); } - $type = $type ?: Loader::parseName($this->name); + $type = $type ?: get_class($this); if (is_array($morph)) { list($morphType, $foreignKey) = $morph; } else { - $morphType = $morph . '_type'; + $morphType = $morph . '_type'; $foreignKey = $morph . '_id'; } return new MorphMany($this, $model, $foreignKey, $morphType, $type); @@ -2024,11 +2143,11 @@ public function morphOne($model, $morph = null, $type = '') $trace = debug_backtrace(false, 2); $morph = Loader::parseName($trace[1]['function']); } - $type = $type ?: Loader::parseName($this->name); + $type = $type ?: get_class($this); if (is_array($morph)) { list($morphType, $foreignKey) = $morph; } else { - $morphType = $morph . '_type'; + $morphType = $morph . '_type'; $foreignKey = $morph . '_id'; } return new MorphOne($this, $model, $foreignKey, $morphType, $type); @@ -2043,7 +2162,7 @@ public function morphOne($model, $morph = null, $type = '') */ public function morphTo($morph = null, $alias = []) { - $trace = debug_backtrace(false, 2); + $trace = debug_backtrace(false, 2); $relation = Loader::parseName($trace[1]['function']); if (is_null($morph)) { @@ -2053,7 +2172,7 @@ public function morphTo($morph = null, $alias = []) if (is_array($morph)) { list($morphType, $foreignKey) = $morph; } else { - $morphType = $morph . '_type'; + $morphType = $morph . '_type'; $foreignKey = $morph . '_id'; } return new MorphTo($this, $morphType, $foreignKey, $alias, $relation); @@ -2062,7 +2181,6 @@ public function morphTo($morph = null, $alias = []) public function __call($method, $args) { $query = $this->db(true, false); - if (method_exists($this, 'scope' . $method)) { // 动态调用命名范围 $method = 'scope' . $method; @@ -2078,7 +2196,6 @@ public static function __callStatic($method, $args) { $model = new static(); $query = $model->db(); - if (method_exists($model, 'scope' . $method)) { // 动态调用命名范围 $method = 'scope' . $method; diff --git a/library/think/Paginator.php b/library/think/Paginator.php index 4f65840ed4..70df0db4f7 100644 --- a/library/think/Paginator.php +++ b/library/think/Paginator.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -44,8 +44,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** @var array 一些配置 */ protected $options = [ 'var_page' => 'page', - 'path' => '/', - 'query' => [], + 'path' => '/', + 'query' => [], 'fragment' => '', ]; @@ -58,7 +58,7 @@ public function __construct($items, $listRows, $currentPage = null, $total = nul $this->options['path'] = '/' != $this->options['path'] ? rtrim($this->options['path'], '/') : $this->options['path']; - $this->simple = $simple; + $this->simple = $simple; $this->listRows = $listRows; if (!$items instanceof Collection) { @@ -67,16 +67,16 @@ public function __construct($items, $listRows, $currentPage = null, $total = nul if ($simple) { $this->currentPage = $this->setCurrentPage($currentPage); - $this->hasMore = count($items) > ($this->listRows); + $this->hasMore = count($items) > ($this->listRows); if ($this->hasMore) { $this->nextItem = $items->slice($this->listRows, 1); } $items = $items->slice(0, $this->listRows); } else { - $this->total = $total; - $this->lastPage = (int) ceil($total / $listRows); + $this->total = $total; + $this->lastPage = (int) ceil($total / $listRows); $this->currentPage = $this->setCurrentPage($currentPage); - $this->hasMore = $this->currentPage < $this->lastPage; + $this->hasMore = $this->currentPage < $this->lastPage; } $this->items = $items; } @@ -118,17 +118,17 @@ protected function url($page) if (strpos($this->options['path'], '[PAGE]') === false) { $parameters = [$this->options['var_page'] => $page]; - $path = $this->options['path']; + $path = $this->options['path']; } else { $parameters = []; - $path = str_replace('[PAGE]', $page, $this->options['path']); + $path = str_replace('[PAGE]', $page, $this->options['path']); } if (count($this->options['query']) > 0) { $parameters = array_merge($this->options['query'], $parameters); } $url = $path; if (!empty($parameters)) { - $url .= '?' . urldecode(http_build_query($parameters, null, '&')); + $url .= '?' . http_build_query($parameters, null, '&'); } return $url . $this->buildFragment(); } @@ -367,19 +367,19 @@ public function toArray() { if ($this->simple) { return [ - 'per_page' => $this->listRows, + 'per_page' => $this->listRows, 'current_page' => $this->currentPage, - 'has_more' => $this->hasMore, - 'next_item' => $this->nextItem, - 'data' => $this->items->toArray(), + 'has_more' => $this->hasMore, + 'next_item' => $this->nextItem, + 'data' => $this->items->toArray(), ]; } else { return [ - 'total' => $this->total, - 'per_page' => $this->listRows, + 'total' => $this->total, + 'per_page' => $this->listRows, 'current_page' => $this->currentPage, - 'last_page' => $this->lastPage, - 'data' => $this->items->toArray(), + 'last_page' => $this->lastPage, + 'data' => $this->items->toArray(), ]; } @@ -395,7 +395,15 @@ public function jsonSerialize() 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; } } diff --git a/library/think/Process.php b/library/think/Process.php index 6f3faa315e..27c0282d0d 100644 --- a/library/think/Process.php +++ b/library/think/Process.php @@ -24,11 +24,11 @@ class Process const ERR = 'err'; const OUT = 'out'; - const STATUS_READY = 'ready'; - const STATUS_STARTED = 'started'; + const STATUS_READY = 'ready'; + const STATUS_STARTED = 'started'; const STATUS_TERMINATED = 'terminated'; - const STDIN = 0; + const STDIN = 0; const STDOUT = 1; const STDERR = 2; @@ -53,8 +53,8 @@ class Process private $enhanceWindowsCompatibility = true; private $enhanceSigchildCompatibility; private $process; - private $status = self::STATUS_READY; - private $incrementalOutputOffset = 0; + private $status = self::STATUS_READY; + private $incrementalOutputOffset = 0; private $incrementalErrorOutputOffset = 0; private $tty; private $pty; @@ -72,9 +72,9 @@ class Process * @var array */ public static $exitCodes = [ - 0 => 'OK', - 1 => 'General error', - 2 => 'Misuse of shell builtins', + 0 => 'OK', + 1 => 'General error', + 2 => 'Misuse of shell builtins', 126 => 'Invoked command cannot execute', 127 => 'Command not found', 128 => 'Invalid exit argument', @@ -130,7 +130,7 @@ public function __construct($commandline, $cwd = null, array $env = null, $input } $this->commandline = $commandline; - $this->cwd = $cwd; + $this->cwd = $cwd; if (null === $this->cwd && (defined('ZEND_THREAD_SAFE') || '\\' === DS)) { $this->cwd = getcwd(); @@ -141,13 +141,13 @@ public function __construct($commandline, $cwd = null, array $env = null, $input $this->input = $input; $this->setTimeout($timeout); - $this->useFileHandles = '\\' === DS; - $this->pty = false; - $this->enhanceWindowsCompatibility = true; + $this->useFileHandles = '\\' === DS; + $this->pty = false; + $this->enhanceWindowsCompatibility = true; $this->enhanceSigchildCompatibility = '\\' !== DS && $this->isSigchildEnabled(); - $this->options = array_replace([ + $this->options = array_replace([ 'suppress_errors' => true, - 'binary_pipes' => true, + 'binary_pipes' => true, ], $options); } @@ -211,8 +211,8 @@ public function start($callback = null) $this->resetProcessData(); $this->starttime = $this->lastOutputTime = microtime(true); - $this->callback = $this->buildCallback($callback); - $descriptors = $this->getDescriptors(); + $this->callback = $this->buildCallback($callback); + $descriptors = $this->getDescriptors(); $commandline = $this->commandline; @@ -279,7 +279,7 @@ public function wait($callback = null) do { $this->checkTimeout(); $running = '\\' === DS ? $this->isRunning() : $this->processPipes->areOpen(); - $close = '\\' !== DS || !$running; + $close = '\\' !== DS || !$running; $this->readPipes(true, $close); } while ($running); @@ -412,7 +412,7 @@ public function getIncrementalOutput() */ public function clearOutput() { - $this->stdout = ''; + $this->stdout = ''; $this->incrementalOutputOffset = 0; return $this; @@ -462,7 +462,7 @@ public function getIncrementalErrorOutput() */ public function clearErrorOutput() { - $this->stderr = ''; + $this->stderr = ''; $this->incrementalErrorOutputOffset = 0; return $this; @@ -995,7 +995,7 @@ private function getDescriptors() */ protected function buildCallback($callback) { - $out = self::OUT; + $out = self::OUT; $callback = function ($type, $data) use ($callback, $out) { if ($out == $type) { $this->addOutput($data); @@ -1112,7 +1112,7 @@ private function close() } $this->exitcode = -1 !== $exitcode ? $exitcode : (null !== $this->exitcode ? $this->exitcode : -1); - $this->status = self::STATUS_TERMINATED; + $this->status = self::STATUS_TERMINATED; if (-1 === $this->exitcode && null !== $this->fallbackExitcode) { $this->exitcode = $this->fallbackExitcode; @@ -1130,17 +1130,17 @@ private function close() */ private function resetProcessData() { - $this->starttime = null; - $this->callback = null; - $this->exitcode = null; - $this->fallbackExitcode = null; - $this->processInformation = null; - $this->stdout = null; - $this->stderr = null; - $this->process = null; - $this->latestSignal = null; - $this->status = self::STATUS_READY; - $this->incrementalOutputOffset = 0; + $this->starttime = null; + $this->callback = null; + $this->exitcode = null; + $this->fallbackExitcode = null; + $this->processInformation = null; + $this->stdout = null; + $this->stderr = null; + $this->process = null; + $this->latestSignal = null; + $this->status = self::STATUS_READY; + $this->incrementalOutputOffset = 0; $this->incrementalErrorOutputOffset = 0; } diff --git a/library/think/Request.php b/library/think/Request.php index 5f05b91606..ff836bd292 100644 --- a/library/think/Request.php +++ b/library/think/Request.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -77,34 +77,34 @@ class Request /** * @var array 请求参数 */ - protected $param = []; - protected $get = []; - protected $post = []; + protected $param = []; + protected $get = []; + protected $post = []; protected $request = []; - protected $route = []; + protected $route = []; protected $put; protected $session = []; - protected $file = []; - protected $cookie = []; - protected $server = []; - protected $header = []; + protected $file = []; + protected $cookie = []; + protected $server = []; + protected $header = []; /** * @var array 资源类型 */ protected $mimeType = [ - 'xml' => 'application/xml,text/xml,application/x-xml', - 'json' => 'application/json,text/x-json,application/jsonrequest,text/json', - 'js' => 'text/javascript,application/javascript,application/x-javascript', - 'css' => 'text/css', - 'rss' => 'application/rss+xml', - 'yaml' => 'application/x-yaml,text/yaml', - 'atom' => 'application/atom+xml', - 'pdf' => 'application/pdf', - 'text' => 'text/plain', + 'xml' => 'application/xml,text/xml,application/x-xml', + 'json' => 'application/json,text/x-json,application/jsonrequest,text/json', + 'js' => 'text/javascript,application/javascript,application/x-javascript', + 'css' => 'text/css', + 'rss' => 'application/rss+xml', + 'yaml' => 'application/x-yaml,text/yaml', + 'atom' => 'application/atom+xml', + 'pdf' => 'application/pdf', + 'text' => 'text/plain', 'image' => 'image/png,image/jpg,image/jpeg,image/pjpeg,image/gif,image/webp,image/*', - 'csv' => 'text/csv', - 'html' => 'text/html,application/xhtml+xml,*/*', + 'csv' => 'text/csv', + 'html' => 'text/html,application/xhtml+xml,*/*', ]; protected $content; @@ -121,6 +121,11 @@ class Request protected $cache; // 缓存是否检查 protected $isCheckCache; + /** + * 是否合并Param + * @var bool + */ + protected $mergeParam = false; /** * 构造函数 @@ -155,8 +160,8 @@ public function __call($method, $args) /** * Hook 方法注入 * @access public - * @param string|array $method 方法名 - * @param mixed $callback callable + * @param string|array $method 方法名 + * @param mixed $callback callable * @return void */ public static function hook($method, $callback = null) @@ -182,30 +187,42 @@ public static function instance($options = []) return self::$instance; } + /** + * 销毁当前请求对象 + * @access public + * @return void + */ + public static function destroy() + { + if (!is_null(self::$instance)) { + self::$instance = null; + } + } + /** * 创建一个URL请求 * @access public - * @param string $uri URL地址 - * @param string $method 请求类型 - * @param array $params 请求参数 - * @param array $cookie - * @param array $files - * @param array $server - * @param string $content + * @param string $uri URL地址 + * @param string $method 请求类型 + * @param array $params 请求参数 + * @param array $cookie + * @param array $files + * @param array $server + * @param string $content * @return \think\Request */ public static function create($uri, $method = 'GET', $params = [], $cookie = [], $files = [], $server = [], $content = null) { - $server['PATH_INFO'] = ''; + $server['PATH_INFO'] = ''; $server['REQUEST_METHOD'] = strtoupper($method); - $info = parse_url($uri); + $info = parse_url($uri); if (isset($info['host'])) { $server['SERVER_NAME'] = $info['host']; - $server['HTTP_HOST'] = $info['host']; + $server['HTTP_HOST'] = $info['host']; } if (isset($info['scheme'])) { if ('https' === $info['scheme']) { - $server['HTTPS'] = 'on'; + $server['HTTPS'] = 'on'; $server['SERVER_PORT'] = 443; } else { unset($server['HTTPS']); @@ -214,7 +231,7 @@ public static function create($uri, $method = 'GET', $params = [], $cookie = [], } if (isset($info['port'])) { $server['SERVER_PORT'] = $info['port']; - $server['HTTP_HOST'] = $server['HTTP_HOST'] . ':' . $info['port']; + $server['HTTP_HOST'] = $server['HTTP_HOST'] . ':' . $info['port']; } if (isset($info['user'])) { $server['PHP_AUTH_USER'] = $info['user']; @@ -225,16 +242,16 @@ public static function create($uri, $method = 'GET', $params = [], $cookie = [], if (!isset($info['path'])) { $info['path'] = '/'; } - $options = []; + $options = []; $options[strtolower($method)] = $params; - $queryString = ''; + $queryString = ''; if (isset($info['query'])) { parse_str(html_entity_decode($info['query']), $query); if (!empty($params)) { - $params = array_replace($query, $params); - $queryString = http_build_query($query, '', '&'); + $params = array_replace($query, $params); + $queryString = http_build_query($params, '', '&'); } else { - $params = $query; + $params = $query; $queryString = $info['query']; } } elseif (!empty($params)) { @@ -245,19 +262,19 @@ public static function create($uri, $method = 'GET', $params = [], $cookie = [], $options['get'] = isset($options['get']) ? array_merge($get, $options['get']) : $get; } - $server['REQUEST_URI'] = $info['path'] . ('' !== $queryString ? '?' . $queryString : ''); + $server['REQUEST_URI'] = $info['path'] . ('' !== $queryString ? '?' . $queryString : ''); $server['QUERY_STRING'] = $queryString; - $options['cookie'] = $cookie; - $options['param'] = $params; - $options['file'] = $files; - $options['server'] = $server; - $options['url'] = $server['REQUEST_URI']; - $options['baseUrl'] = $info['path']; - $options['pathinfo'] = '/' == $info['path'] ? '/' : ltrim($info['path'], '/'); - $options['method'] = $server['REQUEST_METHOD']; - $options['domain'] = isset($info['scheme']) ? $info['scheme'] . '://' . $server['HTTP_HOST'] : ''; - $options['content'] = $content; - self::$instance = new self($options); + $options['cookie'] = $cookie; + $options['param'] = $params; + $options['file'] = $files; + $options['server'] = $server; + $options['url'] = $server['REQUEST_URI']; + $options['baseUrl'] = $info['path']; + $options['pathinfo'] = '/' == $info['path'] ? '/' : ltrim($info['path'], '/'); + $options['method'] = $server['REQUEST_METHOD']; + $options['domain'] = isset($info['scheme']) ? $info['scheme'] . '://' . $server['HTTP_HOST'] : ''; + $options['content'] = $content; + self::$instance = new self($options); return self::$instance; } @@ -317,7 +334,7 @@ public function baseUrl($url = null) $this->baseUrl = $url; return $this; } elseif (!$this->baseUrl) { - $str = $this->url(); + $str = $this->url(); $this->baseUrl = strpos($str, '?') ? strstr($str, '?', true) : $str; } return true === $url ? $this->domain() . $this->baseUrl : $this->baseUrl; @@ -416,7 +433,7 @@ public function pathinfo() public function path() { if (is_null($this->path)) { - $suffix = Config::get('url_html_suffix'); + $suffix = Config::get('url_html_suffix'); $pathinfo = $this->pathinfo(); if (false === $suffix) { // 禁止伪静态访问 @@ -479,8 +496,8 @@ public function type() /** * 设置资源类型 * @access public - * @param string|array $type 资源类型名 - * @param string $val 资源类型 + * @param string|array $type 资源类型名 + * @param string $val 资源类型 * @return void */ public function mimeType($type, $val = '') @@ -495,22 +512,28 @@ public function mimeType($type, $val = '') /** * 当前的请求类型 * @access public - * @param bool $method true 获取原始请求类型 + * @param bool $method true 获取原始请求类型 * @return string */ public function method($method = false) { 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) { if (isset($_POST[Config::get('var_method')])) { - $this->method = strtoupper($_POST[Config::get('var_method')]); - $this->{$this->method}($_POST); + $method = strtoupper($_POST[Config::get('var_method')]); + if (in_array($method, ['GET', 'POST', 'DELETE', 'PUT', 'PATCH'])) { + $this->method = $method; + $this->{$this->method}($_POST); + } else { + $this->method = 'POST'; + } + unset($_POST[Config::get('var_method')]); } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { $this->method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']); } 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; @@ -609,14 +632,14 @@ public function isCgi() /** * 获取当前请求的参数 * @access public - * @param string|array $name 变量名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤方法 + * @param string|array $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function param($name = '', $default = null, $filter = '') { - if (empty($this->param)) { + if (empty($this->mergeParam)) { $method = $this->method(true); // 自动获取请求变量 switch ($method) { @@ -632,7 +655,8 @@ public function param($name = '', $default = null, $filter = '') $vars = []; } // 当前请求参数和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) { // 获取包含文件上传信息的数组 @@ -646,15 +670,16 @@ public function param($name = '', $default = null, $filter = '') /** * 设置获取路由参数 * @access public - * @param string|array $name 变量名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤方法 + * @param string|array $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function route($name = '', $default = null, $filter = '') { if (is_array($name)) { - $this->param = []; + $this->param = []; + $this->mergeParam = false; return $this->route = array_merge($this->route, $name); } return $this->input($this->route, $name, $default, $filter); @@ -663,9 +688,9 @@ public function route($name = '', $default = null, $filter = '') /** * 设置获取GET参数 * @access public - * @param string|array $name 变量名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤方法 + * @param string|array $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function get($name = '', $default = null, $filter = '') @@ -674,7 +699,8 @@ public function get($name = '', $default = null, $filter = '') $this->get = $_GET; } if (is_array($name)) { - $this->param = []; + $this->param = []; + $this->mergeParam = false; return $this->get = array_merge($this->get, $name); } return $this->input($this->get, $name, $default, $filter); @@ -683,9 +709,9 @@ public function get($name = '', $default = null, $filter = '') /** * 设置获取POST参数 * @access public - * @param string $name 变量名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤方法 + * @param string $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function post($name = '', $default = null, $filter = '') @@ -699,7 +725,8 @@ public function post($name = '', $default = null, $filter = '') } } if (is_array($name)) { - $this->param = []; + $this->param = []; + $this->mergeParam = false; return $this->post = array_merge($this->post, $name); } return $this->input($this->post, $name, $default, $filter); @@ -708,9 +735,9 @@ public function post($name = '', $default = null, $filter = '') /** * 设置获取PUT参数 * @access public - * @param string|array $name 变量名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤方法 + * @param string|array $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function put($name = '', $default = null, $filter = '') @@ -724,7 +751,8 @@ public function put($name = '', $default = null, $filter = '') } } if (is_array($name)) { - $this->param = []; + $this->param = []; + $this->mergeParam = false; return $this->put = is_null($this->put) ? $name : array_merge($this->put, $name); } @@ -734,9 +762,9 @@ public function put($name = '', $default = null, $filter = '') /** * 设置获取DELETE参数 * @access public - * @param string|array $name 变量名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤方法 + * @param string|array $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function delete($name = '', $default = null, $filter = '') @@ -747,9 +775,9 @@ public function delete($name = '', $default = null, $filter = '') /** * 设置获取PATCH参数 * @access public - * @param string|array $name 变量名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤方法 + * @param string|array $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function patch($name = '', $default = null, $filter = '') @@ -759,9 +787,9 @@ public function patch($name = '', $default = null, $filter = '') /** * 获取request变量 - * @param string $name 数据名称 - * @param string $default 默认值 - * @param string|array $filter 过滤方法 + * @param string $name 数据名称 + * @param string $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function request($name = '', $default = null, $filter = '') @@ -770,7 +798,8 @@ public function request($name = '', $default = null, $filter = '') $this->request = $_REQUEST; } if (is_array($name)) { - $this->param = []; + $this->param = []; + $this->mergeParam = false; return $this->request = array_merge($this->request, $name); } return $this->input($this->request, $name, $default, $filter); @@ -779,9 +808,9 @@ public function request($name = '', $default = null, $filter = '') /** * 获取session数据 * @access public - * @param string|array $name 数据名称 - * @param string $default 默认值 - * @param string|array $filter 过滤方法 + * @param string|array $name 数据名称 + * @param string $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function session($name = '', $default = null, $filter = '') @@ -798,9 +827,9 @@ public function session($name = '', $default = null, $filter = '') /** * 获取cookie参数 * @access public - * @param string|array $name 数据名称 - * @param string $default 默认值 - * @param string|array $filter 过滤方法 + * @param string|array $name 数据名称 + * @param string $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function cookie($name = '', $default = null, $filter = '') @@ -831,9 +860,9 @@ public function cookie($name = '', $default = null, $filter = '') /** * 获取server参数 * @access public - * @param string|array $name 数据名称 - * @param string $default 默认值 - * @param string|array $filter 过滤方法 + * @param string|array $name 数据名称 + * @param string $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function server($name = '', $default = null, $filter = '') @@ -867,8 +896,8 @@ public function file($name = '') $array = []; foreach ($files as $key => $file) { if (is_array($file['name'])) { - $item = []; - $keys = array_keys($file); + $item = []; + $keys = array_keys($file); $count = count($file['name']); for ($i = 0; $i < $count; $i++) { if (empty($file['tmp_name'][$i]) || !is_file($file['tmp_name'][$i])) { @@ -909,9 +938,9 @@ public function file($name = '') /** * 获取环境变量 - * @param string|array $name 数据名称 - * @param string $default 默认值 - * @param string|array $filter 过滤方法 + * @param string|array $name 数据名称 + * @param string $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function env($name = '', $default = null, $filter = '') @@ -928,8 +957,8 @@ public function env($name = '', $default = null, $filter = '') /** * 设置或者获取当前的Header * @access public - * @param string|array $name header名称 - * @param string $default 默认值 + * @param string|array $name header名称 + * @param string $default 默认值 * @return string */ public function header($name = '', $default = null) @@ -942,7 +971,7 @@ public function header($name = '', $default = null) $server = $this->server ?: $_SERVER; foreach ($server as $key => $val) { if (0 === strpos($key, 'HTTP_')) { - $key = str_replace('_', '-', strtolower(substr($key, 5))); + $key = str_replace('_', '-', strtolower(substr($key, 5))); $header[$key] = $val; } } @@ -967,10 +996,10 @@ public function header($name = '', $default = null) /** * 获取变量 支持过滤和默认值 - * @param array $data 数据源 - * @param string|false $name 字段名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤函数 + * @param array $data 数据源 + * @param string|false $name 字段名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤函数 * @return mixed */ public function input($data = [], $name = '', $default = null, $filter = '') @@ -1051,9 +1080,9 @@ protected function getFilter($filter, $default) /** * 递归过滤给定的值 - * @param mixed $value 键值 - * @param mixed $key 键名 - * @param array $filters 过滤方法+默认值 + * @param mixed $value 键值 + * @param mixed $key 键名 + * @param array $filters 过滤方法+默认值 * @return mixed */ private function filterValue(&$value, $key, $filters) @@ -1093,7 +1122,7 @@ private function filterValue(&$value, $key, $filters) 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 .= ' '; } // TODO 其他安全过滤 @@ -1138,9 +1167,9 @@ private function typeCast(&$data, $type) /** * 是否存在某个请求参数 * @access public - * @param string $name 变量名 - * @param string $type 变量类型 - * @param bool $checkEmpty 是否检测空值 + * @param string $name 变量名 + * @param string $type 变量类型 + * @param bool $checkEmpty 是否检测空值 * @return mixed */ public function has($name, $type = 'param', $checkEmpty = false) @@ -1164,8 +1193,8 @@ public function has($name, $type = 'param', $checkEmpty = false) /** * 获取指定的参数 * @access public - * @param string|array $name 变量名 - * @param string $type 变量类型 + * @param string|array $name 变量名 + * @param string $type 变量类型 * @return mixed */ public function only($name, $type = 'param') @@ -1186,8 +1215,8 @@ public function only($name, $type = 'param') /** * 排除指定参数获取 * @access public - * @param string|array $name 变量名 - * @param string $type 变量类型 + * @param string|array $name 变量名 + * @param string $type 变量类型 * @return mixed */ public function except($name, $type = 'param') @@ -1229,24 +1258,26 @@ public function isSsl() /** * 当前是否Ajax请求 * @access public - * @param bool $ajax true 获取原始ajax请求 + * @param bool $ajax true 获取原始ajax请求 * @return bool */ public function isAjax($ajax = false) { - $value = $this->server('HTTP_X_REQUESTED_WITH', '', 'strtolower'); + $value = $this->server('HTTP_X_REQUESTED_WITH', '', 'strtolower'); $result = ('xmlhttprequest' == $value) ? true : false; if (true === $ajax) { return $result; } else { - return $this->param(Config::get('var_ajax')) ? true : $result; + $result = $this->param(Config::get('var_ajax')) ? true : $result; + $this->mergeParam = false; + return $result; } } /** * 当前是否Pjax请求 * @access public - * @param bool $pjax true 获取原始pjax请求 + * @param bool $pjax true 获取原始pjax请求 * @return bool */ public function isPjax($pjax = false) @@ -1255,25 +1286,31 @@ public function isPjax($pjax = false) if (true === $pjax) { return $result; } else { - return $this->param(Config::get('var_pjax')) ? true : $result; + $result = $this->param(Config::get('var_pjax')) ? true : $result; + $this->mergeParam = false; + return $result; } } /** * 获取客户端IP地址 - * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字 - * @param boolean $adv 是否进行高级模式获取(有可能被伪装) + * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字 + * @param boolean $adv 是否进行高级模式获取(有可能被伪装) * @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; if (null !== $ip) { 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'])) { $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $pos = array_search('unknown', $arr); @@ -1291,7 +1328,7 @@ public function ip($type = 0, $adv = false) } // IP地址合法验证 $long = sprintf("%u", ip2long($ip)); - $ip = $long ? [$ip, $long] : ['0.0.0.0', 0]; + $ip = $long ? [$ip, $long] : ['0.0.0.0', 0]; return $ip[$type]; } @@ -1338,11 +1375,18 @@ public function query() /** * 当前请求的host * @access public + * @param bool $strict true 仅仅获取HOST * @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; } /** @@ -1412,7 +1456,7 @@ public function routeInfo($route = []) /** * 设置或者获取当前请求的调度信息 * @access public - * @param array $dispatch 调度信息 + * @param array $dispatch 调度信息 * @return array */ public function dispatch($dispatch = null) @@ -1463,11 +1507,12 @@ public function controller($controller = null) */ public function action($action = null) { - if (!is_null($action)) { + if (!is_null($action) && !is_bool($action)) { $this->action = $action; return $this; } else { - return $this->action ?: ''; + $name = $this->action ?: ''; + return true === $action ? $name : strtolower($name); } } @@ -1519,7 +1564,7 @@ public function getInput() */ public function token($name = '__token__', $type = 'md5') { - $type = is_callable($type) ? $type : 'md5'; + $type = is_callable($type) ? $type : 'md5'; $token = call_user_func($type, $_SERVER['REQUEST_TIME_FLOAT']); if ($this->isAjax()) { header($name . ': ' . $token); @@ -1531,7 +1576,7 @@ public function token($name = '__token__', $type = 'md5') /** * 设置当前地址的请求缓存 * @access public - * @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id + * @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id * @param mixed $expire 缓存有效期 * @param array $except 缓存排除 * @param string $tag 缓存标签 @@ -1540,7 +1585,7 @@ public function token($name = '__token__', $type = 'md5') public function cache($key, $expire = null, $except = [], $tag = null) { if (!is_array($except)) { - $tag = $except; + $tag = $except; $except = []; } @@ -1594,7 +1639,7 @@ public function cache($key, $expire = null, $except = [], $tag = null) throw new \think\exception\HttpResponseException($response); } elseif (Cache::has($key)) { list($content, $header) = Cache::get($key); - $response = Response::create($content)->header($header); + $response = Response::create($content)->header($header); throw new \think\exception\HttpResponseException($response); } else { $this->cache = [$key, $expire, $tag]; @@ -1616,7 +1661,7 @@ public function getCache() * 设置当前请求绑定的对象实例 * @access public * @param string|array $name 绑定的对象标识 - * @param mixed $obj 绑定的对象实例 + * @param mixed $obj 绑定的对象实例 * @return mixed */ public function bind($name, $obj = null) diff --git a/library/think/Response.php b/library/think/Response.php index bd30bdec8a..28226759d4 100644 --- a/library/think/Response.php +++ b/library/think/Response.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -54,7 +54,7 @@ public function __construct($data = '', $code = 200, array $header = [], $option } $this->contentType($this->contentType, $this->charset); $this->header = array_merge($this->header, $header); - $this->code = $code; + $this->code = $code; } /** @@ -69,9 +69,7 @@ public function __construct($data = '', $code = 200, array $header = [], $option */ public static function create($data = '', $type = '', $code = 200, array $header = [], $options = []) { - $type = empty($type) ? 'null' : strtolower($type); - - $class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst($type); + $class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst(strtolower($type)); if (class_exists($class)) { $response = new $class($data, $code, $header, $options); } else { @@ -105,7 +103,7 @@ public function send() if ($cache) { $this->header['Cache-Control'] = 'max-age=' . $cache[1] . ',must-revalidate'; $this->header['Last-Modified'] = gmdate('D, d M Y H:i:s') . ' GMT'; - $this->header['Expires'] = gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME'] + $cache[1]) . ' GMT'; + $this->header['Expires'] = gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME'] + $cache[1]) . ' GMT'; Cache::tag($cache[2])->set($cache[0], [$data, $this->header], $cache[1]); } } diff --git a/library/think/Route.php b/library/think/Route.php index cef35c9862..b40917bc5e 100644 --- a/library/think/Route.php +++ b/library/think/Route.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -17,38 +17,38 @@ class Route { // 路由规则 private static $rules = [ - 'get' => [], - 'post' => [], - 'put' => [], - 'delete' => [], - 'patch' => [], - 'head' => [], + 'get' => [], + 'post' => [], + 'put' => [], + 'delete' => [], + 'patch' => [], + 'head' => [], 'options' => [], - '*' => [], - 'alias' => [], - 'domain' => [], + '*' => [], + 'alias' => [], + 'domain' => [], 'pattern' => [], - 'name' => [], + 'name' => [], ]; // REST路由操作方法定义 private static $rest = [ - 'index' => ['get', '', 'index'], + 'index' => ['get', '', 'index'], 'create' => ['get', '/create', 'create'], - 'edit' => ['get', '/:id/edit', 'edit'], - 'read' => ['get', '/:id', 'read'], - 'save' => ['post', '', 'save'], + 'edit' => ['get', '/:id/edit', 'edit'], + 'read' => ['get', '/:id', 'read'], + 'save' => ['post', '', 'save'], 'update' => ['put', '/:id', 'update'], 'delete' => ['delete', '/:id', 'delete'], ]; // 不同请求类型的方法前缀 private static $methodPrefix = [ - 'get' => 'get', - 'post' => 'post', - 'put' => 'put', + 'get' => 'get', + 'post' => 'post', + 'put' => 'put', 'delete' => 'delete', - 'patch' => 'patch', + 'patch' => 'patch', ]; // 子域名 @@ -68,8 +68,8 @@ class Route /** * 注册变量规则 * @access public - * @param string|array $name 变量名 - * @param string $rule 变量规则 + * @param string|array $name 变量名 + * @param string $rule 变量规则 * @return void */ public static function pattern($name = null, $rule = '') @@ -84,10 +84,10 @@ public static function pattern($name = null, $rule = '') /** * 注册子域名部署规则 * @access public - * @param string|array $domain 子域名 - * @param mixed $rule 路由规则 - * @param array $option 路由参数 - * @param array $pattern 变量规则 + * @param string|array $domain 子域名 + * @param mixed $rule 路由规则 + * @param array $option 路由参数 + * @param array $pattern 变量规则 * @return void */ public static function domain($domain, $rule = '', $option = [], $pattern = []) @@ -121,8 +121,8 @@ private static function setDomain($domain) /** * 设置路由绑定 * @access public - * @param mixed $bind 绑定信息 - * @param string $type 绑定类型 默认为module 支持 namespace class controller + * @param mixed $bind 绑定信息 + * @param string $type 绑定类型 默认为module 支持 namespace class controller * @return mixed */ public static function bind($bind, $type = 'module') @@ -133,8 +133,8 @@ public static function bind($bind, $type = 'module') /** * 设置或者获取路由标识 * @access public - * @param string|array $name 路由命名标识 数组表示批量设置 - * @param array $value 路由地址及变量信息 + * @param string|array $name 路由命名标识 数组表示批量设置 + * @param array $value 路由地址及变量信息 * @return array */ public static function name($name = '', $value = null) @@ -154,7 +154,7 @@ public static function name($name = '', $value = null) /** * 读取路由绑定 * @access public - * @param string $type 绑定类型 + * @param string $type 绑定类型 * @return mixed */ public static function getBind($type) @@ -165,8 +165,8 @@ public static function getBind($type) /** * 导入配置文件的路由规则 * @access public - * @param array $rule 路由规则 - * @param string $type 请求类型 + * @param array $rule 路由规则 + * @param string $type 请求类型 * @return void */ public static function import(array $rule, $type = '*') @@ -222,11 +222,11 @@ protected static function registerRules($rules, $type = '*') /** * 注册路由规则 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param string $type 请求类型 - * @param array $option 路由参数 - * @param array $pattern 变量规则 + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param string $type 请求类型 + * @param array $option 路由参数 + * @param array $pattern 变量规则 * @return void */ public static function rule($rule, $route = '', $type = '*', $option = [], $pattern = []) @@ -235,7 +235,7 @@ public static function rule($rule, $route = '', $type = '*', $option = [], $patt if (!is_null($group)) { // 路由分组 - $option = array_merge(self::getGroup('option'), $option); + $option = array_merge(self::getGroup('option'), $option); $pattern = array_merge(self::getGroup('pattern'), $pattern); } @@ -243,7 +243,7 @@ public static function rule($rule, $route = '', $type = '*', $option = [], $patt if (strpos($type, '|')) { $option['method'] = $type; - $type = '*'; + $type = '*'; } if (is_array($rule) && empty($route)) { foreach ($rule as $key => $val) { @@ -251,13 +251,13 @@ public static function rule($rule, $route = '', $type = '*', $option = [], $patt $key = array_shift($val); } if (is_array($val)) { - $route = $val[0]; - $option1 = array_merge($option, $val[1]); + $route = $val[0]; + $option1 = array_merge($option, $val[1]); $pattern1 = array_merge($pattern, isset($val[2]) ? $val[2] : []); } else { - $option1 = null; + $option1 = null; $pattern1 = null; - $route = $val; + $route = $val; } self::setRule($key, $route, $type, !is_null($option1) ? $option1 : $option, !is_null($pattern1) ? $pattern1 : $pattern, $group); } @@ -270,12 +270,12 @@ public static function rule($rule, $route = '', $type = '*', $option = [], $patt /** * 设置路由规则 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param string $type 请求类型 - * @param array $option 路由参数 - * @param array $pattern 变量规则 - * @param string $group 所属分组 + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param string $type 请求类型 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @param string $group 所属分组 * @return void */ protected static function setRule($rule, $route, $type = '*', $option = [], $pattern = [], $group = '') @@ -307,7 +307,7 @@ protected static function setRule($rule, $route, $type = '*', $option = [], $pat } $vars = self::parseVar($rule); if (isset($name)) { - $key = $group ? $group . ($rule ? '/' . $rule : '') : $rule; + $key = $group ? $group . ($rule ? '/' . $rule : '') : $rule; $suffix = isset($option['ext']) ? $option['ext'] : null; self::name($name, [$key, $vars, self::$domain, $suffix]); } @@ -335,9 +335,9 @@ protected static function setRule($rule, $route, $type = '*', $option = [], $pat if ('*' == $type) { // 注册路由快捷方式 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; - } else { + } elseif (!self::$domain && !isset(self::$rules[$method][$rule])) { self::$rules[$method][$rule] = true; } } @@ -348,7 +348,7 @@ protected static function setRule($rule, $route, $type = '*', $option = [], $pat /** * 设置当前执行的参数信息 * @access public - * @param array $options 参数信息 + * @param array $options 参数信息 * @return mixed */ protected static function setOption($options = []) @@ -369,7 +369,7 @@ public static function getOption() /** * 获取当前的分组信息 * @access public - * @param string $type 分组信息名称 name option pattern + * @param string $type 分组信息名称 name option pattern * @return mixed */ public static function getGroup($type) @@ -384,32 +384,32 @@ public static function getGroup($type) /** * 设置当前的路由分组 * @access public - * @param string $name 分组名称 - * @param array $option 分组路由参数 - * @param array $pattern 分组变量规则 + * @param string $name 分组名称 + * @param array $option 分组路由参数 + * @param array $pattern 分组变量规则 * @return void */ public static function setGroup($name, $option = [], $pattern = []) { - self::$group['name'] = $name; - self::$group['option'] = $option ?: []; + self::$group['name'] = $name; + self::$group['option'] = $option ?: []; self::$group['pattern'] = $pattern ?: []; } /** * 注册路由分组 * @access public - * @param string|array $name 分组名称或者参数 - * @param array|\Closure $routes 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 + * @param string|array $name 分组名称或者参数 + * @param array|\Closure $routes 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 * @return void */ public static function group($name, $routes, $option = [], $pattern = []) { if (is_array($name)) { $option = $name; - $name = isset($option['name']) ? $option['name'] : ''; + $name = isset($option['name']) ? $option['name'] : ''; } // 分组 $currentGroup = self::getGroup('name'); @@ -418,43 +418,43 @@ public static function group($name, $routes, $option = [], $pattern = []) } if (!empty($name)) { if ($routes instanceof \Closure) { - $currentOption = self::getGroup('option'); + $currentOption = self::getGroup('option'); $currentPattern = self::getGroup('pattern'); self::setGroup($name, array_merge($currentOption, $option), array_merge($currentPattern, $pattern)); call_user_func_array($routes, []); self::setGroup($currentGroup, $currentOption, $currentPattern); if ($currentGroup != $name) { - self::$rules['*'][$name]['route'] = ''; - self::$rules['*'][$name]['var'] = self::parseVar($name); - self::$rules['*'][$name]['option'] = $option; + self::$rules['*'][$name]['route'] = ''; + self::$rules['*'][$name]['var'] = self::parseVar($name); + self::$rules['*'][$name]['option'] = $option; self::$rules['*'][$name]['pattern'] = $pattern; } } else { - $item = []; + $item = []; $completeMatch = Config::get('route_complete_match'); foreach ($routes as $key => $val) { if (is_numeric($key)) { $key = array_shift($val); } if (is_array($val)) { - $route = $val[0]; - $option1 = array_merge($option, isset($val[1]) ? $val[1] : []); + $route = $val[0]; + $option1 = array_merge($option, isset($val[1]) ? $val[1] : []); $pattern1 = array_merge($pattern, isset($val[2]) ? $val[2] : []); } else { $route = $val; } - $options = isset($option1) ? $option1 : $option; + $options = isset($option1) ? $option1 : $option; $patterns = isset($pattern1) ? $pattern1 : $pattern; if ('$' == substr($key, -1, 1)) { // 是否完整匹配 $options['complete_match'] = true; - $key = substr($key, 0, -1); + $key = substr($key, 0, -1); } elseif ($completeMatch) { $options['complete_match'] = true; } - $key = trim($key, '/'); - $vars = self::parseVar($key); + $key = trim($key, '/'); + $vars = self::parseVar($key); $item[] = ['rule' => $key, 'route' => $route, 'var' => $vars, 'option' => $options, 'pattern' => $patterns]; // 设置路由标识 $suffix = isset($options['ext']) ? $options['ext'] : null; @@ -473,7 +473,7 @@ public static function group($name, $routes, $option = [], $pattern = []) } elseif ($routes instanceof \Closure) { // 闭包注册 - $currentOption = self::getGroup('option'); + $currentOption = self::getGroup('option'); $currentPattern = self::getGroup('pattern'); self::setGroup('', array_merge($currentOption, $option), array_merge($currentPattern, $pattern)); call_user_func_array($routes, []); @@ -487,10 +487,10 @@ public static function group($name, $routes, $option = [], $pattern = []) /** * 注册路由 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 * @return void */ public static function any($rule, $route = '', $option = [], $pattern = []) @@ -501,10 +501,10 @@ public static function any($rule, $route = '', $option = [], $pattern = []) /** * 注册GET路由 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 * @return void */ public static function get($rule, $route = '', $option = [], $pattern = []) @@ -515,10 +515,10 @@ public static function get($rule, $route = '', $option = [], $pattern = []) /** * 注册POST路由 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 * @return void */ public static function post($rule, $route = '', $option = [], $pattern = []) @@ -529,10 +529,10 @@ public static function post($rule, $route = '', $option = [], $pattern = []) /** * 注册PUT路由 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 * @return void */ public static function put($rule, $route = '', $option = [], $pattern = []) @@ -543,10 +543,10 @@ public static function put($rule, $route = '', $option = [], $pattern = []) /** * 注册DELETE路由 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 * @return void */ public static function delete($rule, $route = '', $option = [], $pattern = []) @@ -557,10 +557,10 @@ public static function delete($rule, $route = '', $option = [], $pattern = []) /** * 注册PATCH路由 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 * @return void */ public static function patch($rule, $route = '', $option = [], $pattern = []) @@ -571,10 +571,10 @@ public static function patch($rule, $route = '', $option = [], $pattern = []) /** * 注册资源路由 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 * @return void */ public static function resource($rule, $route = '', $option = [], $pattern = []) @@ -590,8 +590,8 @@ public static function resource($rule, $route = '', $option = [], $pattern = []) if (strpos($rule, '.')) { // 注册嵌套资源路由 $array = explode('.', $rule); - $last = array_pop($array); - $item = []; + $last = array_pop($array); + $item = []; foreach ($array as $val) { $item[] = $val . '/:' . (isset($option['var'][$val]) ? $option['var'][$val] : $val . '_id'); } @@ -608,7 +608,7 @@ public static function resource($rule, $route = '', $option = [], $pattern = []) } elseif (strpos($val[1], ':id') && isset($option['var'][$rule])) { $val[1] = str_replace(':id', ':' . $option['var'][$rule], $val[1]); } - $item = ltrim($rule . $val[1], '/'); + $item = ltrim($rule . $val[1], '/'); $option['rest'] = $key; self::rule($item . '$', $route . '/' . $val[2], $val[0], $option, $pattern); } @@ -618,10 +618,10 @@ public static function resource($rule, $route = '', $option = [], $pattern = []) /** * 注册控制器路由 操作方法对应不同的请求后缀 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 + * @param string $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 * @return void */ public static function controller($rule, $route = '', $option = [], $pattern = []) @@ -634,9 +634,9 @@ public static function controller($rule, $route = '', $option = [], $pattern = [ /** * 注册别名路由 * @access public - * @param string|array $rule 路由别名 - * @param string $route 路由地址 - * @param array $option 路由参数 + * @param string|array $rule 路由别名 + * @param string $route 路由地址 + * @param array $option 路由参数 * @return void */ public static function alias($rule = null, $route = '', $option = []) @@ -651,8 +651,8 @@ public static function alias($rule = null, $route = '', $option = []) /** * 设置不同请求类型下面的方法前缀 * @access public - * @param string $method 请求类型 - * @param string $prefix 类型前缀 + * @param string $method 请求类型 + * @param string $prefix 类型前缀 * @return void */ public static function setMethodPrefix($method, $prefix = '') @@ -667,8 +667,8 @@ public static function setMethodPrefix($method, $prefix = '') /** * rest方法定义和修改 * @access public - * @param string $name 方法名称 - * @param array|bool $resource 资源 + * @param string|array $name 方法名称 + * @param array|bool $resource 资源 * @return void */ public static function rest($name, $resource = []) @@ -683,9 +683,9 @@ public static function rest($name, $resource = []) /** * 注册未匹配路由规则后的处理 * @access public - * @param string $route 路由地址 - * @param string $method 请求类型 - * @param array $option 路由参数 + * @param string $route 路由地址 + * @param string $method 请求类型 + * @param array $option 路由参数 * @return void */ public static function miss($route, $method = '*', $option = []) @@ -696,7 +696,7 @@ public static function miss($route, $method = '*', $option = []) /** * 注册一个自动解析的URL路由 * @access public - * @param string $route 路由地址 + * @param string $route 路由地址 * @return void */ public static function auto($route) @@ -726,9 +726,9 @@ public static function rules($rules = '') /** * 检测子域名部署 * @access public - * @param Request $request Request请求对象 - * @param array $currentRules 当前路由规则 - * @param string $method 请求类型 + * @param Request $request Request请求对象 + * @param array $currentRules 当前路由规则 + * @param string $method 请求类型 * @return void */ public static function checkDomain($request, &$currentRules, $method = 'get') @@ -737,7 +737,7 @@ public static function checkDomain($request, &$currentRules, $method = 'get') $rules = self::$rules['domain']; // 开启子域名部署 支持二级和三级域名 if (!empty($rules)) { - $host = $request->host(); + $host = $request->host(true); if (isset($rules[$host])) { // 完整域名或者IP配置 $item = $rules[$host]; @@ -752,9 +752,9 @@ public static function checkDomain($request, &$currentRules, $method = 'get') // 子域名配置 if (!empty($domain)) { // 当前子域名 - $subDomain = implode('.', $domain); + $subDomain = implode('.', $domain); self::$subDomain = $subDomain; - $domain2 = array_pop($domain); + $domain2 = array_pop($domain); if ($domain) { // 存在三级域名 $domain3 = array_pop($domain); @@ -764,12 +764,12 @@ public static function checkDomain($request, &$currentRules, $method = 'get') $item = $rules[$subDomain]; } elseif (isset($rules['*.' . $domain2]) && !empty($domain3)) { // 泛三级域名 - $item = $rules['*.' . $domain2]; + $item = $rules['*.' . $domain2]; $panDomain = $domain3; } elseif (isset($rules['*']) && !empty($domain2)) { // 泛二级域名 if ('www' != $domain2) { - $item = $rules['*']; + $item = $rules['*']; $panDomain = $domain2; } } @@ -790,7 +790,7 @@ public static function checkDomain($request, &$currentRules, $method = 'get') if (strpos($rule, '?')) { // 传入其它参数 - $array = parse_url($rule); + $array = parse_url($rule); $result = $array['path']; parse_str($array['query'], $params); if (isset($panDomain)) { @@ -818,7 +818,7 @@ public static function checkDomain($request, &$currentRules, $method = 'get') self::$domainBind = true; } else { self::$domainRule = $item; - $currentRules = isset($item[$method]) ? $item[$method] : $item['*']; + $currentRules = isset($item[$method]) ? $item[$method] : $item['*']; } } } @@ -827,14 +827,23 @@ public static function checkDomain($request, &$currentRules, $method = 'get') /** * 检测URL路由 * @access public - * @param Request $request Request请求对象 - * @param string $url URL地址 - * @param string $depr URL分隔符 - * @param bool $checkDomain 是否检测域名规则 + * @param Request $request Request请求对象 + * @param string $url URL地址 + * @param string $depr URL分隔符 + * @param bool $checkDomain 是否检测域名规则 * @return false|array */ 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); @@ -888,12 +897,12 @@ private static function getRouteExpress($key) /** * 检测路由规则 * @access private - * @param Request $request - * @param array $rules 路由规则 - * @param string $url URL地址 - * @param string $depr URL分割符 - * @param string $group 路由分组名 - * @param array $options 路由参数(分组) + * @param Request $request + * @param array $rules 路由规则 + * @param string $url URL地址 + * @param string $depr URL分割符 + * @param string $group 路由分组名 + * @param array $options 路由参数(分组) * @return mixed */ private static function checkRoute($request, $rules, $url, $depr = '/', $group = '', $options = []) @@ -905,10 +914,10 @@ private static function checkRoute($request, $rules, $url, $depr = '/', $group = if (!isset($item['rule'])) { continue; } - $rule = $item['rule']; - $route = $item['route']; - $vars = $item['var']; - $option = $item['option']; + $rule = $item['rule']; + $route = $item['route']; + $vars = $item['var']; + $option = $item['option']; $pattern = $item['pattern']; // 检查参数有效性 @@ -940,7 +949,7 @@ private static function checkRoute($request, $rules, $url, $depr = '/', $group = } elseif ($route) { if ('__miss__' == $rule || '__auto__' == $rule) { // 指定特殊路由 - $var = trim($rule, '__'); + $var = trim($rule, '__'); ${$var} = $item; continue; } @@ -971,20 +980,20 @@ private static function checkRoute($request, $rules, $url, $depr = '/', $group = /** * 检测路由别名 * @access private - * @param Request $request - * @param string $url URL地址 - * @param string $depr URL分隔符 + * @param Request $request + * @param string $url URL地址 + * @param string $depr URL分隔符 * @return mixed */ private static function checkRouteAlias($request, $url, $depr) { $array = explode('|', $url); $alias = array_shift($array); - $item = self::$rules['alias'][$alias]; + $item = self::$rules['alias'][$alias]; if (is_array($item)) { list($rule, $option) = $item; - $action = $array[0]; + $action = $array[0]; if (isset($option['allow']) && !in_array($action, explode(',', $option['allow']))) { // 允许操作 return false; @@ -1018,9 +1027,9 @@ private static function checkRouteAlias($request, $url, $depr) /** * 检测URL绑定 * @access private - * @param string $url URL地址 - * @param array $rules 路由规则 - * @param string $depr URL分隔符 + * @param string $url URL地址 + * @param array $rules 路由规则 + * @param string $depr URL分隔符 * @return mixed */ private static function checkUrlBind(&$url, &$rules, $depr = '/') @@ -1049,15 +1058,15 @@ private static function checkUrlBind(&$url, &$rules, $depr = '/') /** * 绑定到类 * @access public - * @param string $url URL地址 - * @param string $class 类名(带命名空间) - * @param string $depr URL分隔符 + * @param string $url URL地址 + * @param string $class 类名(带命名空间) + * @param string $depr URL分隔符 * @return array */ public static function bindToClass($url, $class, $depr = '/') { - $url = str_replace($depr, '|', $url); - $array = explode('|', $url, 2); + $url = str_replace($depr, '|', $url); + $array = explode('|', $url, 2); $action = !empty($array[0]) ? $array[0] : Config::get('default_action'); if (!empty($array[1])) { self::parseUrlParams($array[1]); @@ -1068,16 +1077,16 @@ public static function bindToClass($url, $class, $depr = '/') /** * 绑定到命名空间 * @access public - * @param string $url URL地址 - * @param string $namespace 命名空间 - * @param string $depr URL分隔符 + * @param string $url URL地址 + * @param string $namespace 命名空间 + * @param string $depr URL分隔符 * @return array */ public static function bindToNamespace($url, $namespace, $depr = '/') { - $url = str_replace($depr, '|', $url); - $array = explode('|', $url, 3); - $class = !empty($array[0]) ? $array[0] : Config::get('default_controller'); + $url = str_replace($depr, '|', $url); + $array = explode('|', $url, 3); + $class = !empty($array[0]) ? $array[0] : Config::get('default_controller'); $method = !empty($array[1]) ? $array[1] : Config::get('default_action'); if (!empty($array[2])) { self::parseUrlParams($array[2]); @@ -1088,15 +1097,15 @@ public static function bindToNamespace($url, $namespace, $depr = '/') /** * 绑定到控制器类 * @access public - * @param string $url URL地址 - * @param string $controller 控制器名 (支持带模块名 index/user ) - * @param string $depr URL分隔符 + * @param string $url URL地址 + * @param string $controller 控制器名 (支持带模块名 index/user ) + * @param string $depr URL分隔符 * @return array */ public static function bindToController($url, $controller, $depr = '/') { - $url = str_replace($depr, '|', $url); - $array = explode('|', $url, 2); + $url = str_replace($depr, '|', $url); + $array = explode('|', $url, 2); $action = !empty($array[0]) ? $array[0] : Config::get('default_action'); if (!empty($array[1])) { self::parseUrlParams($array[1]); @@ -1107,15 +1116,15 @@ public static function bindToController($url, $controller, $depr = '/') /** * 绑定到模块/控制器 * @access public - * @param string $url URL地址 - * @param string $controller 控制器类名(带命名空间) - * @param string $depr URL分隔符 + * @param string $url URL地址 + * @param string $controller 控制器类名(带命名空间) + * @param string $depr URL分隔符 * @return array */ public static function bindToModule($url, $controller, $depr = '/') { - $url = str_replace($depr, '|', $url); - $array = explode('|', $url, 2); + $url = str_replace($depr, '|', $url); + $array = explode('|', $url, 2); $action = !empty($array[0]) ? $array[0] : Config::get('default_action'); if (!empty($array[1])) { self::parseUrlParams($array[1]); @@ -1126,8 +1135,8 @@ public static function bindToModule($url, $controller, $depr = '/') /** * 路由参数有效性检查 * @access private - * @param array $option 路由参数 - * @param Request $request Request对象 + * @param array $option 路由参数 + * @param Request $request Request对象 * @return bool */ private static function checkOption($option, $request) @@ -1153,12 +1162,12 @@ private static function checkOption($option, $request) /** * 检测路由规则 * @access private - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param string $url URL地址 - * @param array $pattern 变量规则 - * @param array $option 路由参数 - * @param string $depr URL分隔符(全局) + * @param string $rule 路由规则 + * @param string $route 路由地址 + * @param string $url URL地址 + * @param array $pattern 变量规则 + * @param array $option 路由参数 + * @param string $depr URL分隔符(全局) * @return array|false */ private static function checkRule($rule, $route, $url, $pattern, $option, $depr) @@ -1200,9 +1209,9 @@ private static function checkRule($rule, $route, $url, $pattern, $option, $depr) /** * 解析模块的URL地址 [模块/控制器/操作?]参数1=值1&参数2=值2... * @access public - * @param string $url URL地址 - * @param string $depr URL分隔符 - * @param bool $autoSearch 是否自动深度搜索控制器 + * @param string $url URL地址 + * @param string $depr URL分隔符 + * @param bool $autoSearch 是否自动深度搜索控制器 * @return array */ public static function parseUrl($url, $depr = '/', $autoSearch = false) @@ -1213,22 +1222,22 @@ public static function parseUrl($url, $depr = '/', $autoSearch = false) // 如果有模块/控制器绑定 $url = $bind . ('.' != substr($bind, -1) ? $depr : '') . ltrim($url, $depr); } - $url = str_replace($depr, '|', $url); + $url = str_replace($depr, '|', $url); list($path, $var) = self::parseUrlPath($url); - $route = [null, null, null]; + $route = [null, null, null]; if (isset($path)) { // 解析模块 $module = Config::get('app_multi_module') ? array_shift($path) : null; if ($autoSearch) { // 自动搜索控制器 - $dir = APP_PATH . ($module ? $module . DS : '') . Config::get('url_controller_layer'); + $dir = APP_PATH . ($module ? $module . DS : '') . Config::get('url_controller_layer'); $suffix = App::$suffix || Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : ''; - $item = []; - $find = false; + $item = []; + $find = false; foreach ($path as $val) { $item[] = $val; - $file = $dir . DS . str_replace('.', DS, $val) . $suffix . EXT; - $file = pathinfo($file, PATHINFO_DIRNAME) . DS . Loader::parseName(pathinfo($file, PATHINFO_FILENAME), 1) . EXT; + $file = $dir . DS . str_replace('.', DS, $val) . $suffix . EXT; + $file = pathinfo($file, PATHINFO_DIRNAME) . DS . Loader::parseName(pathinfo($file, PATHINFO_FILENAME), 1) . EXT; if (is_file($file)) { $find = true; break; @@ -1238,7 +1247,7 @@ public static function parseUrl($url, $depr = '/', $autoSearch = false) } if ($find) { $controller = implode('.', $item); - $path = array_slice($path, count($item)); + $path = array_slice($path, count($item)); } else { $controller = array_shift($path); } @@ -1253,7 +1262,7 @@ public static function parseUrl($url, $depr = '/', $autoSearch = false) // 封装路由 $route = [$module, $controller, $action]; // 检查地址是否被定义过路由 - $name = strtolower($module . '/' . Loader::parseName($controller, 1) . '/' . $action); + $name = strtolower($module . '/' . Loader::parseName($controller, 1) . '/' . $action); $name2 = ''; if (empty($module) || isset($bind) && $module == $bind) { $name2 = strtolower(Loader::parseName($controller, 1) . '/' . $action); @@ -1269,7 +1278,7 @@ public static function parseUrl($url, $depr = '/', $autoSearch = false) /** * 解析URL的pathinfo参数和变量 * @access private - * @param string $url URL地址 + * @param string $url URL地址 * @return array */ private static function parseUrlPath($url) @@ -1295,9 +1304,9 @@ private static function parseUrlPath($url) /** * 检测URL和规则路由是否匹配 * @access private - * @param string $url URL地址 - * @param string $rule 路由规则 - * @param array $pattern 变量规则 + * @param string $url URL地址 + * @param string $rule 路由规则 + * @param array $pattern 变量规则 * @return array|false */ private static function match($url, $rule, $pattern) @@ -1309,11 +1318,11 @@ private static function match($url, $rule, $pattern) foreach ($m2 as $key => $val) { // val中定义了多个变量 if (false !== strpos($val, '<') && preg_match_all('/<(\w+(\??))>/', $val, $matches)) { - $value = []; + $value = []; $replace = []; foreach ($matches[1] as $name) { if (strpos($name, '?')) { - $name = substr($name, 0, -1); + $name = substr($name, 0, -1); $replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '\w+') . ')?'; } else { $replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '\w+') . ')'; @@ -1336,7 +1345,7 @@ private static function match($url, $rule, $pattern) if (0 === strpos($val, '[:')) { // 可选参数 - $val = substr($val, 1, -1); + $val = substr($val, 1, -1); $optional = true; } else { $optional = false; @@ -1370,16 +1379,28 @@ private static function match($url, $rule, $pattern) /** * 解析规则路由 * @access private - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param string $pathinfo URL地址 - * @param array $option 路由参数 - * @param array $matches 匹配的变量 + * @param string $rule 路由规则 + * @param string $route 路由地址 + * @param string $pathinfo URL地址 + * @param array $option 路由参数 + * @param array $matches 匹配的变量 + * @param bool $fromCache 通过缓存解析 * @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(); + + //保存解析缓存 + 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) { $rule = explode('/', $rule); @@ -1391,7 +1412,7 @@ private static function parseRule($rule, $route, $pathinfo, $option = [], $match $item = substr($item, 1, -1); } if (0 === strpos($item, ':')) { - $var = substr($item, 1); + $var = substr($item, 1); $matches[$var] = array_shift($paths); } else { // 过滤URL中的静态变量 @@ -1424,12 +1445,12 @@ private static function parseRule($rule, $route, $pathinfo, $option = [], $match $result = call_user_func_array($val, [$matches]); } else { if (is_array($val)) { - $fields = explode('&', $val[1]); - $model = $val[0]; + $fields = explode('&', $val[1]); + $model = $val[0]; $exception = isset($val[2]) ? $val[2] : true; } else { - $fields = ['id']; - $model = $val; + $fields = ['id']; + $model = $val; $exception = true; } $where = []; @@ -1443,7 +1464,7 @@ private static function parseRule($rule, $route, $pathinfo, $option = [], $match } } if ($match) { - $query = strpos($model, '\\') ? $model::where($where) : Loader::model($model)->where($where); + $query = strpos($model, '\\') ? $model::where($where) : Loader::model($model)->where($where); $result = $query->failException($exception)->find(); } } @@ -1492,21 +1513,21 @@ private static function parseRule($rule, $route, $pathinfo, $option = [], $match } elseif (false !== strpos($route, '\\')) { // 路由到方法 list($path, $var) = self::parseUrlPath($route); - $route = str_replace('/', '@', implode('/', $path)); - $method = strpos($route, '@') ? explode('@', $route) : $route; - $result = ['type' => 'method', 'method' => $method, 'var' => $var]; + $route = str_replace('/', '@', implode('/', $path)); + $method = strpos($route, '@') ? explode('@', $route) : $route; + $result = ['type' => 'method', 'method' => $method, 'var' => $var]; } elseif (0 === strpos($route, '@')) { // 路由到控制器 - $route = substr($route, 1); + $route = substr($route, 1); list($route, $var) = self::parseUrlPath($route); - $result = ['type' => 'controller', 'controller' => implode('/', $route), 'var' => $var]; + $result = ['type' => 'controller', 'controller' => implode('/', $route), 'var' => $var]; $request->action(array_pop($route)); $request->controller($route ? array_pop($route) : Config::get('default_controller')); $request->module($route ? array_pop($route) : Config::get('default_module')); App::$modulePath = APP_PATH . (Config::get('app_multi_module') ? $request->module() . DS : ''); } else { // 路由到模块/控制器/操作 - $result = self::parseModule($route); + $result = self::parseModule($route, isset($option['convert']) ? $option['convert'] : false); } // 开启请求缓存 if ($request->isGet() && isset($option['cache'])) { @@ -1514,9 +1535,9 @@ private static function parseRule($rule, $route, $pathinfo, $option = [], $match if (is_array($cache)) { list($key, $expire, $tag) = array_pad($cache, 3, null); } else { - $key = str_replace('|', '/', $pathinfo); + $key = str_replace('|', '/', $pathinfo); $expire = $cache; - $tag = null; + $tag = null; } $request->cache($key, $expire, $tag); } @@ -1526,16 +1547,17 @@ private static function parseRule($rule, $route, $pathinfo, $option = [], $match /** * 解析URL地址为 模块/控制器/操作 * @access private - * @param string $url URL地址 + * @param string $url URL地址 + * @param bool $convert 是否自动转换URL地址 * @return array */ - private static function parseModule($url) + private static function parseModule($url, $convert = false) { list($path, $var) = self::parseUrlPath($url); - $action = array_pop($path); - $controller = !empty($path) ? array_pop($path) : null; - $module = Config::get('app_multi_module') && !empty($path) ? array_pop($path) : null; - $method = Request::instance()->method(); + $action = array_pop($path); + $controller = !empty($path) ? array_pop($path) : null; + $module = Config::get('app_multi_module') && !empty($path) ? array_pop($path) : null; + $method = Request::instance()->method(); if (Config::get('use_action_prefix') && !empty(self::$methodPrefix[$method])) { // 操作方法前缀支持 $action = 0 !== strpos($action, self::$methodPrefix[$method]) ? self::$methodPrefix[$method] . $action : $action; @@ -1543,14 +1565,14 @@ private static function parseModule($url) // 设置当前请求的路由变量 Request::instance()->route($var); // 路由到模块/控制器/操作 - return ['type' => 'module', 'module' => [$module, $controller, $action], 'convert' => false]; + return ['type' => 'module', 'module' => [$module, $controller, $action], 'convert' => $convert]; } /** * 解析URL地址中的参数Request对象 * @access private - * @param string $url 路由规则 - * @param array $var 变量 + * @param string $url 路由规则 + * @param array $var 变量 * @return void */ private static function parseUrlParams($url, &$var = []) @@ -1578,7 +1600,7 @@ private static function parseVar($rule) if (false !== strpos($val, '<') && preg_match_all('/<(\w+(\??))>/', $val, $matches)) { foreach ($matches[1] as $name) { if (strpos($name, '?')) { - $name = substr($name, 0, -1); + $name = substr($name, 0, -1); $optional = true; } else { $optional = false; @@ -1590,14 +1612,34 @@ private static function parseVar($rule) if (0 === strpos($val, '[:')) { // 可选参数 $optional = true; - $val = substr($val, 1, -1); + $val = substr($val, 1, -1); } if (0 === strpos($val, ':')) { // URL变量 - $name = substr($val, 1); + $name = substr($val, 1); $var[$name] = $optional ? 2 : 1; } } 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; + } } diff --git a/library/think/Session.php b/library/think/Session.php index 1d02518e51..ccd262820c 100644 --- a/library/think/Session.php +++ b/library/think/Session.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -16,7 +16,7 @@ class Session { protected static $prefix = ''; - protected static $init = null; + protected static $init = null; /** * 设置或者获取session作用域(前缀) @@ -170,14 +170,14 @@ public static function get($name = '', $prefix = null) // 获取session if (strpos($name, '.')) { list($name1, $name2) = explode('.', $name); - $value = isset($_SESSION[$prefix][$name1][$name2]) ? $_SESSION[$prefix][$name1][$name2] : null; + $value = isset($_SESSION[$prefix][$name1][$name2]) ? $_SESSION[$prefix][$name1][$name2] : null; } else { $value = isset($_SESSION[$prefix][$name]) ? $_SESSION[$prefix][$name] : null; } } else { if (strpos($name, '.')) { list($name1, $name2) = explode('.', $name); - $value = isset($_SESSION[$name1][$name2]) ? $_SESSION[$name1][$name2] : null; + $value = isset($_SESSION[$name1][$name2]) ? $_SESSION[$name1][$name2] : null; } else { $value = isset($_SESSION[$name]) ? $_SESSION[$name] : null; } diff --git a/library/think/Template.php b/library/think/Template.php index 3ec89e5628..568e79a638 100644 --- a/library/think/Template.php +++ b/library/think/Template.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -24,35 +24,35 @@ class Template protected $data = []; // 引擎配置 protected $config = [ - 'view_path' => '', // 模板路径 - 'view_base' => '', - 'view_suffix' => 'html', // 默认模板文件后缀 - 'view_depr' => DS, - 'cache_suffix' => 'php', // 默认模板缓存后缀 + 'view_path' => '', // 模板路径 + 'view_base' => '', + 'view_suffix' => 'html', // 默认模板文件后缀 + 'view_depr' => DS, + 'cache_suffix' => 'php', // 默认模板缓存后缀 'tpl_deny_func_list' => 'echo,exit', // 模板引擎禁用函数 - 'tpl_deny_php' => false, // 默认模板引擎是否禁用PHP原生代码 - 'tpl_begin' => '{', // 模板引擎普通标签开始标记 - 'tpl_end' => '}', // 模板引擎普通标签结束标记 - 'strip_space' => false, // 是否去除模板文件里面的html空格与换行 - 'tpl_cache' => true, // 是否开启模板编译缓存,设为false则每次都会重新编译 - 'compile_type' => 'file', // 模板编译类型 - 'cache_prefix' => '', // 模板缓存前缀标识,可以动态改变 - 'cache_time' => 0, // 模板缓存有效期 0 为永久,(以数字为值,单位:秒) - 'layout_on' => false, // 布局模板开关 - 'layout_name' => 'layout', // 布局模板入口文件 - 'layout_item' => '{__CONTENT__}', // 布局模板的内容替换标识 - 'taglib_begin' => '{', // 标签库标签开始标记 - 'taglib_end' => '}', // 标签库标签结束标记 - 'taglib_load' => true, // 是否使用内置标签库之外的其它标签库,默认自动检测 - 'taglib_build_in' => 'cx', // 内置标签库名称(标签使用不必指定标签库名称),以逗号分隔 注意解析顺序 - 'taglib_pre_load' => '', // 需要额外加载的标签库(须指定标签库名称),多个以逗号分隔 - 'display_cache' => false, // 模板渲染缓存 - 'cache_id' => '', // 模板缓存ID + 'tpl_deny_php' => false, // 默认模板引擎是否禁用PHP原生代码 + 'tpl_begin' => '{', // 模板引擎普通标签开始标记 + 'tpl_end' => '}', // 模板引擎普通标签结束标记 + 'strip_space' => false, // 是否去除模板文件里面的html空格与换行 + 'tpl_cache' => true, // 是否开启模板编译缓存,设为false则每次都会重新编译 + 'compile_type' => 'file', // 模板编译类型 + 'cache_prefix' => '', // 模板缓存前缀标识,可以动态改变 + 'cache_time' => 0, // 模板缓存有效期 0 为永久,(以数字为值,单位:秒) + 'layout_on' => false, // 布局模板开关 + 'layout_name' => 'layout', // 布局模板入口文件 + 'layout_item' => '{__CONTENT__}', // 布局模板的内容替换标识 + 'taglib_begin' => '{', // 标签库标签开始标记 + 'taglib_end' => '}', // 标签库标签结束标记 + 'taglib_load' => true, // 是否使用内置标签库之外的其它标签库,默认自动检测 + 'taglib_build_in' => 'cx', // 内置标签库名称(标签使用不必指定标签库名称),以逗号分隔 注意解析顺序 + 'taglib_pre_load' => '', // 需要额外加载的标签库(须指定标签库名称),多个以逗号分隔 + 'display_cache' => false, // 模板渲染缓存 + 'cache_id' => '', // 模板缓存ID 'tpl_replace_string' => [], - 'tpl_var_identify' => 'array', // .语法变量识别,array|object|'', 为空时自动识别 + 'tpl_var_identify' => 'array', // .语法变量识别,array|object|'', 为空时自动识别 ]; - private $literal = []; + private $literal = []; private $includeFile = []; // 记录所有模板包含的文件路径及更新时间 protected $storage; @@ -63,33 +63,23 @@ class Template */ public function __construct(array $config = []) { - $this->config['cache_path'] = TEMP_PATH; - $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['tpl_begin'] = $this->stripPreg($this->config['tpl_begin']); - $this->config['tpl_end'] = $this->stripPreg($this->config['tpl_end']); + $this->config['cache_path'] = TEMP_PATH; + $this->config = array_merge($this->config, $config); + + $this->config['taglib_begin_origin'] = $this->config['taglib_begin']; + $this->config['taglib_end_origin'] = $this->config['taglib_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'; - $class = false !== strpos($type, '\\') ? $type : '\\think\\template\\driver\\' . ucwords($type); + $type = $this->config['compile_type'] ? $this->config['compile_type'] : 'File'; + $class = false !== strpos($type, '\\') ? $type : '\\think\\template\\driver\\' . ucwords($type); $this->storage = new $class(); } - /** - * 字符串替换 避免正则混淆 - * @access private - * @param string $str - * @return string - */ - private function stripPreg($str) - { - return str_replace( - ['{', '}', '(', ')', '|', '[', ']', '-', '+', '*', '.', '^', '?'], - ['\{', '\}', '\(', '\)', '\|', '\[', '\]', '\-', '\+', '\*', '\.', '\^', '\?'], - $str); - } - /** * 模板变量赋值 * @access public @@ -121,7 +111,7 @@ public function __set($name, $value) * 模板引擎配置项 * @access public * @param array|string $config - * @return void|array + * @return string|void|array */ public function config($config) { @@ -184,7 +174,7 @@ public function fetch($template, $vars = [], $config = []) } $template = $this->parseTemplateFile($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)) { // 缓存无效 重新模板编译 $content = file_get_contents($template); @@ -235,7 +225,7 @@ public function display($content, $vars = [], $config = []) * @access public * @param mixed $name 布局模板名称 false 则关闭布局 * @param string $replace 布局模板内容替换标识 - * @return object + * @return Template */ public function layout($name, $replace = '') { @@ -342,7 +332,7 @@ private function compiler(&$content, $cacheFile) $this->parse($content); if ($this->config['strip_space']) { /* 去除html空格与换行 */ - $find = ['~>\s+<~', '~>(\s+\n|\r)~']; + $find = ['~>\s+<~', '~>(\s+\n|\r)~']; $replace = ['><', '>']; $content = preg_replace($find, $replace, $content); } @@ -475,11 +465,11 @@ private function parseLayout(&$content) private function parseInclude(&$content) { $regex = $this->getRegex('include'); - $func = function ($template) use (&$func, &$regex, &$content) { + $func = function ($template) use (&$func, &$regex, &$content) { if (preg_match_all($regex, $template, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $array = $this->parseAttr($match[0]); - $file = $array['file']; + $file = $array['file']; unset($array['file']); // 分析模板文件名并读取内容 $parseStr = $this->parseTemplateName($file); @@ -510,10 +500,10 @@ private function parseInclude(&$content) */ private function parseExtend(&$content) { - $regex = $this->getRegex('extend'); - $array = $blocks = $baseBlocks = []; + $regex = $this->getRegex('extend'); + $array = $blocks = $baseBlocks = []; $extend = ''; - $func = function ($template) use (&$func, &$regex, &$array, &$extend, &$blocks, &$baseBlocks) { + $func = function ($template) use (&$func, &$regex, &$array, &$extend, &$blocks, &$baseBlocks) { if (preg_match($regex, $template, $matches)) { if (!isset($array[$matches['name']])) { $array[$matches['name']] = 1; @@ -557,13 +547,13 @@ private function parseExtend(&$content) $blocks[$parent]['content'] = str_replace($blocks[$name]['begin'] . $blocks[$name]['content'] . $blocks[$name]['end'], $replace, $blocks[$parent]['content']); } $blocks[$name]['content'] = $replace; - $children[$parent][] = $name; + $children[$parent][] = $name; continue; } } elseif (!empty($val['parent'])) { // 如果子标签没有被继承则用原值 $children[$val['parent']][] = $name; - $blocks[$name] = $val; + $blocks[$name] = $val; } if (!$val['parent']) { // 替换模板中的顶级block标签 @@ -593,7 +583,7 @@ private function parseLiteral(&$content, $restore = false) // 替换literal标签 foreach ($matches as $match) { $this->literal[] = substr($match[0], strlen($match[1]), -strlen($match[2])); - $content = str_replace($match[0], "", $content); + $content = str_replace($match[0], "", $content); $count++; } } else { @@ -618,30 +608,30 @@ private function parseLiteral(&$content, $restore = false) */ private function parseBlock(&$content, $sort = false) { - $regex = $this->getRegex('block'); + $regex = $this->getRegex('block'); $result = []; if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) { $right = $keys = []; foreach ($matches as $match) { if (empty($match['name'][0])) { if (count($right) > 0) { - $tag = array_pop($right); - $start = $tag['offset'] + strlen($tag['tag']); - $length = $match[0][1] - $start; + $tag = array_pop($right); + $start = $tag['offset'] + strlen($tag['tag']); + $length = $match[0][1] - $start; $result[$tag['name']] = [ - 'begin' => $tag['tag'], + 'begin' => $tag['tag'], 'content' => substr($content, $start, $length), - 'end' => $match[0][0], - 'parent' => count($right) ? end($right)['name'] : '', + 'end' => $match[0][0], + 'parent' => count($right) ? end($right)['name'] : '', ]; $keys[$tag['name']] = $match[0][1]; } } else { // 标签头压入栈 $right[] = [ - 'name' => $match[2][0], + 'name' => $match[2][0], 'offset' => $match[0][1], - 'tag' => $match[0][0], + 'tag' => $match[0][0], ]; } } @@ -685,10 +675,11 @@ public function parseTagLib($tagLib, &$content, $hide = false) if (false !== strpos($tagLib, '\\')) { // 支持指定标签库的命名空间 $className = $tagLib; - $tagLib = substr($tagLib, strrpos($tagLib, '\\') + 1); + $tagLib = substr($tagLib, strrpos($tagLib, '\\') + 1); } else { $className = '\\think\\template\\taglib\\' . ucwords($tagLib); } + /** @var Taglib $tLib */ $tLib = new $className($this); $tLib->parseTag($content, $hide ? '' : $tagLib); return; @@ -730,7 +721,7 @@ private function parseTag(&$content) $regex = $this->getRegex('tag'); if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { - $str = stripslashes($match[1]); + $str = stripslashes($match[1]); $flag = substr($str, 0, 1); switch ($flag) { case '$': @@ -738,7 +729,7 @@ private function parseTag(&$content) // 是否带有?号 if (false !== $pos = strpos($str, '?')) { $array = preg_split('/([!=]={1,2}|(?<]={0,1})/', substr($str, 0, $pos), 2, PREG_SPLIT_DELIM_CAPTURE); - $name = $array[0]; + $name = $array[0]; $this->parseVar($name); $this->parseVarFunction($name); @@ -848,7 +839,7 @@ public function parseVar(&$varStr) $parseStr = $_varParseList[$match[0]]; } else { if (strpos($match[0], '.')) { - $vars = explode('.', $match[0]); + $vars = explode('.', $match[0]); $first = array_shift($vars); if ('$Think' == $first) { // 所有以Think.打头的以特殊变量对待 无需模板赋值就可以输出 @@ -902,7 +893,7 @@ public function parseVarFunction(&$varStr) return; } static $_varFunctionList = []; - $_key = md5($varStr); + $_key = md5($varStr); //如果已经解析过该变量字串,则直接返回变量值 if (isset($_varFunctionList[$_key])) { $varStr = $_varFunctionList[$_key]; @@ -931,7 +922,7 @@ public function parseVarFunction(&$varStr) if (isset($args[1])) { if (strstr($args[1], '###')) { $args[1] = str_replace('###', $name, $args[1]); - $name = "$fun($args[1])"; + $name = "$fun($args[1])"; } else { $name = "$fun($name,$args[1])"; } @@ -944,7 +935,7 @@ public function parseVarFunction(&$varStr) } } $_varFunctionList[$_key] = $name; - $varStr = $name; + $varStr = $name; } return; } @@ -958,7 +949,7 @@ public function parseVarFunction(&$varStr) */ public function parseThinkVar($vars) { - $type = strtoupper(trim(array_shift($vars))); + $type = strtoupper(trim(array_shift($vars))); $param = implode('.', $vars); if ($vars) { switch ($type) { @@ -1029,7 +1020,7 @@ public function parseThinkVar($vars) */ private function parseTemplateName($templateName) { - $array = explode(',', $templateName); + $array = explode(',', $templateName); $parseStr = ''; foreach ($array as $templateName) { if (empty($templateName)) { @@ -1067,11 +1058,11 @@ private function parseTemplateFile($template) } if ($this->config['view_base']) { $module = isset($module) ? $module : Request::instance()->module(); - $path = $this->config['view_base'] . ($module ? $module . DS : ''); + $path = $this->config['view_base'] . ($module ? $module . DS : ''); } else { $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)) { @@ -1094,15 +1085,15 @@ private function getRegex($tagName) $regex = ''; if ('tag' == $tagName) { $begin = $this->config['tpl_begin']; - $end = $this->config['tpl_end']; + $end = $this->config['tpl_end']; if (strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1) { $regex = $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>[^' . $end . ']*))' . $end; } else { $regex = $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>(?:(?!' . $end . ').)*))' . $end; } } else { - $begin = $this->config['taglib_begin']; - $end = $this->config['taglib_end']; + $begin = $this->config['taglib_begin']; + $end = $this->config['taglib_end']; $single = strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1 ? true : false; switch ($tagName) { case 'block': @@ -1128,6 +1119,7 @@ private function getRegex($tagName) break; case 'include': $name = 'file'; + // no break case 'taglib': case 'layout': case 'extend': diff --git a/library/think/Url.php b/library/think/Url.php index 8cdd0226dd..4e024a2847 100644 --- a/library/think/Url.php +++ b/library/think/Url.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -34,11 +34,11 @@ public static function build($url = '', $vars = '', $suffix = true, $domain = fa if (0 === strpos($url, '[') && $pos = strpos($url, ']')) { // [name] 表示使用路由命名标识生成URL $name = substr($url, 1, $pos - 1); - $url = 'name' . substr($url, $pos + 1); + $url = 'name' . substr($url, $pos + 1); } if (false === strpos($url, '://') && 0 !== strpos($url, '/')) { $info = parse_url($url); - $url = !empty($info['path']) ? $info['path'] : ''; + $url = !empty($info['path']) ? $info['path'] : ''; if (isset($info['fragment'])) { // 解析锚点 $anchor = $info['fragment']; @@ -87,7 +87,7 @@ public static function build($url = '', $vars = '', $suffix = true, $domain = fa throw new \InvalidArgumentException('route name not exists:' . $name); } else { // 检查别名路由 - $alias = Route::rules('alias'); + $alias = Route::rules('alias'); $matchAlias = false; if ($alias) { // 别名路由解析 @@ -96,7 +96,7 @@ public static function build($url = '', $vars = '', $suffix = true, $domain = fa $val = $val[0]; } if (0 === strpos($url, $val)) { - $url = $key . substr($url, strlen($val)); + $url = $key . substr($url, strlen($val)); $matchAlias = true; break; } @@ -118,14 +118,14 @@ public static function build($url = '', $vars = '', $suffix = true, $domain = fa $type = Route::getBind('type'); if ($type) { $bind = Route::getBind($type); - if (0 === strpos($url, $bind)) { + if ($bind && 0 === strpos($url, $bind)) { $url = substr($url, strlen($bind) + 1); } } } // 还原URL分隔符 $depr = Config::get('pathinfo_depr'); - $url = str_replace('/', $depr, $url); + $url = str_replace('/', $depr, $url); // URL后缀 $suffix = in_array($url, ['/', '']) ? '' : self::parseSuffix($suffix); @@ -177,17 +177,17 @@ protected static function parseUrl($url, &$domain) $url = substr($url, 1); } else { // 解析到 模块/控制器/操作 - $module = $request->module(); + $module = $request->module(); $domains = Route::rules('domain'); if (true === $domain && 2 == substr_count($url, '/')) { $current = $request->host(); - $match = []; - $pos = []; + $match = []; + $pos = []; foreach ($domains as $key => $item) { if (isset($item['[bind]']) && 0 === strpos($url, $item['[bind]'][0])) { $pos[$key] = strlen($item['[bind]'][0]) + 1; - $match[] = $key; - $module = ''; + $match[] = $key; + $module = ''; } } if ($match) { @@ -198,7 +198,7 @@ protected static function parseUrl($url, &$domain) } } self::$bindCheck = true; - $url = substr($url, $pos[$domain]); + $url = substr($url, $pos[$domain]); } } elseif ($domain) { if (isset($domains[$domain]['[bind]'][0])) { @@ -210,17 +210,21 @@ protected static function parseUrl($url, &$domain) } $module = $module ? $module . '/' : ''; - $controller = Loader::parseName($request->controller()); + $controller = $request->controller(); if ('' == $url) { // 空字符串输出当前的 模块/控制器/操作 - $url = $module . $controller . '/' . $request->action(); + $action = $request->action(); } else { - $path = explode('/', $url); - $action = Config::get('url_convert') ? strtolower(array_pop($path)) : array_pop($path); - $controller = empty($path) ? $controller : (Config::get('url_convert') ? Loader::parseName(array_pop($path)) : array_pop($path)); - $module = empty($path) ? $module : array_pop($path) . '/'; - $url = $module . $controller . '/' . $action; + $path = explode('/', $url); + $action = array_pop($path); + $controller = empty($path) ? $controller : array_pop($path); + $module = empty($path) ? $module : array_pop($path) . '/'; + } + if (Config::get('url_convert')) { + $action = strtolower($action); + $controller = Loader::parseName($controller); } + $url = $module . $controller . '/' . $action; } return $url; } @@ -231,7 +235,7 @@ protected static function parseDomain(&$url, $domain) if (!$domain) { return ''; } - $request = Request::instance(); + $request = Request::instance(); $rootDomain = Config::get('url_domain_root'); if (true === $domain) { // 自动判断域名 @@ -245,7 +249,7 @@ protected static function parseDomain(&$url, $domain) foreach ($domains as $key => $rule) { $rule = is_array($rule) ? $rule[0] : $rule; if (is_string($rule) && false === strpos($key, '*') && 0 === strpos($url, $rule)) { - $url = ltrim($url, $rule); + $url = ltrim($url, $rule); $domain = $key; // 生成对应子域名 if (!empty($rootDomain)) { @@ -265,7 +269,7 @@ protected static function parseDomain(&$url, $domain) } else { if (empty($rootDomain)) { - $host = Config::get('app_host') ?: $request->host(); + $host = Config::get('app_host') ?: $request->host(); $rootDomain = substr_count($host, '.') > 1 ? substr(strstr($host, '.'), 1) : $host; } if (substr_count($domain, '.') < 2 && !strpos($domain, $rootDomain)) { @@ -298,7 +302,7 @@ public static function getRuleUrl($rule, &$vars = []) foreach ($rule as $item) { list($url, $pattern, $domain, $suffix) = $item; if (empty($pattern)) { - return [$url, $domain, $suffix]; + return [rtrim($url, '$'), $domain, $suffix]; } $type = Config::get('url_common_param'); foreach ($pattern as $key => $val) { @@ -307,7 +311,7 @@ public static function getRuleUrl($rule, &$vars = []) unset($vars[$key]); $result = [$url, $domain, $suffix]; } elseif (2 == $val) { - $url = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url); + $url = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url); $result = [$url, $domain, $suffix]; } else { break; diff --git a/library/think/Validate.php b/library/think/Validate.php index c12ee6d1b7..92069fa24e 100644 --- a/library/think/Validate.php +++ b/library/think/Validate.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -36,55 +36,56 @@ class Validate // 验证规则默认提示信息 protected static $typeMsg = [ - 'require' => ':attribute不能为空', - 'number' => ':attribute必须是数字', - 'integer' => ':attribute必须是整数', - 'float' => ':attribute必须是浮点数', - 'boolean' => ':attribute必须是布尔值', - 'email' => ':attribute格式不符', - 'array' => ':attribute必须是数组', - 'accepted' => ':attribute必须是yes、on或者1', - 'date' => ':attribute格式不符合', - 'file' => ':attribute不是有效的上传文件', - 'image' => ':attribute不是有效的图像文件', - 'alpha' => ':attribute只能是字母', - 'alphaNum' => ':attribute只能是字母和数字', - 'alphaDash' => ':attribute只能是字母、数字和下划线_及破折号-', - 'activeUrl' => ':attribute不是有效的域名或者IP', - 'chs' => ':attribute只能是汉字', - 'chsAlpha' => ':attribute只能是汉字、字母', - 'chsAlphaNum' => ':attribute只能是汉字、字母和数字', - 'chsDash' => ':attribute只能是汉字、字母、数字和下划线_及破折号-', - 'url' => ':attribute不是有效的URL地址', - 'ip' => ':attribute不是有效的IP地址', - 'dateFormat' => ':attribute必须使用日期格式 :rule', - 'in' => ':attribute必须在 :rule 范围内', - 'notIn' => ':attribute不能在 :rule 范围内', - 'between' => ':attribute只能在 :1 - :2 之间', - 'notBetween' => ':attribute不能在 :1 - :2 之间', - 'length' => ':attribute长度不符合要求 :rule', - 'max' => ':attribute长度不能超过 :rule', - 'min' => ':attribute长度不能小于 :rule', - 'after' => ':attribute日期不能小于 :rule', - 'before' => ':attribute日期不能超过 :rule', - 'expire' => '不在有效期内 :rule', - 'allowIp' => '不允许的IP访问', - 'denyIp' => '禁止的IP访问', - 'confirm' => ':attribute和确认字段:2不一致', - 'different' => ':attribute和比较字段:2不能相同', - 'egt' => ':attribute必须大于等于 :rule', - 'gt' => ':attribute必须大于 :rule', - 'elt' => ':attribute必须小于等于 :rule', - 'lt' => ':attribute必须小于 :rule', - 'eq' => ':attribute必须等于 :rule', - 'unique' => ':attribute已存在', - 'regex' => ':attribute不符合指定规则', - 'method' => '无效的请求类型', - 'token' => '令牌数据无效', - 'fileSize' => '上传文件大小不符', - 'fileExt' => '上传文件后缀不符', - 'fileMime' => '上传文件类型不符', - + 'require' => ':attribute require', + 'number' => ':attribute must be numeric', + 'integer' => ':attribute must be integer', + 'float' => ':attribute must be float', + 'boolean' => ':attribute must be bool', + 'email' => ':attribute not a valid email address', + 'array' => ':attribute must be a array', + 'accepted' => ':attribute must be yes,on or 1', + 'date' => ':attribute not a valid datetime', + 'file' => ':attribute not a valid file', + 'image' => ':attribute not a valid image', + 'alpha' => ':attribute must be alpha', + 'alphaNum' => ':attribute must be alpha-numeric', + 'alphaDash' => ':attribute must be alpha-numeric, dash, underscore', + 'activeUrl' => ':attribute not a valid domain or ip', + 'chs' => ':attribute must be chinese', + 'chsAlpha' => ':attribute must be chinese or alpha', + 'chsAlphaNum' => ':attribute must be chinese,alpha-numeric', + 'chsDash' => ':attribute must be chinese,alpha-numeric,underscore, dash', + 'url' => ':attribute not a valid url', + 'ip' => ':attribute not a valid ip', + 'dateFormat' => ':attribute must be dateFormat of :rule', + 'in' => ':attribute must be in :rule', + 'notIn' => ':attribute be notin :rule', + 'between' => ':attribute must between :1 - :2', + 'notBetween' => ':attribute not between :1 - :2', + 'length' => 'size of :attribute must be :rule', + 'max' => 'max size of :attribute must be :rule', + 'min' => 'min size of :attribute must be :rule', + 'after' => ':attribute cannot be less than :rule', + 'before' => ':attribute cannot exceed :rule', + 'afterWith' => ':attribute cannot be less than :rule', + 'beforeWith' => ':attribute cannot exceed :rule', + 'expire' => ':attribute not within :rule', + 'allowIp' => 'access IP is not allowed', + 'denyIp' => 'access IP denied', + 'confirm' => ':attribute out of accord with :2', + 'different' => ':attribute cannot be same with :2', + 'egt' => ':attribute must greater than or equal :rule', + 'gt' => ':attribute must greater than :rule', + 'elt' => ':attribute must less than or equal :rule', + 'lt' => ':attribute must less than :rule', + 'eq' => ':attribute must equal :rule', + 'unique' => ':attribute has exists', + 'regex' => ':attribute not conform to the rules', + 'method' => 'invalid Request method', + 'token' => 'invalid token', + 'fileSize' => 'filesize not match', + 'fileExt' => 'extensions to upload is not allowed', + 'fileMime' => 'mimetype to upload is not allowed', ]; // 当前验证场景 @@ -111,9 +112,9 @@ class Validate */ public function __construct(array $rules = [], $message = [], $field = []) { - $this->rule = array_merge($this->rule, $rules); + $this->rule = array_merge($this->rule, $rules); $this->message = array_merge($this->message, $message); - $this->field = array_merge($this->field, $field); + $this->field = array_merge($this->field, $field); } /** @@ -264,12 +265,12 @@ public function check($data, $rules = [], $scene = '') if (is_array($scene)) { // 处理场景验证字段 $change = []; - $array = []; + $array = []; foreach ($scene as $k => $val) { if (is_numeric($k)) { $array[] = $val; } else { - $array[] = $k; + $array[] = $k; $change[$k] = $val; } } @@ -279,7 +280,7 @@ public function check($data, $rules = [], $scene = '') // field => rule1|rule2... field=>['rule1','rule2',...] if (is_numeric($key)) { // [field,rule1|rule2,msg1|msg2] - $key = $item[0]; + $key = $item[0]; $rule = $item[1]; if (isset($item[2])) { $msg = is_string($item[2]) ? explode('|', $item[2]) : $item[2]; @@ -288,7 +289,7 @@ public function check($data, $rules = [], $scene = '') } } else { $rule = $item; - $msg = []; + $msg = []; } if (strpos($key, '|')) { // 字段|描述 用于指定属性名称 @@ -340,6 +341,41 @@ public function check($data, $rules = [], $scene = '') 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 @@ -361,28 +397,10 @@ protected function checkItem($field, $value, $rules, $data, $title = '', $msg = foreach ($rules as $key => $rule) { if ($rule instanceof \Closure) { $result = call_user_func_array($rule, [$value, $data]); - $info = is_numeric($key) ? '' : $key; + $info = is_numeric($key) ? '' : $key; } else { // 判断验证类型 - if (is_numeric($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; - } - } else { - $info = $type = $key; - } + list($type, $rule, $info) = $this->getValidateType($key, $rule); // 如果不是require 有数据才会行验证 if (0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) { @@ -418,6 +436,39 @@ protected function checkItem($field, $value, $rules, $data, $title = '', $msg = 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 @@ -681,21 +732,17 @@ protected function ip($value, $rule) */ protected function fileExt($file, $rule) { - if (!($file instanceof File)) { - return false; - } - if (is_string($rule)) { - $rule = explode(',', $rule); - } if (is_array($file)) { foreach ($file as $item) { - if (!$item->checkExt($rule)) { + if (!($item instanceof File) || !$item->checkExt($rule)) { return false; } } return true; - } else { + } elseif ($file instanceof File) { return $file->checkExt($rule); + } else { + return false; } } @@ -708,21 +755,17 @@ protected function fileExt($file, $rule) */ protected function fileMime($file, $rule) { - if (!($file instanceof File)) { - return false; - } - if (is_string($rule)) { - $rule = explode(',', $rule); - } if (is_array($file)) { foreach ($file as $item) { - if (!$item->checkMime($rule)) { + if (!($item instanceof File) || !$item->checkMime($rule)) { return false; } } return true; - } else { + } elseif ($file instanceof File) { return $file->checkMime($rule); + } else { + return false; } } @@ -735,18 +778,17 @@ protected function fileMime($file, $rule) */ protected function fileSize($file, $rule) { - if (!($file instanceof File)) { - return false; - } if (is_array($file)) { foreach ($file as $item) { - if (!$item->checkSize($rule)) { + if (!($item instanceof File) || !$item->checkSize($rule)) { return false; } } return true; - } else { + } elseif ($file instanceof File) { return $file->checkSize($rule); + } else { + return false; } } @@ -763,7 +805,7 @@ protected function image($file, $rule) return false; } if ($rule) { - $rule = explode(',', $rule); + $rule = explode(',', $rule); list($width, $height, $type) = getimagesize($file->getRealPath()); if (isset($rule[2])) { $imageType = strtolower($rule[2]); @@ -838,21 +880,26 @@ protected function unique($value, $rule, $data, $field) // 支持多个字段验证 $fields = explode('^', $key); foreach ($fields as $key) { - $map[$key] = $data[$key]; + if (isset($data[$key])) { + $map[$key] = $data[$key]; + } } } elseif (strpos($key, '=')) { parse_str($key, $map); - } else { + } elseif (isset($data[$field])) { $map[$key] = $data[$field]; + } else { + $map = []; } - $pk = strval(isset($rule[3]) ? $rule[3] : $db->getPk()); - if (isset($rule[2])) { - $map[$pk] = ['neq', $rule[2]]; - } elseif (isset($data[$pk])) { - $map[$pk] = ['neq', $data[$pk]]; + $pk = isset($rule[3]) ? $rule[3] : $db->getPk(); + if (is_string($pk)) { + if (isset($rule[2])) { + $map[$pk] = ['neq', $rule[2]]; + } elseif (isset($data[$pk])) { + $map[$pk] = ['neq', $data[$pk]]; + } } - if ($db->where($map)->field($pk)->find()) { return false; } @@ -885,7 +932,7 @@ protected function filter($value, $rule) list($rule, $param) = explode(',', $rule); } elseif (is_array($rule)) { $param = isset($rule[1]) ? $rule[1] : null; - $rule = $rule[0]; + $rule = $rule[0]; } else { $param = null; } @@ -1072,9 +1119,10 @@ protected function min($value, $rule) * @access protected * @param mixed $value 字段值 * @param mixed $rule 验证规则 + * @param array $data 数据 * @return bool */ - protected function after($value, $rule) + protected function after($value, $rule, $data) { return strtotime($value) >= strtotime($rule); } @@ -1084,13 +1132,42 @@ protected function after($value, $rule) * @access protected * @param mixed $value 字段值 * @param mixed $rule 验证规则 + * @param array $data 数据 * @return bool */ - protected function before($value, $rule) + protected function before($value, $rule, $data) { return strtotime($value) <= strtotime($rule); } + /** + * 验证日期字段 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function afterWith($value, $rule, $data) + { + $rule = $this->getDataValue($data, $rule); + return !is_null($rule) && strtotime($value) >= strtotime($rule); + } + + /** + * 验证日期字段 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function beforeWith($value, $rule, $data) + { + $rule = $this->getDataValue($data, $rule); + return !is_null($rule) && strtotime($value) <= strtotime($rule); + } + /** * 验证有效期 * @access protected @@ -1154,7 +1231,7 @@ protected function regex($value, $rule) // 不是正则表达式则两端补上/ $rule = '/^' . $rule . '$/'; } - return 1 === preg_match($rule, (string) $value); + return is_scalar($value) && 1 === preg_match($rule, (string) $value); } /** @@ -1204,7 +1281,7 @@ protected function getDataValue($data, $key) } elseif (strpos($key, '.')) { // 支持二维数组验证 list($name1, $name2) = explode('.', $key); - $value = isset($data[$name1][$name2]) ? $data[$name1][$name2] : null; + $value = isset($data[$name1][$name2]) ? $data[$name1][$name2] : null; } else { $value = isset($data[$key]) ? $data[$key] : null; } @@ -1233,11 +1310,13 @@ protected function getRuleMsg($attribute, $title, $type, $rule) } elseif (0 === strpos($type, 'require')) { $msg = self::$typeMsg['require']; } else { - $msg = $title . '规则错误'; + $msg = $title . Lang::get('not conform to the rules'); } if (is_string($msg) && 0 === strpos($msg, '{%')) { $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, ':')) { @@ -1250,7 +1329,8 @@ protected function getRuleMsg($attribute, $title, $type, $rule) $msg = str_replace( [':attribute', ':rule', ':1', ':2', ':3'], [$title, (string) $rule, $array[0], $array[1], $array[2]], - $msg); + $msg + ); } return $msg; } diff --git a/library/think/View.php b/library/think/View.php index a521e421d6..9c32002ee5 100644 --- a/library/think/View.php +++ b/library/think/View.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -33,20 +33,20 @@ class View public function __construct($engine = [], $replace = []) { // 初始化模板引擎 - $this->engine((array) $engine); + $this->engine($engine); // 基础替换字符串 $request = Request::instance(); - $base = $request->root(); - $root = strpos($base, '.') ? ltrim(dirname($base), DS) : $base; + $base = $request->root(); + $root = strpos($base, '.') ? ltrim(dirname($base), DS) : $base; if ('' != $root) { $root = '/' . ltrim($root, '/'); } $baseReplace = [ - '__ROOT__' => $root, - '__URL__' => $base . '/' . $request->module() . '/' . Loader::parseName($request->controller()), + '__ROOT__' => $root, + '__URL__' => $base . '/' . $request->module() . '/' . Loader::parseName($request->controller()), '__STATIC__' => $root . '/static', - '__CSS__' => $root . '/static/css', - '__JS__' => $root . '/static/js', + '__CSS__' => $root . '/static/css', + '__JS__' => $root . '/static/js', ]; $this->replace = array_merge($baseReplace, (array) $replace); } @@ -108,7 +108,7 @@ public function assign($name, $value = '') public function engine($options = []) { if (is_string($options)) { - $type = $options; + $type = $options; $options = []; } else { $type = !empty($options['type']) ? $options['type'] : 'Think'; @@ -157,6 +157,9 @@ public function fetch($template = '', $vars = [], $replace = [], $config = [], $ // 渲染输出 try { $method = $renderContent ? 'display' : 'fetch'; + // 允许用户自定义模板的字符串替换 + $replace = array_merge($this->replace, $replace, (array) $this->engine->config('tpl_replace_string')); + $this->engine->config('tpl_replace_string', $replace); $this->engine->$method($template, $vars, $config); } catch (\Exception $e) { ob_end_clean(); @@ -167,11 +170,6 @@ public function fetch($template = '', $vars = [], $replace = [], $config = [], $ $content = ob_get_clean(); // 内容过滤标签 Hook::listen('view_filter', $content); - // 允许用户自定义模板的字符串替换 - $replace = array_merge($this->replace, $replace); - if (!empty($replace)) { - $content = strtr($content, $replace); - } return $content; } diff --git a/library/think/cache/Driver.php b/library/think/cache/Driver.php index d40f48472b..b923adcc05 100644 --- a/library/think/cache/Driver.php +++ b/library/think/cache/Driver.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -188,12 +188,12 @@ public function tag($name, $keys = null, $overlay = false) protected function setTagItem($name) { if ($this->tag) { - $key = 'tag_' . md5($this->tag); + $key = 'tag_' . md5($this->tag); $this->tag = null; if ($this->has($key)) { - $value = explode(',', $this->get($key)); + $value = explode(',', $this->get($key)); $value[] = $name; - $value = implode(',', array_unique($value)); + $value = implode(',', array_unique($value)); } else { $value = $name; } @@ -209,7 +209,7 @@ protected function setTagItem($name) */ protected function getTagItem($tag) { - $key = 'tag_' . md5($tag); + $key = 'tag_' . md5($tag); $value = $this->get($key); if ($value) { return array_filter(explode(',', $value)); diff --git a/library/think/cache/driver/File.php b/library/think/cache/driver/File.php index 95021e0520..6385231a41 100644 --- a/library/think/cache/driver/File.php +++ b/library/think/cache/driver/File.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -20,13 +20,15 @@ class File extends Driver { protected $options = [ - 'expire' => 0, - 'cache_subdir' => true, - 'prefix' => '', - 'path' => CACHE_PATH, + 'expire' => 0, + 'cache_subdir' => true, + 'prefix' => '', + 'path' => CACHE_PATH, 'data_compress' => false, ]; + protected $expire; + /** * 构造函数 * @param array $options @@ -61,10 +63,11 @@ private function init() /** * 取得变量的存储文件名 * @access protected - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 + * @param bool $auto 是否自动创建目录 * @return string */ - protected function getCacheKey($name) + protected function getCacheKey($name, $auto = false) { $name = md5($name); if ($this->options['cache_subdir']) { @@ -75,8 +78,9 @@ protected function getCacheKey($name) $name = $this->options['prefix'] . DS . $name; } $filename = $this->options['path'] . $name . '.php'; - $dir = dirname($filename); - if (!is_dir($dir)) { + $dir = dirname($filename); + + if ($auto && !is_dir($dir)) { mkdir($dir, 0755, true); } return $filename; @@ -107,11 +111,13 @@ public function get($name, $default = false) return $default; } $content = file_get_contents($filename); + $this->expire = null; if (false !== $content) { $expire = (int) substr($content, 8, 12); - if (0 != $expire && $_SERVER['REQUEST_TIME'] > filemtime($filename) + $expire) { + if (0 != $expire && time() > filemtime($filename) + $expire) { return $default; } + $this->expire = $expire; $content = substr($content, 32); if ($this->options['data_compress'] && function_exists('gzcompress')) { //启用数据压缩 @@ -140,7 +146,7 @@ public function set($name, $value, $expire = null) if ($expire instanceof \DateTime) { $expire = $expire->getTimestamp() - time(); } - $filename = $this->getCacheKey($name); + $filename = $this->getCacheKey($name, true); if ($this->tag && !is_file($filename)) { $first = true; } @@ -149,7 +155,7 @@ public function set($name, $value, $expire = null) //数据压缩 $data = gzcompress($data, 3); } - $data = "\n" . $data; + $data = "\n" . $data; $result = file_put_contents($filename, $data); if ($result) { isset($first) && $this->setTagItem($filename); @@ -171,10 +177,13 @@ public function inc($name, $step = 1) { if ($this->has($name)) { $value = $this->get($name) + $step; + $expire = $this->expire; } else { $value = $step; + $expire = 0; } - return $this->set($name, $value, 0) ? $value : false; + + return $this->set($name, $value, $expire) ? $value : false; } /** @@ -188,10 +197,13 @@ public function dec($name, $step = 1) { if ($this->has($name)) { $value = $this->get($name) - $step; + $expire = $this->expire; } else { $value = -$step; + $expire = 0; } - return $this->set($name, $value, 0) ? $value : false; + + return $this->set($name, $value, $expire) ? $value : false; } /** @@ -203,7 +215,10 @@ public function dec($name, $step = 1) public function rm($name) { $filename = $this->getCacheKey($name); - return $this->unlink($filename); + try { + return $this->unlink($filename); + } catch (\Exception $e) { + } } /** @@ -226,7 +241,10 @@ public function clear($tag = null) $files = (array) glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DS : '') . '*'); foreach ($files as $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); } else { unlink($path); diff --git a/library/think/cache/driver/Lite.php b/library/think/cache/driver/Lite.php index 6ca8072ebd..c98debea55 100644 --- a/library/think/cache/driver/Lite.php +++ b/library/think/cache/driver/Lite.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -21,7 +21,7 @@ class Lite extends Driver { protected $options = [ 'prefix' => '', - 'path' => '', + 'path' => '', 'expire' => 0, // 等于 10*365*24*3600(10年) ]; diff --git a/library/think/cache/driver/Memcache.php b/library/think/cache/driver/Memcache.php index dcedec36a5..780a0e933a 100644 --- a/library/think/cache/driver/Memcache.php +++ b/library/think/cache/driver/Memcache.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -16,12 +16,12 @@ class Memcache extends Driver { protected $options = [ - 'host' => '127.0.0.1', - 'port' => 11211, - 'expire' => 0, - 'timeout' => 0, // 超时时间(单位:毫秒) + 'host' => '127.0.0.1', + 'port' => 11211, + 'expire' => 0, + 'timeout' => 0, // 超时时间(单位:毫秒) 'persistent' => true, - 'prefix' => '', + 'prefix' => '', ]; /** @@ -63,7 +63,7 @@ public function __construct($options = []) public function has($name) { $key = $this->getCacheKey($name); - return $this->handler->get($key) ? true : false; + return false !== $this->handler->get($key); } /** @@ -131,9 +131,9 @@ public function inc($name, $step = 1) */ public function dec($name, $step = 1) { - $key = $this->getCacheKey($name); + $key = $this->getCacheKey($name); $value = $this->handler->get($key) - $step; - $res = $this->handler->set($key, $value); + $res = $this->handler->set($key, $value); if (!$res) { return false; } else { diff --git a/library/think/cache/driver/Memcached.php b/library/think/cache/driver/Memcached.php index 1032e254aa..01c253e6e7 100644 --- a/library/think/cache/driver/Memcached.php +++ b/library/think/cache/driver/Memcached.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -16,14 +16,14 @@ class Memcached extends Driver { protected $options = [ - 'host' => '127.0.0.1', - 'port' => 11211, - 'expire' => 0, - 'timeout' => 0, // 超时时间(单位:毫秒) - 'prefix' => '', + 'host' => '127.0.0.1', + 'port' => 11211, + 'expire' => 0, + 'timeout' => 0, // 超时时间(单位:毫秒) + 'prefix' => '', 'username' => '', //账号 'password' => '', //密码 - 'option' => [], + 'option' => [], ]; /** @@ -109,7 +109,7 @@ public function set($name, $value, $expire = null) if ($this->tag && !$this->has($name)) { $first = true; } - $key = $this->getCacheKey($name); + $key = $this->getCacheKey($name); $expire = 0 == $expire ? 0 : $_SERVER['REQUEST_TIME'] + $expire; if ($this->handler->set($key, $value, $expire)) { isset($first) && $this->setTagItem($key); @@ -143,9 +143,9 @@ public function inc($name, $step = 1) */ public function dec($name, $step = 1) { - $key = $this->getCacheKey($name); + $key = $this->getCacheKey($name); $value = $this->handler->get($key) - $step; - $res = $this->handler->set($key, $value); + $res = $this->handler->set($key, $value); if (!$res) { return false; } else { diff --git a/library/think/cache/driver/Redis.php b/library/think/cache/driver/Redis.php index 2b05dcc0c7..f55c451934 100644 --- a/library/think/cache/driver/Redis.php +++ b/library/think/cache/driver/Redis.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -23,14 +23,14 @@ class Redis extends Driver { protected $options = [ - 'host' => '127.0.0.1', - 'port' => 6379, - 'password' => '', - 'select' => 0, - 'timeout' => 0, - 'expire' => 0, + 'host' => '127.0.0.1', + 'port' => 6379, + 'password' => '', + 'select' => 0, + 'timeout' => 0, + 'expire' => 0, 'persistent' => false, - 'prefix' => '', + 'prefix' => '', ]; /** @@ -46,9 +46,12 @@ public function __construct($options = []) if (!empty($options)) { $this->options = array_merge($this->options, $options); } - $func = $this->options['persistent'] ? 'pconnect' : 'connect'; $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']) { $this->handler->auth($this->options['password']); @@ -67,7 +70,7 @@ public function __construct($options = []) */ public function has($name) { - return $this->handler->get($this->getCacheKey($name)) ? true : false; + return (bool) $this->handler->exists($this->getCacheKey($name)); } /** @@ -80,12 +83,17 @@ public function has($name) public function get($name, $default = false) { $value = $this->handler->get($this->getCacheKey($name)); - if (is_null($value)) { + if (is_null($value) || false === $value) { return $default; } - $jsonData = json_decode($value, true); - // 检测是否为JSON数据 true 返回JSON解析数组, false返回源数据 byron sampson - return (null === $jsonData) ? $value : $jsonData; + + try { + $result = 0 === strpos($value, 'think_serialize:') ? unserialize(substr($value, 16)) : $value; + } catch (\Exception $e) { + $result = $default; + } + + return $result; } /** @@ -108,9 +116,8 @@ public function set($name, $value, $expire = null) $first = true; } $key = $this->getCacheKey($name); - //对数组/对象数据进行缓存处理,保证数据完整性 byron sampson - $value = (is_object($value) || is_array($value)) ? json_encode($value) : $value; - if (is_int($expire) && $expire) { + $value = is_scalar($value) ? $value : 'think_serialize:' . serialize($value); + if ($expire) { $result = $this->handler->setex($key, $expire, $value); } else { $result = $this->handler->set($key, $value); @@ -122,26 +129,28 @@ public function set($name, $value, $expire = null) /** * 自增缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function inc($name, $step = 1) { $key = $this->getCacheKey($name); + return $this->handler->incrby($key, $step); } /** * 自减缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function dec($name, $step = 1) { $key = $this->getCacheKey($name); + return $this->handler->decrby($key, $step); } diff --git a/library/think/cache/driver/Sqlite.php b/library/think/cache/driver/Sqlite.php index 0860f4fded..80f5647732 100644 --- a/library/think/cache/driver/Sqlite.php +++ b/library/think/cache/driver/Sqlite.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -20,10 +20,10 @@ class Sqlite extends Driver { protected $options = [ - 'db' => ':memory:', - 'table' => 'sharedmemory', - 'prefix' => '', - 'expire' => 0, + 'db' => ':memory:', + 'table' => 'sharedmemory', + 'prefix' => '', + 'expire' => 0, 'persistent' => false, ]; @@ -41,7 +41,7 @@ public function __construct($options = []) if (!empty($options)) { $this->options = array_merge($this->options, $options); } - $func = $this->options['persistent'] ? 'sqlite_popen' : 'sqlite_open'; + $func = $this->options['persistent'] ? 'sqlite_popen' : 'sqlite_open'; $this->handler = $func($this->options['db']); } @@ -64,8 +64,8 @@ protected function getCacheKey($name) */ public function has($name) { - $name = $this->getCacheKey($name); - $sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . $_SERVER['REQUEST_TIME'] . ') LIMIT 1'; + $name = $this->getCacheKey($name); + $sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . $_SERVER['REQUEST_TIME'] . ') LIMIT 1'; $result = sqlite_query($this->handler, $sql); return sqlite_num_rows($result); } @@ -79,8 +79,8 @@ public function has($name) */ public function get($name, $default = false) { - $name = $this->getCacheKey($name); - $sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . $_SERVER['REQUEST_TIME'] . ') LIMIT 1'; + $name = $this->getCacheKey($name); + $sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . $_SERVER['REQUEST_TIME'] . ') LIMIT 1'; $result = sqlite_query($this->handler, $sql); if (sqlite_num_rows($result)) { $content = sqlite_fetch_single($result); @@ -103,7 +103,7 @@ public function get($name, $default = false) */ public function set($name, $value, $expire = null) { - $name = $this->getCacheKey($name); + $name = $this->getCacheKey($name); $value = sqlite_escape_string(serialize($value)); if (is_null($expire)) { $expire = $this->options['expire']; @@ -118,7 +118,7 @@ public function set($name, $value, $expire = null) $value = gzcompress($value, 3); } if ($this->tag) { - $tag = $this->tag; + $tag = $this->tag; $this->tag = null; } else { $tag = ''; @@ -173,7 +173,7 @@ public function dec($name, $step = 1) public function rm($name) { $name = $this->getCacheKey($name); - $sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\''; + $sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\''; sqlite_query($this->handler, $sql); return true; } @@ -188,7 +188,7 @@ public function clear($tag = null) { if ($tag) { $name = sqlite_escape_string($tag); - $sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE tag=\'' . $name . '\''; + $sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE tag=\'' . $name . '\''; sqlite_query($this->handler, $sql); return true; } diff --git a/library/think/cache/driver/Wincache.php b/library/think/cache/driver/Wincache.php index 77b8722da7..03f8d35779 100644 --- a/library/think/cache/driver/Wincache.php +++ b/library/think/cache/driver/Wincache.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- diff --git a/library/think/cache/driver/Xcache.php b/library/think/cache/driver/Xcache.php index 06754445cc..4d94c033ca 100644 --- a/library/think/cache/driver/Xcache.php +++ b/library/think/cache/driver/Xcache.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- diff --git a/library/think/config/driver/Ini.php b/library/think/config/driver/Ini.php index a223a5785b..bcd12b6974 100644 --- a/library/think/config/driver/Ini.php +++ b/library/think/config/driver/Ini.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- diff --git a/library/think/config/driver/Json.php b/library/think/config/driver/Json.php index 557f75fe0f..479dcc8921 100644 --- a/library/think/config/driver/Json.php +++ b/library/think/config/driver/Json.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- diff --git a/library/think/config/driver/Xml.php b/library/think/config/driver/Xml.php index b573a56273..1158519fec 100644 --- a/library/think/config/driver/Xml.php +++ b/library/think/config/driver/Xml.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- diff --git a/library/think/console/Command.php b/library/think/console/Command.php index d0caad2f6b..3bbc1bc183 100644 --- a/library/think/console/Command.php +++ b/library/think/console/Command.php @@ -26,12 +26,12 @@ class Command private $definition; private $help; private $description; - private $ignoreValidationErrors = false; - private $consoleDefinitionMerged = false; + private $ignoreValidationErrors = false; + private $consoleDefinitionMerged = false; private $consoleDefinitionMergedWithArgs = false; private $code; private $synopsis = []; - private $usages = []; + private $usages = []; /** @var Input */ protected $input; @@ -145,7 +145,7 @@ protected function initialize(Input $input, Output $output) */ public function run(Input $input, Output $output) { - $this->input = $input; + $this->input = $input; $this->output = $output; $this->getSynopsis(true); diff --git a/library/think/console/Input.php b/library/think/console/Input.php index 2482dfdc01..a8103dfe6d 100644 --- a/library/think/console/Input.php +++ b/library/think/console/Input.php @@ -62,8 +62,8 @@ protected function setTokens(array $tokens) */ public function bind(Definition $definition) { - $this->arguments = []; - $this->options = []; + $this->arguments = []; + $this->options = []; $this->definition = $definition; $this->parse(); diff --git a/library/think/console/Output.php b/library/think/console/Output.php index 65dc9fb814..f2eb8147e9 100644 --- a/library/think/console/Output.php +++ b/library/think/console/Output.php @@ -40,15 +40,15 @@ */ class Output { - const VERBOSITY_QUIET = 0; - const VERBOSITY_NORMAL = 1; - const VERBOSITY_VERBOSE = 2; + const VERBOSITY_QUIET = 0; + const VERBOSITY_NORMAL = 1; + const VERBOSITY_VERBOSE = 2; const VERBOSITY_VERY_VERBOSE = 3; - const VERBOSITY_DEBUG = 4; + const VERBOSITY_DEBUG = 4; const OUTPUT_NORMAL = 0; - const OUTPUT_RAW = 1; - const OUTPUT_PLAIN = 2; + const OUTPUT_RAW = 1; + const OUTPUT_PLAIN = 2; private $verbosity = self::VERBOSITY_NORMAL; @@ -100,7 +100,7 @@ public function confirm(Input $input, $question, $default = true) public function choice(Input $input, $question, array $choices, $default = null) { if (null !== $default) { - $values = array_flip($choices); + $values = array_flip($choices); $default = $values[$default]; } @@ -109,7 +109,7 @@ public function choice(Input $input, $question, array $choices, $default = null) protected function askQuestion(Input $input, Question $question) { - $ask = new Ask($input, $this, $question); + $ask = new Ask($input, $this, $question); $answer = $ask->run(); if ($input->isInteractive()) { @@ -198,7 +198,7 @@ public function isDebug() public function describe($object, array $options = []) { $descriptor = new Descriptor(); - $options = array_merge([ + $options = array_merge([ 'raw_text' => false, ], $options); diff --git a/library/think/console/command/Clear.php b/library/think/console/command/Clear.php index 41019ceafe..d5aa58f299 100644 --- a/library/think/console/command/Clear.php +++ b/library/think/console/command/Clear.php @@ -10,8 +10,10 @@ // +---------------------------------------------------------------------- namespace think\console\command; +use think\Cache; use think\console\Command; use think\console\Input; +use think\console\input\Argument; use think\console\input\Option; use think\console\Output; @@ -22,6 +24,7 @@ protected function configure() // 指令配置 $this ->setName('clear') + ->addArgument('type', Argument::OPTIONAL, 'type to clear', null) ->addOption('path', 'd', Option::VALUE_OPTIONAL, 'path to clear', null) ->setDescription('Clear runtime file'); } @@ -30,8 +33,14 @@ protected function execute(Input $input, Output $output) { $path = $input->getOption('path') ?: RUNTIME_PATH; - if (is_dir($path)) { - $this->clearPath($path); + $type = $input->getArgument('type'); + + if ($type == 'route') { + Cache::clear('route_check'); + } else { + if (is_dir($path)) { + $this->clearPath($path); + } } $output->writeln("Clear Successed"); @@ -39,7 +48,7 @@ protected function execute(Input $input, Output $output) protected function clearPath($path) { - $path = realpath($path) . DS; + $path = realpath($path) . DS; $files = scandir($path); if ($files) { foreach ($files as $file) { diff --git a/library/think/console/command/Help.php b/library/think/console/command/Help.php index bae2c65331..bd966775c0 100644 --- a/library/think/console/command/Help.php +++ b/library/think/console/command/Help.php @@ -32,7 +32,8 @@ protected function configure() $this->setName('help')->setDefinition([ new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'), - ])->setDescription('Displays help for a command')->setHelp(<<setDescription('Displays help for a command')->setHelp( + <<%command.name% command displays help for a given command: php %command.full_name% list diff --git a/library/think/console/command/Lists.php b/library/think/console/command/Lists.php index 084ddaa231..3dae966f22 100644 --- a/library/think/console/command/Lists.php +++ b/library/think/console/command/Lists.php @@ -26,7 +26,8 @@ class Lists extends Command */ protected function configure() { - $this->setName('list')->setDefinition($this->createDefinition())->setDescription('Lists commands')->setHelp(<<setName('list')->setDefinition($this->createDefinition())->setDescription('Lists commands')->setHelp( + <<%command.name% command lists all commands: php %command.full_name% @@ -56,7 +57,7 @@ public function getNativeDefinition() protected function execute(Input $input, Output $output) { $output->describe($this->getConsole(), [ - 'raw_text' => $input->getOption('raw'), + 'raw_text' => $input->getOption('raw'), 'namespace' => $input->getArgument('namespace'), ]); } diff --git a/library/think/console/command/optimize/Autoload.php b/library/think/console/command/optimize/Autoload.php index 6a77c2cbaf..8a86db51cd 100644 --- a/library/think/console/command/optimize/Autoload.php +++ b/library/think/console/command/optimize/Autoload.php @@ -40,10 +40,10 @@ protected function execute(Input $input, Output $output) $namespacesToScan = [ App::$namespace . '\\' => realpath(rtrim(APP_PATH)), - 'think\\' => LIB_PATH . 'think', - 'behavior\\' => LIB_PATH . 'behavior', - 'traits\\' => LIB_PATH . 'traits', - '' => realpath(rtrim(EXTEND_PATH)), + 'think\\' => LIB_PATH . 'think', + 'behavior\\' => LIB_PATH . 'behavior', + 'traits\\' => LIB_PATH . 'traits', + '' => realpath(rtrim(EXTEND_PATH)), ]; $root_namespace = Config::get('root_namespace'); @@ -60,7 +60,7 @@ protected function execute(Input $input, Output $output) } $namespaceFilter = $namespace === '' ? null : $namespace; - $classMap = $this->addClassMapCode($dir, $namespaceFilter, $classMap); + $classMap = $this->addClassMapCode($dir, $namespaceFilter, $classMap); } ksort($classMap); @@ -101,24 +101,24 @@ protected function addClassMapCode($dir, $namespace, $classMap) protected function getPathCode($path) { - $baseDir = ''; - $libPath = $this->normalizePath(realpath(LIB_PATH)); - $appPath = $this->normalizePath(realpath(APP_PATH)); + $baseDir = ''; + $libPath = $this->normalizePath(realpath(LIB_PATH)); + $appPath = $this->normalizePath(realpath(APP_PATH)); $extendPath = $this->normalizePath(realpath(EXTEND_PATH)); - $rootPath = $this->normalizePath(realpath(ROOT_PATH)); - $path = $this->normalizePath($path); + $rootPath = $this->normalizePath(realpath(ROOT_PATH)); + $path = $this->normalizePath($path); if ($libPath !== null && strpos($path, $libPath . '/') === 0) { - $path = substr($path, strlen(LIB_PATH)); + $path = substr($path, strlen(LIB_PATH)); $baseDir = 'LIB_PATH'; } elseif ($appPath !== null && strpos($path, $appPath . '/') === 0) { - $path = substr($path, strlen($appPath) + 1); + $path = substr($path, strlen($appPath) + 1); $baseDir = 'APP_PATH'; } elseif ($extendPath !== null && strpos($path, $extendPath . '/') === 0) { - $path = substr($path, strlen($extendPath) + 1); + $path = substr($path, strlen($extendPath) + 1); $baseDir = 'EXTEND_PATH'; } elseif ($rootPath !== null && strpos($path, $rootPath . '/') === 0) { - $path = substr($path, strlen($rootPath) + 1); + $path = substr($path, strlen($rootPath) + 1); $baseDir = 'ROOT_PATH'; } @@ -134,19 +134,19 @@ protected function normalizePath($path) if ($path === false) { return; } - $parts = []; - $path = strtr($path, '\\', '/'); - $prefix = ''; + $parts = []; + $path = strtr($path, '\\', '/'); + $prefix = ''; $absolute = false; if (preg_match('{^([0-9a-z]+:(?://(?:[a-z]:)?)?)}i', $path, $match)) { $prefix = $match[1]; - $path = substr($path, strlen($prefix)); + $path = substr($path, strlen($prefix)); } if (substr($path, 0, 1) === '/') { $absolute = true; - $path = substr($path, 1); + $path = substr($path, 1); } $up = false; @@ -156,7 +156,7 @@ protected function normalizePath($path) $up = !(empty($parts) || '..' === end($parts)); } elseif ('.' !== $chunk && '' !== $chunk) { $parts[] = $chunk; - $up = '..' !== $chunk; + $up = '..' !== $chunk; } } @@ -271,7 +271,7 @@ protected function findClasses($path) ) }ix', $contents, $matches); - $classes = []; + $classes = []; $namespace = ''; for ($i = 0, $len = count($matches['type']); $i < $len; $i++) { diff --git a/library/think/console/command/optimize/Config.php b/library/think/console/command/optimize/Config.php index cadfe5ee93..53f915e8d0 100644 --- a/library/think/console/command/optimize/Config.php +++ b/library/think/console/command/optimize/Config.php @@ -30,7 +30,7 @@ protected function configure() protected function execute(Input $input, Output $output) { - if ($input->hasArgument('module')) { + if ($input->getArgument('module')) { $module = $input->getArgument('module') . DS; } else { $module = ''; @@ -50,7 +50,7 @@ protected function execute(Input $input, Output $output) protected function buildCacheContent($module) { $content = ''; - $path = realpath(APP_PATH . $module) . DS; + $path = realpath(APP_PATH . $module) . DS; if ($module) { // 加载模块配置 @@ -66,7 +66,7 @@ protected function buildCacheContent($module) } // 读取扩展配置文件 if (is_dir(CONF_PATH . $module . 'extra')) { - $dir = CONF_PATH . $module . 'extra'; + $dir = CONF_PATH . $module . 'extra'; $files = scandir($dir); foreach ($files as $file) { if (strpos($file, CONF_EXT)) { diff --git a/library/think/console/command/optimize/Route.php b/library/think/console/command/optimize/Route.php index 6da1d9a626..5f173ccef6 100644 --- a/library/think/console/command/optimize/Route.php +++ b/library/think/console/command/optimize/Route.php @@ -59,16 +59,16 @@ protected function buildClosure(&$value) { if ($value instanceof \Closure) { $reflection = new \ReflectionFunction($value); - $startLine = $reflection->getStartLine(); - $endLine = $reflection->getEndLine(); - $file = $reflection->getFileName(); - $item = file($file); - $content = ''; + $startLine = $reflection->getStartLine(); + $endLine = $reflection->getEndLine(); + $file = $reflection->getFileName(); + $item = file($file); + $content = ''; for ($i = $startLine - 1; $i <= $endLine - 1; $i++) { $content .= $item[$i]; } $start = strpos($content, 'function'); - $end = strrpos($content, '}'); + $end = strrpos($content, '}'); $value = '[__start__' . substr($content, $start, $end - $start + 1) . '__end__]'; } } diff --git a/library/think/console/command/optimize/Schema.php b/library/think/console/command/optimize/Schema.php index 27eb9dbb75..676ba1b93a 100644 --- a/library/think/console/command/optimize/Schema.php +++ b/library/think/console/command/optimize/Schema.php @@ -44,8 +44,9 @@ protected function execute(Input $input, Output $output) if ($input->hasOption('module')) { $module = $input->getOption('module'); // 读取模型 - $list = scandir(APP_PATH . $module . DS . 'model'); - $app = App::$namespace; + $path = APP_PATH . $module . DS . 'model'; + $list = is_dir($path) ? scandir($path) : []; + $app = App::$namespace; foreach ($list as $file) { if (0 === strpos($file, '.')) { continue; @@ -65,8 +66,9 @@ protected function execute(Input $input, Output $output) $dbName = $input->getOption('db'); $tables = Db::connect($config)->getTables($dbName); } elseif (!\think\Config::get('app_multi_module')) { - $app = App::$namespace; - $list = scandir(APP_PATH . 'model'); + $app = App::$namespace; + $path = APP_PATH . 'model'; + $list = is_dir($path) ? scandir($path) : []; foreach ($list as $file) { if (0 === strpos($file, '.')) { continue; @@ -90,10 +92,10 @@ protected function buildModelSchema($class) { $reflect = new \ReflectionClass($class); if (!$reflect->isAbstract() && $reflect->isSubclassOf('\think\Model')) { - $table = $class::getTable(); - $dbName = $class::getConfig('database'); + $table = $class::getTable(); + $dbName = $class::getConfig('database'); $content = 'getFields($table); + $info = $class::getConnection()->getFields($table); $content .= var_export($info, true) . ';'; file_put_contents(RUNTIME_PATH . 'schema' . DS . $dbName . '.' . $table . EXT, $content); } @@ -108,7 +110,7 @@ protected function buildDataBaseSchema($tables, $db, $config) } foreach ($tables as $table) { $content = 'getFields($db . $table); + $info = Db::connect($config)->getFields($db . $table); $content .= var_export($info, true) . ';'; file_put_contents(RUNTIME_PATH . 'schema' . DS . $dbName . $table . EXT, $content); } diff --git a/library/think/console/input/Argument.php b/library/think/console/input/Argument.php index 16223bbeb5..c96720c30a 100644 --- a/library/think/console/input/Argument.php +++ b/library/think/console/input/Argument.php @@ -39,8 +39,8 @@ public function __construct($name, $mode = null, $description = '', $default = n throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); } - $this->name = $name; - $this->mode = $mode; + $this->name = $name; + $this->mode = $mode; $this->description = $description; $this->setDefault($default); diff --git a/library/think/console/input/Definition.php b/library/think/console/input/Definition.php index c71977ec33..dbf4e42371 100644 --- a/library/think/console/input/Definition.php +++ b/library/think/console/input/Definition.php @@ -46,7 +46,7 @@ public function __construct(array $definition = []) public function setDefinition(array $definition) { $arguments = []; - $options = []; + $options = []; foreach ($definition as $item) { if ($item instanceof Option) { $options[] = $item; @@ -65,9 +65,9 @@ public function setDefinition(array $definition) */ public function setArguments($arguments = []) { - $this->arguments = []; - $this->requiredCount = 0; - $this->hasOptional = false; + $this->arguments = []; + $this->requiredCount = 0; + $this->hasOptional = false; $this->hasAnArrayArgument = false; $this->addArguments($arguments); } @@ -195,7 +195,7 @@ public function getArgumentDefaults() */ public function setOptions($options = []) { - $this->options = []; + $this->options = []; $this->shortcuts = []; $this->addOptions($options); } @@ -346,7 +346,7 @@ public function getSynopsis($short = false) $value = sprintf(' %s%s%s', $option->isValueOptional() ? '[' : '', strtoupper($option->getName()), $option->isValueOptional() ? ']' : ''); } - $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; + $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; $elements[] = sprintf('[%s--%s%s]', $shortcut, $option->getName(), $value); } } diff --git a/library/think/console/input/Option.php b/library/think/console/input/Option.php index e5707c9ae2..4c7525205a 100644 --- a/library/think/console/input/Option.php +++ b/library/think/console/input/Option.php @@ -14,7 +14,7 @@ class Option { - const VALUE_NONE = 1; + const VALUE_NONE = 1; const VALUE_REQUIRED = 2; const VALUE_OPTIONAL = 4; const VALUE_IS_ARRAY = 8; @@ -54,7 +54,7 @@ public function __construct($name, $shortcut = null, $mode = null, $description } $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-')); $shortcuts = array_filter($shortcuts); - $shortcut = implode('|', $shortcuts); + $shortcut = implode('|', $shortcuts); if (empty($shortcut)) { throw new \InvalidArgumentException('An option shortcut cannot be empty.'); @@ -67,9 +67,9 @@ public function __construct($name, $shortcut = null, $mode = null, $description throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); } - $this->name = $name; - $this->shortcut = $shortcut; - $this->mode = $mode; + $this->name = $name; + $this->shortcut = $shortcut; + $this->mode = $mode; $this->description = $description; if ($this->isArray() && !$this->acceptValue()) { diff --git a/library/think/console/output/Ask.php b/library/think/console/output/Ask.php index 3933eb296b..4e266d4405 100644 --- a/library/think/console/output/Ask.php +++ b/library/think/console/output/Ask.php @@ -33,8 +33,8 @@ class Ask public function __construct(Input $input, Output $output, Question $question) { - $this->input = $input; - $this->output = $output; + $this->input = $input; + $this->output = $output; $this->question = $question; } @@ -61,7 +61,7 @@ protected function doAsk() { $this->writePrompt(); - $inputStream = STDIN; + $inputStream = STDIN; $autocomplete = $this->question->getAutocompleterValues(); if (null === $autocomplete || !$this->hasSttyAvailable()) { @@ -99,11 +99,11 @@ protected function doAsk() private function autocomplete($inputStream) { $autocomplete = $this->question->getAutocompleterValues(); - $ret = ''; + $ret = ''; - $i = 0; - $ofs = -1; - $matches = $autocomplete; + $i = 0; + $ofs = -1; + $matches = $autocomplete; $numMatches = count($matches); $sttyMode = shell_exec('stty -g'); @@ -120,8 +120,8 @@ private function autocomplete($inputStream) } if ($i === 0) { - $ofs = -1; - $matches = $autocomplete; + $ofs = -1; + $matches = $autocomplete; $numMatches = count($matches); } else { $numMatches = 0; @@ -166,7 +166,7 @@ private function autocomplete($inputStream) ++$i; $numMatches = 0; - $ofs = 0; + $ofs = 0; foreach ($autocomplete as $value) { if (0 === strpos($value, $ret) && $i !== strlen($value)) { @@ -224,7 +224,7 @@ protected function getHiddenResponse($inputStream) if (false !== $shell = $this->getShell()) { $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword'; $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd); - $value = rtrim(shell_exec($command)); + $value = rtrim(shell_exec($command)); $this->output->writeln(''); return $value; @@ -236,7 +236,7 @@ protected function getHiddenResponse($inputStream) protected function validateAttempts($interviewer) { /** @var \Exception $error */ - $error = null; + $error = null; $attempts = $this->question->getMaxAttempts(); while (null === $attempts || $attempts--) { if (null !== $error) { @@ -257,7 +257,7 @@ protected function validateAttempts($interviewer) */ protected function writePrompt() { - $text = $this->question->getQuestion(); + $text = $this->question->getQuestion(); $default = $this->question->getDefault(); switch (true) { @@ -285,7 +285,7 @@ protected function writePrompt() case $this->question instanceof Choice: $choices = $this->question->getChoices(); - $text = sprintf(' %s [%s]:', $text, $choices[$default]); + $text = sprintf(' %s [%s]:', $text, $choices[$default]); break; diff --git a/library/think/console/output/Descriptor.php b/library/think/console/output/Descriptor.php index 23dc64812b..f9333040b7 100644 --- a/library/think/console/output/Descriptor.php +++ b/library/think/console/output/Descriptor.php @@ -82,7 +82,7 @@ protected function describeInputArgument(InputArgument $argument, array $options $default = ''; } - $totalWidth = isset($options['total_width']) ? $options['total_width'] : strlen($argument->getName()); + $totalWidth = isset($options['total_width']) ? $options['total_width'] : strlen($argument->getName()); $spacingWidth = $totalWidth - strlen($argument->getName()) + 2; $this->writeText(sprintf(" %s%s%s%s", $argument->getName(), str_repeat(' ', $spacingWidth), // + 17 = 2 spaces + + + 2 spaces @@ -116,7 +116,7 @@ protected function describeInputOption(InputOption $option, array $options = []) } $totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions([$option]); - $synopsis = sprintf('%s%s', $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ', sprintf('--%s%s', $option->getName(), $value)); + $synopsis = sprintf('%s%s', $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ', sprintf('--%s%s', $option->getName(), $value)); $spacingWidth = $totalWidth - strlen($synopsis) + 2; @@ -213,7 +213,7 @@ protected function describeCommand(Command $command, array $options = []) protected function describeConsole(Console $console, array $options = []) { $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; - $description = new ConsoleDescription($console, $describedNamespace); + $description = new ConsoleDescription($console, $describedNamespace); if (isset($options['raw_text']) && $options['raw_text']) { $width = $this->getColumnWidth($description->getCommands()); diff --git a/library/think/console/output/Formatter.php b/library/think/console/output/Formatter.php index f8bee5527c..a307d68cd1 100644 --- a/library/think/console/output/Formatter.php +++ b/library/think/console/output/Formatter.php @@ -17,7 +17,7 @@ class Formatter { private $decorated = false; - private $styles = []; + private $styles = []; private $styleStack; /** @@ -105,12 +105,12 @@ public function getStyle($name) */ public function format($message) { - $offset = 0; - $output = ''; + $offset = 0; + $output = ''; $tagRegex = '[a-z][a-z0-9_=;-]*'; preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#isx", $message, $matches, PREG_OFFSET_CAPTURE); foreach ($matches[0] as $i => $match) { - $pos = $match[1]; + $pos = $match[1]; $text = $match[0]; if (0 != $pos && '\\' == $message[$pos - 1]) { diff --git a/library/think/console/output/Question.php b/library/think/console/output/Question.php index 03975f2748..a74144bb07 100644 --- a/library/think/console/output/Question.php +++ b/library/think/console/output/Question.php @@ -16,7 +16,7 @@ class Question private $question; private $attempts; - private $hidden = false; + private $hidden = false; private $hiddenFallback = true; private $autocompleterValues; private $validator; @@ -31,7 +31,7 @@ class Question public function __construct($question, $default = null) { $this->question = $question; - $this->default = $default; + $this->default = $default; } /** diff --git a/library/think/console/output/descriptor/Console.php b/library/think/console/output/descriptor/Console.php index 4648b68e66..b33f5da2f2 100644 --- a/library/think/console/output/descriptor/Console.php +++ b/library/think/console/output/descriptor/Console.php @@ -51,7 +51,7 @@ class Console */ public function __construct(ThinkConsole $console, $namespace = null) { - $this->console = $console; + $this->console = $console; $this->namespace = $namespace; } @@ -95,7 +95,7 @@ public function getCommand($name) private function inspectConsole() { - $this->commands = []; + $this->commands = []; $this->namespaces = []; $all = $this->console->all($this->namespace ? $this->console->findNamespace($this->namespace) : null); diff --git a/library/think/console/output/driver/Buffer.php b/library/think/console/output/driver/Buffer.php index c77a2ec4ba..1f8c7e7c85 100644 --- a/library/think/console/output/driver/Buffer.php +++ b/library/think/console/output/driver/Buffer.php @@ -27,7 +27,7 @@ public function __construct(Output $output) public function fetch() { - $content = $this->buffer; + $content = $this->buffer; $this->buffer = ''; return $content; } diff --git a/library/think/console/output/driver/Console.php b/library/think/console/output/driver/Console.php index 8f29fd020a..0cb630e7f3 100644 --- a/library/think/console/output/driver/Console.php +++ b/library/think/console/output/driver/Console.php @@ -30,10 +30,10 @@ class Console public function __construct(Output $output) { - $this->output = $output; + $this->output = $output; $this->formatter = new Formatter(); - $this->stdout = $this->openOutputStream(); - $decorated = $this->hasColorSupport($this->stdout); + $this->stdout = $this->openOutputStream(); + $decorated = $this->hasColorSupport($this->stdout); $this->formatter->setDecorated($decorated); } @@ -75,7 +75,7 @@ public function write($messages, $newline = false, $type = Output::OUTPUT_NORMAL public function renderException(\Exception $e) { - $stderr = $this->openErrorStream(); + $stderr = $this->openErrorStream(); $decorated = $this->hasColorSupport($stderr); $this->formatter->setDecorated($decorated); @@ -94,13 +94,13 @@ public function renderException(\Exception $e) foreach ($this->splitStringByWidth($line, $width - 4) as $line) { $lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $line)) + 4; - $lines[] = [$line, $lineLength]; + $lines[] = [$line, $lineLength]; $len = max($lineLength, $len); } } - $messages = ['', '']; + $messages = ['', '']; $messages[] = $emptyLine = sprintf('%s', str_repeat(' ', $len)); $messages[] = sprintf('%s%s', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title)))); foreach ($lines as $line) { @@ -119,17 +119,17 @@ public function renderException(\Exception $e) $trace = $e->getTrace(); array_unshift($trace, [ 'function' => '', - 'file' => $e->getFile() !== null ? $e->getFile() : 'n/a', - 'line' => $e->getLine() !== null ? $e->getLine() : 'n/a', - 'args' => [], + 'file' => $e->getFile() !== null ? $e->getFile() : 'n/a', + 'line' => $e->getLine() !== null ? $e->getLine() : 'n/a', + 'args' => [], ]); for ($i = 0, $count = count($trace); $i < $count; ++$i) { - $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; - $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; + $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; + $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; $function = $trace[$i]['function']; - $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; - $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; + $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; + $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; $this->write(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line), true, Output::OUTPUT_NORMAL, $stderr); } @@ -205,7 +205,7 @@ private function getSttyColumns() } $descriptorspec = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']]; - $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]); + $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]); if (is_resource($process)) { $info = stream_get_contents($pipes[1]); fclose($pipes[1]); @@ -228,7 +228,7 @@ private function getMode() } $descriptorspec = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']]; - $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]); + $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]); if (is_resource($process)) { $info = stream_get_contents($pipes[1]); fclose($pipes[1]); @@ -266,15 +266,15 @@ private function splitStringByWidth($string, $width) } $utf8String = mb_convert_encoding($string, 'utf8', $encoding); - $lines = []; - $line = ''; + $lines = []; + $line = ''; foreach (preg_split('//u', $utf8String) as $char) { if (mb_strwidth($line . $char, 'utf8') <= $width) { $line .= $char; continue; } $lines[] = str_pad($line, $width); - $line = $char; + $line = $char; } if (strlen($line)) { $lines[] = count($lines) ? str_pad($line, $width) : $line; diff --git a/library/think/console/output/formatter/Style.php b/library/think/console/output/formatter/Style.php index d9b0999870..b86ff2c125 100644 --- a/library/think/console/output/formatter/Style.php +++ b/library/think/console/output/formatter/Style.php @@ -15,31 +15,31 @@ class Style { private static $availableForegroundColors = [ - 'black' => ['set' => 30, 'unset' => 39], - 'red' => ['set' => 31, 'unset' => 39], - 'green' => ['set' => 32, 'unset' => 39], - 'yellow' => ['set' => 33, 'unset' => 39], - 'blue' => ['set' => 34, 'unset' => 39], + 'black' => ['set' => 30, 'unset' => 39], + 'red' => ['set' => 31, 'unset' => 39], + 'green' => ['set' => 32, 'unset' => 39], + 'yellow' => ['set' => 33, 'unset' => 39], + 'blue' => ['set' => 34, 'unset' => 39], 'magenta' => ['set' => 35, 'unset' => 39], - 'cyan' => ['set' => 36, 'unset' => 39], - 'white' => ['set' => 37, 'unset' => 39], + 'cyan' => ['set' => 36, 'unset' => 39], + 'white' => ['set' => 37, 'unset' => 39], ]; private static $availableBackgroundColors = [ - 'black' => ['set' => 40, 'unset' => 49], - 'red' => ['set' => 41, 'unset' => 49], - 'green' => ['set' => 42, 'unset' => 49], - 'yellow' => ['set' => 43, 'unset' => 49], - 'blue' => ['set' => 44, 'unset' => 49], + 'black' => ['set' => 40, 'unset' => 49], + 'red' => ['set' => 41, 'unset' => 49], + 'green' => ['set' => 42, 'unset' => 49], + 'yellow' => ['set' => 43, 'unset' => 49], + 'blue' => ['set' => 44, 'unset' => 49], 'magenta' => ['set' => 45, 'unset' => 49], - 'cyan' => ['set' => 46, 'unset' => 49], - 'white' => ['set' => 47, 'unset' => 49], + 'cyan' => ['set' => 46, 'unset' => 49], + 'white' => ['set' => 47, 'unset' => 49], ]; private static $availableOptions = [ - 'bold' => ['set' => 1, 'unset' => 22], + 'bold' => ['set' => 1, 'unset' => 22], 'underscore' => ['set' => 4, 'unset' => 24], - 'blink' => ['set' => 5, 'unset' => 25], - 'reverse' => ['set' => 7, 'unset' => 27], - 'conceal' => ['set' => 8, 'unset' => 28], + 'blink' => ['set' => 5, 'unset' => 25], + 'reverse' => ['set' => 7, 'unset' => 27], + 'conceal' => ['set' => 8, 'unset' => 28], ]; private $foreground; @@ -162,20 +162,20 @@ public function setOptions(array $options) */ public function apply($text) { - $setCodes = []; + $setCodes = []; $unsetCodes = []; if (null !== $this->foreground) { - $setCodes[] = $this->foreground['set']; + $setCodes[] = $this->foreground['set']; $unsetCodes[] = $this->foreground['unset']; } if (null !== $this->background) { - $setCodes[] = $this->background['set']; + $setCodes[] = $this->background['set']; $unsetCodes[] = $this->background['unset']; } if (count($this->options)) { foreach ($this->options as $option) { - $setCodes[] = $option['set']; + $setCodes[] = $option['set']; $unsetCodes[] = $option['unset']; } } diff --git a/library/think/console/output/question/Choice.php b/library/think/console/output/question/Choice.php index f6760e5ef3..f1d10b64ab 100644 --- a/library/think/console/output/question/Choice.php +++ b/library/think/console/output/question/Choice.php @@ -17,8 +17,8 @@ class Choice extends Question { private $choices; - private $multiselect = false; - private $prompt = ' > '; + private $multiselect = false; + private $prompt = ' > '; private $errorMessage = 'Value "%s" is invalid'; /** @@ -103,10 +103,10 @@ public function setErrorMessage($errorMessage) */ private function getDefaultValidator() { - $choices = $this->choices; + $choices = $this->choices; $errorMessage = $this->errorMessage; - $multiselect = $this->multiselect; - $isAssoc = $this->isAssoc($choices); + $multiselect = $this->multiselect; + $isAssoc = $this->isAssoc($choices); return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) { // Collapse all spaces. diff --git a/library/think/console/output/question/Confirmation.php b/library/think/console/output/question/Confirmation.php index 6598f9b3e5..1eb9be8ddb 100644 --- a/library/think/console/output/question/Confirmation.php +++ b/library/think/console/output/question/Confirmation.php @@ -39,7 +39,7 @@ public function __construct($question, $default = true, $trueAnswerRegex = '/^y/ private function getDefaultNormalizer() { $default = $this->getDefault(); - $regex = $this->trueAnswerRegex; + $regex = $this->trueAnswerRegex; return function ($answer) use ($default, $regex) { if (is_bool($answer)) { diff --git a/library/think/controller/Rest.php b/library/think/controller/Rest.php index 2a28057ec7..6005f55441 100644 --- a/library/think/controller/Rest.php +++ b/library/think/controller/Rest.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -21,12 +21,12 @@ abstract class Rest protected $method; // 当前请求类型 protected $type; // 当前资源类型 // 输出类型 - protected $restMethodList = 'get|post|put|delete'; + protected $restMethodList = 'get|post|put|delete'; protected $restDefaultMethod = 'get'; - protected $restTypeList = 'html|xml|json|rss'; - protected $restDefaultType = 'html'; - protected $restOutputType = [ // REST允许输出的资源类型列表 - 'xml' => 'application/xml', + protected $restTypeList = 'html|xml|json|rss'; + protected $restDefaultType = 'html'; + protected $restOutputType = [ // REST允许输出的资源类型列表 + 'xml' => 'application/xml', 'json' => 'application/json', 'html' => 'text/html', ]; @@ -39,7 +39,7 @@ public function __construct() { // 资源类型检测 $request = Request::instance(); - $ext = $request->ext(); + $ext = $request->ext(); if ('' == $ext) { // 自动检测资源类型 $this->type = $request->type(); diff --git a/library/think/db/Builder.php b/library/think/db/Builder.php index 213cb090cc..9a1693a406 100644 --- a/library/think/db/Builder.php +++ b/library/think/db/Builder.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -11,7 +11,6 @@ namespace think\db; -use BadMethodCallException; use PDO; use think\Exception; @@ -26,11 +25,11 @@ abstract class Builder protected $exp = ['eq' => '=', 'neq' => '<>', 'gt' => '>', 'egt' => '>=', 'lt' => '<', 'elt' => '<=', 'notlike' => 'NOT LIKE', 'not like' => 'NOT LIKE', 'like' => 'LIKE', 'in' => 'IN', 'exp' => 'EXP', 'notin' => 'NOT IN', 'not in' => 'NOT IN', 'between' => 'BETWEEN', 'not between' => 'NOT BETWEEN', 'notbetween' => 'NOT BETWEEN', 'exists' => 'EXISTS', 'notexists' => 'NOT EXISTS', 'not exists' => 'NOT EXISTS', 'null' => 'NULL', 'notnull' => 'NOT NULL', 'not null' => 'NOT NULL', '> time' => '> TIME', '< time' => '< TIME', '>= time' => '>= TIME', '<= time' => '<= TIME', 'between time' => 'BETWEEN TIME', 'not between time' => 'NOT BETWEEN TIME', 'notbetween time' => 'NOT BETWEEN TIME']; // SQL表达式 - protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%'; - protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; + protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT%%LOCK%%COMMENT%'; + protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; - protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; - protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; + protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; + protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; /** * 构造函数 @@ -41,7 +40,7 @@ abstract class Builder public function __construct(Connection $connection, Query $query) { $this->connection = $connection; - $this->query = $query; + $this->query = $query; } /** @@ -99,8 +98,15 @@ protected function parseData($data, $options) $result = []; foreach ($data as $key => $val) { - $item = $this->parseKey($key, $options); - if (is_object($val) && method_exists($val, '__toString')) { + if ('*' != $options['field'] && !in_array($key, $fields, true)) { + continue; + } + + $item = $this->parseKey($key, $options, true); + if ($val instanceof Expression) { + $result[$item] = $val->getValue(); + continue; + } elseif (is_object($val) && method_exists($val, '__toString')) { // 对象数据写入 $val = $val->__toString(); } @@ -110,8 +116,17 @@ protected function parseData($data, $options) } } elseif (is_null($val)) { $result[$item] = 'NULL'; - } elseif (isset($val[0]) && 'exp' == $val[0]) { - $result[$item] = $val[1]; + } elseif (is_array($val) && !empty($val)) { + switch (strtolower($val[0])) { + case 'inc': + $result[$item] = $item . '+' . floatval($val[1]); + break; + case 'dec': + $result[$item] = $item . '-' . floatval($val[1]); + break; + case 'exp': + throw new Exception('not support data:[' . $val[0] . ']'); + } } elseif (is_scalar($val)) { // 过滤非标量数据 if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) { @@ -133,7 +148,7 @@ protected function parseData($data, $options) * @param array $options * @return string */ - protected function parseKey($key, $options = []) + protected function parseKey($key, $options = [], $strict = false) { return $key; } @@ -174,8 +189,10 @@ protected function parseField($fields, $options = []) // 支持 'field1'=>'field2' 这样的字段别名定义 $array = []; foreach ($fields as $key => $field) { - if (!is_numeric($key)) { - $array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options); + if ($field instanceof Expression) { + $array[] = $field->getValue(); + } elseif (!is_numeric($key)) { + $array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options, true); } else { $array[] = $this->parseKey($field, $options); } @@ -197,10 +214,7 @@ protected function parseTable($tables, $options = []) $item = []; foreach ((array) $tables as $key => $table) { if (!is_numeric($key)) { - if (strpos($key, '@think')) { - $key = strstr($key, '@think', true); - } - $key = $this->parseSqlTable($key); + $key = $this->parseSqlTable($key); $item[] = $this->parseKey($key) . ' ' . (isset($options['alias'][$table]) ? $this->parseKey($options['alias'][$table]) : $this->parseKey($table)); } else { $table = $this->parseSqlTable($table); @@ -228,7 +242,7 @@ protected function parseWhere($where, $options) // 附加软删除条件 list($field, $condition) = $options['soft_delete']; - $binds = $this->query->getFieldsBind($options['table']); + $binds = $this->query->getFieldsBind($options['table']); $whereStr = $whereStr ? '( ' . $whereStr . ' ) AND ' : ''; $whereStr = $whereStr . $this->parseWhereItem($field, $condition, '', $options, $binds); } @@ -253,11 +267,13 @@ public function buildWhere($where, $options) } $whereStr = ''; - $binds = $this->query->getFieldsBind($options['table']); + $binds = $this->query->getFieldsBind($options['table']); foreach ($where as $key => $val) { $str = []; foreach ($val as $field => $value) { - if ($value instanceof \Closure) { + if ($value instanceof Expression) { + $str[] = ' ' . $key . ' ( ' . $value->getValue() . ' )'; + } elseif ($value instanceof \Closure) { // 使用闭包查询 $query = new Query($this->connection); call_user_func_array($value, [ & $query]); @@ -268,7 +284,7 @@ public function buildWhere($where, $options) } elseif (strpos($field, '|')) { // 不同字段使用相同查询条件(OR) $array = explode('|', $field); - $item = []; + $item = []; foreach ($array as $k) { $item[] = $this->parseWhereItem($k, $value, '', $options, $binds); } @@ -276,7 +292,7 @@ public function buildWhere($where, $options) } elseif (strpos($field, '&')) { // 不同字段使用相同查询条件(AND) $array = explode('&', $field); - $item = []; + $item = []; foreach ($array as $k) { $item[] = $this->parseWhereItem($k, $value, '', $options, $binds); } @@ -298,7 +314,7 @@ public function buildWhere($where, $options) protected function parseWhereItem($field, $val, $rule = '', $options = [], $binds = [], $bindName = null) { // 字段分析 - $key = $field ? $this->parseKey($field, $options) : ''; + $key = $field ? $this->parseKey($field, $options, true) : ''; // 查询规则和条件 if (!is_array($val)) { @@ -317,7 +333,7 @@ protected function parseWhereItem($field, $val, $rule = '', $options = [], $bind } foreach ($val as $k => $item) { $bindName = 'where_' . str_replace('.', '_', $field) . '_' . $k; - $str[] = $this->parseWhereItem($field, $item, $rule, $options, $binds, $bindName); + $str[] = $this->parseWhereItem($field, $item, $rule, $options, $binds, $bindName); } return '( ' . implode(' ' . $rule . ' ', $str) . ' )'; } @@ -331,13 +347,15 @@ protected function parseWhereItem($field, $val, $rule = '', $options = [], $bind throw new Exception('where express error:' . $exp); } } - $bindName = $bindName ?: 'where_' . str_replace(['.', '-'], '_', $field); + $bindName = $bindName ?: 'where_' . $rule . '_' . str_replace(['.', '-'], '_', $field); if (preg_match('/\W/', $bindName)) { // 处理带非单词字符的字段名 $bindName = md5($bindName); } - if (is_object($value) && method_exists($value, '__toString')) { + if ($value instanceof Expression) { + + } elseif (is_object($value) && method_exists($value, '__toString')) { // 对象数据写入 $value = $value->__toString(); } @@ -368,13 +386,17 @@ protected function parseWhereItem($field, $val, $rule = '', $options = [], $bind $array[] = $key . ' ' . $exp . ' ' . $this->parseValue($item, $field); } $logic = isset($val[2]) ? $val[2] : 'AND'; - $whereStr .= '(' . implode($array, ' ' . strtoupper($logic) . ' ') . ')'; + $whereStr .= '(' . implode(' ' . strtoupper($logic) . ' ', $array) . ')'; } else { $whereStr .= $key . ' ' . $exp . ' ' . $this->parseValue($value, $field); } } elseif ('EXP' == $exp) { // 表达式查询 - $whereStr .= '( ' . $key . ' ' . $value . ' )'; + if ($value instanceof Expression) { + $whereStr .= '( ' . $key . ' ' . $value->getValue() . ' )'; + } else { + throw new Exception('where express error:' . $exp); + } } elseif (in_array($exp, ['NOT NULL', 'NULL'])) { // NULL 查询 $whereStr .= $key . ' IS ' . $exp; @@ -385,9 +407,9 @@ protected function parseWhereItem($field, $val, $rule = '', $options = [], $bind } else { $value = array_unique(is_array($value) ? $value : explode(',', $value)); if (array_key_exists($field, $binds)) { - $bind = []; + $bind = []; $array = []; - $i = 0; + $i = 0; foreach ($value as $v) { $i++; if ($this->query->isBind($bindName . '_in_' . $i)) { @@ -396,7 +418,7 @@ protected function parseWhereItem($field, $val, $rule = '', $options = [], $bind $bindKey = $bindName . '_in_' . $i; } $bind[$bindKey] = [$v, $bindType]; - $array[] = ':' . $bindKey; + $array[] = ':' . $bindKey; } $this->query->bind($bind); $zone = implode(',', $array); @@ -492,6 +514,11 @@ protected function parseDateTime($value, $key, $options = [], $bindName = null, } } $bindName = $bindName ?: $key; + + if ($this->query->isBind($bindName)) { + $bindName .= '_' . str_replace('.', '_', uniqid('', true)); + } + $this->query->bind($bindName, $value, $bindType); return ':' . $bindName; } @@ -520,11 +547,13 @@ protected function parseJoin($join, $options = []) if (!empty($join)) { foreach ($join as $item) { list($table, $type, $on) = $item; - $condition = []; + $condition = []; foreach ((array) $on as $val) { - if (strpos($val, '=')) { + if ($val instanceof Expression) { + $condition[] = $val->getValue(); + } elseif (strpos($val, '=')) { list($val1, $val2) = explode('=', $val, 2); - $condition[] = $this->parseKey($val1, $options) . '=' . $this->parseKey($val2, $options); + $condition[] = $this->parseKey($val1, $options) . '=' . $this->parseKey($val2, $options); } else { $condition[] = $val; } @@ -546,28 +575,29 @@ protected function parseJoin($join, $options = []) */ protected function parseOrder($order, $options = []) { - if (is_array($order)) { - $array = []; - foreach ($order as $key => $val) { + if (empty($order)) { + return ''; + } + + $array = []; + foreach ($order as $key => $val) { + if ($val instanceof Expression) { + $array[] = $val->getValue(); + } elseif ('[rand]' == $val) { + $array[] = $this->parseRand(); + } else { if (is_numeric($key)) { - if ('[rand]' == $val) { - if (method_exists($this, 'parseRand')) { - $array[] = $this->parseRand(); - } else { - throw new BadMethodCallException('method not exists:' . get_class($this) . '-> parseRand'); - } - } elseif (false === strpos($val, '(')) { - $array[] = $this->parseKey($val, $options); - } else { - $array[] = $val; - } + list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' '); } else { - $sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : ''; - $array[] = $this->parseKey($key, $options) . ' ' . $sort; + $sort = $val; } + $sort = strtoupper($sort); + $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : ''; + $array[] = $this->parseKey($key, $options, true) . $sort; } - $order = implode(',', $array); } + $order = implode(',', $array); + return !empty($order) ? ' ORDER BY ' . $order : ''; } @@ -601,6 +631,9 @@ protected function parseHaving($having) */ protected function parseComment($comment) { + if (false !== strpos($comment, '*/')) { + $comment = strstr($comment, '*/', true); + } return !empty($comment) ? ' /* ' . $comment . ' */' : ''; } @@ -630,12 +663,12 @@ protected function parseUnion($union) unset($union['type']); foreach ($union as $u) { if ($u instanceof \Closure) { - $sql[] = $type . ' ' . $this->parseClosure($u, false); + $sql[] = $type . ' ' . $this->parseClosure($u); } elseif (is_string($u)) { - $sql[] = $type . ' ' . $this->parseSqlTable($u); + $sql[] = $type . ' ( ' . $this->parseSqlTable($u) . ' )'; } } - return implode(' ', $sql); + return ' ' . implode(' ', $sql); } /** @@ -650,11 +683,7 @@ protected function parseForce($index) return ''; } - if (is_array($index)) { - $index = join(",", $index); - } - - return sprintf(" FORCE INDEX ( %s ) ", $index); + return sprintf(" FORCE INDEX ( %s ) ", is_array($index) ? implode(',', $index) : $index); } /** @@ -696,7 +725,9 @@ public function select($options = []) $this->parseLock($options['lock']), $this->parseComment($options['comment']), $this->parseForce($options['force']), - ], $this->selectSql); + ], + $this->selectSql + ); return $sql; } @@ -726,7 +757,9 @@ public function insert(array $data, $options = [], $replace = false) implode(' , ', $fields), implode(' , ', $values), $this->parseComment($options['comment']), - ], $this->insertSql); + ], + $this->insertSql + ); return $sql; } @@ -749,7 +782,7 @@ public function insertAll($dataSet, $options = [], $replace = false) $fields = $options['field']; } - foreach ($dataSet as &$data) { + foreach ($dataSet as $data) { foreach ($data as $key => $val) { if (!in_array($key, $fields, true)) { if ($options['strict']) { @@ -768,21 +801,29 @@ public function insertAll($dataSet, $options = [], $replace = false) unset($data[$key]); } } - $value = array_values($data); + $value = array_values($data); $values[] = 'SELECT ' . implode(',', $value); + + if (!isset($insertFields)) { + $insertFields = array_keys($data); + } + } + + foreach ($insertFields as $field) { + $fields[] = $this->parseKey($field, $options, true); } - $fields = array_map([$this, 'parseKey'], array_keys(reset($dataSet))); - $sql = str_replace( + + return str_replace( ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], [ $replace ? 'REPLACE' : 'INSERT', $this->parseTable($options['table'], $options), - implode(' , ', $fields), + implode(' , ', $insertFields), implode(' UNION ALL ', $values), $this->parseComment($options['comment']), - ], $this->insertAllSql); - - return $sql; + ], + $this->insertAllSql + ); } /** @@ -800,7 +841,7 @@ public function selectInsert($fields, $table, $options) } $fields = array_map([$this, 'parseKey'], $fields); - $sql = 'INSERT INTO ' . $this->parseTable($table, $options) . ' (' . implode(',', $fields) . ') ' . $this->select($options); + $sql = 'INSERT INTO ' . $this->parseTable($table, $options) . ' (' . implode(',', $fields) . ') ' . $this->select($options); return $sql; } @@ -814,7 +855,7 @@ public function selectInsert($fields, $table, $options) public function update($data, $options) { $table = $this->parseTable($options['table'], $options); - $data = $this->parseData($data, $options); + $data = $this->parseData($data, $options); if (empty($data)) { return ''; } @@ -833,7 +874,9 @@ public function update($data, $options) $this->parseLimit($options['limit']), $this->parseLock($options['lock']), $this->parseComment($options['comment']), - ], $this->updateSql); + ], + $this->updateSql + ); return $sql; } @@ -857,7 +900,9 @@ public function delete($options) $this->parseLimit($options['limit']), $this->parseLock($options['lock']), $this->parseComment($options['comment']), - ], $this->deleteSql); + ], + $this->deleteSql + ); return $sql; } diff --git a/library/think/db/Connection.php b/library/think/db/Connection.php index 634057a1e1..f899b42b76 100644 --- a/library/think/db/Connection.php +++ b/library/think/db/Connection.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -61,62 +61,64 @@ abstract class Connection // 数据库连接参数配置 protected $config = [ // 数据库类型 - 'type' => '', + 'type' => '', // 服务器地址 - 'hostname' => '', + 'hostname' => '', // 数据库名 - 'database' => '', + 'database' => '', // 用户名 - 'username' => '', + 'username' => '', // 密码 - 'password' => '', + 'password' => '', // 端口 - 'hostport' => '', + 'hostport' => '', // 连接dsn - 'dsn' => '', + 'dsn' => '', // 数据库连接参数 - 'params' => [], + 'params' => [], // 数据库编码默认采用utf8 - 'charset' => 'utf8', + 'charset' => 'utf8', // 数据库表前缀 - 'prefix' => '', + 'prefix' => '', // 数据库调试模式 - 'debug' => false, + 'debug' => false, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, + 'deploy' => 0, // 数据库读写是否分离 主从式有效 - 'rw_separate' => false, + 'rw_separate' => false, // 读写分离后 主服务器数量 - 'master_num' => 1, + 'master_num' => 1, // 指定从服务器序号 - 'slave_no' => '', + 'slave_no' => '', + // 模型写入后自动读取主服务器 + 'read_master' => false, // 是否严格检查字段是否存在 - 'fields_strict' => true, + 'fields_strict' => true, // 数据返回类型 - 'result_type' => PDO::FETCH_ASSOC, + 'result_type' => PDO::FETCH_ASSOC, // 数据集返回类型 - 'resultset_type' => 'array', + 'resultset_type' => 'array', // 自动写入时间戳字段 - 'auto_timestamp' => false, + 'auto_timestamp' => false, // 时间字段取出后的默认时间格式 'datetime_format' => 'Y-m-d H:i:s', // 是否需要进行SQL性能分析 - 'sql_explain' => false, + 'sql_explain' => false, // Builder类 - 'builder' => '', + 'builder' => '', // Query类 - 'query' => '\\think\\db\\Query', + 'query' => '\\think\\db\\Query', // 是否需要断线重连 'break_reconnect' => false, ]; // PDO连接参数 protected $params = [ - PDO::ATTR_CASE => PDO::CASE_NATURAL, - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, + PDO::ATTR_CASE => PDO::CASE_NATURAL, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, PDO::ATTR_STRINGIFY_FETCHES => false, - PDO::ATTR_EMULATE_PREPARES => false, + PDO::ATTR_EMULATE_PREPARES => false, ]; // 绑定参数 @@ -354,19 +356,14 @@ public function query($sql, $bind = [], $master = false, $pdo = false) $this->bind = $bind; } - // 释放前次的查询结果 - if (!empty($this->PDOStatement)) { - $this->free(); - } - Db::$queryTimes++; try { // 调试开始 $this->debug(true); + // 预处理 - if (empty($this->PDOStatement)) { - $this->PDOStatement = $this->linkID->prepare($sql); - } + $this->PDOStatement = $this->linkID->prepare($sql); + // 是否为存储过程调用 $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); // 参数绑定 @@ -378,7 +375,7 @@ public function query($sql, $bind = [], $master = false, $pdo = false) // 执行查询 $this->PDOStatement->execute(); // 调试结束 - $this->debug(false); + $this->debug(false, '', $master); // 返回结果集 return $this->getResult($pdo, $procedure); } catch (\PDOException $e) { @@ -386,6 +383,11 @@ public function query($sql, $bind = [], $master = false, $pdo = false) return $this->close()->query($sql, $bind, $master, $pdo); } 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) { if ($this->isBreak($e)) { return $this->close()->query($sql, $bind, $master, $pdo); @@ -397,13 +399,14 @@ public function query($sql, $bind = [], $master = false, $pdo = false) /** * 执行语句 * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param Query $query 查询对象 * @return int * @throws PDOException * @throws \Exception */ - public function execute($sql, $bind = []) + public function execute($sql, $bind = [], Query $query = null) { $this->initConnect(true); if (!$this->linkID) { @@ -416,19 +419,14 @@ public function execute($sql, $bind = []) $this->bind = $bind; } - //释放前次的查询结果 - if (!empty($this->PDOStatement) && $this->PDOStatement->queryString != $sql) { - $this->free(); - } - Db::$executeTimes++; try { // 调试开始 $this->debug(true); + // 预处理 - if (empty($this->PDOStatement)) { - $this->PDOStatement = $this->linkID->prepare($sql); - } + $this->PDOStatement = $this->linkID->prepare($sql); + // 是否为存储过程调用 $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); // 参数绑定 @@ -440,18 +438,27 @@ public function execute($sql, $bind = []) // 执行语句 $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(); return $this->numRows; } catch (\PDOException $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()); + } catch (\Throwable $e) { + if ($this->isBreak($e)) { + return $this->close()->execute($sql, $bind, $query); + } + throw $e; } catch (\Exception $e) { if ($this->isBreak($e)) { - return $this->close()->execute($sql, $bind); + return $this->close()->execute($sql, $bind, $query); } throw $e; } @@ -466,9 +473,13 @@ public function execute($sql, $bind = []) */ public function getRealSql($sql, array $bind = []) { + if (is_array($sql)) { + $sql = implode(';', $sql); + } + foreach ($bind as $key => $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; if (PDO::PARAM_STR == $type) { $value = $this->quote($value); } elseif (PDO::PARAM_INT == $type) { @@ -480,7 +491,8 @@ public function getRealSql($sql, array $bind = []) str_replace( [':' . $key . ')', ':' . $key . ',', ':' . $key . ' ', ':' . $key . PHP_EOL], [$value . ')', $value . ',', $value . ' ', $value . PHP_EOL], - $sql . ' '); + $sql . ' ' + ); } return rtrim($sql); } @@ -564,7 +576,7 @@ protected function getResult($pdo = false, $procedure = false) // 存储过程返回结果 return $this->procedure(); } - $result = $this->PDOStatement->fetchAll($this->fetchType); + $result = $this->PDOStatement->fetchAll($this->fetchType); $this->numRows = count($result); return $result; } @@ -638,13 +650,15 @@ public function startTrans() ); } - } catch (\PDOException $e) { + } catch (\Exception $e) { if ($this->isBreak($e)) { + --$this->transTimes; return $this->close()->startTrans(); } throw $e; - } catch (\Exception $e) { + } catch (\Error $e) { if ($this->isBreak($e)) { + --$this->transTimes; return $this->close()->startTrans(); } throw $e; @@ -725,7 +739,7 @@ protected function parseSavepointRollBack($name) * @param array $sqlArray SQL批处理指令 * @return boolean */ - public function batchQuery($sqlArray = []) + public function batchQuery($sqlArray = [], $bind = [], Query $query = null) { if (!is_array($sqlArray)) { return false; @@ -734,7 +748,7 @@ public function batchQuery($sqlArray = []) $this->startTrans(); try { foreach ($sqlArray as $sql) { - $this->execute($sql); + $this->execute($sql, $bind, $query); } // 提交事务 $this->commit(); @@ -742,6 +756,7 @@ public function batchQuery($sqlArray = []) $this->rollback(); throw $e; } + return true; } @@ -773,10 +788,12 @@ public function getExecuteTimes() */ public function close() { - $this->linkID = null; + $this->linkID = null; $this->linkWrite = null; - $this->linkRead = null; - $this->links = []; + $this->linkRead = null; + $this->links = []; + // 释放查询 + $this->free(); return $this; } @@ -803,6 +820,7 @@ protected function isBreak($e) 'SSL connection has been closed unexpectedly', 'Error writing data to the connection', 'Resource deadlock avoided', + 'failed with errno', ]; $error = $e->getMessage(); @@ -883,9 +901,10 @@ public function quote($str, $master = true) * @access protected * @param boolean $start 调试开始标记 true 开始 false 结束 * @param string $sql 执行的SQL语句 留空自动获取 + * @param boolean $master 主从标记 * @return void */ - protected function debug($start, $sql = '') + protected function debug($start, $sql = '', $master = false) { if (!empty($this->config['debug'])) { // 开启数据库调试模式 @@ -895,14 +914,14 @@ protected function debug($start, $sql = '') // 记录操作结束时间 Debug::remark('queryEndTime', 'time'); $runtime = Debug::getRangeTime('queryStartTime', 'queryEndTime'); - $sql = $sql ?: $this->getLastsql(); - $result = []; + $sql = $sql ?: $this->getLastsql(); + $result = []; // SQL性能分析 if ($this->config['sql_explain'] && 0 === stripos(trim($sql), 'select')) { $result = $this->getExplain($sql); } // SQL监听 - $this->trigger($sql, $runtime, $result); + $this->trigger($sql, $runtime, $result, $master); } } } @@ -924,19 +943,27 @@ public function listen($callback) * @param string $sql SQL语句 * @param float $runtime 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)) { foreach (self::$event as $callback) { if (is_callable($callback)) { - call_user_func_array($callback, [$sql, $runtime, $explain]); + call_user_func_array($callback, [$sql, $runtime, $explain, $master]); } } } 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)) { Log::record('[ EXPLAIN : ' . var_export($explain, true) . ' ]', 'sql'); } diff --git a/library/think/db/Expression.php b/library/think/db/Expression.php new file mode 100644 index 0000000000..f1b92abd7b --- /dev/null +++ b/library/think/db/Expression.php @@ -0,0 +1,48 @@ + +// +---------------------------------------------------------------------- + +namespace think\db; + +class Expression +{ + /** + * 查询表达式 + * + * @var string + */ + protected $value; + + /** + * 创建一个查询表达式 + * + * @param string $value + * @return void + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * 获取表达式 + * + * @return string + */ + public function getValue() + { + return $this->value; + } + + public function __toString() + { + return (string) $this->value; + } +} diff --git a/library/think/db/Query.php b/library/think/db/Query.php index 6b85aba3bf..4a22c659fb 100644 --- a/library/think/db/Query.php +++ b/library/think/db/Query.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -53,18 +53,20 @@ class Query protected static $info = []; // 回调事件 private static $event = []; + // 读取主库 + protected static $readMaster = []; /** * 构造函数 * @access public * @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->prefix = $this->connection->getConfig('prefix'); - $this->model = $model; + $this->prefix = $this->connection->getConfig('prefix'); + $this->model = $model; // 设置当前连接的Builder对象 $this->setBuilder(); } @@ -82,14 +84,21 @@ public function __call($method, $args) { if (strtolower(substr($method, 0, 5)) == 'getby') { // 根据某个字段获取记录 - $field = Loader::parseName(substr($method, 5)); + $field = Loader::parseName(substr($method, 5)); $where[$field] = $args[0]; return $this->where($where)->find(); } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { // 根据某个字段获取记录的某个值 - $name = Loader::parseName(substr($method, 10)); + $name = Loader::parseName(substr($method, 10)); $where[$name] = $args[0]; return $this->where($where)->value($args[1]); + } elseif ($this->model && method_exists($this->model, 'scope' . $method)) { + // 动态调用命名范围 + $method = 'scope' . $method; + array_unshift($args, $this); + + call_user_func_array([$this->model, $method], $args); + return $this; } else { throw new Exception('method not exist:' . __CLASS__ . '->' . $method); } @@ -126,20 +135,39 @@ public function connect($config) */ protected function setBuilder() { - $class = $this->connection->getBuilder(); + $class = $this->connection->getBuilder(); $this->builder = new $class($this->connection, $this); } /** - * 获取当前的模型对象名 + * 获取当前的模型对象实例 * @access public - * @return string + * @return Model|null */ public function getModel() { 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实例对象 * @access public @@ -183,7 +211,7 @@ public function setTable($table) public function getTable($name = '') { if ($name || empty($this->table)) { - $name = $name ?: $this->name; + $name = $name ?: $this->name; $tableName = $this->prefix; if ($name) { $tableName .= Loader::parseName($name); @@ -204,7 +232,7 @@ public function parseSqlTable($sql) { if (false !== strpos($sql, '__')) { $prefix = $this->prefix; - $sql = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function ($match) use ($prefix) { + $sql = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function ($match) use ($prefix) { return $prefix . strtolower($match[1]); }, $sql); } @@ -238,7 +266,7 @@ public function query($sql, $bind = [], $master = false, $class = false) */ public function execute($sql, $bind = []) { - return $this->connection->execute($sql, $bind); + return $this->connection->execute($sql, $bind, $this); } /** @@ -312,9 +340,9 @@ public function rollback() * @param array $sql SQL批处理指令 * @return boolean */ - public function batchQuery($sql = []) + public function batchQuery($sql = [], $bind = []) { - return $this->connection->batchQuery($sql); + return $this->connection->batchQuery($sql, $bind); } /** @@ -341,12 +369,12 @@ public function getPartitionTableName($data, $field, $rule = []) // 对数据表进行分区 if ($field && isset($data[$field])) { $value = $data[$field]; - $type = $rule['type']; + $type = $rule['type']; switch ($type) { case 'id': // 按照id范围分表 $step = $rule['expr']; - $seq = floor($value / $step) + 1; + $seq = floor($value / $step) + 1; break; case 'year': // 按照年份分表 @@ -369,7 +397,7 @@ public function getPartitionTableName($data, $field, $rule = []) $seq = (ord(substr($type($value), 0, 1)) % $rule['num']) + 1; } else { // 按照字段的首字母的值分表 - $seq = (ord($value{0}) % $rule['num']) + 1; + $seq = (ord($value[0]) % $rule['num']) + 1; } } return $this->getTable() . '_' . $seq; @@ -403,7 +431,7 @@ public function value($field, $default = null, $force = false) if (empty($this->options['table'])) { $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); } if (false === $result) { @@ -415,12 +443,13 @@ public function value($field, $default = null, $force = false) // 返回SQL语句 return $pdo; } + $result = $pdo->fetchColumn(); if ($force) { - $result += 0; + $result = (float) $result; } - if (isset($cache)) { + if (isset($cache) && false !== $result) { // 缓存数据 $this->cacheData($key, $result, $cache); } @@ -447,7 +476,7 @@ public function column($field, $key = '') if (empty($this->options['table'])) { $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); } if (false === $result) { @@ -470,10 +499,10 @@ public function column($field, $key = '') $resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC); if ($resultSet) { $fields = array_keys($resultSet[0]); - $count = count($fields); - $key1 = array_shift($fields); - $key2 = $fields ? array_shift($fields) : ''; - $key = $key ?: $key1; + $count = count($fields); + $key1 = array_shift($fields); + $key2 = $fields ? array_shift($fields) : ''; + $key = $key ?: $key1; if (strpos($key, '.')) { list($alias, $key) = explode('.', $key); } @@ -510,13 +539,43 @@ public function column($field, $key = '') public function count($field = '*') { if (isset($this->options['group'])) { + if (!preg_match('/^[\w\.\*]+$/', $field)) { + throw new Exception('not support data:' . $field); + } // 支持GROUP $options = $this->getOptions(); - $subSql = $this->options($options)->field('count(' . $field . ')')->bind($this->bind)->buildSql(); - return $this->table([$subSql => '_group_count_'])->value('COUNT(*) AS tp_count', 0, true); + $subSql = $this->options($options)->field('count(' . $field . ')')->bind($this->bind)->buildSql(); + + $count = $this->table([$subSql => '_group_count_'])->value('COUNT(*) AS tp_count', 0, true); + } else { + $count = $this->aggregate('COUNT', $field, true); } - return $this->value('COUNT(' . $field . ') AS tp_count', 0, true); + return is_string($count) ? $count : (int) $count; + + } + + /** + * 聚合查询 + * @access public + * @param string $aggregate 聚合方法 + * @param string $field 字段名 + * @param bool $force 强制转为数字类型 + * @return mixed + */ + public function aggregate($aggregate, $field, $force = false) + { + if (0 === stripos($field, 'DISTINCT ')) { + list($distinct, $field) = explode(' ', $field); + } + + if (!preg_match('/^[\w\.\+\-\*]+$/', $field)) { + throw new Exception('not support data:' . $field); + } + + $result = $this->value($aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $field . ') AS tp_' . strtolower($aggregate), 0, $force); + + return $result; } /** @@ -527,29 +586,31 @@ public function count($field = '*') */ public function sum($field) { - return $this->value('SUM(' . $field . ') AS tp_sum', 0, true); + return $this->aggregate('SUM', $field, true); } /** * MIN查询 * @access public * @param string $field 字段名 + * @param bool $force 强制转为数字类型 * @return mixed */ - public function min($field) + public function min($field, $force = true) { - return $this->value('MIN(' . $field . ') AS tp_min', 0, true); + return $this->aggregate('MIN', $field, $force); } /** * MAX查询 * @access public * @param string $field 字段名 + * @param bool $force 强制转为数字类型 * @return mixed */ - public function max($field) + public function max($field, $force = true) { - return $this->value('MAX(' . $field . ') AS tp_max', 0, true); + return $this->aggregate('MAX', $field, $force); } /** @@ -560,7 +621,7 @@ public function max($field) */ public function avg($field) { - return $this->value('AVG(' . $field . ') AS tp_avg', 0, true); + return $this->aggregate('AVG', $field, true); } /** @@ -607,7 +668,7 @@ public function setInc($field, $step = 1, $lazyTime = 0) return true; } } - return $this->setField($field, ['exp', $field . '+' . $step]); + return $this->setField($field, ['inc', $step]); } /** @@ -635,8 +696,9 @@ public function setDec($field, $step = 1, $lazyTime = 0) $this->options = []; return true; } + return $this->setField($field, ['inc', $step]); } - return $this->setField($field, ['exp', $field . '-' . $step]); + return $this->setField($field, ['dec', $step]); } /** @@ -704,7 +766,8 @@ protected function getJoinTable($join, &$alias = null) { // 传入的表名为数组 if (is_array($join)) { - list($table, $alias) = each($join); + $table = $join; + $alias = array_shift($join); } else { $join = trim($join); if (false !== strpos($join, '(')) { @@ -725,13 +788,9 @@ protected function getJoinTable($join, &$alias = null) $table = $this->getTable($table); } } - } - if (isset($alias)) { - if (isset($this->options['alias'][$table])) { - $table = $table . '@think' . uniqid(); + if (isset($alias) && $table != $alias) { + $table = [$table => $alias]; } - $table = [$table => $alias]; - $this->alias($table); } return $table; } @@ -769,18 +828,25 @@ public function field($field, $except = false, $tableName = '', $prefix = '', $a { if (empty($field)) { return $this; + } elseif ($field instanceof Expression) { + $this->options['field'][] = $field; + return $this; } + if (is_string($field)) { + if (preg_match('/[\<\'\"\(]/', $field)) { + return $this->fieldRaw($field); + } $field = array_map('trim', explode(',', $field)); } if (true === $field) { // 获取全部字段 $fields = $this->getTableInfo($tableName ?: (isset($this->options['table']) ? $this->options['table'] : ''), 'fields'); - $field = $fields ?: ['*']; + $field = $fields ?: ['*']; } elseif ($except) { // 字段排除 $fields = $this->getTableInfo($tableName ?: (isset($this->options['table']) ? $this->options['table'] : ''), 'fields'); - $field = $fields ? array_diff($fields, $field) : $field; + $field = $fields ? array_diff($fields, $field) : $field; } if ($tableName) { // 添加统一的前缀 @@ -794,12 +860,30 @@ public function field($field, $except = false, $tableName = '', $prefix = '', $a } 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); 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 @@ -828,7 +912,7 @@ public function inc($field, $step = 1) { $fields = is_string($field) ? explode(',', $field) : $field; foreach ($fields as $field) { - $this->data($field, ['exp', $field . '+' . $step]); + $this->data($field, ['inc', $step]); } return $this; } @@ -844,7 +928,7 @@ public function dec($field, $step = 1) { $fields = is_string($field) ? explode(',', $field) : $field; foreach ($fields as $field) { - $this->data($field, ['exp', $field . '-' . $step]); + $this->data($field, ['dec', $step]); } return $this; } @@ -858,29 +942,40 @@ public function dec($field, $step = 1) */ public function exp($field, $value) { - $this->data($field, ['exp', $value]); + $this->data($field, $this->raw($value)); return $this; } + /** + * 使用表达式设置数据 + * @access public + * @param mixed $value 表达式 + * @return Expression + */ + public function raw($value) + { + return new Expression($value); + } + /** * 指定JOIN查询字段 * @access public * @param string|array $table 数据表 * @param string|array $field 查询字段 - * @param string|array $on JOIN条件 + * @param mixed $on JOIN条件 * @param string $type JOIN类型 * @return $this */ public function view($join, $field = true, $on = null, $type = 'INNER') { $this->options['view'] = true; - if (is_array($join) && key($join) !== 0) { + if (is_array($join) && key($join) === 0) { 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 { $fields = []; - $table = $this->getJoinTable($join, $alias); + $table = $this->getJoinTable($join, $alias); if (true === $field) { $fields = $alias . '.*'; @@ -890,7 +985,7 @@ public function view($join, $field = true, $on = null, $type = 'INNER') } foreach ($field as $key => $val) { if (is_numeric($key)) { - $fields[] = $alias . '.' . $val; + $fields[] = $alias . '.' . $val; $this->options['map'][$val] = $alias . '.' . $val; } else { if (preg_match('/[,=\.\'\"\(\s]/', $key)) { @@ -898,7 +993,7 @@ public function view($join, $field = true, $on = null, $type = 'INNER') } else { $name = $alias . '.' . $key; } - $fields[$name] = $val; + $fields[$name] = $val; $this->options['map'][$val] = $name; } } @@ -975,6 +1070,37 @@ public function whereXor($field, $op = null, $condition = null) 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查询条件 * @access public @@ -984,7 +1110,7 @@ public function whereXor($field, $op = null, $condition = null) */ public function whereNull($field, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'null', null); + $this->parseWhereExp($logic, $field, 'null', null, [], true); return $this; } @@ -997,7 +1123,7 @@ public function whereNull($field, $logic = 'AND') */ public function whereNotNull($field, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'notnull', null); + $this->parseWhereExp($logic, $field, 'notnull', null, [], true); return $this; } @@ -1037,7 +1163,7 @@ public function whereNotExists($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; } @@ -1051,7 +1177,7 @@ public function whereIn($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; } @@ -1065,7 +1191,7 @@ public function whereNotIn($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; } @@ -1079,7 +1205,7 @@ public function whereLike($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; } @@ -1093,7 +1219,7 @@ public function whereNotLike($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; } @@ -1107,7 +1233,7 @@ public function whereBetween($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; } @@ -1121,7 +1247,7 @@ public function whereNotBetween($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; } @@ -1148,9 +1274,10 @@ public function useSoftDelete($field, $condition = null) * @param mixed $op 查询表达式 * @param mixed $condition 查询条件 * @param array $param 查询参数 + * @param bool $strict 严格模式 * @return void */ - protected function parseWhereExp($logic, $field, $op, $condition, $param = []) + protected function parseWhereExp($logic, $field, $op, $condition, $param = [], $strict = false) { $logic = strtoupper($logic); if ($field instanceof \Closure) { @@ -1161,8 +1288,17 @@ protected function parseWhereExp($logic, $field, $op, $condition, $param = []) if (is_string($field) && !empty($this->options['via']) && !strpos($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)) { // 参数绑定 $this->bind($op); @@ -1176,28 +1312,35 @@ protected function parseWhereExp($logic, $field, $op, $condition, $param = []) } } elseif ($field && is_string($field)) { // 字符串查询 - $where[$field] = ['null', '']; + $where[$field] = ['null', '']; $this->options['multi'][$logic][$field][] = $where[$field]; } } elseif (is_array($op)) { $where[$field] = $param; } elseif (in_array(strtolower($op), ['null', 'notnull', 'not null'])) { // null查询 - $where[$field] = [$op, '']; + $where[$field] = [$op, '']; + $this->options['multi'][$logic][$field][] = $where[$field]; } elseif (is_null($condition)) { // 字段相等查询 - $where[$field] = ['eq', $op]; + $where[$field] = ['eq', $op]; + $this->options['multi'][$logic][$field][] = $where[$field]; } else { - $where[$field] = [$op, $condition, isset($param[2]) ? $param[2] : null]; - if ('exp' == strtolower($op) && isset($param[2]) && is_array($param[2])) { + if ('exp' == strtolower($op)) { + $where[$field] = ['exp', $this->raw($condition)]; // 参数绑定 - $this->bind($param[2]); + if (isset($param[2]) && is_array($param[2])) { + $this->bind($param[2]); + } + } else { + $where[$field] = [$op, $condition]; } // 记录一个字段多次查询条件 $this->options['multi'][$logic][$field][] = $where[$field]; } + if (!empty($where)) { if (!isset($this->options['where'][$logic])) { $this->options['where'][$logic] = []; @@ -1239,6 +1382,7 @@ public function removeWhereField($field, $logic = 'AND') $logic = strtoupper($logic); if (isset($this->options['where'][$logic][$field])) { unset($this->options['where'][$logic][$field]); + unset($this->options['multi'][$logic][$field]); } return $this; } @@ -1309,20 +1453,20 @@ public function page($page, $listRows = null) public function paginate($listRows = null, $simple = false, $config = []) { if (is_int($simple)) { - $total = $simple; + $total = $simple; $simple = false; } if (is_array($listRows)) { - $config = array_merge(Config::get('paginate'), $listRows); + $config = array_merge(Config::get('paginate'), $listRows); $listRows = $config['list_rows']; } else { - $config = array_merge(Config::get('paginate'), $config); + $config = array_merge(Config::get('paginate'), $config); $listRows = $listRows ?: $config['list_rows']; } /** @var Paginator $class */ $class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\paginator\\driver\\' . ucwords($config['type']); - $page = isset($config['page']) ? (int) $config['page'] : call_user_func([ + $page = isset($config['page']) ? (int) $config['page'] : call_user_func([ $class, 'getCurrentPage', ], $config['var_page']); @@ -1336,12 +1480,12 @@ public function paginate($listRows = null, $simple = false, $config = []) unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']); - $bind = $this->bind; - $total = $this->count(); + $bind = $this->bind; + $total = $this->count(); $results = $this->options($options)->bind($bind)->page($page, $listRows)->select(); } elseif ($simple) { $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select(); - $total = null; + $total = null; } else { $results = $this->page($page, $listRows)->select(); } @@ -1361,7 +1505,7 @@ public function table($table) // 子查询 } elseif (strpos($table, ',')) { $tables = explode(',', $table); - $table = []; + $table = []; foreach ($tables as $item) { list($item, $alias) = explode(' ', trim($item)); if ($alias) { @@ -1379,7 +1523,7 @@ public function table($table) } } else { $tables = $table; - $table = []; + $table = []; foreach ($tables as $key => $val) { if (is_numeric($key)) { $table[] = $val; @@ -1414,31 +1558,59 @@ public function using($using) */ public function order($field, $order = null) { - if (!empty($field)) { - if (is_string($field)) { - if (!empty($this->options['via'])) { - $field = $this->options['via'] . '.' . $field; - } - $field = empty($order) ? $field : [$field => $order]; - } elseif (!empty($this->options['via'])) { - foreach ($field as $key => $val) { - if (is_numeric($key)) { - $field[$key] = $this->options['via'] . '.' . $val; - } else { - $field[$this->options['via'] . '.' . $key] = $val; - unset($field[$key]); - } - } - } - if (!isset($this->options['order'])) { - $this->options['order'] = []; + if (empty($field)) { + return $this; + } elseif ($field instanceof Expression) { + $this->options['order'][] = $field; + return $this; + } + + if (is_string($field)) { + if (!empty($this->options['via'])) { + $field = $this->options['via'] . '.' . $field; } - if (is_array($field)) { - $this->options['order'] = array_merge($this->options['order'], $field); + if (strpos($field, ',')) { + $field = array_map('trim', explode(',', $field)); } else { - $this->options['order'][] = $field; + $field = empty($order) ? $field : [$field => $order]; + } + } elseif (!empty($this->options['via'])) { + foreach ($field as $key => $val) { + if (is_numeric($key)) { + $field[$key] = $this->options['via'] . '.' . $val; + } else { + $field[$this->options['via'] . '.' . $key] = $val; + unset($field[$key]); + } } } + if (!isset($this->options['order'])) { + $this->options['order'] = []; + } + if (is_array($field)) { + $this->options['order'] = array_merge($this->options['order'], $field); + } else { + $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; } @@ -1455,7 +1627,7 @@ public function cache($key = true, $expire = null, $tag = null) // 增加快捷调用方式 cache(10) 等同于 cache(true, 10) if ($key instanceof \DateTime || (is_numeric($key) && is_null($expire))) { $expire = $key; - $key = true; + $key = true; } if (false !== $key) { @@ -1496,7 +1668,7 @@ public function having($having) */ public function lock($lock = false) { - $this->options['lock'] = $lock; + $this->options['lock'] = $lock; $this->options['master'] = true; return $this; } @@ -1735,7 +1907,7 @@ public function getTableInfo($tableName = '', $fetch = '') } list($guid) = explode(' ', $tableName); - $db = $this->getConfig('database'); + $db = $this->getConfig('database'); if (!isset(self::$info[$db . '.' . $guid])) { if (!strpos($guid, '.')) { $schema = $db . '.' . $guid; @@ -1743,13 +1915,13 @@ public function getTableInfo($tableName = '', $fetch = '') $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'; } else { $info = $this->connection->getFields($guid); } $fields = array_keys($info); - $bind = $type = []; + $bind = $type = []; foreach ($info as $key => $val) { // 记录字段类型 $type[$key] = $val['type']; @@ -1780,7 +1952,7 @@ public function getPk($options = '') if (!empty($this->pk)) { $pk = $this->pk; } else { - $pk = $this->getTableInfo(is_array($options) ? $options['table'] : $options, 'pk'); + $pk = $this->getTableInfo(is_array($options) && isset($options['table']) ? $options['table'] : $options, 'pk'); } return $pk; } @@ -1801,7 +1973,7 @@ public function getFieldsType($table = '') public function getFieldsBind($table = '') { $types = $this->getFieldsType($table); - $bind = []; + $bind = []; if ($types) { foreach ($types as $key => $type) { $bind[$key] = $this->getFieldBindType($type); @@ -1818,7 +1990,9 @@ public function getFieldsBind($table = '') */ 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; } elseif (preg_match('/bool/is', $type)) { $bind = PDO::PARAM_BOOL; @@ -1900,30 +2074,29 @@ public function with($with) $with = explode(',', $with); } - $first = true; - $currentModel = $this->model; + $first = true; /** @var Model $class */ - $class = new $currentModel; + $class = $this->model; foreach ($with as $key => $relation) { $subRelation = ''; - $closure = false; + $closure = false; if ($relation instanceof \Closure) { // 支持闭包查询过滤关联条件 - $closure = $relation; - $relation = $key; + $closure = $relation; + $relation = $key; $with[$key] = $key; } elseif (is_array($relation)) { $subRelation = $relation; - $relation = $key; + $relation = $key; } elseif (is_string($relation) && strpos($relation, '.')) { - $with[$key] = $relation; + $with[$key] = $relation; list($relation, $subRelation) = explode('.', $relation, 2); } /** @var Relation $model */ $relation = Loader::parseName($relation, 1, false); - $model = $class->$relation(); + $model = $class->$relation(); if ($model instanceof OneToOne && 0 == $model->getEagerlyType()) { $model->eagerly($this, $relation, $subRelation, $closure, $first); $first = false; @@ -1957,14 +2130,23 @@ public function withCount($relation, $subQuery = true) $this->field('*'); } foreach ($relations as $key => $relation) { - $closure = false; + $closure = $name = null; if ($relation instanceof \Closure) { - $closure = $relation; + $closure = $relation; + $relation = $key; + } elseif (!is_int($key)) { + $name = $relation; $relation = $key; } $relation = Loader::parseName($relation, 1, false); - $count = '(' . (new $this->model)->$relation()->getRelationCountQuery($closure) . ')'; - $this->field([$count => Loader::parseName($relation) . '_count']); + + $count = '(' . $this->model->$relation()->getRelationCountQuery($closure, $name) . ')'; + + if (empty($name)) { + $name = Loader::parseName($relation) . '_count'; + } + + $this->field([$count => $name]); } } return $this; @@ -2048,7 +2230,7 @@ protected function parsePkWhere($data, &$options) // 根据复合主键查询 foreach ($pk as $key) { if (isset($data[$key])) { - $attr = isset($alias) ? $alias . '.' . $key : $key; + $attr = isset($alias) ? $alias . '.' . $key : $key; $where[$attr] = $data[$key]; } else { throw new Exception('miss complex primary data'); @@ -2079,7 +2261,7 @@ public function insert(array $data = [], $replace = false, $getLastInsID = false { // 分析查询表达式 $options = $this->parseExpress(); - $data = array_merge($options['data'], $data); + $data = array_merge($options['data'], $data); // 生成SQL语句 $sql = $this->builder->insert($data, $options, $replace); // 获取参数绑定 @@ -2090,9 +2272,9 @@ public function insert(array $data = [], $replace = false, $getLastInsID = false } // 执行操作 - $result = 0 === $sql ? 0 : $this->execute($sql, $bind); + $result = 0 === $sql ? 0 : $this->execute($sql, $bind, $this); if ($result) { - $sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null); + $sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null); $lastInsId = $this->getLastInsID($sequence); if ($lastInsId) { $pk = $this->getPk($options); @@ -2126,27 +2308,40 @@ public function insertGetId(array $data, $replace = false, $sequence = null) /** * 批量插入记录 * @access public - * @param mixed $dataSet 数据集 - * @param boolean $replace 是否replace + * @param mixed $dataSet 数据集 + * @param boolean $replace 是否replace + * @param integer $limit 每次写入数据限制 * @return integer|string */ - public function insertAll(array $dataSet, $replace = false) + public function insertAll(array $dataSet, $replace = false, $limit = null) { // 分析查询表达式 $options = $this->parseExpress(); if (!is_array(reset($dataSet))) { return false; } + // 生成SQL语句 - $sql = $this->builder->insertAll($dataSet, $options, $replace); + if (is_null($limit)) { + $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(); if ($options['fetch_sql']) { // 获取实际执行的SQL语句 return $this->connection->getRealSql($sql, $bind); + } elseif (is_array($sql)) { + // 执行操作 + return $this->batchQuery($sql, $bind, $this); } else { // 执行操作 - return $this->execute($sql, $bind); + return $this->execute($sql, $bind, $this); } } @@ -2164,7 +2359,7 @@ public function selectInsert($fields, $table) $options = $this->parseExpress(); // 生成SQL语句 $table = $this->parseSqlTable($table); - $sql = $this->builder->selectInsert($fields, $table, $options); + $sql = $this->builder->selectInsert($fields, $table, $options); // 获取参数绑定 $bind = $this->getBind(); if ($options['fetch_sql']) { @@ -2172,7 +2367,7 @@ public function selectInsert($fields, $table) return $this->connection->getRealSql($sql, $bind); } else { // 执行操作 - return $this->execute($sql, $bind); + return $this->execute($sql, $bind, $this); } } @@ -2187,8 +2382,8 @@ public function selectInsert($fields, $table) public function update(array $data = []) { $options = $this->parseExpress(); - $data = array_merge($options['data'], $data); - $pk = $this->getPk($options); + $data = array_merge($options['data'], $data); + $pk = $this->getPk($options); if (isset($options['cache']) && is_string($options['cache']['key'])) { $key = $options['cache']['key']; } @@ -2239,13 +2434,13 @@ public function update(array $data = []) Cache::clear($options['cache']['tag']); } // 执行操作 - $result = '' == $sql ? 0 : $this->execute($sql, $bind); + $result = '' == $sql ? 0 : $this->execute($sql, $bind, $this); if ($result) { if (is_string($pk) && isset($where[$pk])) { $data[$pk] = $where[$pk]; } elseif (is_string($pk) && isset($key) && strpos($key, '|')) { list($a, $val) = explode('|', $key); - $data[$pk] = $val; + $data[$pk] = $val; } $options['data'] = $data; $this->trigger('after_update', $options); @@ -2308,7 +2503,7 @@ public function select($data = null) // 判断查询缓存 $cache = $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); } if (false === $resultSet) { @@ -2342,11 +2537,10 @@ public function select($data = null) // 数据列表读取后的处理 if (!empty($this->model)) { // 生成模型对象 - $modelName = $this->model; if (count($resultSet) > 0) { foreach ($resultSet as $key => $result) { /** @var Model $model */ - $model = new $modelName($result); + $model = $this->model->newInstance($result); $model->isUpdate(true); // 关联查询 @@ -2366,7 +2560,7 @@ public function select($data = null) // 模型数据集转换 $resultSet = $model->toCollection($resultSet); } else { - $resultSet = (new $modelName)->toCollection($resultSet); + $resultSet = $this->model->toCollection($resultSet); } } elseif ('collection' == $this->connection->getConfig('resultset_type')) { // 返回Collection对象 @@ -2410,10 +2604,16 @@ protected function getCacheKey($value, $options, $bind = []) } elseif (is_array($value) && is_string($value[0]) && 'eq' == strtolower($value[0])) { $data = $value[1]; } + $prefix = $this->connection->getConfig('database') . '.'; + if (isset($data)) { - return 'think:' . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data; - } else { - return md5(serialize($options) . serialize($bind)); + return 'think:' . $prefix . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data; + } + + try { + return md5($prefix . serialize($options) . serialize($bind)); + } catch (\Exception $e) { + throw new Exception('closure not support cache(true)'); } } @@ -2436,7 +2636,7 @@ public function find($data = null) } // 分析查询表达式 $options = $this->parseExpress(); - $pk = $this->getPk($options); + $pk = $this->getPk($options); if (!is_null($data)) { // AR模式分析主键条件 $this->parsePkWhere($data, $options); @@ -2445,16 +2645,16 @@ public function find($data = null) } $options['limit'] = 1; - $result = false; + $result = false; if (empty($options['fetch_sql']) && !empty($options['cache'])) { // 判断查询缓存 $cache = $options['cache']; 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'])) { $key = $cache['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); } @@ -2471,7 +2671,7 @@ public function find($data = null) if (!is_array($data)) { if (isset($key) && strpos($key, '|')) { list($a, $val) = explode('|', $key); - $item[$pk] = $val; + $item[$pk] = $val; } else { $item[$pk] = $data; } @@ -2492,7 +2692,7 @@ public function find($data = null) $result = isset($resultSet[0]) ? $resultSet[0] : null; } - if (isset($cache) && false !== $result) { + if (isset($cache) && $result) { // 缓存数据 $this->cacheData($key, $result, $cache); } @@ -2502,8 +2702,7 @@ public function find($data = null) if (!empty($result)) { if (!empty($this->model)) { // 返回模型对象 - $model = $this->model; - $result = new $model($result); + $result = $this->model->newInstance($result); $result->isUpdate(true, isset($options['where']['AND']) ? $options['where']['AND'] : null); // 关联查询 if (!empty($options['relation'])) { @@ -2534,7 +2733,8 @@ public function find($data = null) protected function throwNotFound($options = []) { 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 { $table = is_array($options['table']) ? key($options['table']) : $options['table']; throw new DataNotFoundException('table data not Found:' . $table, $table, $options); @@ -2582,48 +2782,54 @@ public function findOrFail($data = null) public function chunk($count, $callback, $column = null, $order = 'asc') { $options = $this->getOptions(); - if (isset($options['table'])) { - $table = is_array($options['table']) ? key($options['table']) : $options['table']; - } else { - $table = ''; - } - $column = $column ?: $this->getPk($table); - if (is_array($column)) { - $column = $column[0]; + if (empty($options['table'])) { + $options['table'] = $this->getTable(); } + $column = $column ?: $this->getPk($options); + if (isset($options['order'])) { if (App::$debug) { throw new \LogicException('chunk not support call order'); } unset($options['order']); } - $bind = $this->bind; - $resultSet = $this->options($options)->limit($count)->order($column, $order)->select(); - if (strpos($column, '.')) { - list($alias, $key) = explode('.', $column); + $bind = $this->bind; + if (is_array($column)) { + $times = 1; + $query = $this->options($options)->page($times, $count); } else { - $key = $column; - } - if ($resultSet instanceof Collection) { - $resultSet = $resultSet->all(); + if (strpos($column, '.')) { + list($alias, $key) = explode('.', $column); + } else { + $key = $column; + } + $query = $this->options($options)->limit($count); } + $resultSet = $query->order($column, $order)->select(); + + while (count($resultSet) > 0) { + if ($resultSet instanceof Collection) { + $resultSet = $resultSet->all(); + } - while (!empty($resultSet)) { if (false === call_user_func($callback, $resultSet)) { return false; } - $end = end($resultSet); - $lastId = is_array($end) ? $end[$key] : $end->getData($key); - $resultSet = $this->options($options) - ->limit($count) - ->bind($bind) - ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId) - ->order($column, $order) - ->select(); - if ($resultSet instanceof Collection) { - $resultSet = $resultSet->all(); + + if (is_array($column)) { + $times++; + $query = $this->options($options)->page($times, $count); + } else { + $end = end($resultSet); + $lastId = is_array($end) ? $end[$key] : $end->getData($key); + $query = $this->options($options) + ->limit($count) + ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId); } + + $resultSet = $query->bind($bind)->order($column, $order)->select(); } + return true; } @@ -2634,7 +2840,7 @@ public function chunk($count, $callback, $column = null, $order = 'asc') */ public function getBind() { - $bind = $this->bind; + $bind = $this->bind; $this->bind = []; return $bind; } @@ -2663,7 +2869,7 @@ public function delete($data = null) { // 分析查询表达式 $options = $this->parseExpress(); - $pk = $this->getPk($options); + $pk = $this->getPk($options); if (isset($options['cache']) && is_string($options['cache']['key'])) { $key = $options['cache']['key']; } @@ -2700,12 +2906,12 @@ public function delete($data = null) Cache::clear($options['cache']['tag']); } // 执行操作 - $result = $this->execute($sql, $bind); + $result = $this->execute($sql, $bind, $this); if ($result) { if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) { list($a, $val) = explode('|', $key); - $item[$pk] = $val; - $data = $item; + $item[$pk] = $val; + $data = $item; } $options['data'] = $data; $this->trigger('after_delete', $options); @@ -2785,6 +2991,10 @@ protected function parseExpress() } } + 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) { if (!isset($options[$name])) { $options[$name] = ''; @@ -2794,10 +3004,10 @@ protected function parseExpress() if (isset($options['page'])) { // 根据页数计算limit list($page, $listRows) = $options['page']; - $page = $page > 0 ? $page : 1; - $listRows = $listRows > 0 ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 20); - $offset = $listRows * ($page - 1); - $options['limit'] = $offset . ',' . $listRows; + $page = $page > 0 ? $page : 1; + $listRows = $listRows > 0 ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 20); + $offset = $listRows * ($page - 1); + $options['limit'] = $offset . ',' . $listRows; } $this->options = []; @@ -2828,7 +3038,7 @@ protected function trigger($event, $params = []) $result = false; if (isset(self::$event[$event])) { $callback = self::$event[$event]; - $result = call_user_func_array($callback, [$params, $this]); + $result = call_user_func_array($callback, [$params, $this]); } return $result; } diff --git a/library/think/db/builder/Mysql.php b/library/think/db/builder/Mysql.php index 5bc9d03b14..dd5920b5af 100644 --- a/library/think/db/builder/Mysql.php +++ b/library/think/db/builder/Mysql.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -12,28 +12,95 @@ namespace think\db\builder; use think\db\Builder; +use think\Exception; /** * mysql数据库驱动 */ class Mysql extends Builder { + + protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES %DATA% %COMMENT%'; protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; + /** + * 生成insertall SQL + * @access public + * @param array $dataSet 数据集 + * @param array $options 表达式 + * @param bool $replace 是否replace + * @return string + * @throws Exception + */ + public function insertAll($dataSet, $options = [], $replace = false) + { + // 获取合法的字段 + if ('*' == $options['field']) { + $fields = array_keys($this->query->getFieldsType($options['table'])); + } else { + $fields = $options['field']; + } + + foreach ($dataSet as $data) { + foreach ($data as $key => $val) { + if (!in_array($key, $fields, true)) { + if ($options['strict']) { + throw new Exception('fields not exists:[' . $key . ']'); + } + unset($data[$key]); + } elseif (is_null($val)) { + $data[$key] = 'NULL'; + } elseif (is_scalar($val)) { + $data[$key] = $this->parseValue($val, $key); + } elseif (is_object($val) && method_exists($val, '__toString')) { + // 对象数据写入 + $data[$key] = $val->__toString(); + } else { + // 过滤掉非标量数据 + unset($data[$key]); + } + } + $value = array_values($data); + $values[] = '( ' . implode(',', $value) . ' )'; + + if (!isset($insertFields)) { + $insertFields = array_map([$this, 'parseKey'], array_keys($data)); + } + } + + return str_replace( + ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], + [ + $replace ? 'REPLACE' : 'INSERT', + $this->parseTable($options['table'], $options), + implode(' , ', $insertFields), + implode(' , ', $values), + $this->parseComment($options['comment']), + ], + $this->insertAllSql + ); + } + /** * 字段和表名处理 * @access protected - * @param string $key + * @param mixed $key * @param array $options * @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); if (strpos($key, '$.') && false === strpos($key, '(')) { // JSON字段支持 list($field, $name) = explode('$.', $key); - $key = 'json_extract(' . $field . ', \'$.' . $name . '\')'; + return 'json_extract(' . $field . ', \'$.' . $name . '\')'; } elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) { list($table, $key) = explode('.', $key, 2); if ('__TABLE__' == $table) { @@ -43,7 +110,11 @@ protected function parseKey($key, $options = []) $table = $options['alias'][$table]; } } - if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) { + + if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) { + throw new Exception('not support data:' . $key); + } + if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) { $key = '`' . $key . '`'; } if (isset($table)) { diff --git a/library/think/db/builder/Pgsql.php b/library/think/db/builder/Pgsql.php index b690401c85..fab509b457 100644 --- a/library/think/db/builder/Pgsql.php +++ b/library/think/db/builder/Pgsql.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -18,7 +18,7 @@ */ class Pgsql extends Builder { - protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; + protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; /** @@ -44,17 +44,23 @@ public function parseLimit($limit) /** * 字段和表名处理 * @access protected - * @param string $key + * @param mixed $key * @param array $options * @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); if (strpos($key, '$.') && false === strpos($key, '(')) { // JSON字段支持 list($field, $name) = explode('$.', $key); - $key = $field . '->>\'' . $name . '\''; + $key = $field . '->>\'' . $name . '\''; } elseif (strpos($key, '.')) { list($table, $key) = explode('.', $key, 2); if ('__TABLE__' == $table) { diff --git a/library/think/db/builder/Sqlite.php b/library/think/db/builder/Sqlite.php index 28a5d6f112..c727f04b8d 100644 --- a/library/think/db/builder/Sqlite.php +++ b/library/think/db/builder/Sqlite.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -52,12 +52,18 @@ protected function parseRand() /** * 字段和表名处理 * @access protected - * @param string $key + * @param mixed $key * @param array $options * @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); if (strpos($key, '.')) { list($table, $key) = explode('.', $key, 2); diff --git a/library/think/db/builder/Sqlsrv.php b/library/think/db/builder/Sqlsrv.php index 59ea021ffd..b85a7c50db 100644 --- a/library/think/db/builder/Sqlsrv.php +++ b/library/think/db/builder/Sqlsrv.php @@ -12,18 +12,19 @@ namespace think\db\builder; use think\db\Builder; +use think\db\Expression; /** * Sqlsrv数据库驱动 */ class Sqlsrv extends Builder { - protected $selectSql = 'SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS thinkphp) AS T1 %LIMIT%%COMMENT%'; + protected $selectSql = 'SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS thinkphp) AS T1 %LIMIT%%COMMENT%'; protected $selectInsertSql = 'SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%'; - protected $updateSql = 'UPDATE %TABLE% SET %SET% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; - protected $deleteSql = 'DELETE FROM %TABLE% %USING% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; - protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; - protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; + protected $updateSql = 'UPDATE %TABLE% SET %SET% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; + protected $deleteSql = 'DELETE FROM %TABLE% %USING% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; + protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; + protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; /** * order分析 @@ -34,25 +35,29 @@ class Sqlsrv extends Builder */ protected function parseOrder($order, $options = []) { - if (is_array($order)) { - $array = []; - foreach ($order as $key => $val) { - if (is_numeric($key)) { - if (false === strpos($val, '(')) { - $array[] = $this->parseKey($val, $options); - } elseif ('[rand]' == $val) { - $array[] = $this->parseRand(); - } else { - $array[] = $val; - } + if (empty($order)) { + return ' ORDER BY rand()'; + } + + $array = []; + foreach ($order as $key => $val) { + if ($val instanceof Expression) { + $array[] = $val->getValue(); + } elseif (is_numeric($key)) { + if (false === strpos($val, '(')) { + $array[] = $this->parseKey($val, $options); + } elseif ('[rand]' == $val) { + $array[] = $this->parseRand(); } else { - $sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : ''; - $array[] = $this->parseKey($key, $options) . ' ' . $sort; + $array[] = $val; } + } else { + $sort = in_array(strtolower(trim($val)), ['asc', 'desc'], true) ? ' ' . $val : ''; + $array[] = $this->parseKey($key, $options, true) . ' ' . $sort; } - $order = implode(',', $array); } - return !empty($order) ? ' ORDER BY ' . $order : ' ORDER BY rand()'; + + return ' ORDER BY ' . implode(',', $array); } /** @@ -68,12 +73,17 @@ protected function parseRand() /** * 字段和表名处理 * @access protected - * @param string $key + * @param mixed $key * @param array $options * @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); if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) { list($table, $key) = explode('.', $key, 2); @@ -84,7 +94,11 @@ protected function parseKey($key, $options = []) $table = $options['alias'][$table]; } } - if (!is_numeric($key) && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) { + + if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) { + throw new Exception('not support data:' . $key); + } + if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)\[.\s]/', $key))) { $key = '[' . $key . ']'; } if (isset($table)) { diff --git a/library/think/db/connector/Mysql.php b/library/think/db/connector/Mysql.php index 9d146c7136..347c2fd074 100644 --- a/library/think/db/connector/Mysql.php +++ b/library/think/db/connector/Mysql.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -61,16 +61,16 @@ public function getFields($tableName) } $tableName = '`' . $tableName . '`'; } - $sql = 'SHOW COLUMNS FROM ' . $tableName; - $pdo = $this->query($sql, [], false, true); + $sql = 'SHOW COLUMNS FROM ' . $tableName; + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); - $info = []; + $info = []; if ($result) { foreach ($result as $key => $val) { - $val = array_change_key_case($val); + $val = array_change_key_case($val); $info[$val['field']] = [ - 'name' => $val['field'], - 'type' => $val['type'], + 'name' => $val['field'], + 'type' => $val['type'], 'notnull' => (bool) ('' === $val['null']), // not null is empty, null is yes 'default' => $val['default'], 'primary' => (strtolower($val['key']) == 'pri'), @@ -89,10 +89,10 @@ public function getFields($tableName) */ public function getTables($dbName = '') { - $sql = !empty($dbName) ? 'SHOW TABLES FROM ' . $dbName : 'SHOW TABLES '; - $pdo = $this->query($sql, [], false, true); + $sql = !empty($dbName) ? 'SHOW TABLES FROM ' . $dbName : 'SHOW TABLES '; + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); - $info = []; + $info = []; foreach ($result as $key => $val) { $info[$key] = current($val); } @@ -107,7 +107,7 @@ public function getTables($dbName = '') */ protected function getExplain($sql) { - $pdo = $this->linkID->query("EXPLAIN " . $sql); + $pdo = $this->linkID->query("EXPLAIN " . $sql); $result = $pdo->fetch(PDO::FETCH_ASSOC); $result = array_change_key_case($result); if (isset($result['extra'])) { diff --git a/library/think/db/connector/Pgsql.php b/library/think/db/connector/Pgsql.php index 761fbac4b0..ec92785a84 100644 --- a/library/think/db/connector/Pgsql.php +++ b/library/think/db/connector/Pgsql.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -46,17 +46,17 @@ public function getFields($tableName) { list($tableName) = explode(' ', $tableName); - $sql = 'select fields_name as "field",fields_type as "type",fields_not_null as "null",fields_key_name as "key",fields_default as "default",fields_default as "extra" from table_msg(\'' . $tableName . '\');'; + $sql = 'select fields_name as "field",fields_type as "type",fields_not_null as "null",fields_key_name as "key",fields_default as "default",fields_default as "extra" from table_msg(\'' . $tableName . '\');'; - $pdo = $this->query($sql, [], false, true); + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); - $info = []; + $info = []; if ($result) { foreach ($result as $key => $val) { - $val = array_change_key_case($val); + $val = array_change_key_case($val); $info[$val['field']] = [ - 'name' => $val['field'], - 'type' => $val['type'], + 'name' => $val['field'], + 'type' => $val['type'], 'notnull' => (bool) ('' !== $val['null']), 'default' => $val['default'], 'primary' => !empty($val['key']), @@ -75,10 +75,10 @@ public function getFields($tableName) */ public function getTables($dbName = '') { - $sql = "select tablename as Tables_in_test from pg_tables where schemaname ='public'"; - $pdo = $this->query($sql, [], false, true); + $sql = "select tablename as Tables_in_test from pg_tables where schemaname ='public'"; + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); - $info = []; + $info = []; foreach ($result as $key => $val) { $info[$key] = current($val); } diff --git a/library/think/db/connector/Sqlite.php b/library/think/db/connector/Sqlite.php index fd7f02b010..820757caf1 100644 --- a/library/think/db/connector/Sqlite.php +++ b/library/think/db/connector/Sqlite.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -43,17 +43,17 @@ protected function parseDsn($config) public function getFields($tableName) { list($tableName) = explode(' ', $tableName); - $sql = 'PRAGMA table_info( ' . $tableName . ' )'; + $sql = 'PRAGMA table_info( ' . $tableName . ' )'; - $pdo = $this->query($sql, [], false, true); + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); - $info = []; + $info = []; if ($result) { foreach ($result as $key => $val) { - $val = array_change_key_case($val); + $val = array_change_key_case($val); $info[$val['name']] = [ - 'name' => $val['name'], - 'type' => $val['type'], + 'name' => $val['name'], + 'type' => $val['type'], 'notnull' => 1 === $val['notnull'], 'default' => $val['dflt_value'], 'primary' => '1' == $val['pk'], @@ -77,9 +77,9 @@ public function getTables($dbName = '') . "UNION ALL SELECT name FROM sqlite_temp_master " . "WHERE type='table' ORDER BY name"; - $pdo = $this->query($sql, [], false, true); + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); - $info = []; + $info = []; foreach ($result as $key => $val) { $info[$key] = current($val); } diff --git a/library/think/db/connector/Sqlsrv.php b/library/think/db/connector/Sqlsrv.php index 08ad45e262..218209b9ab 100644 --- a/library/think/db/connector/Sqlsrv.php +++ b/library/think/db/connector/Sqlsrv.php @@ -21,8 +21,8 @@ class Sqlsrv extends Connection { // PDO连接参数 protected $params = [ - PDO::ATTR_CASE => PDO::CASE_NATURAL, - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_CASE => PDO::CASE_NATURAL, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_STRINGIFY_FETCHES => false, ]; protected $builder = '\\think\\db\\builder\\Sqlsrv'; @@ -50,7 +50,10 @@ protected function parseDsn($config) public function getFields($tableName) { list($tableName) = explode(' ', $tableName); - $sql = "SELECT column_name, data_type, column_default, is_nullable + $tableNames = explode('.', $tableName); + $tableName = isset($tableNames[1]) ? $tableNames[1] : $tableNames[0]; + + $sql = "SELECT column_name, data_type, column_default, is_nullable FROM information_schema.tables AS t JOIN information_schema.columns AS c ON t.table_catalog = c.table_catalog @@ -58,15 +61,15 @@ public function getFields($tableName) AND t.table_name = c.table_name WHERE t.table_name = '$tableName'"; - $pdo = $this->query($sql, [], false, true); + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); - $info = []; + $info = []; if ($result) { foreach ($result as $key => $val) { - $val = array_change_key_case($val); + $val = array_change_key_case($val); $info[$val['column_name']] = [ - 'name' => $val['column_name'], - 'type' => $val['data_type'], + 'name' => $val['column_name'], + 'type' => $val['data_type'], 'notnull' => (bool) ('' === $val['is_nullable']), // not null is empty, null is yes 'default' => $val['column_default'], 'primary' => false, @@ -100,9 +103,9 @@ public function getTables($dbName = '') WHERE TABLE_TYPE = 'BASE TABLE' "; - $pdo = $this->query($sql, [], false, true); + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); - $info = []; + $info = []; foreach ($result as $key => $val) { $info[$key] = current($val); } diff --git a/library/think/db/exception/BindParamException.php b/library/think/db/exception/BindParamException.php index d0e2387bca..4ed195468e 100644 --- a/library/think/db/exception/BindParamException.php +++ b/library/think/db/exception/BindParamException.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- diff --git a/library/think/db/exception/DataNotFoundException.php b/library/think/db/exception/DataNotFoundException.php index e399b06377..3aa9c669ed 100644 --- a/library/think/db/exception/DataNotFoundException.php +++ b/library/think/db/exception/DataNotFoundException.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -26,7 +26,7 @@ class DataNotFoundException extends DbException public function __construct($message, $table = '', array $config = []) { $this->message = $message; - $this->table = $table; + $this->table = $table; $this->setData('Database Config', $config); } diff --git a/library/think/db/exception/ModelNotFoundException.php b/library/think/db/exception/ModelNotFoundException.php index 2180ab072f..a160339161 100644 --- a/library/think/db/exception/ModelNotFoundException.php +++ b/library/think/db/exception/ModelNotFoundException.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -25,7 +25,7 @@ class ModelNotFoundException extends DbException public function __construct($message, $model = '', array $config = []) { $this->message = $message; - $this->model = $model; + $this->model = $model; $this->setData('Database Config', $config); } diff --git a/library/think/debug/Console.php b/library/think/debug/Console.php index c17911b2d7..1fb7ffb6db 100644 --- a/library/think/debug/Console.php +++ b/library/think/debug/Console.php @@ -44,9 +44,9 @@ public function __construct($config = []) */ public function output(Response $response, array $log = []) { - $request = Request::instance(); + $request = Request::instance(); $contentType = $response->getHeader('Content-Type'); - $accept = $request->header('accept'); + $accept = $request->header('accept'); if (strpos($accept, 'application/json') === 0 || $request->isAjax()) { return false; } elseif (!empty($contentType) && strpos($contentType, 'html') === false) { @@ -54,8 +54,8 @@ public function output(Response $response, array $log = []) } // 获取基本信息 $runtime = number_format(microtime(true) - THINK_START_TIME, 10); - $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; - $mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); + $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; + $mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); if (isset($_SERVER['HTTP_HOST'])) { $uri = $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; @@ -92,7 +92,7 @@ public function output(Response $response, array $log = []) default: // 调试信息 if (strpos($name, '|')) { // 多组信息 - $names = explode('|', $name); + $names = explode('|', $name); $result = []; foreach ($names as $name) { $result = array_merge($result, isset($log[$name]) ? $log[$name] : []); @@ -120,9 +120,9 @@ public function output(Response $response, array $log = []) protected function console($type, $msg) { - $type = strtolower($type); + $type = strtolower($type); $trace_tabs = array_values($this->config['trace_tabs']); - $line[] = ($type == $trace_tabs[0] || '调试' == $type || '错误' == $type) + $line[] = ($type == $trace_tabs[0] || '调试' == $type || '错误' == $type) ? "console.group('{$type}');" : "console.groupCollapsed('{$type}');"; @@ -137,18 +137,18 @@ protected function console($type, $msg) } break; case '错误': - $msg = str_replace("\n", '\n', json_encode($m)); - $style = 'color:#F4006B;font-size:14px;'; + $msg = str_replace("\n", '\n', json_encode($m)); + $style = 'color:#F4006B;font-size:14px;'; $line[] = "console.error(\"%c{$msg}\", \"{$style}\");"; break; case 'sql': - $msg = str_replace("\n", '\n', $m); - $style = "color:#009bb4;"; + $msg = str_replace("\n", '\n', $m); + $style = "color:#009bb4;"; $line[] = "console.log(\"%c{$msg}\", \"{$style}\");"; break; default: - $m = is_string($key) ? $key . ' ' . $m : $key + 1 . ' ' . $m; - $msg = json_encode($m); + $m = is_string($key) ? $key . ' ' . $m : $key + 1 . ' ' . $m; + $msg = json_encode($m); $line[] = "console.log({$msg});"; break; } diff --git a/library/think/debug/Html.php b/library/think/debug/Html.php index f8651aa927..9dbcfbfb59 100644 --- a/library/think/debug/Html.php +++ b/library/think/debug/Html.php @@ -32,7 +32,7 @@ class Html public function __construct(array $config = []) { $this->config['trace_file'] = THINK_PATH . 'tpl/page_trace.tpl'; - $this->config = array_merge($this->config, $config); + $this->config = array_merge($this->config, $config); } /** @@ -44,18 +44,18 @@ public function __construct(array $config = []) */ public function output(Response $response, array $log = []) { - $request = Request::instance(); + $request = Request::instance(); $contentType = $response->getHeader('Content-Type'); - $accept = $request->header('accept'); + $accept = $request->header('accept'); if (strpos($accept, 'application/json') === 0 || $request->isAjax()) { return false; } elseif (!empty($contentType) && strpos($contentType, 'html') === false) { return false; } // 获取基本信息 - $runtime = number_format(microtime(true) - THINK_START_TIME, 10); - $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; - $mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); + $runtime = number_format(microtime(true) - THINK_START_TIME, 10, '.', ''); + $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; + $mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); // 页面Trace信息 if (isset($_SERVER['HTTP_HOST'])) { @@ -91,7 +91,7 @@ public function output(Response $response, array $log = []) default: // 调试信息 if (strpos($name, '|')) { // 多组信息 - $names = explode('|', $name); + $names = explode('|', $name); $result = []; foreach ($names as $name) { $result = array_merge($result, isset($log[$name]) ? $log[$name] : []); diff --git a/library/think/exception/ClassNotFoundException.php b/library/think/exception/ClassNotFoundException.php index eb22e73018..84604142ec 100644 --- a/library/think/exception/ClassNotFoundException.php +++ b/library/think/exception/ClassNotFoundException.php @@ -17,7 +17,7 @@ class ClassNotFoundException extends \RuntimeException public function __construct($message, $class = '') { $this->message = $message; - $this->class = $class; + $this->class = $class; } /** diff --git a/library/think/exception/DbException.php b/library/think/exception/DbException.php index 532af5ef33..8be78b6eab 100644 --- a/library/think/exception/DbException.php +++ b/library/think/exception/DbException.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -28,12 +28,12 @@ class DbException extends Exception public function __construct($message, array $config, $sql, $code = 10500) { $this->message = $message; - $this->code = $code; + $this->code = $code; $this->setData('Database Status', [ - 'Error Code' => $code, + 'Error Code' => $code, 'Error Message' => $message, - 'Error SQL' => $sql, + 'Error SQL' => $sql, ]); unset($config['username'], $config['password']); diff --git a/library/think/exception/ErrorException.php b/library/think/exception/ErrorException.php index e3f18375d9..8d8dc3a2a0 100644 --- a/library/think/exception/ErrorException.php +++ b/library/think/exception/ErrorException.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -38,10 +38,10 @@ class ErrorException extends Exception public function __construct($severity, $message, $file, $line, array $context = []) { $this->severity = $severity; - $this->message = $message; - $this->file = $file; - $this->line = $line; - $this->code = 0; + $this->message = $message; + $this->file = $file; + $this->line = $line; + $this->code = 0; empty($context) || $this->setData('Error Context', $context); } diff --git a/library/think/exception/Handle.php b/library/think/exception/Handle.php index f523db09f0..d78a98db73 100644 --- a/library/think/exception/Handle.php +++ b/library/think/exception/Handle.php @@ -43,15 +43,15 @@ public function report(Exception $exception) // 收集异常数据 if (App::$debug) { $data = [ - 'file' => $exception->getFile(), - 'line' => $exception->getLine(), + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), 'message' => $this->getMessage($exception), - 'code' => $this->getCode($exception), + 'code' => $this->getCode($exception), ]; $log = "[{$data['code']}]{$data['message']}[{$data['file']}:{$data['line']}]"; } else { $data = [ - 'code' => $this->getCode($exception), + 'code' => $this->getCode($exception), 'message' => $this->getMessage($exception), ]; $log = "[{$data['code']}]{$data['message']}"; @@ -115,7 +115,7 @@ public function renderForConsole(Output $output, Exception $e) */ protected function renderHttpException(HttpException $e) { - $status = $e->getStatusCode(); + $status = $e->getStatusCode(); $template = Config::get('http_exception_template'); if (!App::$debug && !empty($template[$status])) { return Response::create($template[$status], 'view', $status)->assign(['e' => $e]); @@ -134,29 +134,29 @@ protected function convertExceptionToResponse(Exception $exception) if (App::$debug) { // 调试模式,获取详细的错误信息 $data = [ - 'name' => get_class($exception), - 'file' => $exception->getFile(), - 'line' => $exception->getLine(), + 'name' => get_class($exception), + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), 'message' => $this->getMessage($exception), - 'trace' => $exception->getTrace(), - 'code' => $this->getCode($exception), - 'source' => $this->getSourceCode($exception), - 'datas' => $this->getExtendData($exception), - 'tables' => [ - 'GET Data' => $_GET, - 'POST Data' => $_POST, - 'Files' => $_FILES, - 'Cookies' => $_COOKIE, - 'Session' => isset($_SESSION) ? $_SESSION : [], - 'Server/Request Data' => $_SERVER, + 'trace' => $exception->getTrace(), + 'code' => $this->getCode($exception), + 'source' => $this->getSourceCode($exception), + 'datas' => $this->getExtendData($exception), + 'tables' => [ + 'GET Data' => $_GET, + 'POST Data' => $_POST, + 'Files' => $_FILES, + 'Cookies' => $_COOKIE, + 'Session' => isset($_SESSION) ? $_SESSION : [], + 'Server/Request Data' => $_SERVER, 'Environment Variables' => $_ENV, - 'ThinkPHP Constants' => $this->getConst(), + 'ThinkPHP Constants' => $this->getConst(), ], ]; } else { // 部署模式仅显示 Code 和 Message $data = [ - 'code' => $this->getCode($exception), + 'code' => $this->getCode($exception), 'message' => $this->getMessage($exception), ]; @@ -177,7 +177,7 @@ protected function convertExceptionToResponse(Exception $exception) extract($data); include Config::get('exception_tmpl'); // 获取并清空缓存 - $content = ob_get_clean(); + $content = ob_get_clean(); $response = new Response($content, 'html'); if ($exception instanceof HttpException) { @@ -221,10 +221,10 @@ protected function getMessage(Exception $exception) } if (strpos($message, ':')) { - $name = strstr($message, ':', true); + $name = strstr($message, ':', true); $message = Lang::has($name) ? Lang::get($name) . strstr($message, ':') : $message; } elseif (strpos($message, ',')) { - $name = strstr($message, ',', true); + $name = strstr($message, ',', true); $message = Lang::has($name) ? Lang::get($name) . ':' . substr(strstr($message, ','), 1) : $message; } elseif (Lang::has($message)) { $message = Lang::get($message); @@ -241,13 +241,13 @@ protected function getMessage(Exception $exception) protected function getSourceCode(Exception $exception) { // 读取前9行和后9行 - $line = $exception->getLine(); + $line = $exception->getLine(); $first = ($line - 9 > 0) ? $line - 9 : 1; try { $contents = file($exception->getFile()); - $source = [ - 'first' => $first, + $source = [ + 'first' => $first, 'source' => array_slice($contents, $first - 1, 19), ]; } catch (Exception $e) { diff --git a/library/think/exception/HttpException.php b/library/think/exception/HttpException.php index 01a27fc230..8f398b1479 100644 --- a/library/think/exception/HttpException.php +++ b/library/think/exception/HttpException.php @@ -19,7 +19,7 @@ class HttpException extends \RuntimeException public function __construct($statusCode, $message = null, \Exception $previous = null, array $headers = [], $code = 0) { $this->statusCode = $statusCode; - $this->headers = $headers; + $this->headers = $headers; parent::__construct($message, $code, $previous); } diff --git a/library/think/exception/PDOException.php b/library/think/exception/PDOException.php index ebd53dff97..d51aa0ce24 100644 --- a/library/think/exception/PDOException.php +++ b/library/think/exception/PDOException.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -29,8 +29,8 @@ public function __construct(\PDOException $exception, array $config, $sql, $code $error = $exception->errorInfo; $this->setData('PDO Error Info', [ - 'SQLSTATE' => $error[0], - 'Driver Error Code' => isset($error[1]) ? $error[1] : 0, + 'SQLSTATE' => $error[0], + 'Driver Error Code' => isset($error[1]) ? $error[1] : 0, 'Driver Error Message' => isset($error[2]) ? $error[2] : '', ]); diff --git a/library/think/exception/TemplateNotFoundException.php b/library/think/exception/TemplateNotFoundException.php index 420206931c..7e5177e5d3 100644 --- a/library/think/exception/TemplateNotFoundException.php +++ b/library/think/exception/TemplateNotFoundException.php @@ -17,7 +17,7 @@ class TemplateNotFoundException extends \RuntimeException public function __construct($message, $template = '') { - $this->message = $message; + $this->message = $message; $this->template = $template; } diff --git a/library/think/exception/ThrowableError.php b/library/think/exception/ThrowableError.php index 87b6b9d74f..f4fb19e764 100644 --- a/library/think/exception/ThrowableError.php +++ b/library/think/exception/ThrowableError.php @@ -17,13 +17,13 @@ public function __construct(\Throwable $e) { if ($e instanceof \ParseError) { - $message = 'Parse error: ' . $e->getMessage(); + $message = 'Parse error: ' . $e->getMessage(); $severity = E_PARSE; } elseif ($e instanceof \TypeError) { - $message = 'Type error: ' . $e->getMessage(); + $message = 'Type error: ' . $e->getMessage(); $severity = E_RECOVERABLE_ERROR; } else { - $message = 'Fatal error: ' . $e->getMessage(); + $message = 'Fatal error: ' . $e->getMessage(); $severity = E_ERROR; } diff --git a/library/think/exception/ValidateException.php b/library/think/exception/ValidateException.php index b3684169c5..cfb88ebc2e 100644 --- a/library/think/exception/ValidateException.php +++ b/library/think/exception/ValidateException.php @@ -17,7 +17,7 @@ class ValidateException extends \RuntimeException public function __construct($error) { - $this->error = $error; + $this->error = $error; $this->message = is_array($error) ? implode("\n\r", $error) : $error; } diff --git a/library/think/log/driver/File.php b/library/think/log/driver/File.php index d82a5243e8..f71d88293d 100644 --- a/library/think/log/driver/File.php +++ b/library/think/log/driver/File.php @@ -12,6 +12,7 @@ namespace think\log\driver; use think\App; +use think\Request; /** * 本地化调试输出到文件 @@ -20,13 +21,14 @@ class File { protected $config = [ 'time_format' => ' c ', - 'file_size' => 2097152, - 'path' => LOG_PATH, + 'single' => false, + 'file_size' => 2097152, + 'path' => LOG_PATH, 'apart_level' => [], + 'max_files' => 0, + 'json' => false, ]; - protected $writed = []; - // 实例化并传入参数 public function __construct($config = []) { @@ -38,82 +40,233 @@ public function __construct($config = []) /** * 日志写入接口 * @access public - * @param array $log 日志信息 + * @param array $log 日志信息 + * @param bool $append 是否追加请求信息 * @return bool */ - public function save(array $log = []) + public function save(array $log = [], $append = false) { - $cli = IS_CLI ? '_cli' : ''; - $destination = $this->config['path'] . date('Ym') . DS . date('d') . $cli . '.log'; + $destination = $this->getMasterLogFile(); $path = dirname($destination); !is_dir($path) && mkdir($path, 0755, true); - $info = ''; + $info = []; foreach ($log as $type => $val) { - $level = ''; + foreach ($val as $msg) { if (!is_string($msg)) { $msg = var_export($msg, true); } - $level .= '[ ' . $type . ' ] ' . $msg . "\r\n"; + + $info[$type][] = $this->config['json'] ? $msg : '[ ' . $type . ' ] ' . $msg; } - if (in_array($type, $this->config['apart_level'])) { + + if (!$this->config['json'] && (true === $this->config['apart_level'] || in_array($type, $this->config['apart_level']))) { // 独立记录的日志级别 - $filename = $path . DS . date('d') . '_' . $type . $cli . '.log'; - $this->write($level, $filename, true); - } else { - $info .= $level; + $filename = $this->getApartLevelFile($path, $type); + + $this->write($info[$type], $filename, true, $append); + unset($info[$type]); } } + if ($info) { - return $this->write($info, $destination); + return $this->write($info, $destination, false, $append); } + return true; } - protected function write($message, $destination, $apart = false) + /** + * 获取主日志文件名 + * @access public + * @return string + */ + protected function getMasterLogFile() { - //检测日志文件大小,超过配置大小则备份日志文件重新生成 - if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) { - rename($destination, dirname($destination) . DS . time() . '-' . basename($destination)); - $this->writed[$destination] = false; - } + $cli = PHP_SAPI == 'cli' ? '_cli' : ''; + if ($this->config['single']) { + $name = is_string($this->config['single']) ? $this->config['single'] : 'single'; + $destination = $this->config['path'] . $name . $cli . '.log'; + } else { + if ($this->config['max_files']) { + $filename = date('Ymd') . $cli . '.log'; + $files = glob($this->config['path'] . '*.log'); - if (empty($this->writed[$destination]) && !IS_CLI) { - if (App::$debug && !$apart) { - // 获取基本信息 - if (isset($_SERVER['HTTP_HOST'])) { - $current_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; - } else { - $current_uri = "cmd:" . implode(' ', $_SERVER['argv']); + try { + if (count($files) > $this->config['max_files']) { + unlink($files[0]); + } + } catch (\Exception $e) { } + } else { + $filename = date('Ym') . DIRECTORY_SEPARATOR . date('d') . $cli . '.log'; + } - $runtime = round(microtime(true) - THINK_START_TIME, 10); - $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; - $time_str = ' [运行时间:' . number_format($runtime, 6) . 's][吞吐率:' . $reqs . 'req/s]'; - $memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); - $memory_str = ' [内存消耗:' . $memory_use . 'kb]'; - $file_load = ' [文件加载:' . count(get_included_files()) . ']'; + $destination = $this->config['path'] . $filename; + } + + return $destination; + } + + /** + * 获取独立日志文件名 + * @access public + * @param string $path 日志目录 + * @param string $type 日志类型 + * @return string + */ + protected function getApartLevelFile($path, $type) + { + $cli = PHP_SAPI == 'cli' ? '_cli' : ''; + + if ($this->config['single']) { + $name = is_string($this->config['single']) ? $this->config['single'] : 'single'; + } elseif ($this->config['max_files']) { + $name = date('Ymd'); + } else { + $name = date('d'); + } + + return $path . DIRECTORY_SEPARATOR . $name . '_' . $type . $cli . '.log'; + } - $message = '[ info ] ' . $current_uri . $time_str . $memory_str . $file_load . "\r\n" . $message; + /** + * 日志写入 + * @access protected + * @param array $message 日志信息 + * @param string $destination 日志文件 + * @param bool $apart 是否独立文件写入 + * @param bool $append 是否追加请求信息 + * @return bool + */ + protected function write($message, $destination, $apart = false, $append = false) + { + // 检测日志文件大小,超过配置大小则备份日志文件重新生成 + $this->checkLogSize($destination); + + // 日志信息封装 + $info['timestamp'] = date($this->config['time_format']); + + foreach ($message as $type => $msg) { + $msg = is_array($msg) ? implode("\r\n", $msg) : $msg; + if (PHP_SAPI == 'cli') { + $info['msg'] = $msg; + $info['type'] = $type; + } else { + $info[$type] = $msg; } - $now = date($this->config['time_format']); - $server = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '0.0.0.0'; - $remote = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0'; - $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'CLI'; - $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; - $message = "---------------------------------------------------------------\r\n[{$now}] {$server} {$remote} {$method} {$uri}\r\n" . $message; - - $this->writed[$destination] = true; } - if (IS_CLI) { - $now = date($this->config['time_format']); - $message = "[{$now}]" . $message; + if (PHP_SAPI == 'cli') { + $message = $this->parseCliLog($info); + } else { + // 添加调试日志 + $this->getDebugLog($info, $append, $apart); + + $message = $this->parseLog($info); } return error_log($message, 3, $destination); } + /** + * 检查日志文件大小并自动生成备份文件 + * @access protected + * @param string $destination 日志文件 + * @return void + */ + protected function checkLogSize($destination) + { + if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) { + try { + rename($destination, dirname($destination) . DIRECTORY_SEPARATOR . time() . '-' . basename($destination)); + } catch (\Exception $e) { + } + } + } + + /** + * CLI日志解析 + * @access protected + * @param array $info 日志信息 + * @return string + */ + protected function parseCliLog($info) + { + if ($this->config['json']) { + $message = json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n"; + } else { + $now = $info['timestamp']; + unset($info['timestamp']); + + $message = implode("\r\n", $info); + + $message = "[{$now}]" . $message . "\r\n"; + } + + return $message; + } + + /** + * 解析日志 + * @access protected + * @param array $info 日志信息 + * @return string + */ + protected function parseLog($info) + { + $request = Request::instance(); + $requestInfo = [ + 'ip' => $request->ip(), + 'method' => $request->method(), + 'host' => $request->host(), + 'uri' => $request->url(), + ]; + + if ($this->config['json']) { + $info = $requestInfo + $info; + return json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n"; + } + + array_unshift($info, "---------------------------------------------------------------\r\n[{$info['timestamp']}] {$requestInfo['ip']} {$requestInfo['method']} {$requestInfo['host']}{$requestInfo['uri']}"); + unset($info['timestamp']); + + return implode("\r\n", $info) . "\r\n"; + } + + protected function getDebugLog(&$info, $append, $apart) + { + if (App::$debug && $append) { + + if ($this->config['json']) { + // 获取基本信息 + $runtime = round(microtime(true) - THINK_START_TIME, 10); + $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; + + $memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); + + $info = [ + 'runtime' => number_format($runtime, 6) . 's', + 'reqs' => $reqs . 'req/s', + 'memory' => $memory_use . 'kb', + 'file' => count(get_included_files()), + ] + $info; + + } elseif (!$apart) { + // 增加额外的调试信息 + $runtime = round(microtime(true) - THINK_START_TIME, 10); + $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; + + $memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); + + $time_str = '[运行时间:' . number_format($runtime, 6) . 's] [吞吐率:' . $reqs . 'req/s]'; + $memory_str = ' [内存消耗:' . $memory_use . 'kb]'; + $file_load = ' [文件加载:' . count(get_included_files()) . ']'; + + array_unshift($info, $time_str . $memory_str . $file_load); + } + } + } } diff --git a/library/think/log/driver/Socket.php b/library/think/log/driver/Socket.php index d30bba3049..23cb73c13c 100644 --- a/library/think/log/driver/Socket.php +++ b/library/think/log/driver/Socket.php @@ -23,21 +23,21 @@ class Socket protected $config = [ // socket服务器地址 - 'host' => 'localhost', + 'host' => 'localhost', // 是否显示加载的文件列表 'show_included_files' => false, // 日志强制记录到配置的client_id - 'force_client_ids' => [], + 'force_client_ids' => [], // 限制允许读取日志的client_id - 'allow_client_ids' => [], + 'allow_client_ids' => [], ]; protected $css = [ - 'sql' => 'color:#009bb4;', + 'sql' => 'color:#009bb4;', 'sql_warn' => 'color:#009bb4;font-size:14px;', - 'error' => 'color:#f4006b;font-size:14px;', - 'page' => 'color:#40e2ff;background:#171717;', - 'big' => 'font-size:20px;color:red;', + 'error' => 'color:#f4006b;font-size:14px;', + 'page' => 'color:#40e2ff;background:#171717;', + 'big' => 'font-size:20px;color:red;', ]; protected $allowForceClientIds = []; //配置强制推送且被授权的client_id @@ -60,19 +60,19 @@ public function __construct(array $config = []) * @param array $log 日志信息 * @return bool */ - public function save(array $log = []) + public function save(array $log = [], $append = false) { if (!$this->check()) { return false; } $trace = []; if (App::$debug) { - $runtime = round(microtime(true) - THINK_START_TIME, 10); - $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; - $time_str = ' [运行时间:' . number_format($runtime, 6) . 's][吞吐率:' . $reqs . 'req/s]'; + $runtime = round(microtime(true) - THINK_START_TIME, 10); + $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; + $time_str = ' [运行时间:' . number_format($runtime, 6) . 's][吞吐率:' . $reqs . 'req/s]'; $memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); $memory_str = ' [内存消耗:' . $memory_use . 'kb]'; - $file_load = ' [文件加载:' . count(get_included_files()) . ']'; + $file_load = ' [文件加载:' . count(get_included_files()) . ']'; if (isset($_SERVER['HTTP_HOST'])) { $current_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; @@ -82,16 +82,16 @@ public function save(array $log = []) // 基本信息 $trace[] = [ 'type' => 'group', - 'msg' => $current_uri . $time_str . $memory_str . $file_load, - 'css' => $this->css['page'], + 'msg' => $current_uri . $time_str . $memory_str . $file_load, + 'css' => $this->css['page'], ]; } foreach ($log as $type => $val) { $trace[] = [ 'type' => 'groupCollapsed', - 'msg' => '[ ' . $type . ' ]', - 'css' => isset($this->css[$type]) ? $this->css[$type] : '', + 'msg' => '[ ' . $type . ' ]', + 'css' => isset($this->css[$type]) ? $this->css[$type] : '', ]; foreach ($val as $msg) { if (!is_string($msg)) { @@ -99,39 +99,39 @@ public function save(array $log = []) } $trace[] = [ 'type' => 'log', - 'msg' => $msg, - 'css' => '', + 'msg' => $msg, + 'css' => '', ]; } $trace[] = [ 'type' => 'groupEnd', - 'msg' => '', - 'css' => '', + 'msg' => '', + 'css' => '', ]; } if ($this->config['show_included_files']) { $trace[] = [ 'type' => 'groupCollapsed', - 'msg' => '[ file ]', - 'css' => '', + 'msg' => '[ file ]', + 'css' => '', ]; $trace[] = [ 'type' => 'log', - 'msg' => implode("\n", get_included_files()), - 'css' => '', + 'msg' => implode("\n", get_included_files()), + 'css' => '', ]; $trace[] = [ 'type' => 'groupEnd', - 'msg' => '', - 'css' => '', + 'msg' => '', + 'css' => '', ]; } $trace[] = [ 'type' => 'groupEnd', - 'msg' => '', - 'css' => '', + 'msg' => '', + 'css' => '', ]; $tabid = $this->getClientArg('tabid'); @@ -162,12 +162,12 @@ public function save(array $log = []) protected function sendToClient($tabid, $client_id, $logs, $force_client_id) { $logs = [ - 'tabid' => $tabid, - 'client_id' => $client_id, - 'logs' => $logs, + 'tabid' => $tabid, + 'client_id' => $client_id, + 'logs' => $logs, 'force_client_id' => $force_client_id, ]; - $msg = @json_encode($logs); + $msg = @json_encode($logs); $address = '/' . $client_id; //将client_id作为地址, server端通过地址判断将日志发布给谁 $this->send($this->config['host'], $msg, $address); } @@ -233,7 +233,7 @@ protected function getClientArg($name) protected function send($host, $message = '', $address = '/') { $url = 'http://' . $host . ':' . $this->port . $address; - $ch = curl_init(); + $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $message); diff --git a/library/think/model/Collection.php b/library/think/model/Collection.php index e5f9ceec4d..0406533c17 100644 --- a/library/think/model/Collection.php +++ b/library/think/model/Collection.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -16,20 +16,6 @@ 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 diff --git a/library/think/model/Merge.php b/library/think/model/Merge.php index d944979e11..1381b662e8 100644 --- a/library/think/model/Merge.php +++ b/library/think/model/Merge.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -19,8 +19,8 @@ class Merge extends Model { protected $relationModel = []; // HAS ONE 关联的模型列表 - protected $fk = ''; // 外键名 默认为主表名_id - protected $mapFields = []; // 需要处理的模型映射字段,避免混淆 array( id => 'user.id' ) + protected $fk = ''; // 外键名 默认为主表名_id + protected $mapFields = []; // 需要处理的模型映射字段,避免混淆 array( id => 'user.id' ) /** * 构造函数 @@ -60,13 +60,13 @@ public static function get($data = null, $with = [], $cache = false) */ protected static function attachQuery($query) { - $class = new static(); + $class = new static(); $master = $class->name; $fields = self::getModelField($query, $master, '', $class->mapFields, $class->field); $query->alias($master)->field($fields); foreach ($class->relationModel as $key => $model) { - $name = is_int($key) ? $model : $key; + $name = is_int($key) ? $model : $key; $table = is_int($key) ? $query->getTable($name) : $model; $query->join($table . ' ' . $name, $name . '.' . $class->fk . '=' . $master . '.' . $class->getPk()); $fields = self::getModelField($query, $name, $table, $class->mapFields, $class->field); @@ -89,7 +89,7 @@ protected static function getModelField($query, $name, $table = '', $map = [], $ { // 获取模型的字段信息 $fields = $fields ?: $query->getTableInfo($table, 'fields'); - $array = []; + $array = []; foreach ($fields as $field) { if ($key = array_search($name . '.' . $field, $map)) { // 需要处理映射字段 @@ -215,7 +215,7 @@ public function save($data = [], $where = [], $sequence = null) // 写入附表数据 foreach ($this->relationModel as $key => $model) { - $name = is_int($key) ? $model : $key; + $name = is_int($key) ? $model : $key; $table = is_int($key) ? $db->getTable($model) : $model; // 处理关联模型数据 $data = $this->parseData($name, $data); @@ -259,7 +259,7 @@ public function save($data = [], $where = [], $sequence = null) unset($source[$pk]); } foreach ($this->relationModel as $key => $model) { - $name = is_int($key) ? $model : $key; + $name = is_int($key) ? $model : $key; $table = is_int($key) ? $db->getTable($model) : $model; // 处理关联模型数据 $data = $this->parseData($name, $source); diff --git a/library/think/model/Pivot.php b/library/think/model/Pivot.php index 259a8a03d0..13525cdd7e 100644 --- a/library/think/model/Pivot.php +++ b/library/think/model/Pivot.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -24,11 +24,11 @@ class Pivot extends Model /** * 架构函数 * @access public - * @param Model $parent 上级模型 * @param array|object $data 数据 + * @param Model $parent 上级模型 * @param string $table 中间数据表名 */ - public function __construct(Model $parent = null, $data = [], $table = '') + public function __construct($data = [], Model $parent = null, $table = '') { $this->parent = $parent; @@ -37,8 +37,6 @@ public function __construct(Model $parent = null, $data = [], $table = '') } parent::__construct($data); - - $this->class = $this->name; } } diff --git a/library/think/model/Relation.php b/library/think/model/Relation.php index 19a06c1009..25fe88dbde 100644 --- a/library/think/model/Relation.php +++ b/library/think/model/Relation.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -35,6 +35,8 @@ abstract class Relation protected $localKey; // 基础查询 protected $baseQuery; + // 是否为自关联 + protected $selfRelation; /** * 获取关联的所属模型 @@ -47,13 +49,13 @@ public function getParent() } /** - * 获取当前的关联模型类 + * 获取当前的关联模型对象实例 * @access public - * @return string + * @return Model */ public function getModel() { - return $this->model; + return $this->query->getModel(); } /** @@ -66,6 +68,28 @@ public function getQuery() 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 diff --git a/library/think/model/relation/BelongsTo.php b/library/think/model/relation/BelongsTo.php index 9b66e0c393..1dfb341adb 100644 --- a/library/think/model/relation/BelongsTo.php +++ b/library/think/model/relation/BelongsTo.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -29,13 +29,13 @@ class BelongsTo extends OneToOne */ public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER', $relation = null) { - $this->parent = $parent; - $this->model = $model; + $this->parent = $parent; + $this->model = $model; $this->foreignKey = $foreignKey; - $this->localKey = $localKey; - $this->joinType = $joinType; - $this->query = (new $model)->db(); - $this->relation = $relation; + $this->localKey = $localKey; + $this->joinType = $joinType; + $this->query = (new $model)->db(); + $this->relation = $relation; } /** @@ -52,6 +52,7 @@ public function getRelation($subRelation = '', $closure = null) call_user_func_array($closure, [ & $this->query]); } $relationModel = $this->query + ->removeWhereField($this->localKey) ->where($this->localKey, $this->parent->$foreignKey) ->relation($subRelation) ->find(); @@ -79,14 +80,16 @@ public function has($operator = '>=', $count = 1, $id = '*') /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = []) + public function hasWhere($where = [], $fields = null) { - $table = $this->query->getTable(); - $model = basename(str_replace('\\', '/', get_class($this->parent))); + $table = $this->query->getTable(); + $model = basename(str_replace('\\', '/', get_class($this->parent))); $relation = basename(str_replace('\\', '/', $this->model)); + if (is_array($where)) { foreach ($where as $key => $val) { if (false === strpos($key, '.')) { @@ -95,9 +98,11 @@ public function hasWhere($where = []) } } } + $fields = $this->getRelationQueryFields($fields, $model); + return $this->parent->db()->alias($model) - ->field($model . '.*') - ->join($table . ' ' . $relation, $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $this->joinType) + ->field($fields) + ->join([$table => $relation], $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $this->joinType) ->where($where); } @@ -112,7 +117,7 @@ public function hasWhere($where = []) */ protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure) { - $localKey = $this->localKey; + $localKey = $this->localKey; $foreignKey = $this->foreignKey; $range = []; @@ -124,7 +129,8 @@ protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure) } if (!empty($range)) { - $data = $this->eagerlyWhere($this, [ + $this->query->removeWhereField($localKey); + $data = $this->eagerlyWhere($this->query, [ $localKey => [ 'in', $range, @@ -165,9 +171,10 @@ protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure) */ protected function eagerlyOne(&$result, $relation, $subRelation, $closure) { - $localKey = $this->localKey; + $localKey = $this->localKey; $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])) { $relationModel = null; @@ -194,7 +201,7 @@ protected function eagerlyOne(&$result, $relation, $subRelation, $closure) public function associate($model) { $foreignKey = $this->foreignKey; - $pk = $model->getPk(); + $pk = $model->getPk(); $this->parent->setAttr($foreignKey, $model->$pk); $this->parent->save(); @@ -216,4 +223,21 @@ public function dissociate() 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; + } + } } diff --git a/library/think/model/relation/BelongsToMany.php b/library/think/model/relation/BelongsToMany.php index ef08abb4da..ca5a4fd67c 100644 --- a/library/think/model/relation/BelongsToMany.php +++ b/library/think/model/relation/BelongsToMany.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -12,6 +12,7 @@ namespace think\model\relation; use think\Collection; +use think\Db; use think\db\Query; use think\Exception; use think\Loader; @@ -28,6 +29,8 @@ class BelongsToMany extends Relation protected $pivotName; // 中间表模型对象 protected $pivot; + // 中间表数据名称 + protected $pivotDataName = 'pivot'; /** * 构造函数 @@ -40,18 +43,22 @@ class BelongsToMany extends Relation */ public function __construct(Model $parent, $model, $table, $foreignKey, $localKey) { - $this->parent = $parent; - $this->model = $model; + $this->parent = $parent; + $this->model = $model; $this->foreignKey = $foreignKey; - $this->localKey = $localKey; + $this->localKey = $localKey; if (false !== strpos($table, '\\')) { $this->pivotName = $table; - $this->middle = basename(str_replace('\\', '/', $table)); + $this->middle = basename(str_replace('\\', '/', $table)); } else { $this->middle = $table; } $this->query = (new $model)->db(); $this->pivot = $this->newPivot(); + + if ('think\model\Pivot' == get_class($this->pivot)) { + $this->pivot->name($this->middle); + } } /** @@ -66,14 +73,46 @@ public function pivot($pivot) } /** - * 实例化中间表模型 + * 设置中间表数据名称 + * @access public + * @param string $name + * @return $this + */ + public function pivotDataName($name) + { + $this->pivotDataName = $name; + return $this; + } + + /** + * 获取中间表更新条件 * @param $data - * @return mixed + * @return array + */ + protected function getUpdateWhere($data) + { + return [ + $this->localKey => $data[$this->localKey], + $this->foreignKey => $data[$this->foreignKey], + ]; + } + + /** + * 实例化中间表模型 + * @param array $data + * @param bool $isUpdate + * @return Pivot + * @throws Exception */ - protected function newPivot($data = []) + protected function newPivot($data = [], $isUpdate = false) { - $pivot = $this->pivotName ?: '\\think\\model\\Pivot'; - return new $pivot($this->parent, $data, $this->middle); + $class = $this->pivotName ?: '\\think\\model\\Pivot'; + $pivot = new $class($data, $this->parent, $this->middle); + if ($pivot instanceof Pivot) { + return $isUpdate ? $pivot->isUpdate(true, $this->getUpdateWhere($data)) : $pivot; + } else { + throw new Exception('pivot model must extends: \think\model\Pivot'); + } } /** @@ -93,7 +132,7 @@ protected function hydratePivot($models) } } } - $model->setRelation('pivot', $this->newPivot($pivot)); + $model->setRelation($this->pivotDataName, $this->newPivot($pivot, true)); } } @@ -104,8 +143,8 @@ protected function hydratePivot($models) protected function buildQuery() { $foreignKey = $this->foreignKey; - $localKey = $this->localKey; - $pk = $this->parent->getPk(); + $localKey = $this->localKey; + $pk = $this->parent->getPk(); // 关联查询 $condition['pivot.' . $localKey] = $this->parent->$pk; return $this->belongsToManyQuery($foreignKey, $localKey, $condition); @@ -206,11 +245,12 @@ public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER' /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query * @throws Exception */ - public function hasWhere($where = []) + public function hasWhere($where = [], $fields = null) { throw new Exception('relation not support: hasWhere'); } @@ -240,10 +280,10 @@ public function wherePivot($field, $op = null, $condition = null) */ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) { - $localKey = $this->localKey; + $localKey = $this->localKey; $foreignKey = $this->foreignKey; - $pk = $resultSet[0]->getPk(); + $pk = $resultSet[0]->getPk(); $range = []; foreach ($resultSet as $result) { // 获取关联外键列表 @@ -307,10 +347,10 @@ public function eagerlyResult(&$result, $relation, $subRelation, $closure) */ public function relationCount($result, $closure) { - $pk = $result->getPk(); + $pk = $result->getPk(); $count = 0; if (isset($result->$pk)) { - $pk = $result->$pk; + $pk = $result->$pk; $count = $this->belongsToManyQuery($this->foreignKey, $this->localKey, ['pivot.' . $this->localKey => $pk])->count(); } return $count; @@ -320,14 +360,22 @@ public function relationCount($result, $closure) * 获取关联统计子查询 * @access public * @param \Closure $closure 闭包 + * @param string $name 统计数据别名 * @return string */ - public function getRelationCountQuery($closure) + public function getRelationCountQuery($closure, &$name = null) { + if ($closure) { + $return = call_user_func_array($closure, [ & $this->query]); + if ($return && is_string($return)) { + $name = $return; + } + } + return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ 'pivot.' . $this->localKey => [ 'exp', - '=' . $this->parent->getTable() . '.' . $this->parent->getPk(), + Db::raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()), ], ])->fetchSql()->count(); } @@ -358,7 +406,7 @@ protected function eagerlyManyToMany($where, $relation, $subRelation = '') } } } - $set->setRelation('pivot', $this->newPivot($pivot)); + $set->setRelation($this->pivotDataName, $this->newPivot($pivot, true)); $data[$pivot[$this->localKey]][] = $set; } return $data; @@ -376,15 +424,15 @@ protected function belongsToManyQuery($foreignKey, $localKey, $condition = []) { // 关联查询封装 $tableName = $this->query->getTable(); - $table = $this->pivot->getTable(); - $fields = $this->getQueryFields($tableName); + $table = $this->pivot->getTable(); + $fields = $this->getQueryFields($tableName); $query = $this->query->field($fields) ->field(true, false, $table, 'pivot', 'pivot__'); if (empty($this->baseQuery)) { $relationFk = $this->query->getPk(); - $query->join($table . ' pivot', 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk) + $query->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk) ->where($condition); } return $query; @@ -450,18 +498,18 @@ public function attach($data, $pivot = []) } elseif ($data instanceof Model) { // 根据关联表主键直接写入中间表 $relationFk = $data->getPk(); - $id = $data->$relationFk; + $id = $data->$relationFk; } if ($id) { // 保存中间表数据 - $pk = $this->parent->getPk(); + $pk = $this->parent->getPk(); $pivot[$this->localKey] = $this->parent->$pk; - $ids = (array) $id; + $ids = (array) $id; foreach ($ids as $id) { $pivot[$this->foreignKey] = $id; $this->pivot->insert($pivot, true); - $result[] = $this->newPivot($pivot); + $result[] = $this->newPivot($pivot, true); } if (count($result) == 1) { // 返回中间表模型对象 @@ -473,6 +521,29 @@ public function attach($data, $pivot = []) } } + /** + * 判断是否存在关联数据 + * @access public + * @param mixed $data 数据 可以使用关联模型对象 或者 关联对象的主键 + * @return Pivot + * @throws Exception + */ + public function attached($data) + { + if ($data instanceof Model) { + $relationFk = $data->getPk(); + $id = $data->$relationFk; + } else { + $id = $data; + } + + $pk = $this->parent->getPk(); + + $pivot = $this->pivot->where($this->localKey, $this->parent->$pk)->where($this->foreignKey, $id)->find(); + + return $pivot ?: false; + } + /** * 解除关联的一个中间表数据 * @access public @@ -490,10 +561,10 @@ public function detach($data = null, $relationDel = false) } elseif ($data instanceof Model) { // 根据关联表主键直接写入中间表 $relationFk = $data->getPk(); - $id = $data->$relationFk; + $id = $data->$relationFk; } // 删除中间表数据 - $pk = $this->parent->getPk(); + $pk = $this->parent->getPk(); $pivot[$this->localKey] = $this->parent->$pk; if (isset($id)) { $pivot[$this->foreignKey] = is_array($id) ? ['in', $id] : $id; @@ -517,9 +588,9 @@ public function sync($ids, $detaching = true) $changes = [ 'attached' => [], 'detached' => [], - 'updated' => [], + 'updated' => [], ]; - $pk = $this->parent->getPk(); + $pk = $this->parent->getPk(); $current = $this->pivot->where($this->localKey, $this->parent->$pk) ->column($this->foreignKey); $records = []; @@ -563,9 +634,9 @@ public function sync($ids, $detaching = true) protected function baseQuery() { if (empty($this->baseQuery) && $this->parent->getData()) { - $pk = $this->parent->getPk(); + $pk = $this->parent->getPk(); $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; } } diff --git a/library/think/model/relation/HasMany.php b/library/think/model/relation/HasMany.php index eb9bf66de5..2524076443 100644 --- a/library/think/model/relation/HasMany.php +++ b/library/think/model/relation/HasMany.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -28,11 +28,11 @@ class HasMany extends Relation */ public function __construct(Model $parent, $model, $foreignKey, $localKey) { - $this->parent = $parent; - $this->model = $model; + $this->parent = $parent; + $this->model = $model; $this->foreignKey = $foreignKey; - $this->localKey = $localKey; - $this->query = (new $model)->db(); + $this->localKey = $localKey; + $this->query = (new $model)->db(); } /** @@ -46,7 +46,7 @@ public function getRelation($subRelation = '', $closure = null) if ($closure) { call_user_func_array($closure, [ & $this->query]); } - $list = $this->relation($subRelation)->select(); + $list = $this->relation($subRelation)->select(); $parent = clone $this->parent; foreach ($list as &$model) { @@ -68,7 +68,7 @@ public function getRelation($subRelation = '', $closure = null) public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) { $localKey = $this->localKey; - $range = []; + $range = []; foreach ($resultSet as $result) { // 获取关联外键列表 if (isset($result->$localKey)) { @@ -77,7 +77,7 @@ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) } if (!empty($range)) { - $data = $this->eagerlyOneToMany(new $this->model, [ + $data = $this->eagerlyOneToMany($this->query, [ $this->foreignKey => [ 'in', $range, @@ -114,7 +114,7 @@ public function eagerlyResult(&$result, $relation, $subRelation, $closure) $localKey = $this->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])) { $data[$result->$localKey] = []; @@ -138,12 +138,12 @@ public function eagerlyResult(&$result, $relation, $subRelation, $closure) public function relationCount($result, $closure) { $localKey = $this->localKey; - $count = 0; + $count = 0; if (isset($result->$localKey)) { if ($closure) { 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; } @@ -152,20 +152,19 @@ public function relationCount($result, $closure) * 创建关联统计子查询 * @access public * @param \Closure $closure 闭包 + * @param string $name 统计数据别名 * @return string */ - public function getRelationCountQuery($closure) + public function getRelationCountQuery($closure, &$name = null) { if ($closure) { - call_user_func_array($closure, [ & $this->query]); + $return = call_user_func_array($closure, [ & $this->query]); + if ($return && is_string($return)) { + $name = $return; + } } $localKey = $this->localKey ?: $this->parent->getPk(); - return $this->query->where([ - $this->foreignKey => [ - 'exp', - '=' . $this->parent->getTable() . '.' . $localKey, - ], - ])->fetchSql()->count(); + return $this->query->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $localKey)->fetchSql()->count(); } /** @@ -185,7 +184,7 @@ protected function eagerlyOneToMany($model, $where, $relation, $subRelation = '' if ($closure) { call_user_func_array($closure, [ & $model]); } - $list = $model->where($where)->with($subRelation)->select(); + $list = $model->removeWhereField($foreignKey)->where($where)->with($subRelation)->select(); // 组装模型数据 $data = []; @@ -206,12 +205,31 @@ public function save($data) if ($data instanceof Model) { $data = $data->getData(); } + // 保存关联表数据 - $model = new $this->model; $data[$this->foreignKey] = $this->parent->{$this->localKey}; + + $model = new $this->model(); return $model->save($data) ? $model : false; } + /** + * 创建关联对象实例 + * @param array $data + * @return Model + */ + public function make($data = []) + { + if ($data instanceof Model) { + $data = $data->getData(); + } + + // 保存关联表数据 + $data[$this->foreignKey] = $this->parent->{$this->localKey}; + + return new $this->model($data); + } + /** * 批量保存当前关联数据对象 * @access public @@ -238,14 +256,14 @@ public function saveAll(array $dataSet) */ public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') { - $table = $this->query->getTable(); - $model = basename(str_replace('\\', '/', get_class($this->parent))); + $table = $this->query->getTable(); + $model = basename(str_replace('\\', '/', get_class($this->parent))); $relation = basename(str_replace('\\', '/', $this->model)); return $this->parent->db() ->alias($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) ->having('count(' . $id . ')' . $operator . $count); } @@ -253,14 +271,16 @@ public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER' /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = []) + public function hasWhere($where = [], $fields = null) { - $table = $this->query->getTable(); - $model = basename(str_replace('\\', '/', get_class($this->parent))); + $table = $this->query->getTable(); + $model = basename(str_replace('\\', '/', get_class($this->parent))); $relation = basename(str_replace('\\', '/', $this->model)); + if (is_array($where)) { foreach ($where as $key => $val) { if (false === strpos($key, '.')) { @@ -269,9 +289,13 @@ public function hasWhere($where = []) } } } + + $fields = $this->getRelationQueryFields($fields, $model); + return $this->parent->db()->alias($model) - ->field($model . '.*') - ->join($table . ' ' . $relation, $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey) + ->field($fields) + ->group($model . '.' . $this->localKey) + ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey) ->where($where); } diff --git a/library/think/model/relation/HasManyThrough.php b/library/think/model/relation/HasManyThrough.php index 1573fc65ba..846ebc301f 100644 --- a/library/think/model/relation/HasManyThrough.php +++ b/library/think/model/relation/HasManyThrough.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -36,13 +36,13 @@ class HasManyThrough extends Relation */ public function __construct(Model $parent, $model, $through, $foreignKey, $throughKey, $localKey) { - $this->parent = $parent; - $this->model = $model; - $this->through = $through; + $this->parent = $parent; + $this->model = $model; + $this->through = $through; $this->foreignKey = $foreignKey; $this->throughKey = $throughKey; - $this->localKey = $localKey; - $this->query = (new $model)->db(); + $this->localKey = $localKey; + $this->query = (new $model)->db(); } /** @@ -77,10 +77,11 @@ public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER' /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = []) + public function hasWhere($where = [], $fields = null) { throw new Exception('relation not support: hasWhere'); } @@ -92,10 +93,9 @@ public function hasWhere($where = []) * @param string $relation 当前关联名 * @param string $subRelation 子关联名 * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 * @return void */ - public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class) + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) {} /** @@ -105,10 +105,9 @@ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, * @param string $relation 当前关联名 * @param string $subRelation 子关联名 * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 * @return void */ - public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class) + public function eagerlyResult(&$result, $relation, $subRelation, $closure) {} /** @@ -121,6 +120,18 @@ public function eagerlyResult(&$result, $relation, $subRelation, $closure, $clas public function relationCount($result, $closure) {} + /** + * 创建关联统计子查询 + * @access public + * @param \Closure $closure 闭包 + * @param string $name 统计数据别名 + * @return string + */ + public function getRelationCountQuery($closure, &$name = null) + { + throw new Exception('relation not support: withCount'); + } + /** * 执行基础查询(进执行一次) * @access protected @@ -129,12 +140,12 @@ public function relationCount($result, $closure) protected function baseQuery() { if (empty($this->baseQuery) && $this->parent->getData()) { - $through = $this->through; - $alias = Loader::parseName(basename(str_replace('\\', '/', $this->model))); + $through = $this->through; + $alias = Loader::parseName(basename(str_replace('\\', '/', $this->model))); $throughTable = $through::getTable(); - $pk = (new $through)->getPk(); - $throughKey = $this->throughKey; - $modelTable = $this->parent->getTable(); + $pk = (new $through)->getPk(); + $throughKey = $this->throughKey; + $modelTable = $this->parent->getTable(); $this->query->field($alias . '.*')->alias($alias) ->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey) ->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey) diff --git a/library/think/model/relation/HasOne.php b/library/think/model/relation/HasOne.php index 3d4a3630c2..a4529bff58 100644 --- a/library/think/model/relation/HasOne.php +++ b/library/think/model/relation/HasOne.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -28,12 +28,12 @@ class HasOne extends OneToOne */ public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER') { - $this->parent = $parent; - $this->model = $model; + $this->parent = $parent; + $this->model = $model; $this->foreignKey = $foreignKey; - $this->localKey = $localKey; - $this->joinType = $joinType; - $this->query = (new $model)->db(); + $this->localKey = $localKey; + $this->joinType = $joinType; + $this->query = (new $model)->db(); } /** @@ -50,7 +50,11 @@ public function getRelation($subRelation = '', $closure = null) 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) { $relationModel->setParent(clone $this->parent); @@ -66,29 +70,31 @@ public function getRelation($subRelation = '', $closure = null) */ public function has() { - $table = $this->query->getTable(); - $model = basename(str_replace('\\', '/', get_class($this->parent))); - $relation = basename(str_replace('\\', '/', $this->model)); - $localKey = $this->localKey; + $table = $this->query->getTable(); + $model = basename(str_replace('\\', '/', get_class($this->parent))); + $relation = basename(str_replace('\\', '/', $this->model)); + $localKey = $this->localKey; $foreignKey = $this->foreignKey; return $this->parent->db() ->alias($model) ->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); }); } /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = []) + public function hasWhere($where = [], $fields = null) { - $table = $this->query->getTable(); - $model = basename(str_replace('\\', '/', get_class($this->parent))); + $table = $this->query->getTable(); + $model = basename(str_replace('\\', '/', get_class($this->parent))); $relation = basename(str_replace('\\', '/', $this->model)); + if (is_array($where)) { foreach ($where as $key => $val) { if (false === strpos($key, '.')) { @@ -97,9 +103,11 @@ public function hasWhere($where = []) } } } + $fields = $this->getRelationQueryFields($fields, $model); + return $this->parent->db()->alias($model) - ->field($model . '.*') - ->join($table . ' ' . $relation, $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $this->joinType) + ->field($fields) + ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $this->joinType) ->where($where); } @@ -114,7 +122,7 @@ public function hasWhere($where = []) */ protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure) { - $localKey = $this->localKey; + $localKey = $this->localKey; $foreignKey = $this->foreignKey; $range = []; @@ -126,7 +134,8 @@ protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure) } if (!empty($range)) { - $data = $this->eagerlyWhere($this, [ + $this->query->removeWhereField($foreignKey); + $data = $this->eagerlyWhere($this->query, [ $foreignKey => [ 'in', $range, @@ -166,9 +175,10 @@ protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure) */ protected function eagerlyOne(&$result, $relation, $subRelation, $closure) { - $localKey = $this->localKey; + $localKey = $this->localKey; $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])) { @@ -186,4 +196,20 @@ protected function eagerlyOne(&$result, $relation, $subRelation, $closure) } } + /** + * 执行基础查询(仅执行一次) + * @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; + } + } } diff --git a/library/think/model/relation/MorphMany.php b/library/think/model/relation/MorphMany.php index 87c2d66c3b..3eaa391847 100644 --- a/library/think/model/relation/MorphMany.php +++ b/library/think/model/relation/MorphMany.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -11,6 +11,7 @@ namespace think\model\relation; +use think\Db; use think\db\Query; use think\Exception; use think\Loader; @@ -36,12 +37,12 @@ class MorphMany extends Relation */ public function __construct(Model $parent, $model, $morphKey, $morphType, $type) { - $this->parent = $parent; - $this->model = $model; - $this->type = $type; - $this->morphKey = $morphKey; + $this->parent = $parent; + $this->model = $model; + $this->type = $type; + $this->morphKey = $morphKey; $this->morphType = $morphType; - $this->query = (new $model)->db(); + $this->query = (new $model)->db(); } /** @@ -55,7 +56,7 @@ public function getRelation($subRelation = '', $closure = null) if ($closure) { call_user_func_array($closure, [ & $this->query]); } - $list = $this->relation($subRelation)->select(); + $list = $this->relation($subRelation)->select(); $parent = clone $this->parent; foreach ($list as &$model) { @@ -82,10 +83,11 @@ public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER' /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = []) + public function hasWhere($where = [], $fields = null) { throw new Exception('relation not support: hasWhere'); } @@ -102,9 +104,9 @@ public function hasWhere($where = []) public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) { $morphType = $this->morphType; - $morphKey = $this->morphKey; - $type = $this->type; - $range = []; + $morphKey = $this->morphKey; + $type = $this->type; + $range = []; foreach ($resultSet as $result) { $pk = $result->getPk(); // 获取关联外键列表 @@ -115,7 +117,7 @@ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) if (!empty($range)) { $data = $this->eagerlyMorphToMany([ - $morphKey => ['in', $range], + $morphKey => ['in', $range], $morphType => $type, ], $relation, $subRelation, $closure); // 关联属性名 @@ -148,7 +150,7 @@ public function eagerlyResult(&$result, $relation, $subRelation, $closure) $pk = $result->getPk(); if (isset($result->$pk)) { $data = $this->eagerlyMorphToMany([ - $this->morphKey => $result->$pk, + $this->morphKey => $result->$pk, $this->morphType => $this->type, ], $relation, $subRelation, $closure); @@ -174,7 +176,7 @@ public function eagerlyResult(&$result, $relation, $subRelation, $closure) */ public function relationCount($result, $closure) { - $pk = $result->getPk(); + $pk = $result->getPk(); $count = 0; if (isset($result->$pk)) { if ($closure) { @@ -186,21 +188,25 @@ public function relationCount($result, $closure) } /** - * 获取关联统计子查询 + * 创建关联统计子查询 * @access public * @param \Closure $closure 闭包 + * @param string $name 统计数据别名 * @return string */ - public function getRelationCountQuery($closure) + public function getRelationCountQuery($closure, &$name = null) { if ($closure) { - call_user_func_array($closure, [ & $this->query]); + $return = call_user_func_array($closure, [ & $this->query]); + if ($return && is_string($return)) { + $name = $return; + } } return $this->query->where([ - $this->morphKey => [ + $this->morphKey => [ 'exp', - '=' . $this->parent->getTable() . '.' . $this->parent->getPk(), + Db::raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()), ], $this->morphType => $this->type, ])->fetchSql()->count(); @@ -221,7 +227,7 @@ protected function eagerlyMorphToMany($where, $relation, $subRelation = '', $clo if ($closure) { call_user_func_array($closure, [ & $this]); } - $list = $this->query->where($where)->with($subRelation)->select(); + $list = $this->query->where($where)->with($subRelation)->select(); $morphKey = $this->morphKey; // 组装模型数据 $data = []; @@ -242,13 +248,36 @@ public function save($data) if ($data instanceof Model) { $data = $data->getData(); } + // 保存关联表数据 $pk = $this->parent->getPk(); - $model = new $this->model; - $data[$this->morphKey] = $this->parent->$pk; + $data[$this->morphKey] = $this->parent->$pk; $data[$this->morphType] = $this->type; - return $model->save($data) ? $model : false; + + $model = new $this->model(); + + return $model->save() ? $model : false; + } + + /** + * 创建关联对象实例 + * @param array $data + * @return Model + */ + public function make($data = []) + { + if ($data instanceof Model) { + $data = $data->getData(); + } + + // 保存关联表数据 + $pk = $this->parent->getPk(); + + $data[$this->morphKey] = $this->parent->$pk; + $data[$this->morphType] = $this->type; + + return new $this->model($data); } /** @@ -274,8 +303,8 @@ public function saveAll(array $dataSet) protected function baseQuery() { if (empty($this->baseQuery) && $this->parent->getData()) { - $pk = $this->parent->getPk(); - $map[$this->morphKey] = $this->parent->$pk; + $pk = $this->parent->getPk(); + $map[$this->morphKey] = $this->parent->$pk; $map[$this->morphType] = $this->type; $this->query->where($map); $this->baseQuery = true; diff --git a/library/think/model/relation/MorphOne.php b/library/think/model/relation/MorphOne.php index 4dfd1c0ff7..76762dde8a 100644 --- a/library/think/model/relation/MorphOne.php +++ b/library/think/model/relation/MorphOne.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -36,12 +36,12 @@ class MorphOne extends Relation */ public function __construct(Model $parent, $model, $morphKey, $morphType, $type) { - $this->parent = $parent; - $this->model = $model; - $this->type = $type; - $this->morphKey = $morphKey; + $this->parent = $parent; + $this->model = $model; + $this->type = $type; + $this->morphKey = $morphKey; $this->morphType = $morphType; - $this->query = (new $model)->db(); + $this->query = (new $model)->db(); } /** @@ -81,10 +81,11 @@ public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER' /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = []) + public function hasWhere($where = [], $fields = null) { throw new Exception('relation not support: hasWhere'); } @@ -101,9 +102,9 @@ public function hasWhere($where = []) public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) { $morphType = $this->morphType; - $morphKey = $this->morphKey; - $type = $this->type; - $range = []; + $morphKey = $this->morphKey; + $type = $this->type; + $range = []; foreach ($resultSet as $result) { $pk = $result->getPk(); // 获取关联外键列表 @@ -114,7 +115,7 @@ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) if (!empty($range)) { $data = $this->eagerlyMorphToOne([ - $morphKey => ['in', $range], + $morphKey => ['in', $range], $morphType => $type, ], $relation, $subRelation, $closure); // 关联属性名 @@ -147,9 +148,9 @@ public function eagerlyResult(&$result, $relation, $subRelation, $closure) { $pk = $result->getPk(); if (isset($result->$pk)) { - $pk = $result->$pk; + $pk = $result->$pk; $data = $this->eagerlyMorphToOne([ - $this->morphKey => $pk, + $this->morphKey => $pk, $this->morphType => $this->type, ], $relation, $subRelation, $closure); @@ -180,7 +181,7 @@ protected function eagerlyMorphToOne($where, $relation, $subRelation = '', $clos if ($closure) { call_user_func_array($closure, [ & $this]); } - $list = $this->query->where($where)->with($subRelation)->find(); + $list = $this->query->where($where)->with($subRelation)->find(); $morphKey = $this->morphKey; // 组装模型数据 $data = []; @@ -197,6 +198,28 @@ protected function eagerlyMorphToOne($where, $relation, $subRelation = '', $clos * @return Model|false */ public function save($data) + { + if ($data instanceof Model) { + $data = $data->getData(); + } + + // 保存关联表数据 + $pk = $this->parent->getPk(); + + $data[$this->morphKey] = $this->parent->$pk; + $data[$this->morphType] = $this->type; + + $model = new $this->model(); + + return $model->save() ? $model : false; + } + + /** + * 创建关联对象实例 + * @param array $data + * @return Model + */ + public function make($data = []) { if ($data instanceof Model) { $data = $data->getData(); @@ -204,10 +227,10 @@ public function save($data) // 保存关联表数据 $pk = $this->parent->getPk(); - $model = new $this->model; - $data[$this->morphKey] = $this->parent->$pk; + $data[$this->morphKey] = $this->parent->$pk; $data[$this->morphType] = $this->type; - return $model->save($data) ? $model : false; + + return new $this->model($data); } /** @@ -218,12 +241,23 @@ public function save($data) protected function baseQuery() { if (empty($this->baseQuery) && $this->parent->getData()) { - $pk = $this->parent->getPk(); - $map[$this->morphKey] = $this->parent->$pk; + $pk = $this->parent->getPk(); + $map[$this->morphKey] = $this->parent->$pk; $map[$this->morphType] = $this->type; $this->query->where($map); $this->baseQuery = true; } } + /** + * 创建关联统计子查询 + * @access public + * @param \Closure $closure 闭包 + * @param string $name 统计数据别名 + * @return string + */ + public function getRelationCountQuery($closure, &$name = null) + { + throw new Exception('relation not support: withCount'); + } } diff --git a/library/think/model/relation/MorphTo.php b/library/think/model/relation/MorphTo.php index 9136d78ed7..f50de03b1d 100644 --- a/library/think/model/relation/MorphTo.php +++ b/library/think/model/relation/MorphTo.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -36,11 +36,23 @@ class MorphTo extends Relation */ public function __construct(Model $parent, $morphType, $morphKey, $alias = [], $relation = null) { - $this->parent = $parent; + $this->parent = $parent; $this->morphType = $morphType; - $this->morphKey = $morphKey; - $this->alias = $alias; - $this->relation = $relation; + $this->morphKey = $morphKey; + $this->alias = $alias; + $this->relation = $relation; + } + + /** + * 获取当前的关联模型类的实例 + * @access public + * @return Model + */ + public function getModel() + { + $morphType = $this->morphType; + $model = $this->parseModel($this->parent->$morphType); + return (new $model); } /** @@ -51,12 +63,12 @@ public function __construct(Model $parent, $morphType, $morphKey, $alias = [], $ */ public function getRelation($subRelation = '', $closure = null) { - $morphKey = $this->morphKey; + $morphKey = $this->morphKey; $morphType = $this->morphType; // 多态模型 $model = $this->parseModel($this->parent->$morphType); // 主键数据 - $pk = $this->parent->$morphKey; + $pk = $this->parent->$morphKey; $relationModel = (new $model)->relation($subRelation)->find($pk); if ($relationModel) { @@ -82,17 +94,18 @@ public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER' /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = []) + public function hasWhere($where = [], $fields = null) { throw new Exception('relation not support: hasWhere'); } /** * 解析模型的完整命名空间 - * @access public + * @access protected * @param string $model 模型名(或者完整类名) * @return string */ @@ -144,9 +157,9 @@ public function removeOption() */ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) { - $morphKey = $this->morphKey; + $morphKey = $this->morphKey; $morphType = $this->morphType; - $range = []; + $range = []; foreach ($resultSet as $result) { // 获取关联外键列表 if (!empty($result->$morphKey)) { @@ -160,10 +173,10 @@ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) foreach ($range as $key => $val) { // 多态类型映射 $model = $this->parseModel($key); - $obj = new $model; - $pk = $obj->getPk(); - $list = $obj->all($val, $subRelation); - $data = []; + $obj = new $model; + $pk = $obj->getPk(); + $list = $obj->all($val, $subRelation); + $data = []; foreach ($list as $k => $vo) { $data[$vo->$pk] = $vo; } @@ -196,7 +209,7 @@ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) */ public function eagerlyResult(&$result, $relation, $subRelation, $closure) { - $morphKey = $this->morphKey; + $morphKey = $this->morphKey; $morphType = $this->morphType; // 多态类型映射 $model = $this->parseModel($result->{$this->morphType}); @@ -226,7 +239,7 @@ public function relationCount($result, $closure) protected function eagerlyMorphToOne($model, $relation, &$result, $subRelation = '') { // 预载入关联查询 支持嵌套预载入 - $pk = $this->parent->{$this->morphKey}; + $pk = $this->parent->{$this->morphKey}; $data = (new $model)->with($subRelation)->find($pk); if ($data) { $data->setParent(clone $result); @@ -238,17 +251,18 @@ protected function eagerlyMorphToOne($model, $relation, &$result, $subRelation = /** * 添加关联数据 * @access public - * @param Model $model 关联模型对象 + * @param Model $model 关联模型对象 + * @param string $type 多态类型 * @return Model */ - public function associate($model) + public function associate($model, $type = '') { - $morphKey = $this->morphKey; + $morphKey = $this->morphKey; $morphType = $this->morphType; - $pk = $model->getPk(); + $pk = $model->getPk(); $this->parent->setAttr($morphKey, $model->$pk); - $this->parent->setAttr($morphType, get_class($model)); + $this->parent->setAttr($morphType, $type ?: get_class($model)); $this->parent->save(); return $this->parent->setRelation($this->relation, $model); @@ -261,7 +275,7 @@ public function associate($model) */ public function dissociate() { - $morphKey = $this->morphKey; + $morphKey = $this->morphKey; $morphType = $this->morphType; $this->parent->setAttr($morphKey, null); @@ -271,4 +285,15 @@ public function dissociate() return $this->parent->setRelation($this->relation, null); } + /** + * 创建关联统计子查询 + * @access public + * @param \Closure $closure 闭包 + * @param string $name 统计数据别名 + * @return string + */ + public function getRelationCountQuery($closure, &$name = null) + { + throw new Exception('relation not support: withCount'); + } } diff --git a/library/think/model/relation/OneToOne.php b/library/think/model/relation/OneToOne.php index 66d86a93b2..353ce21b4f 100644 --- a/library/think/model/relation/OneToOne.php +++ b/library/think/model/relation/OneToOne.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -57,18 +57,18 @@ public function joinType($type) */ public function eagerly(Query $query, $relation, $subRelation, $closure, $first) { - $name = Loader::parseName(basename(str_replace('\\', '/', $query->getModel()))); - $alias = $name; + $name = Loader::parseName(basename(str_replace('\\', '/', get_class($query->getModel())))); + if ($first) { $table = $query->getTable(); - $query->table([$table => $alias]); + $query->table([$table => $name]); if ($query->getOptions('field')) { $field = $query->getOptions('field'); $query->removeOption('field'); } else { $field = true; } - $query->field($field, false, $table, $alias); + $query->field($field, false, $table, $name); $field = null; } @@ -78,9 +78,9 @@ public function eagerly(Query $query, $relation, $subRelation, $closure, $first) $query->via($joinAlias); 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 { - $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) { @@ -304,6 +304,8 @@ protected function bindAttr($model, &$result, $bindAttr) */ protected function eagerlyWhere($model, $where, $key, $relation, $subRelation = '', $closure = false) { + $this->baseQuery = true; + // 预载入关联查询 支持嵌套预载入 if ($closure) { call_user_func_array($closure, [ & $model]); @@ -321,4 +323,15 @@ protected function eagerlyWhere($model, $where, $key, $relation, $subRelation = return $data; } + /** + * 创建关联统计子查询 + * @access public + * @param \Closure $closure 闭包 + * @param string $name 统计数据别名 + * @return string + */ + public function getRelationCountQuery($closure, &$name = null) + { + throw new Exception('relation not support: withCount'); + } } diff --git a/library/think/paginator/driver/Bootstrap.php b/library/think/paginator/driver/Bootstrap.php index 58fa94364b..849298adaf 100644 --- a/library/think/paginator/driver/Bootstrap.php +++ b/library/think/paginator/driver/Bootstrap.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -61,26 +61,26 @@ protected function getLinks() return ''; $block = [ - 'first' => null, + 'first' => null, 'slider' => null, - 'last' => null + 'last' => null ]; - $side = 3; + $side = 3; $window = $side * 2; if ($this->lastPage < $window + 6) { $block['first'] = $this->getUrlRange(1, $this->lastPage); } elseif ($this->currentPage <= $window) { $block['first'] = $this->getUrlRange(1, $window + 2); - $block['last'] = $this->getUrlRange($this->lastPage - 1, $this->lastPage); + $block['last'] = $this->getUrlRange($this->lastPage - 1, $this->lastPage); } elseif ($this->currentPage > ($this->lastPage - $window)) { $block['first'] = $this->getUrlRange(1, 2); - $block['last'] = $this->getUrlRange($this->lastPage - ($window + 2), $this->lastPage); + $block['last'] = $this->getUrlRange($this->lastPage - ($window + 2), $this->lastPage); } else { - $block['first'] = $this->getUrlRange(1, 2); + $block['first'] = $this->getUrlRange(1, 2); $block['slider'] = $this->getUrlRange($this->currentPage - $side, $this->currentPage + $side); - $block['last'] = $this->getUrlRange($this->lastPage - 1, $this->lastPage); + $block['last'] = $this->getUrlRange($this->lastPage - 1, $this->lastPage); } $html = ''; diff --git a/library/think/process/Builder.php b/library/think/process/Builder.php index da56163972..2995bd10c7 100644 --- a/library/think/process/Builder.php +++ b/library/think/process/Builder.php @@ -19,10 +19,10 @@ class Builder private $cwd; private $env = null; private $input; - private $timeout = 60; - private $options = []; - private $inheritEnv = true; - private $prefix = []; + private $timeout = 60; + private $options = []; + private $inheritEnv = true; + private $prefix = []; private $outputDisabled = false; /** @@ -213,7 +213,7 @@ public function getProcess() $options = $this->options; $arguments = array_merge($this->prefix, $this->arguments); - $script = implode(' ', array_map([__NAMESPACE__ . '\\Utils', 'escapeArgument'], $arguments)); + $script = implode(' ', array_map([__NAMESPACE__ . '\\Utils', 'escapeArgument'], $arguments)); if ($this->inheritEnv) { // include $_ENV for BC purposes diff --git a/library/think/process/Utils.php b/library/think/process/Utils.php index f94c6488e4..5e116b7e7f 100644 --- a/library/think/process/Utils.php +++ b/library/think/process/Utils.php @@ -24,7 +24,7 @@ public static function escapeArgument($argument) return escapeshellarg($argument); } $escapedArgument = ''; - $quote = false; + $quote = false; foreach (preg_split('/(")/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) { if ('"' === $part) { $escapedArgument .= '\\"'; diff --git a/library/think/process/exception/Timeout.php b/library/think/process/exception/Timeout.php index d5f1162f43..3e96df7287 100644 --- a/library/think/process/exception/Timeout.php +++ b/library/think/process/exception/Timeout.php @@ -17,14 +17,14 @@ class Timeout extends \RuntimeException { const TYPE_GENERAL = 1; - const TYPE_IDLE = 2; + const TYPE_IDLE = 2; private $process; private $timeoutType; public function __construct(Process $process, $timeoutType) { - $this->process = $process; + $this->process = $process; $this->timeoutType = $timeoutType; parent::__construct(sprintf('The process "%s" exceeded the timeout of %s seconds.', $process->getCommandLine(), $this->getExceededTimeout())); diff --git a/library/think/process/pipes/Unix.php b/library/think/process/pipes/Unix.php index fd99a5d67e..f047631414 100644 --- a/library/think/process/pipes/Unix.php +++ b/library/think/process/pipes/Unix.php @@ -25,8 +25,8 @@ class Unix extends Pipes public function __construct($ttyMode, $ptyMode, $input, $disableOutput) { - $this->ttyMode = (bool) $ttyMode; - $this->ptyMode = (bool) $ptyMode; + $this->ttyMode = (bool) $ttyMode; + $this->ptyMode = (bool) $ptyMode; $this->disableOutput = (bool) $disableOutput; if (is_resource($input)) { diff --git a/library/think/process/pipes/Windows.php b/library/think/process/pipes/Windows.php index bba7e9b869..95a32e3fa2 100644 --- a/library/think/process/pipes/Windows.php +++ b/library/think/process/pipes/Windows.php @@ -97,12 +97,12 @@ public function readAndWrite($blocking, $close = false) $this->write($blocking, $close); $read = []; - $fh = $this->fileHandles; + $fh = $this->fileHandles; foreach ($fh as $type => $fileHandle) { if (0 !== fseek($fileHandle, $this->readBytes[$type])) { continue; } - $data = ''; + $data = ''; $dataread = null; while (!feof($fileHandle)) { if (false !== $dataread = fread($fileHandle, self::CHUNK_SIZE)) { @@ -196,7 +196,7 @@ private function write($blocking, $close) return; } - if (null !== $w && 0 < count($r)) { + if (null !== $r && 0 < count($r)) { $data = ''; while ($dataread = fread($r['input'], self::CHUNK_SIZE)) { $data .= $dataread; diff --git a/library/think/response/Json.php b/library/think/response/Json.php index 538fc5a0df..c906bfc180 100644 --- a/library/think/response/Json.php +++ b/library/think/response/Json.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- diff --git a/library/think/response/Jsonp.php b/library/think/response/Jsonp.php index de8fb30418..69afaa0494 100644 --- a/library/think/response/Jsonp.php +++ b/library/think/response/Jsonp.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -18,9 +18,9 @@ class Jsonp extends Response { // 输出参数 protected $options = [ - 'var_jsonp_handler' => 'callback', + 'var_jsonp_handler' => 'callback', 'default_jsonp_handler' => 'jsonpReturn', - 'json_encode_param' => JSON_UNESCAPED_UNICODE, + 'json_encode_param' => JSON_UNESCAPED_UNICODE, ]; protected $contentType = 'application/javascript'; @@ -37,7 +37,7 @@ protected function output($data) try { // 返回JSON数据格式到客户端 包含状态信息 [当url_common_param为false时是无法获取到$_GET的数据的,故使用Request来获取] $var_jsonp_handler = Request::instance()->param($this->options['var_jsonp_handler'], ""); - $handler = !empty($var_jsonp_handler) ? $var_jsonp_handler : $this->options['default_jsonp_handler']; + $handler = !empty($var_jsonp_handler) ? $var_jsonp_handler : $this->options['default_jsonp_handler']; $data = json_encode($data, $this->options['json_encode_param']); diff --git a/library/think/response/Redirect.php b/library/think/response/Redirect.php index 0ea90a4898..91694cb2cb 100644 --- a/library/think/response/Redirect.php +++ b/library/think/response/Redirect.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- diff --git a/library/think/response/View.php b/library/think/response/View.php index de75515a9f..f10c4d1e27 100644 --- a/library/think/response/View.php +++ b/library/think/response/View.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -18,9 +18,9 @@ class View extends Response { // 输出参数 - protected $options = []; - protected $vars = []; - protected $replace = []; + protected $options = []; + protected $vars = []; + protected $replace = []; protected $contentType = 'text/html'; /** diff --git a/library/think/response/Xml.php b/library/think/response/Xml.php index 87479be96c..a21d075ff8 100644 --- a/library/think/response/Xml.php +++ b/library/think/response/Xml.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -26,9 +26,9 @@ class Xml extends Response //数字索引的子节点名 'item_node' => 'item', // 数字索引子节点key转换的属性名 - 'item_key' => 'id', + 'item_key' => 'id', // 数据编码 - 'encoding' => 'utf-8', + 'encoding' => 'utf-8', ]; protected $contentType = 'text/xml'; @@ -66,7 +66,7 @@ protected function xmlEncode($data, $root, $item, $attr, $id, $encoding) } $attr = trim($attr); $attr = empty($attr) ? '' : " {$attr}"; - $xml = ""; + $xml = ""; $xml .= "<{$root}{$attr}>"; $xml .= $this->dataToXml($data, $item, $id); $xml .= ""; @@ -91,7 +91,7 @@ protected function dataToXml($data, $item, $id) foreach ($data as $key => $val) { if (is_numeric($key)) { $id && $attr = " {$id}=\"{$key}\""; - $key = $item; + $key = $item; } $xml .= "<{$key}{$attr}>"; $xml .= (is_array($val) || is_object($val)) ? $this->dataToXml($val, $item, $id) : $val; diff --git a/library/think/session/driver/Memcache.php b/library/think/session/driver/Memcache.php index 0c02e23ec4..fd2fc8d828 100644 --- a/library/think/session/driver/Memcache.php +++ b/library/think/session/driver/Memcache.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -17,12 +17,12 @@ class Memcache extends SessionHandler { protected $handler = null; - protected $config = [ - 'host' => '127.0.0.1', // memcache主机 - 'port' => 11211, // memcache端口 - 'expire' => 3600, // session有效期 - 'timeout' => 0, // 连接超时时间(单位:毫秒) - 'persistent' => true, // 长连接 + protected $config = [ + 'host' => '127.0.0.1', // memcache主机 + 'port' => 11211, // memcache端口 + 'expire' => 3600, // session有效期 + 'timeout' => 0, // 连接超时时间(单位:毫秒) + 'persistent' => true, // 长连接 'session_name' => '', // memcache key前缀 ]; diff --git a/library/think/session/driver/Memcached.php b/library/think/session/driver/Memcached.php index bcaf8a1b5b..66b401789e 100644 --- a/library/think/session/driver/Memcached.php +++ b/library/think/session/driver/Memcached.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -17,14 +17,14 @@ class Memcached extends SessionHandler { protected $handler = null; - protected $config = [ - 'host' => '127.0.0.1', // memcache主机 - 'port' => 11211, // memcache端口 - 'expire' => 3600, // session有效期 - 'timeout' => 0, // 连接超时时间(单位:毫秒) + protected $config = [ + 'host' => '127.0.0.1', // memcache主机 + 'port' => 11211, // memcache端口 + 'expire' => 3600, // session有效期 + 'timeout' => 0, // 连接超时时间(单位:毫秒) 'session_name' => '', // memcache key前缀 - 'username' => '', //账号 - 'password' => '', //密码 + 'username' => '', //账号 + 'password' => '', //密码 ]; public function __construct($config = []) diff --git a/library/think/session/driver/Redis.php b/library/think/session/driver/Redis.php index a4c2b54a67..0a51a4df92 100644 --- a/library/think/session/driver/Redis.php +++ b/library/think/session/driver/Redis.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -18,14 +18,14 @@ class Redis extends SessionHandler { /** @var \Redis */ protected $handler = null; - protected $config = [ - 'host' => '127.0.0.1', // redis主机 - 'port' => 6379, // redis端口 - 'password' => '', // 密码 - 'select' => 0, // 操作库 - 'expire' => 3600, // 有效期(秒) - 'timeout' => 0, // 超时时间(秒) - 'persistent' => true, // 是否长连接 + protected $config = [ + 'host' => '127.0.0.1', // redis主机 + 'port' => 6379, // redis端口 + 'password' => '', // 密码 + 'select' => 0, // 操作库 + 'expire' => 3600, // 有效期(秒) + 'timeout' => 0, // 超时时间(秒) + 'persistent' => true, // 是否长连接 'session_name' => '', // sessionkey前缀 ]; diff --git a/library/think/template/TagLib.php b/library/think/template/TagLib.php index d343bed055..84d499efe8 100644 --- a/library/think/template/TagLib.php +++ b/library/think/template/TagLib.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -28,7 +28,7 @@ class TagLib * @var string * @access protected */ - protected $xml = ''; + protected $xml = ''; protected $tags = []; // 标签定义 /** * 标签库名称 @@ -87,9 +87,9 @@ public function __construct($template) public function parseTag(&$content, $lib = '') { $tags = []; - $lib = $lib ? strtolower($lib) . ':' : ''; + $lib = $lib ? strtolower($lib) . ':' : ''; foreach ($this->tags as $name => $val) { - $close = !isset($val['close']) || $val['close'] ? 1 : 0; + $close = !isset($val['close']) || $val['close'] ? 1 : 0; $tags[$close][$lib . $name] = $name; if (isset($val['alias'])) { // 别名设置 @@ -113,9 +113,9 @@ public function parseTag(&$content, $lib = '') if (!empty($right[$name])) { // $match[0][1]为标签结束符在模板中的位置 $nodes[$match[0][1]] = [ - 'name' => $name, + 'name' => $name, 'begin' => array_pop($right[$name]), // 标签开始符 - 'end' => $match[0], // 标签结束符 + 'end' => $match[0], // 标签结束符 ]; } } else { @@ -134,10 +134,10 @@ public function parseTag(&$content, $lib = '') // 标签替换 从后向前 foreach ($nodes as $pos => $node) { // 对应的标签名 - $name = $tags[1][$node['name']]; + $name = $tags[1][$node['name']]; $alias = $lib . $name != $node['name'] ? ($lib ? strstr($node['name'], $lib) : $node['name']) : ''; // 解析标签属性 - $attrs = $this->parseAttr($node['begin'][0], $name, $alias); + $attrs = $this->parseAttr($node['begin'][0], $name, $alias); $method = 'tag' . $name; // 读取标签库中对应的标签内容 replace[0]用来替换标签头,replace[1]用来替换标签尾 $replace = explode($break, $this->$method($attrs, $break)); @@ -169,13 +169,13 @@ public function parseTag(&$content, $lib = '') } // 自闭合标签 if (!empty($tags[0])) { - $regex = $this->getRegex(array_keys($tags[0]), 0); + $regex = $this->getRegex(array_keys($tags[0]), 0); $content = preg_replace_callback($regex, function ($matches) use (&$tags, &$lib) { // 对应的标签名 - $name = $tags[0][strtolower($matches[1])]; + $name = $tags[0][strtolower($matches[1])]; $alias = $lib . $name != $matches[1] ? ($lib ? strstr($matches[1], $lib) : $matches[1]) : ''; // 解析标签属性 - $attrs = $this->parseAttr($matches[0], $name, $alias); + $attrs = $this->parseAttr($matches[0], $name, $alias); $method = 'tag' . $name; return $this->$method($attrs, ''); }, $content); @@ -192,9 +192,9 @@ public function parseTag(&$content, $lib = '') */ public function getRegex($tags, $close) { - $begin = $this->tpl->config('taglib_begin'); - $end = $this->tpl->config('taglib_end'); - $single = strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1 ? true : false; + $begin = $this->tpl->config('taglib_begin'); + $end = $this->tpl->config('taglib_end'); + $single = strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1 ? true : false; $tagName = is_array($tags) ? implode('|', $tags) : $tags; if ($single) { if ($close) { @@ -224,7 +224,7 @@ public function getRegex($tags, $close) */ public function parseAttr($str, $name, $alias = '') { - $regex = '/\s+(?>(?P[\w-]+)\s*)=(?>\s*)([\"\'])(?P(?:(?!\\2).)*)\\2/is'; + $regex = '/\s+(?>(?P[\w-]+)\s*)=(?>\s*)([\"\'])(?P(?:(?!\\2).)*)\\2/is'; $result = []; if (preg_match_all($regex, $str, $matches)) { foreach ($matches['name'] as $key => $val) { @@ -236,8 +236,8 @@ public function parseAttr($str, $name, $alias = '') if (isset($val['alias'])) { $array = (array) $val['alias']; if (in_array($name, explode(',', $array[0]))) { - $tag = $val; - $type = !empty($array[1]) ? $array[1] : 'type'; + $tag = $val; + $type = !empty($array[1]) ? $array[1] : 'type'; $result[$type] = $name; break; } @@ -247,7 +247,7 @@ public function parseAttr($str, $name, $alias = '') $tag = $this->tags[$name]; // 设置了标签别名 if (!empty($alias) && isset($tag['alias'])) { - $type = !empty($tag['alias'][1]) ? $tag['alias'][1] : 'type'; + $type = !empty($tag['alias'][1]) ? $tag['alias'][1] : 'type'; $result[$type] = $alias; } } @@ -264,8 +264,8 @@ public function parseAttr($str, $name, $alias = '') if (!empty($this->tags[$name]['expression'])) { static $_taglibs; if (!isset($_taglibs[$name])) { - $_taglibs[$name][0] = strlen(ltrim($this->tpl->config('taglib_begin'), '\\') . $name); - $_taglibs[$name][1] = strlen(ltrim($this->tpl->config('taglib_end'), '\\')); + $_taglibs[$name][0] = strlen($this->tpl->config('taglib_begin_origin') . $name); + $_taglibs[$name][1] = strlen($this->tpl->config('taglib_end_origin')); } $result['expression'] = substr($str, $_taglibs[$name][0], -$_taglibs[$name][1]); // 清除自闭合标签尾部/ diff --git a/library/think/template/driver/File.php b/library/think/template/driver/File.php index b27e726597..a9a86bf2a3 100644 --- a/library/think/template/driver/File.php +++ b/library/think/template/driver/File.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -15,6 +15,8 @@ class File { + protected $cacheFile; + /** * 写入编译缓存 * @param string $cacheFile 缓存的文件名 @@ -42,12 +44,13 @@ public function write($cacheFile, $content) */ public function read($cacheFile, $vars = []) { + $this->cacheFile = $cacheFile; if (!empty($vars) && is_array($vars)) { // 模板阵列变量分解成为独立变量 extract($vars, EXTR_OVERWRITE); } //载入模版缓存文件 - include $cacheFile; + include $this->cacheFile; } /** diff --git a/library/think/template/taglib/Cx.php b/library/think/template/taglib/Cx.php index af7a54c891..03005c52a1 100644 --- a/library/think/template/taglib/Cx.php +++ b/library/think/template/taglib/Cx.php @@ -26,29 +26,29 @@ class Cx extends Taglib // 标签定义 protected $tags = [ // 标签定义: attr 属性列表 close 是否闭合(0 或者1 默认1) alias 标签别名 level 嵌套层次 - 'php' => ['attr' => ''], - 'volist' => ['attr' => 'name,id,offset,length,key,mod', 'alias' => 'iterate'], - 'foreach' => ['attr' => 'name,id,item,key,offset,length,mod', 'expression' => true], - 'if' => ['attr' => 'condition', 'expression' => true], - 'elseif' => ['attr' => 'condition', 'close' => 0, 'expression' => true], - 'else' => ['attr' => '', 'close' => 0], - 'switch' => ['attr' => 'name', 'expression' => true], - 'case' => ['attr' => 'value,break', 'expression' => true], - 'default' => ['attr' => '', 'close' => 0], - 'compare' => ['attr' => 'name,value,type', 'alias' => ['eq,equal,notequal,neq,gt,lt,egt,elt,heq,nheq', 'type']], - 'range' => ['attr' => 'name,value,type', 'alias' => ['in,notin,between,notbetween', 'type']], - 'empty' => ['attr' => 'name'], - 'notempty' => ['attr' => 'name'], - 'present' => ['attr' => 'name'], + 'php' => ['attr' => ''], + 'volist' => ['attr' => 'name,id,offset,length,key,mod', 'alias' => 'iterate'], + 'foreach' => ['attr' => 'name,id,item,key,offset,length,mod', 'expression' => true], + 'if' => ['attr' => 'condition', 'expression' => true], + 'elseif' => ['attr' => 'condition', 'close' => 0, 'expression' => true], + 'else' => ['attr' => '', 'close' => 0], + 'switch' => ['attr' => 'name', 'expression' => true], + 'case' => ['attr' => 'value,break', 'expression' => true], + 'default' => ['attr' => '', 'close' => 0], + 'compare' => ['attr' => 'name,value,type', 'alias' => ['eq,equal,notequal,neq,gt,lt,egt,elt,heq,nheq', 'type']], + 'range' => ['attr' => 'name,value,type', 'alias' => ['in,notin,between,notbetween', 'type']], + 'empty' => ['attr' => 'name'], + 'notempty' => ['attr' => 'name'], + 'present' => ['attr' => 'name'], 'notpresent' => ['attr' => 'name'], - 'defined' => ['attr' => 'name'], + 'defined' => ['attr' => 'name'], 'notdefined' => ['attr' => 'name'], - 'load' => ['attr' => 'file,href,type,value,basepath', 'close' => 0, 'alias' => ['import,css,js', 'type']], - 'assign' => ['attr' => 'name,value', 'close' => 0], - 'define' => ['attr' => 'name,value', 'close' => 0], - 'for' => ['attr' => 'start,end,name,comparison,step'], - 'url' => ['attr' => 'link,vars,suffix,domain', 'close' => 0, 'expression' => true], - 'function' => ['attr' => 'name,vars,use,call'], + 'load' => ['attr' => 'file,href,type,value,basepath', 'close' => 0, 'alias' => ['import,css,js', 'type']], + 'assign' => ['attr' => 'name,value', 'close' => 0], + 'define' => ['attr' => 'name,value', 'close' => 0], + 'for' => ['attr' => 'start,end,name,comparison,step'], + 'url' => ['attr' => 'link,vars,suffix,domain', 'close' => 0, 'expression' => true], + 'function' => ['attr' => 'name,vars,use,call'], ]; /** @@ -80,16 +80,16 @@ public function tagPhp($tag, $content) */ public function tagVolist($tag, $content) { - $name = $tag['name']; - $id = $tag['id']; - $empty = isset($tag['empty']) ? $tag['empty'] : ''; - $key = !empty($tag['key']) ? $tag['key'] : 'i'; - $mod = isset($tag['mod']) ? $tag['mod'] : '2'; + $name = $tag['name']; + $id = $tag['id']; + $empty = isset($tag['empty']) ? $tag['empty'] : ''; + $key = !empty($tag['key']) ? $tag['key'] : 'i'; + $mod = isset($tag['mod']) ? $tag['mod'] : '2'; $offset = !empty($tag['offset']) && is_numeric($tag['offset']) ? intval($tag['offset']) : 0; $length = !empty($tag['length']) && is_numeric($tag['length']) ? intval($tag['length']) : 'null'; // 允许使用函数设定数据集 {$vo.name} $parseStr = 'autoBuildVar($name); $parseStr .= '$_result=' . $name . ';'; @@ -136,22 +136,22 @@ public function tagForeach($tag, $content) if (!empty($tag['expression'])) { $expression = ltrim(rtrim($tag['expression'], ')'), '('); $expression = $this->autoBuildVar($expression); - $parseStr = ''; + $parseStr = ''; $parseStr .= $content; $parseStr .= ''; return $parseStr; } - $name = $tag['name']; - $key = !empty($tag['key']) ? $tag['key'] : 'key'; - $item = !empty($tag['id']) ? $tag['id'] : $tag['item']; - $empty = isset($tag['empty']) ? $tag['empty'] : ''; + $name = $tag['name']; + $key = !empty($tag['key']) ? $tag['key'] : 'key'; + $item = !empty($tag['id']) ? $tag['id'] : $tag['item']; + $empty = isset($tag['empty']) ? $tag['empty'] : ''; $offset = !empty($tag['offset']) && is_numeric($tag['offset']) ? intval($tag['offset']) : 0; $length = !empty($tag['length']) && is_numeric($tag['length']) ? intval($tag['length']) : 'null'; $parseStr = 'autoBuildVar($name); $parseStr .= $var . '=' . $name . '; '; $name = $var; @@ -215,7 +215,7 @@ public function tagIf($tag, $content) { $condition = !empty($tag['expression']) ? $tag['expression'] : $tag['condition']; $condition = $this->parseCondition($condition); - $parseStr = '' . $content . ''; + $parseStr = '' . $content . ''; return $parseStr; } @@ -231,7 +231,7 @@ public function tagElseif($tag, $content) { $condition = !empty($tag['expression']) ? $tag['expression'] : $tag['condition']; $condition = $this->parseCondition($condition); - $parseStr = ''; + $parseStr = ''; return $parseStr; } @@ -263,8 +263,8 @@ public function tagElse($tag) */ public function tagSwitch($tag, $content) { - $name = !empty($tag['expression']) ? $tag['expression'] : $tag['name']; - $name = $this->autoBuildVar($name); + $name = !empty($tag['expression']) ? $tag['expression'] : $tag['name']; + $name = $this->autoBuildVar($name); $parseStr = '' . $content . ''; return $parseStr; } @@ -278,14 +278,14 @@ public function tagSwitch($tag, $content) */ public function tagCase($tag, $content) { - $value = !empty($tag['expression']) ? $tag['expression'] : $tag['value']; - $flag = substr($value, 0, 1); + $value = isset($tag['expression']) ? $tag['expression'] : $tag['value']; + $flag = substr($value, 0, 1); if ('$' == $flag || ':' == $flag) { $value = $this->autoBuildVar($value); $value = 'case ' . $value . ':'; } elseif (strpos($value, '|')) { $values = explode('|', $value); - $value = ''; + $value = ''; foreach ($values as $val) { $value .= 'case "' . addslashes($val) . '":'; } @@ -293,7 +293,7 @@ public function tagCase($tag, $content) $value = 'case "' . $value . '":'; } $parseStr = '' . $content; - $isBreak = isset($tag['break']) ? $tag['break'] : ''; + $isBreak = isset($tag['break']) ? $tag['break'] : ''; if ('' == $isBreak || $isBreak) { $parseStr .= ''; } @@ -325,11 +325,11 @@ public function tagDefault($tag) */ public function tagCompare($tag, $content) { - $name = $tag['name']; + $name = $tag['name']; $value = $tag['value']; - $type = isset($tag['type']) ? $tag['type'] : 'eq'; // 比较类型 - $name = $this->autoBuildVar($name); - $flag = substr($value, 0, 1); + $type = isset($tag['type']) ? $tag['type'] : 'eq'; // 比较类型 + $name = $this->autoBuildVar($name); + $flag = substr($value, 0, 1); if ('$' == $flag || ':' == $flag) { $value = $this->autoBuildVar($value); } else { @@ -343,7 +343,7 @@ public function tagCompare($tag, $content) $type = 'neq'; break; } - $type = $this->parseCondition(' ' . $type . ' '); + $type = $this->parseCondition(' ' . $type . ' '); $parseStr = '' . $content . ''; return $parseStr; } @@ -360,25 +360,25 @@ public function tagCompare($tag, $content) */ public function tagRange($tag, $content) { - $name = $tag['name']; + $name = $tag['name']; $value = $tag['value']; - $type = isset($tag['type']) ? $tag['type'] : 'in'; // 比较类型 + $type = isset($tag['type']) ? $tag['type'] : 'in'; // 比较类型 $name = $this->autoBuildVar($name); $flag = substr($value, 0, 1); if ('$' == $flag || ':' == $flag) { $value = $this->autoBuildVar($value); - $str = 'is_array(' . $value . ')?' . $value . ':explode(\',\',' . $value . ')'; + $str = 'is_array(' . $value . ')?' . $value . ':explode(\',\',' . $value . ')'; } else { $value = '"' . $value . '"'; - $str = 'explode(\',\',' . $value . ')'; + $str = 'explode(\',\',' . $value . ')'; } if ('between' == $type) { $parseStr = '= $_RANGE_VAR_[0] && ' . $name . '<= $_RANGE_VAR_[1]):?>' . $content . ''; } elseif ('notbetween' == $type) { $parseStr = '$_RANGE_VAR_[1]):?>' . $content . ''; } else { - $fun = ('in' == $type) ? 'in_array' : '!in_array'; + $fun = ('in' == $type) ? 'in_array' : '!in_array'; $parseStr = '' . $content . ''; } return $parseStr; @@ -395,8 +395,8 @@ public function tagRange($tag, $content) */ public function tagPresent($tag, $content) { - $name = $tag['name']; - $name = $this->autoBuildVar($name); + $name = $tag['name']; + $name = $this->autoBuildVar($name); $parseStr = '' . $content . ''; return $parseStr; } @@ -412,8 +412,8 @@ public function tagPresent($tag, $content) */ public function tagNotpresent($tag, $content) { - $name = $tag['name']; - $name = $this->autoBuildVar($name); + $name = $tag['name']; + $name = $this->autoBuildVar($name); $parseStr = '' . $content . ''; return $parseStr; } @@ -429,8 +429,8 @@ public function tagNotpresent($tag, $content) */ public function tagEmpty($tag, $content) { - $name = $tag['name']; - $name = $this->autoBuildVar($name); + $name = $tag['name']; + $name = $this->autoBuildVar($name); $parseStr = 'isEmpty())): ?>' . $content . ''; return $parseStr; } @@ -446,8 +446,8 @@ public function tagEmpty($tag, $content) */ public function tagNotempty($tag, $content) { - $name = $tag['name']; - $name = $this->autoBuildVar($name); + $name = $tag['name']; + $name = $this->autoBuildVar($name); $parseStr = 'isEmpty()))): ?>' . $content . ''; return $parseStr; } @@ -461,7 +461,7 @@ public function tagNotempty($tag, $content) */ public function tagDefined($tag, $content) { - $name = $tag['name']; + $name = $tag['name']; $parseStr = '' . $content . ''; return $parseStr; } @@ -475,7 +475,7 @@ public function tagDefined($tag, $content) */ public function tagNotdefined($tag, $content) { - $name = $tag['name']; + $name = $tag['name']; $parseStr = '' . $content . ''; return $parseStr; } @@ -490,10 +490,10 @@ public function tagNotdefined($tag, $content) */ public function tagLoad($tag, $content) { - $file = isset($tag['file']) ? $tag['file'] : $tag['href']; - $type = isset($tag['type']) ? strtolower($tag['type']) : ''; + $file = isset($tag['file']) ? $tag['file'] : $tag['href']; + $type = isset($tag['type']) ? strtolower($tag['type']) : ''; $parseStr = ''; - $endStr = ''; + $endStr = ''; // 判断是否存在加载条件 允许使用函数判断(默认为isset) if (isset($tag['value'])) { $name = $tag['value']; @@ -580,16 +580,16 @@ public function tagDefine($tag, $content) public function tagFor($tag, $content) { //设置默认值 - $start = 0; - $end = 0; - $step = 1; + $start = 0; + $end = 0; + $step = 1; $comparison = 'lt'; - $name = 'i'; - $rand = rand(); //添加随机数,防止嵌套变量冲突 + $name = 'i'; + $rand = rand(); //添加随机数,防止嵌套变量冲突 //获取属性 foreach ($tag as $key => $value) { $value = trim($value); - $flag = substr($value, 0, 1); + $flag = substr($value, 0, 1); if ('$' == $flag || ':' == $flag) { $value = $this->autoBuildVar($value); } @@ -630,8 +630,8 @@ public function tagFor($tag, $content) */ public function tagUrl($tag, $content) { - $url = isset($tag['link']) ? $tag['link'] : ''; - $vars = isset($tag['vars']) ? $tag['vars'] : ''; + $url = isset($tag['link']) ? $tag['link'] : ''; + $vars = isset($tag['vars']) ? $tag['vars'] : ''; $suffix = isset($tag['suffix']) ? $tag['suffix'] : 'true'; $domain = isset($tag['domain']) ? $tag['domain'] : 'false'; return ''; @@ -659,7 +659,7 @@ public function tagFunction($tag, $content) $name = !empty($tag['name']) ? $tag['name'] : 'func'; $vars = !empty($tag['vars']) ? $tag['vars'] : ''; $call = !empty($tag['call']) ? $tag['call'] : ''; - $use = ['&$' . $name]; + $use = ['&$' . $name]; if (!empty($tag['use'])) { foreach (explode(',', $tag['use']) as $val) { $use[] = '&' . ltrim(trim($val), '&'); diff --git a/library/think/view/driver/Php.php b/library/think/view/driver/Php.php index 468d3611da..b7599c55ba 100644 --- a/library/think/view/driver/Php.php +++ b/library/think/view/driver/Php.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -22,14 +22,18 @@ class Php // 模板引擎参数 protected $config = [ // 视图基础目录(集中式) - 'view_base' => '', + 'view_base' => '', // 模板起始路径 - 'view_path' => '', + 'view_path' => '', // 模板文件后缀 'view_suffix' => 'php', // 模板文件名分隔符 - 'view_depr' => DS, + 'view_depr' => DS, + // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 + 'auto_rule' => 1, ]; + protected $template; + protected $content; public function __construct($config = []) { @@ -68,16 +72,12 @@ public function fetch($template, $data = []) if (!is_file($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'); - if (isset($data['template'])) { - $__template__ = $template; - extract($data, EXTR_OVERWRITE); - include $__template__; - } else { - extract($data, EXTR_OVERWRITE); - include $template; - } + + extract($data, EXTR_OVERWRITE); + include $this->template; } /** @@ -89,14 +89,10 @@ public function fetch($template, $data = []) */ public function display($content, $data = []) { - if (isset($data['content'])) { - $__content__ = $content; - extract($data, EXTR_OVERWRITE); - eval('?>' . $__content__); - } else { - extract($data, EXTR_OVERWRITE); - eval('?>' . $content); - } + $this->content = $content; + + extract($data, EXTR_OVERWRITE); + eval('?>' . $this->content); } /** @@ -120,19 +116,19 @@ private function parseTemplate($template) if ($this->config['view_base']) { // 基础视图目录 $module = isset($module) ? $module : $request->module(); - $path = $this->config['view_base'] . ($module ? $module . DS : ''); + $path = $this->config['view_base'] . ($module ? $module . DS : ''); } else { $path = isset($module) ? APP_PATH . $module . DS . 'view' . DS : $this->config['view_path']; } $depr = $this->config['view_depr']; if (0 !== strpos($template, '/')) { - $template = str_replace(['/', ':'], $depr, $template); + $template = str_replace(['/', ':'], $depr, $template); $controller = Loader::parseName($request->controller()); if ($controller) { 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)) { $template = str_replace('.', DS, $controller) . $depr . $template; } diff --git a/library/think/view/driver/Think.php b/library/think/view/driver/Think.php index 39a14ca338..74b040778c 100644 --- a/library/think/view/driver/Think.php +++ b/library/think/view/driver/Think.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -25,15 +25,17 @@ class Think // 模板引擎参数 protected $config = [ // 视图基础目录(集中式) - 'view_base' => '', + 'view_base' => '', // 模板起始路径 - 'view_path' => '', + 'view_path' => '', // 模板文件后缀 'view_suffix' => 'html', // 模板文件名分隔符 - 'view_depr' => DS, + 'view_depr' => DS, // 是否开启模板编译缓存,设为false则每次都会重新编译 - 'tpl_cache' => true, + 'tpl_cache' => true, + // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 + 'auto_rule' => 1, ]; public function __construct($config = []) @@ -115,19 +117,19 @@ private function parseTemplate($template) if ($this->config['view_base']) { // 基础视图目录 $module = isset($module) ? $module : $request->module(); - $path = $this->config['view_base'] . ($module ? $module . DS : ''); + $path = $this->config['view_base'] . ($module ? $module . DS : ''); } else { $path = isset($module) ? APP_PATH . $module . DS . 'view' . DS : $this->config['view_path']; } $depr = $this->config['view_depr']; if (0 !== strpos($template, '/')) { - $template = str_replace(['/', ':'], $depr, $template); + $template = str_replace(['/', ':'], $depr, $template); $controller = Loader::parseName($request->controller()); if ($controller) { 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)) { $template = str_replace('.', DS, $controller) . $depr . $template; } @@ -154,7 +156,7 @@ public function config($name, $value = null) return $this->template->config($name); } else { $this->template->$name = $value; - $this->config[$name] = $value; + $this->config[$name] = $value; } } diff --git a/library/traits/controller/Jump.php b/library/traits/controller/Jump.php index 472f23d19b..aa90b1139d 100644 --- a/library/traits/controller/Jump.php +++ b/library/traits/controller/Jump.php @@ -1,5 +1,4 @@ server('HTTP_REFERER'))) { $url = Request::instance()->server('HTTP_REFERER'); - } elseif ('' !== $url) { - $url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : Url::build($url); + } elseif ('' !== $url && !strpos($url, '://') && 0 !== strpos($url, '/')) { + $url = Url::build($url); } + + $type = $this->getResponseType(); $result = [ 'code' => 1, - 'msg' => $msg, + 'msg' => $msg, 'data' => $data, - 'url' => $url, + 'url' => $url, 'wait' => $wait, ]; - $type = $this->getResponseType(); 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); } + $response = Response::create($result, $type)->header($header); + throw new HttpResponseException($response); } /** * 操作错误跳转的快捷方法 * @access protected - * @param mixed $msg 提示信息 - * @param string $url 跳转的URL地址 - * @param mixed $data 返回的数据 - * @param integer $wait 跳转等待时间 - * @param array $header 发送的Header信息 + * @param mixed $msg 提示信息 + * @param string $url 跳转的 URL 地址 + * @param mixed $data 返回的数据 + * @param int $wait 跳转等待时间 + * @param array $header 发送的 Header 信息 * @return void + * @throws HttpResponseException */ protected function error($msg = '', $url = null, $data = '', $wait = 3, array $header = []) { if (is_null($url)) { $url = Request::instance()->isAjax() ? '' : 'javascript:history.back(-1);'; - } elseif ('' !== $url) { - $url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : Url::build($url); + } elseif ('' !== $url && !strpos($url, '://') && 0 !== strpos($url, '/')) { + $url = Url::build($url); } + + $type = $this->getResponseType(); $result = [ 'code' => 0, - 'msg' => $msg, + 'msg' => $msg, 'data' => $data, - 'url' => $url, + 'url' => $url, 'wait' => $wait, ]; - $type = $this->getResponseType(); 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); } + $response = Response::create($result, $type)->header($header); + throw new HttpResponseException($response); } /** - * 返回封装后的API数据到客户端 + * 返回封装后的 API 数据到客户端 * @access protected - * @param mixed $data 要返回的数据 - * @param integer $code 返回的code - * @param mixed $msg 提示信息 - * @param string $type 返回数据格式 - * @param array $header 发送的Header信息 + * @param mixed $data 要返回的数据 + * @param int $code 返回的 code + * @param mixed $msg 提示信息 + * @param string $type 返回数据格式 + * @param array $header 发送的 Header 信息 * @return void + * @throws HttpResponseException */ protected function result($data, $code = 0, $msg = '', $type = '', array $header = []) { $result = [ 'code' => $code, - 'msg' => $msg, + 'msg' => $msg, 'time' => Request::instance()->server('REQUEST_TIME'), 'data' => $data, ]; - $type = $type ?: $this->getResponseType(); + $type = $type ?: $this->getResponseType(); $response = Response::create($result, $type)->header($header); + throw new HttpResponseException($response); } /** - * URL重定向 + * URL 重定向 * @access protected - * @param string $url 跳转的URL表达式 - * @param array|integer $params 其它URL参数 - * @param integer $code http code - * @param array $with 隐式传参 + * @param string $url 跳转的 URL 表达式 + * @param array|int $params 其它 URL 参数 + * @param int $code http code + * @param array $with 隐式传参 * @return void + * @throws HttpResponseException */ protected function redirect($url, $params = [], $code = 302, $with = []) { - $response = new Redirect($url); if (is_integer($params)) { - $code = $params; + $code = $params; $params = []; } + + $response = new Redirect($url); $response->code($code)->params($params)->with($with); + throw new HttpResponseException($response); } /** - * 获取当前的response 输出类型 + * 获取当前的 response 输出类型 * @access protected * @return string */ protected function getResponseType() { - $isAjax = Request::instance()->isAjax(); - return $isAjax ? Config::get('default_ajax_return') : Config::get('default_return_type'); + return Request::instance()->isAjax() + ? Config::get('default_ajax_return') + : Config::get('default_return_type'); } } diff --git a/library/traits/model/SoftDelete.php b/library/traits/model/SoftDelete.php index 5501bd40f3..a83f2d9dd4 100644 --- a/library/traits/model/SoftDelete.php +++ b/library/traits/model/SoftDelete.php @@ -2,6 +2,7 @@ namespace traits\model; +use think\Collection; use think\db\Query; use think\Model; @@ -10,7 +11,6 @@ */ trait SoftDelete { - /** * 判断当前实例是否被软删除 * @access public @@ -19,22 +19,21 @@ trait SoftDelete public function trashed() { $field = $this->getDeleteTimeField(); - if (!empty($this->data[$field])) { + + if ($field && !empty($this->data[$field])) { return true; } return false; } /** - * 查询软删除数据 + * 查询包含软删除的数据 * @access public * @return Query */ public static function withTrashed() { - $model = new static(); - $field = $model->getDeleteTimeField(true); - return $model->getQuery(); + return (new static )->getQuery(); } /** @@ -46,14 +45,18 @@ public static function onlyTrashed() { $model = new static(); $field = $model->getDeleteTimeField(true); - return $model->getQuery() - ->useSoftDelete($field, ['not null', '']); + + if ($field) { + return $model->getQuery()->useSoftDelete($field, ['not null', '']); + } else { + return $model->getQuery(); + } } /** * 删除当前的记录 * @access public - * @param bool $force 是否强制删除 + * @param bool $force 是否强制删除 * @return integer */ public function delete($force = false) @@ -61,64 +64,71 @@ public function delete($force = false) if (false === $this->trigger('before_delete', $this)) { return false; } + $name = $this->getDeleteTimeField(); - if (!$force) { + if ($name && !$force) { // 软删除 $this->data[$name] = $this->autoWriteTimestamp($name); - $result = $this->isUpdate()->save(); + $result = $this->isUpdate()->save(); } else { - // 删除条件 - $where = $this->getWhere(); - // 删除当前模型数据 - $result = $this->getQuery()->where($where)->delete(); + // 强制删除当前模型数据 + $result = $this->getQuery()->where($this->getWhere())->delete(); } // 关联删除 if (!empty($this->relationWrite)) { foreach ($this->relationWrite as $key => $name) { - $name = is_numeric($key) ? $name : $key; - $model = $this->getAttr($name); - if ($model instanceof Model) { - $model->delete($force); + $name = is_numeric($key) ? $name : $key; + $result = $this->getRelation($name); + if ($result instanceof Model) { + $result->delete(); + } elseif ($result instanceof Collection || is_array($result)) { + foreach ($result as $model) { + $model->delete(); + } } } } $this->trigger('after_delete', $this); + // 清空原始数据 $this->origin = []; + return $result; } /** * 删除记录 * @access public - * @param mixed $data 主键列表 支持闭包查询条件 + * @param mixed $data 主键列表(支持闭包查询条件) * @param bool $force 是否强制删除 * @return integer 成功删除的记录数 */ public static function destroy($data, $force = false) { + if (is_null($data)) { + return 0; + } + // 包含软删除数据 - $query = self::withTrashed(); + $query = (new static())->db(false); if (is_array($data) && key($data) !== 0) { $query->where($data); $data = null; } elseif ($data instanceof \Closure) { call_user_func_array($data, [ & $query]); $data = null; - } elseif (is_null($data)) { - return 0; } - $resultSet = $query->select($data); - $count = 0; - if ($resultSet) { + $count = 0; + if ($resultSet = $query->select($data)) { foreach ($resultSet as $data) { $result = $data->delete($force); $count += $result; } } + return $count; } @@ -130,46 +140,61 @@ public static function destroy($data, $force = false) */ public function restore($where = []) { - $name = $this->getDeleteTimeField(); if (empty($where)) { - $pk = $this->getPk(); + $pk = $this->getPk(); $where[$pk] = $this->getData($pk); } - // 恢复删除 - return $this->getQuery() - ->useSoftDelete($name, ['not null', '']) - ->where($where) - ->update([$name => null]); + + $name = $this->getDeleteTimeField(); + + if ($name) { + // 恢复删除 + return $this->getQuery() + ->useSoftDelete($name, ['not null', '']) + ->where($where) + ->update([$name => null]); + } else { + return 0; + } } /** * 查询默认不包含软删除数据 * @access protected * @param Query $query 查询对象 - * @return void + * @return Query */ protected function base($query) { $field = $this->getDeleteTimeField(true); - $query->useSoftDelete($field); + return $field ? $query->useSoftDelete($field) : $query; } /** * 获取软删除字段 * @access public - * @param bool $read 是否查询操作 写操作的时候会自动去掉表别名 + * @param bool $read 是否查询操作(写操作的时候会自动去掉表别名) * @return string */ 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, '.')) { $field = '__TABLE__.' . $field; } + if (!$read && strpos($field, '.')) { $array = explode('.', $field); $field = array_pop($array); } + return $field; } } diff --git a/library/traits/think/Instance.php b/library/traits/think/Instance.php index ba45ddd839..428c8fde68 100644 --- a/library/traits/think/Instance.php +++ b/library/traits/think/Instance.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -15,31 +15,40 @@ trait Instance { + /** + * @var null|static 实例对象 + */ protected static $instance = null; /** - * @param array $options + * 获取示例 + * @param array $options 实例配置 * @return static */ public static function instance($options = []) { - if (is_null(self::$instance)) { - self::$instance = new self($options); - } + if (is_null(self::$instance)) self::$instance = new self($options); + 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)) { - self::$instance = new self(); - } + if (is_null(self::$instance)) self::$instance = new self(); + $call = substr($method, 1); - if (0 === strpos($method, '_') && is_callable([self::$instance, $call])) { - return call_user_func_array([self::$instance, $call], $params); - } else { + + if (0 !== strpos($method, '_') || !is_callable([self::$instance, $call])) { throw new Exception("method not exists:" . $method); } + + return call_user_func_array([self::$instance, $call], $params); } } diff --git a/start.php b/start.php index 8af62ef3a4..adb1bc65ca 100644 --- a/start.php +++ b/start.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | 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 ) // +---------------------------------------------------------------------- @@ -12,7 +12,8 @@ namespace think; // ThinkPHP 引导文件 -// 加载基础文件 +// 1. 加载基础文件 require __DIR__ . '/base.php'; -// 执行应用 + +// 2. 执行应用 App::run()->send(); diff --git a/tests/application/config.php b/tests/application/config.php index d94a8641dc..606a15a389 100644 --- a/tests/application/config.php +++ b/tests/application/config.php @@ -12,22 +12,22 @@ return [ 'url_route_on' => true, - 'log' => [ + 'log' => [ 'type' => 'file', // 支持 socket trace file ], - 'view' => [ + 'view' => [ // 模板引擎 - 'engine_type' => 'think', + 'engine_type' => 'think', // 模板引擎配置 'engine_config' => [ // 模板路径 - 'view_path' => '', + 'view_path' => '', // 模板后缀 'view_suffix' => '.html', // 模板文件名分隔符 - 'view_depr' => DS, + 'view_depr' => DS, ], // 输出字符串替换 - 'parse_str' => [], + 'parse_str' => [], ], ]; diff --git a/tests/application/database.php b/tests/application/database.php index 24434efb2c..4db67def59 100644 --- a/tests/application/database.php +++ b/tests/application/database.php @@ -12,33 +12,33 @@ return [ // 数据库类型 - 'type' => 'mysql', + 'type' => 'mysql', // 数据库连接DSN配置 - 'dsn' => '', + 'dsn' => '', // 服务器地址 - 'hostname' => '127.0.0.1', + 'hostname' => '127.0.0.1', // 数据库名 - 'database' => '', + 'database' => '', // 数据库用户名 - 'username' => 'root', + 'username' => 'root', // 数据库密码 - 'password' => '', + 'password' => '', // 数据库连接端口 - 'hostport' => '', + 'hostport' => '', // 数据库连接参数 - 'params' => [], + 'params' => [], // 数据库编码默认采用utf8 - 'charset' => 'utf8', + 'charset' => 'utf8', // 数据库表前缀 - 'prefix' => '', + 'prefix' => '', // 数据库调试模式 - 'debug' => true, + 'debug' => true, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, + 'deploy' => 0, // 数据库读写是否分离 主从式有效 'rw_separate' => false, // 读写分离后 主服务器数量 - 'master_num' => 1, + 'master_num' => 1, // 指定从服务器序号 - 'slave_no' => '', + 'slave_no' => '', ]; diff --git a/tests/application/route.php b/tests/application/route.php index 45bcf79d6a..0e17220f7f 100644 --- a/tests/application/route.php +++ b/tests/application/route.php @@ -14,8 +14,8 @@ '__pattern__' => [ 'name' => '\w+', ], - '[hello]' => [ - ':id' => ['index/hello', ['method' => 'get'], ['id' => '\d+']], + '[hello]' => [ + ':id' => ['index/hello', ['method' => 'get'], ['id' => '\d+']], ':name' => ['index/hello', ['method' => 'post']], ], diff --git a/tests/thinkphp/library/think/display.html b/tests/application/views/display.html similarity index 100% rename from tests/thinkphp/library/think/display.html rename to tests/application/views/display.html diff --git a/tests/application/views/display.phtml b/tests/application/views/display.phtml new file mode 100644 index 0000000000..e99d3025e5 --- /dev/null +++ b/tests/application/views/display.phtml @@ -0,0 +1 @@ +{$name??'default'} \ No newline at end of file diff --git a/tests/thinkphp/library/think/extend.html b/tests/application/views/extend.html similarity index 100% rename from tests/thinkphp/library/think/extend.html rename to tests/application/views/extend.html diff --git a/tests/thinkphp/library/think/extend2.html b/tests/application/views/extend2.html similarity index 100% rename from tests/thinkphp/library/think/extend2.html rename to tests/application/views/extend2.html diff --git a/tests/thinkphp/library/think/include.html b/tests/application/views/include.html similarity index 100% rename from tests/thinkphp/library/think/include.html rename to tests/application/views/include.html diff --git a/tests/thinkphp/library/think/include2.html b/tests/application/views/include2.html similarity index 100% rename from tests/thinkphp/library/think/include2.html rename to tests/application/views/include2.html diff --git a/tests/thinkphp/library/think/layout.html b/tests/application/views/layout.html similarity index 100% rename from tests/thinkphp/library/think/layout.html rename to tests/application/views/layout.html diff --git a/tests/thinkphp/library/think/layout2.html b/tests/application/views/layout2.html similarity index 100% rename from tests/thinkphp/library/think/layout2.html rename to tests/application/views/layout2.html diff --git a/tests/conf/apcu.ini b/tests/conf/apcu.ini deleted file mode 100644 index 9c2abed2c9..0000000000 --- a/tests/conf/apcu.ini +++ /dev/null @@ -1,3 +0,0 @@ -extension=apcu.so - -apc.enable_cli=1 diff --git a/tests/conf/apcu_bc.ini b/tests/conf/apcu_bc.ini deleted file mode 100644 index a7f6182da1..0000000000 --- a/tests/conf/apcu_bc.ini +++ /dev/null @@ -1,4 +0,0 @@ -extension=apcu.so -extension=apc.so - -apc.enable_cli=1 diff --git a/tests/extensions/5.4/apcu.so b/tests/extensions/5.4/apcu.so deleted file mode 100755 index 42a24a660e..0000000000 Binary files a/tests/extensions/5.4/apcu.so and /dev/null differ diff --git a/tests/extensions/5.5/apcu.so b/tests/extensions/5.5/apcu.so deleted file mode 100755 index 26e5228a6b..0000000000 Binary files a/tests/extensions/5.5/apcu.so and /dev/null differ diff --git a/tests/extensions/5.6/apcu.so b/tests/extensions/5.6/apcu.so deleted file mode 100755 index fe952969ee..0000000000 Binary files a/tests/extensions/5.6/apcu.so and /dev/null differ diff --git a/tests/extensions/7.0/apc.so b/tests/extensions/7.0/apc.so deleted file mode 100755 index 7948350dab..0000000000 Binary files a/tests/extensions/7.0/apc.so and /dev/null differ diff --git a/tests/extensions/7.0/apcu.so b/tests/extensions/7.0/apcu.so deleted file mode 100755 index c2f0ad1402..0000000000 Binary files a/tests/extensions/7.0/apcu.so and /dev/null differ diff --git a/tests/script/install.sh b/tests/script/install.sh index 1e1a981d3f..8344169c46 100755 --- a/tests/script/install.sh +++ b/tests/script/install.sh @@ -3,12 +3,6 @@ if [ $(phpenv version-name) != "hhvm" ]; then cp tests/extensions/$(phpenv version-name)/*.so $(php-config --extension-dir) - if [ $(phpenv version-name) = "7.0" ]; then - phpenv config-add tests/conf/apcu_bc.ini - else - phpenv config-add tests/conf/apcu.ini - fi - phpenv config-add tests/conf/memcached.ini phpenv config-add tests/conf/redis.ini diff --git a/tests/thinkphp/library/think/buildTest.php b/tests/thinkphp/library/think/buildTest.php index 137749ef84..3f884c3057 100644 --- a/tests/thinkphp/library/think/buildTest.php +++ b/tests/thinkphp/library/think/buildTest.php @@ -24,17 +24,17 @@ public function testRun() { $build = [ // Test run directory - '__dir__' => ['runtime/cache', 'runtime/log', 'runtime/temp', 'runtime/template'], + '__dir__' => ['runtime/cache', 'runtime/log', 'runtime/temp', 'runtime/template'], '__file__' => ['common.php'], // Test generation module - 'demo' => [ - '__file__' => ['common.php'], - '__dir__' => ['behavior', 'controller', 'model', 'view', 'service'], + 'demo' => [ + '__file__' => ['common.php'], + '__dir__' => ['behavior', 'controller', 'model', 'view', 'service'], 'controller' => ['Index', 'Test', 'UserType'], - 'model' => ['User', 'UserType'], - 'service' => ['User', 'UserType'], - 'view' => ['index/index'], + 'model' => ['User', 'UserType'], + 'service' => ['User', 'UserType'], + 'view' => ['index/index'], ], ]; Build::run($build); diff --git a/tests/thinkphp/library/think/cache/driver/redisTest.php b/tests/thinkphp/library/think/cache/driver/redisTest.php index 839e5165a4..2000ee4363 100644 --- a/tests/thinkphp/library/think/cache/driver/redisTest.php +++ b/tests/thinkphp/library/think/cache/driver/redisTest.php @@ -41,7 +41,7 @@ public function testGet() $cache = $this->prepare(); $this->assertEquals('string_test', $cache->get('string_test')); $this->assertEquals(11, $cache->get('number_test')); - $result = $cache->get('array_test'); + $result = $cache->get('array_test'); $this->assertEquals('array_test', $result['array_test']); } @@ -54,7 +54,7 @@ public function testStoreSpecialValues() $redis->handler()->setnx('key', 'value'); $value = $redis->handler()->get('key'); $this->assertEquals('value', $value); - + $redis->handler()->hset('hash', 'key', 'value'); $value = $redis->handler()->hget('hash', 'key'); $this->assertEquals('value', $value); diff --git a/tests/thinkphp/library/think/cacheTest.php b/tests/thinkphp/library/think/cacheTest.php index dbcd705372..4edd91ef6f 100644 --- a/tests/thinkphp/library/think/cacheTest.php +++ b/tests/thinkphp/library/think/cacheTest.php @@ -26,9 +26,9 @@ public function tearDown() $this->ConfigTearDown(); call_user_func(\Closure::bind(function () { - Cache::$handler = null; - Cache::$instance = []; - Cache::$readTimes = 0; + Cache::$handler = null; + Cache::$instance = []; + Cache::$readTimes = 0; Cache::$writeTimes = 0; }, null, '\think\Cache')); } @@ -249,24 +249,24 @@ public function provideTestConnect() { $provideData = []; - $options = ['type' => null]; - $expected = '\think\cache\driver\File'; + $options = ['type' => null]; + $expected = '\think\cache\driver\File'; $provideData[] = [$options, $expected]; - $options = ['type' => 'File']; - $expected = '\think\cache\driver\File'; + $options = ['type' => 'File']; + $expected = '\think\cache\driver\File'; $provideData[] = [$options, $expected]; - $options = ['type' => 'Lite']; - $expected = '\think\cache\driver\Lite'; + $options = ['type' => 'Lite']; + $expected = '\think\cache\driver\Lite'; $provideData[] = [$options, $expected]; - $options = ['type' => 'Memcached']; - $expected = '\think\cache\driver\Memcached'; + $options = ['type' => 'Memcached']; + $expected = '\think\cache\driver\Memcached'; $provideData[] = [$options, $expected]; - $options = ['type' => 'Redis']; - $expected = '\think\cache\driver\Redis'; + $options = ['type' => 'Redis']; + $expected = '\think\cache\driver\Redis'; $provideData[] = [$options, $expected]; // TODO @@ -293,16 +293,16 @@ public function provideTestInit() { $provideData = []; - $options = []; - $expected = '\think\cache\driver\File'; + $options = []; + $expected = '\think\cache\driver\File'; $provideData[] = [$options, $expected]; - $options = ['type' => 'File']; - $expected = '\think\cache\driver\File'; + $options = ['type' => 'File']; + $expected = '\think\cache\driver\File'; $provideData[] = [$options, $expected]; - $options = ['type' => 'Lite']; - $expected = '\think\cache\driver\Lite'; + $options = ['type' => 'Lite']; + $expected = '\think\cache\driver\Lite'; $provideData[] = [$options, $expected]; return $provideData; diff --git a/tests/thinkphp/library/think/config/ConfigInitTrait.php b/tests/thinkphp/library/think/config/ConfigInitTrait.php index ce2b3977fe..5f8a8c8964 100644 --- a/tests/thinkphp/library/think/config/ConfigInitTrait.php +++ b/tests/thinkphp/library/think/config/ConfigInitTrait.php @@ -36,12 +36,12 @@ public static function setUpBeforeClass() return !is_null($value) ? Config::$config = $value : Config::$config; }, null, '\\Think\\Config'); - self::$internalRangeFoo = \Closure::bind(function ($value = null) { + self::$internalRangeFoo = \Closure::bind(function ($value = null) { return !is_null($value) ? Config::$range = $value : Config::$range; }, null, '\\Think\\Config'); self::$originConfig = call_user_func(self::$internalConfigFoo); - self::$originRange = call_user_func(self::$internalRangeFoo); + self::$originRange = call_user_func(self::$internalRangeFoo); } public function tearDown() diff --git a/tests/thinkphp/library/think/configTest.php b/tests/thinkphp/library/think/configTest.php index 4c92df5d90..a8f0137274 100644 --- a/tests/thinkphp/library/think/configTest.php +++ b/tests/thinkphp/library/think/configTest.php @@ -42,10 +42,10 @@ public function testRange() public function testLoad() { - $file = APP_PATH . 'config' . EXT; + $file = APP_PATH . 'config' . EXT; $config = array_change_key_case(include $file); - $name = '_name_'; - $range = '_test_'; + $name = '_name_'; + $range = '_test_'; $this->assertEquals($config, Config::load($file, $name, $range)); $this->assertNotEquals(null, Config::load($file, $name, $range)); @@ -100,13 +100,13 @@ public function testSet() $range = '_test_'; // without dot syntax - $name = 'name'; + $name = 'name'; $value = 'value'; Config::set($name, $value, $range); $config = call_user_func(self::$internalConfigFoo); $this->assertEquals($value, $config[$range][$name]); // with dot syntax - $name = 'one.two'; + $name = 'one.two'; $value = 'dot value'; Config::set($name, $value, $range); $config = call_user_func(self::$internalConfigFoo); @@ -137,13 +137,13 @@ public function testReset() 'abcd' => 'efg', 'hijk' => 'lmn', ], - 'a' => 'b', + 'a' => 'b', ]); Config::reset($range); $config = call_user_func(self::$internalConfigFoo); $this->assertEquals([ $range => [], - 'a' => 'b', + 'a' => 'b', ], $config); } } diff --git a/tests/thinkphp/library/think/controllerTest.php b/tests/thinkphp/library/think/controllerTest.php index fba8d6c5c5..3680292d4f 100644 --- a/tests/thinkphp/library/think/controllerTest.php +++ b/tests/thinkphp/library/think/controllerTest.php @@ -40,26 +40,26 @@ public function assignTest() public function fetchTest() { - $template = dirname(__FILE__) . '/display.html'; + $template = APP_PATH . 'views' . DS .'display.html'; return $this->fetch($template, ['name' => 'ThinkPHP']); } public function displayTest() { - $template = dirname(__FILE__) . '/display.html'; + $template = APP_PATH . 'views' . DS .'display.html'; return $this->display($template, ['name' => 'ThinkPHP']); } public function test() { - $data = [ - 'username' => 'username', - 'nickname' => 'nickname', - 'password' => '123456', + $data = [ + 'username' => 'username', + 'nickname' => 'nickname', + 'password' => '123456', 'repassword' => '123456', - 'email' => 'abc@abc.com', - 'sex' => '0', - 'age' => '20', - 'code' => '1234', + 'email' => 'abc@abc.com', + 'sex' => '0', + 'age' => '20', + 'code' => '1234', ]; $validate = [ @@ -149,8 +149,8 @@ public function testBeforeAction() private function getView($controller) { - $view = new View(); - $rc = new ReflectionClass(get_class($controller)); + $view = new View(); + $rc = new ReflectionClass(get_class($controller)); $property = $rc->getProperty('view'); $property->setAccessible(true); $property->setValue($controller, $view); @@ -159,19 +159,19 @@ private function getView($controller) public function testFetch() { - $controller = new Foo(Request::instance()); - $view = $this->getView($controller); - $template = dirname(__FILE__) . '/display.html'; - $viewFetch = $view->fetch($template, ['name' => 'ThinkPHP']); + $controller = new Foo(Request::instance()); + $view = $this->getView($controller); + $template = APP_PATH . 'views' . DS .'display.html'; + $viewFetch = $view->fetch($template, ['name' => 'ThinkPHP']); $this->assertEquals($controller->fetchTest(), $viewFetch); } public function testDisplay() { - $controller = new Foo; - $view = $this->getView($controller); - $template = dirname(__FILE__) . '/display.html'; - $viewFetch = $view->display($template, ['name' => 'ThinkPHP']); + $controller = new Foo; + $view = $this->getView($controller); + $template = APP_PATH . 'views' . DS .'display.html'; + $viewFetch = $view->display($template, ['name' => 'ThinkPHP']); $this->assertEquals($controller->displayTest(), $viewFetch); } @@ -179,7 +179,7 @@ public function testDisplay() public function testAssign() { $controller = new Foo(Request::instance()); - $view = $this->getView($controller); + $view = $this->getView($controller); $controller->assignTest(); $expect = ['abcd' => 'dcba', 'key1' => 'value1', 'key2' => 'value2']; $this->assertAttributeEquals($expect, 'data', $view); diff --git a/tests/thinkphp/library/think/cookieTest.php b/tests/thinkphp/library/think/cookieTest.php index 24cb153e4b..c2d97729ed 100644 --- a/tests/thinkphp/library/think/cookieTest.php +++ b/tests/thinkphp/library/think/cookieTest.php @@ -25,24 +25,24 @@ class cookieTest extends \PHPUnit_Framework_TestCase protected $default = [ // cookie 名称前缀 - 'prefix' => '', + 'prefix' => '', // cookie 保存时间 - 'expire' => 0, + 'expire' => 0, // cookie 保存路径 - 'path' => '/', + 'path' => '/', // cookie 有效域名 - 'domain' => '', + 'domain' => '', // cookie 启用安全传输 - 'secure' => false, + 'secure' => false, // httponly设置 - 'httponly' => '', + 'httponly' => '', // 是否使用 setcookie 'setcookie' => false, ]; protected function setUp() { - $reflectedClass = new ReflectionClass('\think\Cookie'); + $reflectedClass = new ReflectionClass('\think\Cookie'); $reflectedPropertyConfig = $reflectedClass->getProperty('config'); $reflectedPropertyConfig->setAccessible(true); $reflectedPropertyConfig->setValue($this->default); @@ -53,15 +53,15 @@ public function testInit() { $config = [ // cookie 名称前缀 - 'prefix' => 'think_', + 'prefix' => 'think_', // cookie 保存时间 - 'expire' => 0, + 'expire' => 0, // cookie 保存路径 - 'path' => '/path/to/test/', + 'path' => '/path/to/test/', // cookie 有效域名 - 'domain' => '.thinkphp.cn', + 'domain' => '.thinkphp.cn', // cookie 启用安全传输 - 'secure' => true, + 'secure' => true, // httponly设置 'httponly' => '1', ]; @@ -101,7 +101,7 @@ public function testSet() Cookie::set($name, $value, 'expire=100&prefix=pre_'); $this->assertEquals($value, $_COOKIE['pre_' . $name]); - $name = 'name4'; + $name = 'name4'; $value = ['_test_中文_']; Cookie::set($name, $value); $this->assertEquals('think:' . json_encode([urlencode('_test_中文_')]), $_COOKIE[$name]); @@ -110,9 +110,9 @@ public function testSet() public function testGet() { $_COOKIE = [ - 'a' => 'b', + 'a' => 'b', 'pre_abc' => 'c', - 'd' => 'think:' . json_encode([urlencode('_test_中文_')]), + 'd' => 'think:' . json_encode([urlencode('_test_中文_')]), ]; $this->assertEquals('b', Cookie::get('a')); $this->assertEquals(null, Cookie::get('does_not_exist')); @@ -123,7 +123,7 @@ public function testGet() public function testDelete() { $_COOKIE = [ - 'a' => 'b', + 'a' => 'b', 'pre_abc' => 'c', ]; $this->assertEquals('b', Cookie::get('a')); @@ -141,7 +141,7 @@ public function testClear() $this->assertEquals(null, Cookie::clear()); $_COOKIE = [ - 'a' => 'b', + 'a' => 'b', 'pre_abc' => 'c', ]; Cookie::clear('pre_'); diff --git a/tests/thinkphp/library/think/dbTest.php b/tests/thinkphp/library/think/dbTest.php index 5724ee2149..f81a11a94b 100644 --- a/tests/thinkphp/library/think/dbTest.php +++ b/tests/thinkphp/library/think/dbTest.php @@ -25,43 +25,43 @@ private function getConfig() { return [ // 数据库类型 - 'type' => 'mysql', + 'type' => 'mysql', // 服务器地址 - 'hostname' => '127.0.0.1', + 'hostname' => '127.0.0.1', // 数据库名 - 'database' => 'test', + 'database' => 'test', // 用户名 - 'username' => 'root', + 'username' => 'root', // 密码 - 'password' => '', + 'password' => '', // 端口 - 'hostport' => '', + 'hostport' => '', // 连接dsn - 'dsn' => '', + 'dsn' => '', // 数据库连接参数 - 'params' => [], + 'params' => [], // 数据库编码默认采用utf8 - 'charset' => 'utf8', + 'charset' => 'utf8', // 数据库表前缀 - 'prefix' => 'tp_', + 'prefix' => 'tp_', // 数据库调试模式 - 'debug' => true, + 'debug' => true, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, + 'deploy' => 0, // 数据库读写是否分离 主从式有效 - 'rw_separate' => false, + 'rw_separate' => false, // 读写分离后 主服务器数量 - 'master_num' => 1, + 'master_num' => 1, // 指定从服务器序号 - 'slave_no' => '', + 'slave_no' => '', // 是否严格检查字段是否存在 - 'fields_strict' => true, + 'fields_strict' => true, // 数据集返回类型 array 数组 collection Collection对象 'resultset_type' => 'array', // 是否自动写入时间戳字段 'auto_timestamp' => false, // 是否需要进行SQL性能分析 - 'sql_explain' => false, + 'sql_explain' => false, ]; } @@ -140,7 +140,7 @@ public function testConnect() public function testExecute() { $config = $this->getConfig(); - $sql = $this->getCreateTableSql(); + $sql = $this->getCreateTableSql(); foreach ($sql as $one) { Db::connect($config)->execute($one); } @@ -151,7 +151,7 @@ public function testExecute() public function testQuery() { $config = $this->getConfig(); - $sql = $this->getCreateTableSql(); + $sql = $this->getCreateTableSql(); Db::connect($config)->batchQuery($sql); $tableQueryResult = Db::connect($config)->query("show tables;"); @@ -165,7 +165,7 @@ public function testQuery() public function testBatchQuery() { $config = $this->getConfig(); - $sql = $this->getCreateTableSql(); + $sql = $this->getCreateTableSql(); Db::connect($config)->batchQuery($sql); $tableNum = Db::connect($config)->execute("show tables;"); @@ -174,27 +174,27 @@ public function testBatchQuery() public function testTable() { - $config = $this->getConfig(); + $config = $this->getConfig(); $tableName = 'tp_user'; - $result = Db::connect($config)->table($tableName); + $result = Db::connect($config)->table($tableName); $this->assertEquals($tableName, $result->getOptions()['table']); } public function testName() { - $config = $this->getConfig(); + $config = $this->getConfig(); $tableName = 'user'; - $result = Db::connect($config)->name($tableName); + $result = Db::connect($config)->name($tableName); $this->assertEquals($config['prefix'] . $tableName, $result->getTable()); } public function testInsert() { $config = $this->getConfig(); - $data = [ - 'username' => 'chunice', - 'password' => md5('chunice'), - 'status' => 1, + $data = [ + 'username' => 'chunice', + 'password' => md5('chunice'), + 'status' => 1, 'create_time' => time(), ]; $result = Db::connect($config)->name('user')->insert($data); @@ -204,10 +204,10 @@ public function testInsert() public function testUpdate() { $config = $this->getConfig(); - $data = [ - 'username' => 'chunice_update', - 'password' => md5('chunice'), - 'status' => 1, + $data = [ + 'username' => 'chunice_update', + 'password' => md5('chunice'), + 'status' => 1, 'create_time' => time(), ]; $result = Db::connect($config)->name('user')->where('username', 'chunice')->update($data); @@ -216,7 +216,7 @@ public function testUpdate() public function testFind() { - $config = $this->getConfig(); + $config = $this->getConfig(); $mustFind = Db::connect($config)->name('user')->where('username', 'chunice_update')->find(); $this->assertNotEmpty($mustFind); $mustNotFind = Db::connect($config)->name('user')->where('username', 'chunice')->find(); @@ -238,7 +238,7 @@ public function testInsertAll() public function testSelect() { - $config = $this->getConfig(); + $config = $this->getConfig(); $mustFound = Db::connect($config)->name('user')->where('status', 1)->select(); $this->assertNotEmpty($mustFound); $mustNotFound = Db::connect($config)->name('user')->where('status', 0)->select(); @@ -247,7 +247,7 @@ public function testSelect() public function testValue() { - $config = $this->getConfig(); + $config = $this->getConfig(); $username = Db::connect($config)->name('user')->where('id', 1)->value('username'); $this->assertEquals('chunice_update', $username); $usernameNull = Db::connect($config)->name('user')->where('id', 0)->value('username'); @@ -256,7 +256,7 @@ public function testValue() public function testColumn() { - $config = $this->getConfig(); + $config = $this->getConfig(); $username = Db::connect($config)->name('user')->where('status', 1)->column('username'); $this->assertNotEmpty($username); $usernameNull = Db::connect($config)->name('user')->where('status', 0)->column('username'); @@ -267,12 +267,12 @@ public function testColumn() public function testInsertGetId() { $config = $this->getConfig(); - $id = Db::connect($config)->name('user')->order('id', 'desc')->value('id'); + $id = Db::connect($config)->name('user')->order('id', 'desc')->value('id'); $data = [ - 'username' => uniqid(), - 'password' => md5('chunice'), - 'status' => 1, + 'username' => uniqid(), + 'password' => md5('chunice'), + 'status' => 1, 'create_time' => time(), ]; $lastId = Db::connect($config)->name('user')->insertGetId($data); @@ -283,10 +283,10 @@ public function testInsertGetId() public function testGetLastInsId() { $config = $this->getConfig(); - $data = [ - 'username' => uniqid(), - 'password' => md5('chunice'), - 'status' => 1, + $data = [ + 'username' => uniqid(), + 'password' => md5('chunice'), + 'status' => 1, 'create_time' => time(), ]; $lastId = Db::connect($config)->name('user')->insertGetId($data); @@ -308,7 +308,7 @@ public function testSetField() public function testSetInc() { - $config = $this->getConfig(); + $config = $this->getConfig(); $originCreateTime = Db::connect($config)->name('user')->where('id', 1)->value('create_time'); Db::connect($config)->name('user')->where('id', 1)->setInc('create_time'); $newCreateTime = Db::connect($config)->name('user')->where('id', 1)->value('create_time'); @@ -318,7 +318,7 @@ public function testSetInc() public function testSetDec() { - $config = $this->getConfig(); + $config = $this->getConfig(); $originCreateTime = Db::connect($config)->name('user')->where('id', 1)->value('create_time'); Db::connect($config)->name('user')->where('id', 1)->setDec('create_time'); $newCreateTime = Db::connect($config)->name('user')->where('id', 1)->value('create_time'); @@ -342,7 +342,7 @@ public function testCache() { $config = $this->getConfig(); $result = Db::connect($config)->name('user')->where('id', 1)->cache('key', 60)->find(); - $cache = \think\Cache::get('key'); + $cache = \think\Cache::get('key'); $this->assertEquals($result, $cache); $updateCache = Db::connect($config)->name('user')->cache('key')->find(1); diff --git a/tests/thinkphp/library/think/debugTest.php b/tests/thinkphp/library/think/debugTest.php index 494694a33f..838310a272 100644 --- a/tests/thinkphp/library/think/debugTest.php +++ b/tests/thinkphp/library/think/debugTest.php @@ -65,7 +65,7 @@ public function testRemark() public function testGetRangeTime() { $start = "testGetRangeTimeStart"; - $end = "testGetRangeTimeEnd"; + $end = "testGetRangeTimeEnd"; Debug::remark($start); usleep(20000); // \think\Debug::remark($end); @@ -82,7 +82,7 @@ public function testGetRangeTime() public function testGetUseTime() { $time = Debug::getUseTime(); - $this->assertLessThan(20, $time); + $this->assertLessThan(30, $time); } /** @@ -103,7 +103,7 @@ public function testGetThroughputRate() public function testGetRangeMem() { $start = "testGetRangeMemStart"; - $end = "testGetRangeMemEnd"; + $end = "testGetRangeMemEnd"; Debug::remark($start); $str = ""; for ($i = 0; $i < 10000; $i++) { @@ -133,7 +133,7 @@ public function testGetUseMem() public function testGetMemPeak() { $start = "testGetMemPeakStart"; - $end = "testGetMemPeakEnd"; + $end = "testGetMemPeakEnd"; Debug::remark($start); $str = ""; for ($i = 0; $i < 100000; $i++) { @@ -169,10 +169,10 @@ public function testDump() return; } - $var = []; + $var = []; $var["key"] = "val"; - $output = Debug::dump($var, false, $label = "label"); - $array = explode("array", json_encode($output)); + $output = Debug::dump($var, false, $label = "label"); + $array = explode("array", json_encode($output)); if (IS_WIN) { $this->assertEquals("(1) {\\n [\\\"key\\\"] => string(3) \\\"val\\\"\\n}\\n\\r\\n\"", end($array)); } elseif (strstr(PHP_OS, 'Darwin')) { @@ -190,7 +190,7 @@ public function testInjectWithErrorType() Config::set('trace', ['type' => 'NullDebug']); $response = new Response(); - $context = 'TestWithErrorType'; + $context = 'TestWithErrorType'; Debug::inject($response, $context); } @@ -200,20 +200,20 @@ public function testInject() Config::set('trace', ['type' => 'Console']); $response = new Response(); - $context = 'TestWithoutBodyTag'; + $context = 'TestWithoutBodyTag'; Debug::inject($response, $context); $this->assertNotEquals('TestWithoutBodyTag', $context); $this->assertStringStartsWith('TestWithoutBodyTag', $context); $response = new Response(); - $context = ''; + $context = ''; Debug::inject($response, $context); $this->assertNotEquals('', $context); $this->assertStringStartsWith('', $context); $this->assertStringEndsWith('', $context); $response = new Redirect(); - $context = ''; + $context = ''; Debug::inject($response, $context); $this->assertEquals('', $context); } diff --git a/tests/thinkphp/library/think/exceptionTest.php b/tests/thinkphp/library/think/exceptionTest.php index 66957ce67d..43887635a1 100644 --- a/tests/thinkphp/library/think/exceptionTest.php +++ b/tests/thinkphp/library/think/exceptionTest.php @@ -40,7 +40,7 @@ public function testDebugData() { $data = ['a' => 'b', 'c' => 'd']; try { - $e = new MyException("Error Processing Request", 1); + $e = new MyException("Error Processing Request", 1); $method = new ReflectionMethod($e, 'setData'); $method->setAccessible(true); $method->invokeArgs($e, ['test', $data]); diff --git a/tests/thinkphp/library/think/hookTest.php b/tests/thinkphp/library/think/hookTest.php index 930ecf533a..6f15e1a360 100644 --- a/tests/thinkphp/library/think/hookTest.php +++ b/tests/thinkphp/library/think/hookTest.php @@ -26,16 +26,18 @@ public function testRun() Hook::add('my_pos', '\tests\thinkphp\library\think\behavior\One'); Hook::add('my_pos', ['\tests\thinkphp\library\think\behavior\Two']); Hook::add('my_pos', '\tests\thinkphp\library\think\behavior\Three', true); - $data['id'] = 0; + $data['id'] = 0; $data['name'] = 'thinkphp'; Hook::listen('my_pos', $data); $this->assertEquals(2, $data['id']); $this->assertEquals('thinkphp', $data['name']); - $this->assertEquals([ + $this->assertEquals( + [ '\tests\thinkphp\library\think\behavior\Three', '\tests\thinkphp\library\think\behavior\One', '\tests\thinkphp\library\think\behavior\Two'], - Hook::get('my_pos')); + Hook::get('my_pos') + ); } public function testImport() @@ -46,7 +48,7 @@ public function testImport() ]); Hook::import(['my_pos' => ['\tests\thinkphp\library\think\behavior\Two']], false); Hook::import(['my_pos' => ['\tests\thinkphp\library\think\behavior\Three', '_overlay' => true]]); - $data['id'] = 0; + $data['id'] = 0; $data['name'] = 'thinkphp'; Hook::listen('my_pos', $data); $this->assertEquals(3, $data['id']); @@ -55,7 +57,7 @@ public function testImport() public function testExec() { - $data['id'] = 0; + $data['id'] = 0; $data['name'] = 'thinkphp'; $this->assertEquals(true, Hook::exec('\tests\thinkphp\library\think\behavior\One')); $this->assertEquals(false, Hook::exec('\tests\thinkphp\library\think\behavior\One', 'test', $data)); diff --git a/tests/thinkphp/library/think/lang/lang.php b/tests/thinkphp/library/think/lang/lang.php index 96880b1563..13417707c1 100644 --- a/tests/thinkphp/library/think/lang/lang.php +++ b/tests/thinkphp/library/think/lang/lang.php @@ -1,4 +1,4 @@ '加载', + 'load' => '加载', ]; diff --git a/tests/thinkphp/library/think/paginateTest.php b/tests/thinkphp/library/think/paginateTest.php index 8cd4550738..112239ed99 100644 --- a/tests/thinkphp/library/think/paginateTest.php +++ b/tests/thinkphp/library/think/paginateTest.php @@ -31,7 +31,7 @@ public function testPaginatorInfo() public function testPaginatorRender() { - $p = Bootstrap::make($array = ['item3', 'item4'], 2, 2, 100); + $p = Bootstrap::make($array = ['item3', 'item4'], 2, 2, 100); $render = ''; $this->assertEquals($render, $p->render()); diff --git a/tests/thinkphp/library/think/requestTest.php b/tests/thinkphp/library/think/requestTest.php index 3da94a2e9b..d2b207a856 100644 --- a/tests/thinkphp/library/think/requestTest.php +++ b/tests/thinkphp/library/think/requestTest.php @@ -110,7 +110,7 @@ public function testmethod() Config::set('var_method', '_method'); $_POST['_method'] = 'POST'; - $request = Request::create('', ''); + $request = Request::create('', ''); $this->assertEquals('POST', $request->method()); $this->assertEquals('GET', $request->method(true)); $this->assertTrue($request->isPost()); @@ -187,14 +187,14 @@ public function testIsPjax() public function testIsMobile() { - $request = Request::create(''); + $request = Request::create(''); $_SERVER['HTTP_VIA'] = 'wap'; $this->assertTrue($request->isMobile()); } public function testBind() { - $request = Request::create(''); + $request = Request::create(''); $request->user = 'User1'; $request->bind(['user' => 'User2']); $this->assertEquals('User2', $request->user); diff --git a/tests/thinkphp/library/think/responseTest.php b/tests/thinkphp/library/think/responseTest.php index 62d1574734..e339bb1d3f 100644 --- a/tests/thinkphp/library/think/responseTest.php +++ b/tests/thinkphp/library/think/responseTest.php @@ -79,16 +79,16 @@ protected function tearDown() */ public function testSend() { - $dataArr = []; + $dataArr = []; $dataArr["key"] = "value"; $response = Response::create($dataArr, 'json'); - $result = $response->getContent(); + $result = $response->getContent(); $this->assertEquals('{"key":"value"}', $result); $request = Request::instance(); $request->get(['callback' => 'callback']); $response = Response::create($dataArr, 'jsonp'); - $result = $response->getContent(); + $result = $response->getContent(); $this->assertEquals('callback({"key":"value"});', $result); } diff --git a/tests/thinkphp/library/think/routeTest.php b/tests/thinkphp/library/think/routeTest.php index 31e1b8611f..eb856527bf 100644 --- a/tests/thinkphp/library/think/routeTest.php +++ b/tests/thinkphp/library/think/routeTest.php @@ -51,10 +51,10 @@ public function testImport() { $rule = [ '__domain__' => ['subdomain2.thinkphp.cn' => 'blog1'], - '__alias__' => ['blog1' => 'blog1'], - '__rest__' => ['res' => ['index/blog']], - 'bbb' => ['index/blog1', ['method' => 'POST']], - 'ddd' => '', + '__alias__' => ['blog1' => 'blog1'], + '__rest__' => ['res' => ['index/blog']], + 'bbb' => ['index/blog1', ['method' => 'POST']], + 'ddd' => '', ['hello1/:ddd', 'index/hello1', ['method' => 'POST']], ]; Route::import($rule); diff --git a/tests/thinkphp/library/think/sessionTest.php b/tests/thinkphp/library/think/sessionTest.php index 5fd95f102c..c9efeae186 100644 --- a/tests/thinkphp/library/think/sessionTest.php +++ b/tests/thinkphp/library/think/sessionTest.php @@ -74,23 +74,23 @@ public function testInit() Session::prefix(null); $config = [ // cookie 名称前缀 - 'prefix' => 'think_', + 'prefix' => 'think_', // cookie 保存时间 - 'expire' => 60, + 'expire' => 60, // cookie 保存路径 - 'path' => '/path/to/test/session/', + 'path' => '/path/to/test/session/', // cookie 有效域名 - 'domain' => '.thinkphp.cn', + 'domain' => '.thinkphp.cn', 'var_session_id' => 'sessionidtest', - 'id' => 'sess_8fhgkjuakhatbeg2fa14lo84q1', - 'name' => 'session_name', - 'use_trans_sid' => '1', - 'use_cookies' => '1', - 'cache_limiter' => '60', - 'cache_expire' => '60', - 'type' => '', // memcache - 'namespace' => '\\think\\session\\driver\\', // ? - 'auto_start' => '1', + 'id' => 'sess_8fhgkjuakhatbeg2fa14lo84q1', + 'name' => 'session_name', + 'use_trans_sid' => '1', + 'use_cookies' => '1', + 'cache_limiter' => '60', + 'cache_expire' => '60', + 'type' => '', // memcache + 'namespace' => '\\think\\session\\driver\\', // ? + 'auto_start' => '1', ]; $_REQUEST[$config['var_session_id']] = $config['id']; @@ -142,22 +142,22 @@ public function testException() { $config = [ // cookie 名称前缀 - 'prefix' => 'think_', + 'prefix' => 'think_', // cookie 保存时间 - 'expire' => 0, + 'expire' => 0, // cookie 保存路径 - 'path' => '/path/to/test/session/', + 'path' => '/path/to/test/session/', // cookie 有效域名 - 'domain' => '.thinkphp.cn', + 'domain' => '.thinkphp.cn', 'var_session_id' => 'sessionidtest', - 'id' => 'sess_8fhgkjuakhatbeg2fa14lo84q1', - 'name' => 'session_name', - 'use_trans_sid' => '1', - 'use_cookies' => '1', - 'cache_limiter' => '60', - 'cache_expire' => '60', - 'type' => '\\think\\session\\driver\\Memcache', // - 'auto_start' => '1', + 'id' => 'sess_8fhgkjuakhatbeg2fa14lo84q1', + 'name' => 'session_name', + 'use_trans_sid' => '1', + 'use_cookies' => '1', + 'cache_limiter' => '60', + 'cache_expire' => '60', + 'type' => '\\think\\session\\driver\\Memcache', // + 'auto_start' => '1', ]; // 测试session驱动是否存在 diff --git a/tests/thinkphp/library/think/template/taglib/cxTest.php b/tests/thinkphp/library/think/template/taglib/cxTest.php index 8aee392fa2..9a3bea7106 100644 --- a/tests/thinkphp/library/think/template/taglib/cxTest.php +++ b/tests/thinkphp/library/think/template/taglib/cxTest.php @@ -24,7 +24,7 @@ class cxTest extends \PHPUnit_Framework_TestCase public function testPhp() { $template = new template(); - $cx = new Cx($template); + $cx = new Cx($template); $content = <<\$val} @@ -107,7 +107,7 @@ public function testForeach() public function testIf() { $template = new template(); - $cx = new Cx($template); + $cx = new Cx($template); $content = <<display($content); @@ -548,10 +548,10 @@ public function testUrl() public function testFunction() { $template = new template(); - $data = [ + $data = [ 'list' => ['language' => 'php', 'version' => ['5.4', '5.5']], - 'a' => '[', - 'b' => ']', + 'a' => '[', + 'b' => ']', ]; $content = << -EOF; - - $template->parse($content); - $this->assertEquals($data, $content); - - $content = << -EOF; - - $template->parse($content); - $this->assertEquals($data, $content); - - $content = << -EOF; - - $template->parse($content); - $this->assertEquals($data, $content); - - $content = << -EOF; - - $template->parse($content); - $this->assertEquals($data, $content); - - $content = << -EOF; - - $template->parse($content); - $this->assertEquals($data, $content); - - $content = << -EOF; - - $template->parse($content); - $this->assertEquals($data, $content); - - $content = << -EOF; - - $template->parse($content); - $this->assertEquals($data, $content); - - $content = << -EOF; - - $template->parse($content); - $this->assertEquals($data, $content); - - $content = << -EOF; - - $template->parse($content); - $this->assertEquals($data, $content); - - $content = << -EOF; - - $template->parse($content); - $this->assertEquals($data, $content); - - $content = << -EOF; - - $template->parse($content); - $this->assertEquals($data, $content); - - $content = << -EOF; - - $template->parse($content); - $this->assertEquals($data, $content); - - $content = <<parse($content); - $this->assertEquals($data, $content); - - $content = <<parse($content); - $this->assertEquals($data, $content); + /** + * @var Template + */ + protected $template; + public function setUp() + { + $this->template = new Template(); } - public function testVarFunction() + public function testAssign() { - $template = new Template(); + $reflectProperty = new \ReflectionProperty(get_class($this->template), 'data'); + $reflectProperty->setAccessible(true); - $content = << -EOF; + $this->template->assign('version', 'ThinkPHP3.2'); + $data = $reflectProperty->getValue($this->template); + $this->assertEquals('ThinkPHP3.2', $data['version']); - $template->parse($content); - $this->assertEquals($data, $content); + $this->template->assign(['name' => 'Gao', 'version' => 'ThinkPHP5']); + $data = $reflectProperty->getValue($this->template); + $this->assertEquals('Gao', $data['name']); + $this->assertEquals('ThinkPHP5', $data['version']); + } - $content = << -EOF; + public function testGet() + { + $this->template = new Template(); + $data = [ + 'project' => 'ThinkPHP', + 'version' => [ + 'ThinkPHP5' => ['Think5.0', 'Think5.1'] + ] + ]; + $this->template->assign($data); - $template->parse($content); - $this->assertEquals($data, $content); + $this->assertSame($data, $this->template->get()); + $this->assertSame('ThinkPHP', $this->template->get('project')); + $this->assertSame(['Think5.0', 'Think5.1'], $this->template->get('version.ThinkPHP5')); + $this->assertNull($this->template->get('version.ThinkPHP3.2')); + } - $content = << - -EOF; + /** + * @dataProvider provideTestParseWithVar + */ + public function testParseWithVar($content, $expected) + { + $this->template = new Template(); - $template->parse($content); - $this->assertEquals($data, $content); + $this->template->parse($content); + $this->assertEquals($expected, $content); } - public function testVarIdentify() + /** + * @dataProvider provideTestParseWithVarFunction + */ + public function testParseWithVarFunction($content, $expected) { - $config['tpl_begin'] = '<#'; - $config['tpl_end'] = '#>'; - $config['tpl_var_identify'] = ''; - $template = new Template($config); + $this->template = new Template(); - $content = << -EOF; - $data = <<a)) ? (is_array(\$info)?\$info['a']:\$info->a) : 'test'; ?> -EOF; + $this->template->parse($content); + $this->assertEquals($expected, $content); + } - $template->parse($content); - $this->assertEquals($data, $content); + /** + * @dataProvider provideTestParseWithVarIdentify + */ + public function testParseWithVarIdentify($content, $expected, $config) + { + $this->template = new Template($config); - $content = << -EOF; - $data = <<a)) echo 'test'; ?> -EOF; + $this->template->parse($content); + $this->assertEquals($expected, $content); + } - $template->parse($content); - $this->assertEquals($data, $content); + /** + * @dataProvider provideTestParseWithThinkVar + */ + public function testParseWithThinkVar($content, $expected) + { + $config['tpl_begin'] = '{'; + $config['tpl_end'] = '}'; + $this->template = new Template($config); - $content = << -EOF; - $data = <<a)==(is_array(\$info)?\$info['b']:\$info->b)) echo 'test'; ?> -EOF; + $_SERVER['SERVER_NAME'] = 'server_name'; + $_GET['action'] = 'action'; + $_POST['action'] = 'action'; + $_COOKIE['name'] = 'name'; + $_SESSION['action'] = ['name' => 'name']; - $template->parse($content); - $this->assertEquals($data, $content); + $this->template->parse($content); + $this->assertEquals($expected, $content); + } - $content = << -EOF; - $data = <<a) ?: 'test')?'yes':'no'; ?> -EOF; - $template->parse($content); - $this->assertEquals($data, $content); + /** + * @expectedException \think\exception\TemplateNotFoundException + */ + public function testFetchWithEmptyTemplate() + { + $this->template = new Template(); - $template2 = new Template(); - $template2->tpl_var_identify = 'obj'; - $content = <<b)?'yes':'no'; ?> -EOF; - $template2->parse($content); - $this->assertEquals($data, $content); + $this->template->fetch('Foo'); } - public function testThinkVar() + /** + * @dataProvider provideTestFetchWithNoCache + */ + public function testFetchWithNoCache($data, $expected) { - $config['tpl_begin'] = '{'; - $config['tpl_end'] = '}'; - $template = new Template($config); + $this->template = new Template(); - $_SERVER['SERVER_NAME'] = 'server_name'; - $_GET['action'] = 'action'; - $_POST['action'] = 'action'; - $_COOKIE['name'] = 'name'; - $_SESSION['action'] = ['name' => 'name']; - define('SITE_NAME', 'site_name'); + $this->template->fetch($data['template'], $data['vars'], $data['config']); - $content = << -{\$Think.GET.action}
-{\$Think.POST.action}
-{\$Think.COOKIE.action}
-{\$Think.COOKIE.action.name}
-{\$Think.SESSION.action}
-{\$Think.SESSION.action.name}
-{\$Think.ENV.OS}
-{\$Think.REQUEST.action}
-{\$Think.CONST.SITE_NAME}
-{\$Think.LANG.action}
-{\$Think.CONFIG.action.name}
-{\$Think.NOW}
-{\$Think.VERSION}
-{\$Think.LDELIM}
-{\$Think.RDELIM}
-{\$Think.SITE_NAME}
-{\$Think.SITE.URL} -EOF; - $data = <<server('SERVER_NAME'); ?>
-get('action'); ?>
-post('action'); ?>
-
-
-
-
-env('OS'); ?>
-request('action'); ?>
-
-
-
-
-
-
-
-
- -EOF; - $template->parse($content); - $this->assertEquals($data, $content); + $this->expectOutputString($expected); } - public function testFetch() + public function testFetchWithCache() { - $template = new Template(); - $template->assign('name', 'name'); + $this->template = new Template(); + + $data = [ + 'name' => 'value' + ]; $config = [ - 'strip_space' => true, - 'view_path' => dirname(__FILE__) . DS, - 'cache_id' => '__CACHE_ID__', + 'cache_id' => 'TEST_FETCH_WITH_CACHE', 'display_cache' => true, ]; - $data = ['name' => 'value']; - $template->layout('layout')->fetch('display', $data, $config); + + $this->template->fetch(APP_PATH . 'views' . DS .'display.html', $data, $config); + $this->expectOutputString('value'); + $this->assertEquals('value', Cache::get($config['cache_id'])); } public function testDisplay() { - $config['view_path'] = dirname(__FILE__) . DS; - $config['view_suffix'] = '.html'; - $config['layout_on'] = true; - $config['layout_name'] = 'layout'; - $template = new Template($config); - $files = ['extend' => 'extend', 'include' => 'include']; - $template->assign('files', $files); - $template->assign('user', ['name' => 'name', 'account' => 100]); - $template->assign('message', 'message'); - $template->assign('info', ['value' => 'value']); + $config = [ + 'view_path' => APP_PATH . DS . 'views' . DS, + 'view_suffix' => '.html', + 'layout_on' => true, + 'layout_name' => 'layout' + ]; + + $this->template = new Template($config); + + $this->template->assign('files', ['extend' => 'extend', 'include' => 'include']); + $this->template->assign('user', ['name' => 'name', 'account' => 100]); + $this->template->assign('message', 'message'); + $this->template->assign('info', ['value' => 'value']); $content = << header
@@ -382,33 +211,205 @@ public function testDisplay() php code
EOF; - $template->display($content); - $this->expectOutputString($content2); -// $template->parse($content); - // var_dump($content); + $this->template->display($content); + $this->expectOutputString($expected); } - public function testVarAssign() + /** + * @dataProvider provideTestLayout + */ + public function testLayout($data, $expected) { - $template = new Template(); - $template->assign('name', 'value'); - $value = $template->get('name'); - $this->assertEquals('value', $value); + $this->template = new Template(); + + $this->template->layout($data['name'], $data['replace']); + + $this->assertSame($expected['layout_on'], $this->template->config('layout_on')); + $this->assertSame($expected['layout_name'], $this->template->config('layout_name')); + $this->assertSame($expected['layout_item'], $this->template->config('layout_item')); } - public function testVarGet() + public function testParseAttr() { - $template = new Template(); - $data = ['a' => 'a', 'b' => 'b']; - $template->assign($data); - $this->assertEquals($data, $template->get()); + $attributes = $this->template->parseAttr(""); + $this->assertSame(['version' => 'ThinkPHP', 'name' => 'Gao'], $attributes); + + $attributes = $this->template->parseAttr("TestCase", 'version'); + $this->assertSame('ThinkPHP', $attributes); } public function testIsCache() { - $template = new Template(['cache_id' => '__CACHE_ID__', 'display_cache' => true]); - $this->assertTrue($template->isCache('__CACHE_ID__')); - $template->display_cache = false; - $this->assertTrue(!$template->isCache('__CACHE_ID__')); + $this->template = new Template(); + $config = [ + 'cache_id' => rand(0, 10000) . rand(0, 10000) . time(), + 'display_cache' => true + ]; + + $this->assertFalse($this->template->isCache($config['cache_id'])); + + $this->template->fetch(APP_PATH . 'views' . DS .'display.html', [], $config); + $this->assertTrue($this->template->isCache($config['cache_id'])); + } + + public function provideTestParseWithVar() + { + return [ + ["{\$name.a.b}", ""], + ["{\$name.a??'test'}", ""], + ["{\$name.a?='test'}", ""], + ["{\$name.a?:'test'}", ""], + ["{\$name.a?\$name.b:'no'}", ""], + ["{\$name.a==\$name.b?='test'}", ""], + ["{\$name.a==\$name.b?'a':'b'}", ""], + ["{\$name.a|default='test'==\$name.b?'a':'b'}", ""], + ["{\$name.a|trim==\$name.b?='eq'}", ""], + ["{:ltrim(rtrim(\$name.a))}", ""], + ["{~echo(trim(\$name.a))}", ""], + ["{++\$name.a}", ""], + ["{/*\$name*/}", ""], + ["{\$0a}", "{\$0a}"] + ]; + } + + public function provideTestParseWithVarFunction() + { + return [ + ["{\$name.a.b|default='test'}", ""], + ["{\$create_time|date=\"y-m-d\",###}", ""], + ["{\$name}\n{\$name|trim|substr=0,3}", "\n"] + ]; + } + + public function provideTestParseWithVarIdentify() + { + $config['tpl_begin'] = '<#'; + $config['tpl_end'] = '#>'; + $config['tpl_var_identify'] = ''; + + return [ + [ + "<#\$info.a??'test'#>", + "a)) ? (is_array(\$info)?\$info['a']:\$info->a) : 'test'; ?>", + $config + ], + [ + "<#\$info.a?='test'#>", + "a)) echo 'test'; ?>", + $config + ], + [ + "<#\$info.a==\$info.b?='test'#>", + "a)==(is_array(\$info)?\$info['b']:\$info->b)) echo 'test'; ?>", + $config + ], + [ + "<#\$info.a|default='test'?'yes':'no'#>", + "a) ?: 'test')?'yes':'no'; ?>", + $config + ], + [ + "{\$info2.b|trim?'yes':'no'}", + "b)?'yes':'no'; ?>", + array_merge(['tpl_var_identify' => 'obj']) + ] + ]; + } + + public function provideTestParseWithThinkVar() + { + return [ + ["{\$Think.SERVER.SERVER_NAME}
", "server('SERVER_NAME'); ?>
"], + ["{\$Think.GET.action}
", "get('action'); ?>
"], + ["{\$Think.POST.action}
", "post('action'); ?>
"], + ["{\$Think.COOKIE.action}
", "
"], + ["{\$Think.COOKIE.action.name}
", "
"], + ["{\$Think.SESSION.action}
", "
"], + ["{\$Think.SESSION.action.name}
", "
"], + ["{\$Think.ENV.OS}
", "env('OS'); ?>
"], + ["{\$Think.REQUEST.action}
", "request('action'); ?>
"], + ["{\$Think.CONST.THINK_VERSION}
", "
"], + ["{\$Think.LANG.action}
", "
"], + ["{\$Think.CONFIG.action.name}
", "
"], + ["{\$Think.NOW}
", "
"], + ["{\$Think.VERSION}
", "
"], + ["{\$Think.LDELIM}
", "
"], + ["{\$Think.RDELIM}
", "
"], + ["{\$Think.THINK_VERSION}
", "
"], + ["{\$Think.SITE.URL}", ""] + ]; + } + + public function provideTestFetchWithNoCache() + { + $provideData = []; + + $this->template = [ + 'template' => APP_PATH . 'views' . DS .'display.html', + 'vars' => [], + 'config' => [] + ]; + $expected = 'default'; + $provideData[] = [$this->template, $expected]; + + $this->template = [ + 'template' => APP_PATH . 'views' . DS .'display.html', + 'vars' => ['name' => 'ThinkPHP5'], + 'config' => [] + ]; + $expected = 'ThinkPHP5'; + $provideData[] = [$this->template, $expected]; + + $this->template = [ + 'template' => 'views@display', + 'vars' => [], + 'config' => [ + 'view_suffix' => 'html' + ] + ]; + $expected = 'default'; + $provideData[] = [$this->template, $expected]; + + $this->template = [ + 'template' => 'views@/display', + 'vars' => ['name' => 'ThinkPHP5'], + 'config' => [ + 'view_suffix' => 'phtml' + ] + ]; + $expected = 'ThinkPHP5'; + $provideData[] = [$this->template, $expected]; + + $this->template = [ + 'template' => 'display', + 'vars' => ['name' => 'ThinkPHP5'], + 'config' => [ + 'view_suffix' => 'html', + 'view_base' => APP_PATH . 'views' . DS + ] + ]; + $expected = 'ThinkPHP5'; + $provideData[] = [$this->template, $expected]; + + return $provideData; + } + + public function provideTestLayout() + { + $provideData = []; + + $data = ['name' => false, 'replace' => '']; + $expected = ['layout_on' => false, 'layout_name' => 'layout', 'layout_item' => '{__CONTENT__}']; + $provideData[] = [$data, $expected]; + + $data = ['name' => null, 'replace' => '']; + $expected = ['layout_on' => true, 'layout_name' => 'layout', 'layout_item' => '{__CONTENT__}']; + $provideData[] = [$data, $expected]; + + $data = ['name' => 'ThinkName', 'replace' => 'ThinkReplace']; + $expected = ['layout_on' => true, 'layout_name' => 'ThinkName', 'layout_item' => 'ThinkReplace']; + $provideData[] = [$data, $expected]; + + return $provideData; } } diff --git a/tests/thinkphp/library/think/urlTest.php b/tests/thinkphp/library/think/urlTest.php index 401d973480..b7dd803327 100644 --- a/tests/thinkphp/library/think/urlTest.php +++ b/tests/thinkphp/library/think/urlTest.php @@ -28,17 +28,17 @@ class urlTest extends \PHPUnit_Framework_TestCase public function setUp() { Route::rules(['get' => [], - 'post' => [], - 'put' => [], - 'delete' => [], - 'patch' => [], - 'head' => [], - 'options' => [], - '*' => [], - 'alias' => [], - 'domain' => [], - 'pattern' => [], - 'name' => []]); + 'post' => [], + 'put' => [], + 'delete' => [], + 'patch' => [], + 'head' => [], + 'options' => [], + '*' => [], + 'alias' => [], + 'domain' => [], + 'pattern' => [], + 'name' => []]); Route::name([]); } diff --git a/tests/thinkphp/library/think/validateTest.php b/tests/thinkphp/library/think/validateTest.php index 081a6f2b80..27ccc54574 100644 --- a/tests/thinkphp/library/think/validateTest.php +++ b/tests/thinkphp/library/think/validateTest.php @@ -24,84 +24,84 @@ class validateTest extends \PHPUnit_Framework_TestCase public function testCheck() { $rule = [ - 'name' => 'require|max:25', - 'age' => 'number|between:1,120', + 'name' => 'require|max:25', + 'age' => 'number|between:1,120', 'email' => 'email', ]; $msg = [ 'name.require' => '名称必须', - 'name.max' => '名称最多不能超过25个字符', - 'age.number' => '年龄必须是数字', - 'age.between' => '年龄只能在1-120之间', - 'email' => '邮箱格式错误', + 'name.max' => '名称最多不能超过25个字符', + 'age.number' => '年龄必须是数字', + 'age.between' => '年龄只能在1-120之间', + 'email' => '邮箱格式错误', ]; $data = [ - 'name' => 'thinkphp', - 'age' => 10, + 'name' => 'thinkphp', + 'age' => 10, 'email' => 'thinkphp@qq.com', ]; $validate = new Validate($rule, $msg); - $result = $validate->check($data); + $result = $validate->check($data); $this->assertEquals(true, $result); } public function testRule() { $rule = [ - 'name' => 'require|method:get|alphaNum|max:25|expire:2016-1-1,2026-1-1', - 'account' => 'requireIf:name,thinkphp|alphaDash|min:4|length:4,30', - 'age' => 'number|between:1,120', - 'email' => 'requireWith:name|email', - 'host' => 'activeUrl|activeUrl:A', - 'url' => 'url', - 'ip' => 'ip|ip:ipv4', - 'score' => 'float|gt:60|notBetween:90,100|notIn:70,80|lt:100|elt:100|egt:60', - 'status' => 'integer|in:0,1,2', - 'begin_time' => 'after:2016-3-18', - 'end_time' => 'before:2016-10-01', - 'info' => 'require|array|length:4|max:5|min:2', - 'info.name' => 'require|length:8|alpha|same:thinkphp', - 'value' => 'same:100|different:status', - 'bool' => 'boolean', - 'title' => 'chsAlpha', - 'city' => 'chs', - 'nickname' => 'chsDash', - 'aliasname' => 'chsAlphaNum', - 'file' => 'file|fileSize:20480', - 'image' => 'image|fileMime:image/png|image:80,80,png', - 'test' => 'test', + 'name' => 'require|method:get|alphaNum|max:25|expire:2016-1-1,2026-1-1', + 'account' => 'requireIf:name,thinkphp|alphaDash|min:4|length:4,30', + 'age' => 'number|between:1,120', + 'email' => 'requireWith:name|email', + 'host' => 'activeUrl|activeUrl:A', + 'url' => 'url', + 'ip' => 'ip|ip:ipv4', + 'score' => 'float|gt:60|notBetween:90,100|notIn:70,80|lt:100|elt:100|egt:60', + 'status' => 'integer|in:0,1,2', + 'begin_time' => 'after:2016-3-18|beforeWith:end_time', + 'end_time' => 'before:2016-10-01|afterWith:begin_time', + 'info' => 'require|array|length:4|max:5|min:2', + 'info.name' => 'require|length:8|alpha|same:thinkphp', + 'value' => 'same:100|different:status', + 'bool' => 'boolean', + 'title' => 'chsAlpha', + 'city' => 'chs', + 'nickname' => 'chsDash', + 'aliasname' => 'chsAlphaNum', + 'file' => 'file|fileSize:20480', + 'image' => 'image|fileMime:image/png|image:80,80,png', + 'test' => 'test', ]; $data = [ - 'name' => 'thinkphp', - 'account' => 'liuchen', - 'age' => 10, - 'email' => 'thinkphp@qq.com', - 'host' => 'thinkphp.cn', - 'url' => 'http://thinkphp.cn/topic', - 'ip' => '114.34.54.5', - 'score' => '89.15', - 'status' => 1, + 'name' => 'thinkphp', + 'account' => 'liuchen', + 'age' => 10, + 'email' => 'thinkphp@qq.com', + 'host' => 'thinkphp.cn', + 'url' => 'http://thinkphp.cn/topic', + 'ip' => '114.34.54.5', + 'score' => '89.15', + 'status' => 1, 'begin_time' => '2016-3-20', - 'end_time' => '2016-5-1', - 'info' => [1, 2, 3, 'name' => 'thinkphp'], - 'zip' => '200000', - 'date' => '16-3-8', - 'ok' => 'yes', - 'value' => 100, - 'bool' => true, - 'title' => '流年ThinkPHP', - 'city' => '上海', - 'nickname' => '流年ThinkPHP_2016', - 'aliasname' => '流年Think2016', - 'file' => new File(THINK_PATH . 'base.php'), - 'image' => new File(THINK_PATH . 'logo.png'), - 'test' => 'test', + 'end_time' => '2016-5-1', + 'info' => [1, 2, 3, 'name' => 'thinkphp'], + 'zip' => '200000', + 'date' => '16-3-8', + 'ok' => 'yes', + 'value' => 100, + 'bool' => true, + 'title' => '流年ThinkPHP', + 'city' => '上海', + 'nickname' => '流年ThinkPHP_2016', + 'aliasname' => '流年Think2016', + 'file' => new File(THINK_PATH . 'base.php'), + 'image' => new File(THINK_PATH . 'logo.png'), + 'test' => 'test', ]; $validate = new Validate($rule); $validate->extend('test', function ($value) {return 'test' == $value ? true : false;}); $validate->rule('zip', '/^\d{6}$/'); $validate->rule([ - 'ok' => 'require|accepted', + 'ok' => 'require|accepted', 'date' => 'date|dateFormat:y-m-d', ]); $result = $validate->batch()->check($data); @@ -114,26 +114,26 @@ public function testMsg() $validate->message('name.require', '名称必须'); $validate->message([ 'name.require' => '名称必须', - 'name.max' => '名称最多不能超过25个字符', - 'age.number' => '年龄必须是数字', - 'age.between' => '年龄只能在1-120之间', - 'email' => '邮箱格式错误', + 'name.max' => '名称最多不能超过25个字符', + 'age.number' => '年龄必须是数字', + 'age.between' => '年龄只能在1-120之间', + 'email' => '邮箱格式错误', ]); } public function testMake() { $rule = [ - 'name' => 'require|max:25', - 'age' => 'number|between:1,120', + 'name' => 'require|max:25', + 'age' => 'number|between:1,120', 'email' => 'email', ]; $msg = [ 'name.require' => '名称必须', - 'name.max' => '名称最多不能超过25个字符', - 'age.number' => '年龄必须是数字', - 'age.between' => '年龄只能在1-120之间', - 'email' => '邮箱格式错误', + 'name.max' => '名称最多不能超过25个字符', + 'age.number' => '年龄必须是数字', + 'age.between' => '年龄只能在1-120之间', + 'email' => '邮箱格式错误', ]; $validate = Validate::make($rule, $msg); } @@ -143,7 +143,7 @@ public function testExtend() $validate = new Validate(['name' => 'check:1']); $validate->extend('check', function ($value, $rule) {return $rule == $value ? true : false;}); $validate->extend(['check' => function ($value, $rule) {return $rule == $value ? true : false;}]); - $data = ['name' => 1]; + $data = ['name' => 1]; $result = $validate->check($data); $this->assertEquals(true, $result); } @@ -151,20 +151,20 @@ public function testExtend() public function testScene() { $rule = [ - 'name' => 'require|max:25', - 'age' => 'number|between:1,120', + 'name' => 'require|max:25', + 'age' => 'number|between:1,120', 'email' => 'email', ]; $msg = [ 'name.require' => '名称必须', - 'name.max' => '名称最多不能超过25个字符', - 'age.number' => '年龄必须是数字', - 'age.between' => '年龄只能在1-120之间', - 'email' => '邮箱格式错误', + 'name.max' => '名称最多不能超过25个字符', + 'age.number' => '年龄必须是数字', + 'age.between' => '年龄只能在1-120之间', + 'email' => '邮箱格式错误', ]; $data = [ - 'name' => 'thinkphp', - 'age' => 10, + 'name' => 'thinkphp', + 'age' => 10, 'email' => 'thinkphp@qq.com', ]; $validate = new Validate($rule); @@ -179,15 +179,15 @@ public function testSetTypeMsg() { $rule = [ 'name|名称' => 'require|max:25', - 'age' => 'number|between:1,120', - 'email' => 'email', + 'age' => 'number|between:1,120', + 'email' => 'email', ['sex', 'in:1,2', '性别错误'], ]; $data = [ - 'name' => '', - 'age' => 10, + 'name' => '', + 'age' => 10, 'email' => 'thinkphp@qq.com', - 'sex' => '3', + 'sex' => '3', ]; $validate = new Validate($rule); $validate->setTypeMsg('require', ':attribute必须'); diff --git a/tests/thinkphp/library/think/viewTest.php b/tests/thinkphp/library/think/viewTest.php index 5bb7de16c0..49605b8a97 100644 --- a/tests/thinkphp/library/think/viewTest.php +++ b/tests/thinkphp/library/think/viewTest.php @@ -38,7 +38,7 @@ public function testGetInstance() */ public function testAssign() { - $view_instance = \think\View::instance(); + $view_instance = \think\View::instance(); $view_instance->key = 'value'; $this->assertTrue(isset($view_instance->key)); $this->assertEquals('value', $view_instance->key); @@ -57,12 +57,12 @@ public function testAssign() public function testEngine() { $view_instance = \think\View::instance(); - $data = $view_instance->engine('php'); - $data = $view_instance->engine(['type' => 'php', 'view_path' => '', 'view_suffix' => '.php', 'view_depr' => DS]); - $php_engine = new \think\view\driver\Php(['view_path' => '', 'view_suffix' => '.php', 'view_depr' => DS]); + $data = $view_instance->engine('php'); + $data = $view_instance->engine(['type' => 'php', 'view_path' => '', 'view_suffix' => '.php', 'view_depr' => DS]); + $php_engine = new \think\view\driver\Php(['view_path' => '', 'view_suffix' => '.php', 'view_depr' => DS]); $this->assertAttributeEquals($php_engine, 'engine', $view_instance); //测试模板引擎驱动 - $data = $view_instance->engine(['type' => 'think', 'view_path' => '', 'view_suffix' => '.html', 'view_depr' => DS]); + $data = $view_instance->engine(['type' => 'think', 'view_path' => '', 'view_suffix' => '.html', 'view_depr' => DS]); $think_engine = new \think\view\driver\Think(['view_path' => '', 'view_suffix' => '.html', 'view_depr' => DS]); $this->assertAttributeEquals($think_engine, 'engine', $view_instance); } diff --git a/tests/thinkphp/library/traits/controller/jumpTest.php b/tests/thinkphp/library/traits/controller/jumpTest.php index 47e9dbfe63..45a84bdae9 100644 --- a/tests/thinkphp/library/traits/controller/jumpTest.php +++ b/tests/thinkphp/library/traits/controller/jumpTest.php @@ -28,7 +28,7 @@ class jumpTest extends \PHPUnit_Framework_TestCase public function setUp() { $this->testClass = new testClassWithJump(); - $this->request = Request::create(''); + $this->request = Request::create(''); $this->originServerData = Request::instance()->server(); } @@ -156,48 +156,48 @@ public function provideTestSuccess() $provideData = []; $arguments = ['', null, '', 3, []]; - $expected = [ + $expected = [ 'header' => [ 'Content-Type' => 'text/html; charset=utf-8' ], - 'data' => View::instance(Config::get('template'), Config::get('view_replace_str')) + 'data' => View::instance(Config::get('template'), Config::get('view_replace_str')) ->fetch(Config::get('dispatch_error_tmpl'), [ 'code' => 1, - 'msg' => '', + 'msg' => '', 'data' => '', - 'url' => '/index.php/', + 'url' => '/index.php/', 'wait' => 3, ]) ]; $provideData[] = [$arguments, $expected, ['server' => ['HTTP_REFERER' => null], 'return' => 'html']]; $arguments = ['thinkphp', null, ['foo'], 4, ['Power-By' => 'thinkphp', 'Content-Type' => 'text/html; charset=gbk']]; - $expected = [ + $expected = [ 'header' => [ 'Content-Type' => 'text/html; charset=gbk', 'Power-By' => 'thinkphp' ], - 'data' => View::instance(Config::get('template'), Config::get('view_replace_str')) + 'data' => View::instance(Config::get('template'), Config::get('view_replace_str')) ->fetch(Config::get('dispatch_error_tmpl'), [ 'code' => 1, - 'msg' => 'thinkphp', + 'msg' => 'thinkphp', 'data' => ['foo'], - 'url' => 'http://www.thinkphp.cn', + 'url' => 'http://www.thinkphp.cn', 'wait' => 4, ]) ]; $provideData[] = [$arguments, $expected, ['server' => ['HTTP_REFERER' => 'http://www.thinkphp.cn'], 'return' => 'html']]; $arguments = ['thinkphp', 'index', ['foo'], 5, []]; - $expected = [ + $expected = [ 'header' => [ 'Content-Type' => 'application/json; charset=utf-8' ], - 'data' => [ + 'data' => [ 'code' => 1, - 'msg' => 'thinkphp', + 'msg' => 'thinkphp', 'data' => ['foo'], - 'url' => '/index.php/index.html', + 'url' => '/index.php/index.html', 'wait' => 5, ] ]; @@ -211,48 +211,48 @@ public function provideTestError() $provideData = []; $arguments = ['', null, '', 3, []]; - $expected = [ + $expected = [ 'header' => [ 'Content-Type' => 'text/html; charset=utf-8' ], - 'data' => View::instance(Config::get('template'), Config::get('view_replace_str')) + 'data' => View::instance(Config::get('template'), Config::get('view_replace_str')) ->fetch(Config::get('dispatch_error_tmpl'), [ 'code' => 0, - 'msg' => '', + 'msg' => '', 'data' => '', - 'url' => 'javascript:history.back(-1);', + 'url' => 'javascript:history.back(-1);', 'wait' => 3, ]) ]; $provideData[] = [$arguments, $expected, ['return' => 'html']]; $arguments = ['thinkphp', 'http://www.thinkphp.cn', ['foo'], 4, ['Power-By' => 'thinkphp', 'Content-Type' => 'text/html; charset=gbk']]; - $expected = [ + $expected = [ 'header' => [ 'Content-Type' => 'text/html; charset=gbk', 'Power-By' => 'thinkphp' ], - 'data' => View::instance(Config::get('template'), Config::get('view_replace_str')) + 'data' => View::instance(Config::get('template'), Config::get('view_replace_str')) ->fetch(Config::get('dispatch_error_tmpl'), [ 'code' => 0, - 'msg' => 'thinkphp', + 'msg' => 'thinkphp', 'data' => ['foo'], - 'url' => 'http://www.thinkphp.cn', + 'url' => 'http://www.thinkphp.cn', 'wait' => 4, ]) ]; $provideData[] = [$arguments, $expected, ['return' => 'html']]; $arguments = ['thinkphp', '', ['foo'], 5, []]; - $expected = [ + $expected = [ 'header' => [ 'Content-Type' => 'application/json; charset=utf-8' ], - 'data' => [ + 'data' => [ 'code' => 0, - 'msg' => 'thinkphp', + 'msg' => 'thinkphp', 'data' => ['foo'], - 'url' => '', + 'url' => '', 'wait' => 5, ] ]; @@ -266,13 +266,13 @@ public function provideTestResult() $provideData = []; $arguments = [null, 0, '', '', []]; - $expected = [ + $expected = [ 'header' => [ 'Content-Type' => 'text/html; charset=utf-8' ], 'data' => [ 'code' => 0, - 'msg' => '', + 'msg' => '', 'time' => Request::create('')->server('REQUEST_TIME'), 'data' => null, ] @@ -280,14 +280,14 @@ public function provideTestResult() $provideData[] = [$arguments, $expected, ['return' => 'html']]; $arguments = [['foo'], 200, 'thinkphp', 'json', ['Power-By' => 'thinkphp']]; - $expected = [ + $expected = [ 'header' => [ 'Power-By' => 'thinkphp', 'Content-Type' => 'application/json; charset=utf-8' ], - 'data' => [ + 'data' => [ 'code' => 200, - 'msg' => 'thinkphp', + 'msg' => 'thinkphp', 'time' => 1000, 'data' => ['foo'], ] @@ -303,22 +303,22 @@ public function provideTestRedirect() $provideData = []; $arguments = ['', [], 302, []]; - $expected = [ - 'code'=> 302, + $expected = [ + 'code' => 302, 'url' => '/index.php/' ]; $provideData[] = [$arguments, $expected, []]; $arguments = ['index', 302, null, []]; - $expected = [ - 'code'=> 302, + $expected = [ + 'code' => 302, 'url' => '/index.php/index.html' ]; $provideData[] = [$arguments, $expected, []]; $arguments = ['http://www.thinkphp.cn', 301, 302, []]; - $expected = [ - 'code'=> 301, + $expected = [ + 'code' => 301, 'url' => 'http://www.thinkphp.cn' ]; $provideData[] = [$arguments, $expected, []]; diff --git a/tests/thinkphp/library/traits/model/softDeleteTest.php b/tests/thinkphp/library/traits/model/softDeleteTest.php index 8410efacec..b330267a06 100644 --- a/tests/thinkphp/library/traits/model/softDeleteTest.php +++ b/tests/thinkphp/library/traits/model/softDeleteTest.php @@ -134,43 +134,43 @@ class testClassWithSoftDelete extends Model public $connection = [ // 数据库类型 - 'type' => 'mysql', + 'type' => 'mysql', // 服务器地址 - 'hostname' => '127.0.0.1', + 'hostname' => '127.0.0.1', // 数据库名 - 'database' => 'test', + 'database' => 'test', // 用户名 - 'username' => 'root', + 'username' => 'root', // 密码 - 'password' => '', + 'password' => '', // 端口 - 'hostport' => '', + 'hostport' => '', // 连接dsn - 'dsn' => '', + 'dsn' => '', // 数据库连接参数 - 'params' => [], + 'params' => [], // 数据库编码默认采用utf8 - 'charset' => 'utf8', + 'charset' => 'utf8', // 数据库表前缀 - 'prefix' => '', + 'prefix' => '', // 数据库调试模式 - 'debug' => true, + 'debug' => true, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, + 'deploy' => 0, // 数据库读写是否分离 主从式有效 - 'rw_separate' => false, + 'rw_separate' => false, // 读写分离后 主服务器数量 - 'master_num' => 1, + 'master_num' => 1, // 指定从服务器序号 - 'slave_no' => '', + 'slave_no' => '', // 是否严格检查字段是否存在 - 'fields_strict' => true, + 'fields_strict' => true, // 数据集返回类型 array 数组 collection Collection对象 'resultset_type' => 'array', // 是否自动写入时间戳字段 'auto_timestamp' => false, // 是否需要进行SQL性能分析 - 'sql_explain' => false, + 'sql_explain' => false, ]; use SoftDelete {