Browse Source

[更新]composer框架升级

Anyon 7 years ago
parent
commit
350ae1d046
100 changed files with 2938 additions and 1764 deletions
  1. 1 1
      composer.json
  2. 2 2
      thinkphp/CONTRIBUTING.md
  3. 1 1
      thinkphp/LICENSE.txt
  4. 1 1
      thinkphp/README.md
  5. 4 2
      thinkphp/base.php
  6. 1 1
      thinkphp/helper.php
  7. 118 53
      thinkphp/lang/zh-cn.php
  8. 247 177
      thinkphp/library/think/App.php
  9. 90 60
      thinkphp/library/think/Build.php
  10. 68 43
      thinkphp/library/think/Cache.php
  11. 197 116
      thinkphp/library/think/Collection.php
  12. 99 63
      thinkphp/library/think/Config.php
  13. 243 118
      thinkphp/library/think/Console.php
  14. 58 48
      thinkphp/library/think/Controller.php
  15. 104 60
      thinkphp/library/think/Cookie.php
  16. 50 26
      thinkphp/library/think/Db.php
  17. 84 44
      thinkphp/library/think/Debug.php
  18. 8 5
      thinkphp/library/think/Env.php
  19. 44 28
      thinkphp/library/think/Error.php
  20. 11 10
      thinkphp/library/think/Exception.php
  21. 156 93
      thinkphp/library/think/File.php
  22. 37 25
      thinkphp/library/think/Hook.php
  23. 84 42
      thinkphp/library/think/Lang.php
  24. 266 179
      thinkphp/library/think/Loader.php
  25. 102 78
      thinkphp/library/think/Log.php
  26. 116 35
      thinkphp/library/think/Model.php
  27. 1 1
      thinkphp/library/think/Paginator.php
  28. 5 2
      thinkphp/library/think/Request.php
  29. 1 1
      thinkphp/library/think/Response.php
  30. 3 3
      thinkphp/library/think/Route.php
  31. 1 1
      thinkphp/library/think/Session.php
  32. 7 5
      thinkphp/library/think/Template.php
  33. 5 4
      thinkphp/library/think/Url.php
  34. 141 97
      thinkphp/library/think/Validate.php
  35. 12 9
      thinkphp/library/think/View.php
  36. 8 2
      thinkphp/library/think/cache/Driver.php
  37. 7 4
      thinkphp/library/think/cache/driver/File.php
  38. 2 2
      thinkphp/library/think/cache/driver/Lite.php
  39. 1 1
      thinkphp/library/think/cache/driver/Memcache.php
  40. 1 1
      thinkphp/library/think/cache/driver/Memcached.php
  41. 24 15
      thinkphp/library/think/cache/driver/Redis.php
  42. 2 2
      thinkphp/library/think/cache/driver/Sqlite.php
  43. 1 1
      thinkphp/library/think/cache/driver/Wincache.php
  44. 1 1
      thinkphp/library/think/cache/driver/Xcache.php
  45. 1 1
      thinkphp/library/think/config/driver/Ini.php
  46. 1 1
      thinkphp/library/think/config/driver/Json.php
  47. 1 1
      thinkphp/library/think/config/driver/Xml.php
  48. 5 0
      thinkphp/library/think/console/command/optimize/Route.php
  49. 3 3
      thinkphp/library/think/controller/Rest.php
  50. 25 13
      thinkphp/library/think/db/Builder.php
  51. 35 15
      thinkphp/library/think/db/Connection.php
  52. 116 81
      thinkphp/library/think/db/Query.php
  53. 61 2
      thinkphp/library/think/db/builder/Mysql.php
  54. 1 1
      thinkphp/library/think/db/builder/Pgsql.php
  55. 1 1
      thinkphp/library/think/db/builder/Sqlite.php
  56. 1 1
      thinkphp/library/think/db/connector/Mysql.php
  57. 1 1
      thinkphp/library/think/db/connector/Pgsql.php
  58. 1 1
      thinkphp/library/think/db/connector/Sqlite.php
  59. 1 1
      thinkphp/library/think/db/exception/BindParamException.php
  60. 1 1
      thinkphp/library/think/db/exception/DataNotFoundException.php
  61. 1 1
      thinkphp/library/think/db/exception/ModelNotFoundException.php
  62. 1 1
      thinkphp/library/think/debug/Console.php
  63. 1 1
      thinkphp/library/think/exception/DbException.php
  64. 1 1
      thinkphp/library/think/exception/ErrorException.php
  65. 1 1
      thinkphp/library/think/exception/PDOException.php
  66. 15 6
      thinkphp/library/think/log/driver/File.php
  67. 1 1
      thinkphp/library/think/model/Collection.php
  68. 1 1
      thinkphp/library/think/model/Merge.php
  69. 3 5
      thinkphp/library/think/model/Pivot.php
  70. 1 1
      thinkphp/library/think/model/Relation.php
  71. 17 12
      thinkphp/library/think/model/relation/BelongsTo.php
  72. 19 8
      thinkphp/library/think/model/relation/BelongsToMany.php
  73. 15 9
      thinkphp/library/think/model/relation/HasMany.php
  74. 6 7
      thinkphp/library/think/model/relation/HasManyThrough.php
  75. 15 10
      thinkphp/library/think/model/relation/HasOne.php
  76. 4 3
      thinkphp/library/think/model/relation/MorphMany.php
  77. 4 3
      thinkphp/library/think/model/relation/MorphOne.php
  78. 8 6
      thinkphp/library/think/model/relation/MorphTo.php
  79. 13 3
      thinkphp/library/think/model/relation/OneToOne.php
  80. 1 1
      thinkphp/library/think/paginator/driver/Bootstrap.php
  81. 1 1
      thinkphp/library/think/response/Json.php
  82. 1 1
      thinkphp/library/think/response/Jsonp.php
  83. 1 1
      thinkphp/library/think/response/Redirect.php
  84. 1 1
      thinkphp/library/think/response/View.php
  85. 1 1
      thinkphp/library/think/response/Xml.php
  86. 1 1
      thinkphp/library/think/session/driver/Memcache.php
  87. 1 1
      thinkphp/library/think/session/driver/Memcached.php
  88. 1 1
      thinkphp/library/think/session/driver/Redis.php
  89. 1 1
      thinkphp/library/think/template/TagLib.php
  90. 1 1
      thinkphp/library/think/template/driver/File.php
  91. 1 1
      thinkphp/library/think/view/driver/Php.php
  92. 1 1
      thinkphp/library/think/view/driver/Think.php
  93. 53 34
      thinkphp/library/traits/controller/Jump.php
  94. 44 32
      thinkphp/library/traits/model/SoftDelete.php
  95. 22 13
      thinkphp/library/traits/think/Instance.php
  96. 4 3
      thinkphp/start.php
  97. 1 0
      thinkphp/tpl/dispatch_jump.tpl
  98. 1 1
      thinkphp/tpl/think_exception.tpl
  99. 1 1
      vendor/autoload.php
  100. 2 0
      vendor/composer/autoload_classmap.php

+ 1 - 1
composer.json

@@ -20,7 +20,7 @@
         "qiniu/php-sdk": "^7.0",
         "zoujingli/wechat-php-sdk": "dev-master",
         "zoujingli/ip2region": "^1.0",
-        "topthink/framework": "^5.0",
+        "topthink/framework": "5.0.14",
         "topthink/think-captcha": "^1.0",
         "topthink/think-mongo": "^1.1",
         "topthink/think-queue": "^1.0",

+ 2 - 2
thinkphp/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`*

+ 1 - 1
thinkphp/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® 商标和著作权所有者为上海顶想信息科技有限公司。
 

+ 1 - 1
thinkphp/README.md

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

+ 4 - 2
thinkphp/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 <liu21st@gmail.com>
 // +----------------------------------------------------------------------
 
-define('THINK_VERSION', '5.0.11');
+define('THINK_VERSION', '5.0.14');
 define('THINK_START_TIME', microtime(true));
 define('THINK_START_MEM', memory_get_usage());
 define('EXT', '.php');
@@ -40,8 +40,10 @@ require CORE_PATH . 'Loader.php';
 // 加载环境变量配置文件
 if (is_file(ROOT_PATH . '.env')) {
     $env = parse_ini_file(ROOT_PATH . '.env', true);
+
     foreach ($env as $key => $val) {
         $name = ENV_PREFIX . strtoupper($key);
+
         if (is_array($val)) {
             foreach ($val as $k => $v) {
                 $item = $name . '_' . strtoupper($k);

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

+ 118 - 53
thinkphp/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,123 @@
 // 核心中文语言包
 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'       => '缺少需要写入的数据',
-    '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方法',
+    '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'                                       => '缺少需要写入的数据',
+    '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方法',
+
+    // 上传错误信息
+    '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'                                  => '规则错误',
 ];

+ 247 - 177
thinkphp/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 @@ use think\exception\RouteNotFoundException;
 
 /**
  * App 应用管理
- * @author  liu21st <liu21st@gmail.com>
+ * @author liu21st <liu21st@gmail.com>
  */
 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 @@ class App
 
             // 默认语言
             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 @@ class App
                 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 @@ class App
                 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 @@ class App
             $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')
@@ -170,8 +312,10 @@ class App
     {
         $reflect = new \ReflectionFunction($function);
         $args    = self::bindParams($reflect, $vars);
+
         // 记录执行信息
         self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info');
+
         return $reflect->invokeArgs($args);
     }
 
@@ -191,28 +335,27 @@ class App
             // 静态方法
             $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);
         $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 @@ class App
      */
     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();
         $class = $param->getClass();
+
         if ($class) {
             $className = $class->getName();
             $bind      = Request::instance()->$name;
+
             if ($bind instanceof $className) {
                 $result = $bind;
             } else {
                 if (method_exists($className, 'invoke')) {
                     $method = new \ReflectionMethod($className, 'invoke');
+
                     if ($method->isPublic() && $method->isStatic()) {
                         return $className::invoke(Request::instance());
                     }
                 }
-                $result = method_exists($className, 'instance') ? $className::instance() : new $className;
+
+                $result = method_exists($className, 'instance') ?
+                $className::instance() :
+                new $className;
             }
         } elseif (1 == $type && !empty($vars)) {
             $result = array_shift($vars);
@@ -281,65 +430,85 @@ class App
         } 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');
             $available = false;
+
             if ($bind) {
                 // 绑定模块
                 list($bindModule) = explode('/', $bind);
+
                 if (empty($result[0])) {
                     $module    = $bindModule;
                     $available = true;
@@ -355,8 +524,13 @@ class App
                 // 初始化模块
                 $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,11 +539,16 @@ class App
             $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']);
         $controller = $convert ? strtolower($controller) : $controller;
@@ -385,7 +564,12 @@ class App
         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());
         }
@@ -412,124 +596,10 @@ class App
     }
 
     /**
-     * 初始化应用
-     */
-    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
      */
@@ -538,6 +608,7 @@ class App
         $path   = $request->path();
         $depr   = $config['pathinfo_depr'];
         $result = false;
+
         // 路由检测
         $check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on'];
         if ($check) {
@@ -545,18 +616,14 @@ class App
             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);
                     }
                 }
             }
@@ -564,15 +631,18 @@ class App
             // 路由检测(根据路由定义返回不同的URL调度)
             $result = Route::check($request, $path, $depr, $config['url_domain_deploy']);
             $must   = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must'];
+
             if ($must && false === $result) {
                 // 路由无效
                 throw new RouteNotFoundException();
             }
         }
+
+        // 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索
         if (false === $result) {
-            // 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索
             $result = Route::parseUrl($path, $depr, $config['controller_auto_search']);
         }
+
         return $result;
     }
 

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

+ 68 - 43
thinkphp/library/think/Cache.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,71 +15,86 @@ use think\cache\Driver;
 
 class Cache
 {
-    protected static $instance = [];
-    public static $readTimes   = 0;
-    public static $writeTimes  = 0;
+    /**
+     * @var array 缓存的实例
+     */
+    public static $instance = [];
+
+    /**
+     * @var int 缓存读取次数
+     */
+    public static $readTimes = 0;
+
+    /**
+     * @var int 缓存写入次数
+     */
+    public static $writeTimes = 0;
 
     /**
-     * 操作句柄
-     * @var object
-     * @access protected
+     * @var object 操作句柄
      */
-    protected static $handler;
+    public static $handler;
 
     /**
-     * 连接缓存
+     * 连接缓存驱动
      * @access public
-     * @param array         $options  配置数组
-     * @param bool|string   $name 缓存连接标识 true 强制重新连接
+     * @param  array       $options 配置数组
+     * @param  bool|string $name    缓存连接标识 true 强制重新连接
      * @return Driver
      */
     public static function connect(array $options = [], $name = false)
     {
         $type = !empty($options['type']) ? $options['type'] : 'File';
+
         if (false === $name) {
             $name = md5(serialize($options));
         }
 
         if (true === $name || !isset(self::$instance[$name])) {
-            $class = false !== strpos($type, '\\') ? $type : '\\think\\cache\\driver\\' . ucwords($type);
+            $class = false === strpos($type, '\\') ?
+            '\\think\\cache\\driver\\' . ucwords($type) :
+            $type;
 
             // 记录初始化信息
             App::$debug && Log::record('[ CACHE ] INIT ' . $type, 'info');
+
             if (true === $name) {
                 return new $class($options);
-            } else {
-                self::$instance[$name] = new $class($options);
             }
+
+            self::$instance[$name] = new $class($options);
         }
+
         return self::$instance[$name];
     }
 
     /**
      * 自动初始化缓存
      * @access public
-     * @param array         $options  配置数组
+     * @param  array $options 配置数组
      * @return Driver
      */
     public static function init(array $options = [])
     {
         if (is_null(self::$handler)) {
-            // 自动初始化缓存
-            if (!empty($options)) {
-                $connect = self::connect($options);
-            } elseif ('complex' == Config::get('cache.type')) {
-                $connect = self::connect(Config::get('cache.default'));
-            } else {
-                $connect = self::connect(Config::get('cache'));
+            if (empty($options) && 'complex' == Config::get('cache.type')) {
+                $default = Config::get('cache.default');
+                // 获取默认缓存配置,并连接
+                $options = Config::get('cache.' . $default['type']) ?: $default;
+            } elseif (empty($options)) {
+                $options = Config::get('cache');
             }
-            self::$handler = $connect;
+
+            self::$handler = self::connect($options);
         }
+
         return self::$handler;
     }
 
     /**
      * 切换缓存类型 需要配置 cache.type 为 complex
      * @access public
-     * @param string $name 缓存标识
+     * @param  string $name 缓存标识
      * @return Driver
      */
     public static function store($name = '')
@@ -87,131 +102,141 @@ class Cache
         if ('' !== $name && 'complex' == Config::get('cache.type')) {
             return self::connect(Config::get('cache.' . $name), strtolower($name));
         }
+
         return self::init();
     }
 
     /**
      * 判断缓存是否存在
      * @access public
-     * @param string $name 缓存变量名
+     * @param  string $name 缓存变量名
      * @return bool
      */
     public static function has($name)
     {
         self::$readTimes++;
+
         return self::init()->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)

+ 197 - 116
thinkphp/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 @@ use JsonSerializable;
 
 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,74 +55,86 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
         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
+     * @param  mixed $items 新的数据
      * @return static
      */
-    public function flip()
+    public function merge($items)
     {
-        return new static(array_flip($this->items));
+        return new static(array_merge($this->items, $this->convertToArray($items)));
     }
 
     /**
-     * 比较数组,返回交集
-     *
-     * @param  mixed $items
+     * 比较数组,返回差集生成的新 Collection 实例
+     * @access public
+     * @param  mixed $items 做比较的数据
      * @return static
      */
-    public function intersect($items)
+    public function diff($items)
     {
-        return new static(array_intersect($this->items, $this->convertToArray($items)));
+        return new static(array_diff($this->items, $this->convertToArray($items)));
     }
 
     /**
-     * 返回数组中所有的键名
-     *
+     * 比较数组,返回交集组成的 Collection 新实例
+     * @access public
+     * @param  mixed $items 比较数据
      * @return static
      */
-    public function keys()
+    public function intersect($items)
     {
-        return new static(array_keys($this->items));
+        return new static(array_intersect($this->items, $this->convertToArray($items)));
     }
 
     /**
-     * 删除数组的最后一个元素(出栈)
-     *
+     * 返回并删除数据中的的最后一个元素(出栈)
+     * @access public
      * @return mixed
      */
     public function pop()
@@ -116,42 +143,74 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
     }
 
     /**
-     * 通过使用用户自定义函数,以字符串返回数组
-     *
-     * @param  callable $callback
-     * @param  mixed    $initial
+     * 返回并删除数据中首个元素
+     * @access public
      * @return mixed
      */
-    public function reduce(callable $callback, $initial = null)
+    public function shift()
     {
-        return array_reduce($this->items, $callback, $initial);
+        return array_shift($this->items);
     }
 
     /**
-     * 以相反的顺序返回数组。
-     *
-     * @return static
+     * 在数组开头插入一个元素
+     * @access public
+     * @param mixed $value 值
+     * @param mixed $key   键名
+     * @return void
      */
-    public function reverse()
+    public function unshift($value, $key = null)
     {
-        return new static(array_reverse($this->items));
+        if (is_null($key)) {
+            array_unshift($this->items, $value);
+        } else {
+            $this->items = [$key => $value] + $this->items;
+        }
     }
 
     /**
-     * 删除数组中首个元素,并返回被删除元素的值
-     *
+     * 在数组结尾插入一个元素
+     * @access public
+     * @param  mixed $value 值
+     * @param  mixed $key   键名
+     * @return void
+     */
+    public function push($value, $key = null)
+    {
+        if (is_null($key)) {
+            $this->items[] = $value;
+        } else {
+            $this->items[$key] = $value;
+        }
+    }
+
+    /**
+     * 通过使用用户自定义函数,以字符串返回数组
+     * @access public
+     * @param  callable $callback 回调函数
+     * @param  mixed    $initial  初始值
      * @return mixed
      */
-    public function shift()
+    public function reduce(callable $callback, $initial = null)
     {
-        return array_shift($this->items);
+        return array_reduce($this->items, $callback, $initial);
     }
 
     /**
-     * 把一个数组分割为新的数组块.
-     *
-     * @param  int  $size
-     * @param  bool $preserveKeys
+     * 以相反的顺序创建一个新的 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)
@@ -166,33 +225,21 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
     }
 
     /**
-     * 在数组开头插入一个元素
-     * @param mixed $value
-     * @param null  $key
-     * @return int
-     */
-    public function unshift($value, $key = null)
-    {
-        if (is_null($key)) {
-            array_unshift($this->items, $value);
-        } else {
-            $this->items = [$key => $value] + $this->items;
-        }
-    }
-
-    /**
-     * 给每个元素执行个回调
-     *
-     * @param  callable $callback
+     * 给数据中的每个元素执行回调
+     * @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;
             }
         }
@@ -201,46 +248,47 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
     }
 
     /**
-     * 用回调函数过滤数组中的元素
-     * @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;
             $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)) {
+            } 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;
@@ -249,34 +297,30 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
                 }
             }
         }
+
         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()
@@ -284,16 +328,15 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
         $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)
@@ -301,17 +344,35 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
         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)) {
@@ -321,33 +382,51 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
         }
     }
 
+    /**
+     * 删除指定键值
+     * @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)
@@ -355,22 +434,24 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
         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;
     }
 }

+ 99 - 63
thinkphp/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 @@ namespace think;
 
 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,108 @@ class Config
 
         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 {

+ 243 - 118
thinkphp/library/think/Console.php

@@ -20,20 +20,49 @@ 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,6 +76,12 @@ class Console
         "think\\console\\command\\optimize\\Schema",
     ];
 
+    /**
+     * Console constructor.
+     * @access public
+     * @param  string $name    名称
+     * @param  string $version 版本
+     */
     public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
     {
         $this->name    = $name;
@@ -60,38 +95,44 @@ class Console
         }
     }
 
+    /**
+     * 初始化 Console
+     * @access public
+     * @param  bool $run 是否运行 Console
+     * @return int|Console
+     */
     public static function init($run = true)
     {
         static $console;
+
         if (!$console) {
-            // 实例化console
+            // 实例化 console
             $console = new self('Think Console', '0.1');
+
             // 读取指令集
             if (is_file(CONF_PATH . 'command' . EXT)) {
                 $commands = include CONF_PATH . 'command' . EXT;
+
                 if (is_array($commands)) {
                     foreach ($commands as $command) {
-                        if (class_exists($command) && is_subclass_of($command, "\\think\\console\\Command")) {
-                            // 注册指令
-                            $console->add(new $command());
-                        }
+                        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')
     {
@@ -110,9 +151,9 @@ class Console
 
     /**
      * 执行当前的指令
+     * @access public
      * @return int
      * @throws \Exception
-     * @api
      */
     public function run()
     {
@@ -124,27 +165,21 @@ class Console
         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 +189,14 @@ class Console
 
     /**
      * 执行指令
-     * @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,6 +205,7 @@ class Console
 
         $name = $this->getCommandName($input);
 
+        // 获取帮助信息
         if (true === $input->hasParameterOption(['--help', '-h'])) {
             if (!$name) {
                 $name  = 'help';
@@ -182,25 +220,26 @@ class Console
             $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 +247,9 @@ class Console
     }
 
     /**
-     * Gets the help message.
-     * @return string A help message.
+     * 获取帮助信息
+     * @access public
+     * @return string
      */
     public function getHelp()
     {
@@ -217,27 +257,34 @@ class Console
     }
 
     /**
-     * 是否捕获异常
-     * @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 +294,21 @@ class Console
 
     /**
      * 设置名称
-     * @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 +317,30 @@ class Console
 
     /**
      * 设置版本
-     * @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('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
+            return sprintf(
+                '<info>%s</info> version <comment>%s</comment>',
+                $this->getName(),
+                $this->getVersion()
+            );
         }
 
         return '<info>Console Tool</info>';
@@ -288,7 +348,8 @@ class Console
 
     /**
      * 注册一个指令
-     * @param string $name
+     * @access public
+     * @param string $name 指令名称
      * @return Command
      */
     public function register($name)
@@ -297,32 +358,37 @@ class Console
     }
 
     /**
-     * 添加指令
-     * @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 +402,17 @@ class Console
 
     /**
      * 获取指令
-     * @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 +432,8 @@ class Console
 
     /**
      * 某个指令是否存在
-     * @param string $name 指令名称
+     * @access public
+     * @param  string $name 指令名称
      * @return bool
      */
     public function has($name)
@@ -373,16 +443,22 @@ class Console
 
     /**
      * 获取所有的命名空间
+     * @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 +466,25 @@ class Console
     }
 
     /**
-     * 查找注册命名空间中的名称或缩写。
+     * 查找注册命名空间中的名称或缩写
+     * @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 +500,14 @@ class Console
         }
 
         $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 +515,22 @@ class Console
 
     /**
      * 查找指令
-     * @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 +550,7 @@ class Console
 
         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 +561,9 @@ class Console
         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 +571,20 @@ class Console
 
     /**
      * 获取所有的指令
-     * @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 +592,8 @@ class Console
 
     /**
      * 获取可能的指令名
-     * @param array $names
+     * @access public
+     * @param  array $names 指令名
      * @return array
      */
     public static function getAbbreviations($names)
@@ -511,7 +601,7 @@ class Console
         $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 +610,11 @@ class Console
     }
 
     /**
-     * 配置基于用户的参数和选项的输入和输出实例。
-     * @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 +643,10 @@ class Console
 
     /**
      * 执行指令
-     * @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 +656,9 @@ class Console
     }
 
     /**
-     * 获取指令的基础名称
-     * @param Input $input
+     * 获取指令的名称
+     * @access protected
+     * @param  Input $input 输入实例
      * @return string
      */
     protected function getCommandName(Input $input)
@@ -574,6 +668,7 @@ class Console
 
     /**
      * 获取默认输入定义
+     * @access protected
      * @return InputDefinition
      */
     protected function getDefaultInputDefinition()
@@ -591,41 +686,55 @@ class Console
     }
 
     /**
-     * 设置默认命令
-     * @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 +747,17 @@ class Console
 
     /**
      * 查找可替代的建议
-     * @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;
         $alternatives = [];
-
         $collectionParts = [];
+
         foreach ($collection as $item) {
             $collectionParts[$item] = explode(':', $item);
         }
@@ -655,6 +765,7 @@ class Console
         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 +774,14 @@ class Console
                 }
 
                 $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 +790,18 @@ class Console
 
         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 +809,28 @@ class Console
 
     /**
      * 设置默认的指令
-     * @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 {

+ 58 - 48
thinkphp/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 @@ class Controller
         }
     }
 
-    // 初始化
+    /**
+     * 初始化操作
+     * @access protected
+     */
     protected function _initialize()
     {
     }
@@ -74,8 +79,9 @@ class Controller
     /**
      * 前置操作
      * @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 @@ class Controller
             if (is_string($options['only'])) {
                 $options['only'] = explode(',', $options['only']);
             }
+
             if (!in_array($this->request->action(), $options['only'])) {
                 return;
             }
@@ -90,6 +97,7 @@ class Controller
             if (is_string($options['except'])) {
                 $options['except'] = explode(',', $options['except']);
             }
+
             if (in_array($this->request->action(), $options['except'])) {
                 return;
             }
@@ -101,10 +109,10 @@ class Controller
     /**
      * 加载模板输出
      * @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 @@ class Controller
     /**
      * 渲染内容输出
      * @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 @@ class Controller
     /**
      * 模板变量赋值
      * @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 @@ class Controller
     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,21 @@ class Controller
             $v = Loader::validate();
             $v->rule($validate);
         } else {
+            // 支持场景
             if (strpos($validate, '.')) {
-                // 支持场景
                 list($validate, $scene) = explode('.', $validate);
             }
+
             $v = Loader::validate($validate);
-            if (!empty($scene)) {
-                $v->scene($scene);
-            }
-        }
-        // 是否批量验证
-        if ($batch || $this->batchValidate) {
-            $v->batch(true);
-        }
 
-        if (is_array($message)) {
-            $v->message($message);
+            !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 +213,11 @@ class Controller
         if (!$v->check($data)) {
             if ($this->failException) {
                 throw new ValidateException($v->getError());
-            } else {
-                return $v->getError();
             }
-        } else {
-            return true;
+
+            return $v->getError();
         }
+
+        return true;
     }
 }

+ 104 - 60
thinkphp/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 @@ namespace think;
 
 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 @@ class Cookie
         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,40 @@ class Cookie
             } 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,33 +127,39 @@ class Cookie
         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;
 
@@ -145,80 +167,102 @@ class Cookie
             // 获取全部
             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;
+
         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);
         }
     }
-
 }

+ 50 - 26
thinkphp/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 @@ use think\db\Query;
  */
 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 @@ class Db
         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,30 +126,27 @@ class Db
         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&param2=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'],
             'username' => isset($info['user']) ? $info['user'] : '',
@@ -144,13 +162,19 @@ class Db
         } 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);
     }
 }

+ 84 - 44
thinkphp/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,21 +16,27 @@ use think\response\Redirect;
 
 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['peak'][$name] = memory_get_peak_usage();
@@ -39,23 +45,26 @@ class Debug
 
     /**
      * 统计某个区间的时间(微秒)使用情况 返回值以秒为单位
-     * @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 @@ class Debug
 
     /**
      * 获取当前访问的吞吐率情况
+     * @access public
      * @return string
      */
     public static function getThroughputRate()
@@ -73,9 +83,10 @@ class Debug
 
     /**
      * 记录区间的内存使用情况
-     * @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,19 +94,23 @@ class Debug
         if (!isset(self::$mem['mem'][$end])) {
             self::$mem['mem'][$end] = memory_get_usage();
         }
+
         $size = self::$mem['mem'][$end] - self::$mem['mem'][$start];
         $a    = ['B', 'KB', 'MB', 'GB', 'TB'];
         $pos  = 0;
+
         while ($size >= 1024) {
             $size /= 1024;
             $pos++;
         }
+
         return round($size, $dec) . " " . $a[$pos];
     }
 
     /**
      * 统计从开始到统计时的内存使用情况
-     * @param integer|string $dec 小数位
+     * @access public
+     * @param  integer $dec 小数位
      * @return string
      */
     public static function getUseMem($dec = 2)
@@ -103,103 +118,128 @@ class Debug
         $size = memory_get_usage() - THINK_START_MEM;
         $a    = ['B', 'KB', 'MB', 'GB', 'TB'];
         $pos  = 0;
+
         while ($size >= 1024) {
             $size /= 1024;
             $pos++;
         }
+
         return round($size, $dec) . " " . $a[$pos];
     }
 
     /**
      * 统计区间的内存峰值情况
-     * @param string            $start 开始标签
-     * @param string            $end 结束标签
-     * @param integer|string    $dec 小数位
-     * @return mixed
+     * @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;
+
         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 = '<pre>' . $label . $output . '</pre>';
         }
+
         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, '</body>');
                 if (false !== $pos) {
                     $content = substr($content, 0, $pos) . $output . substr($content, $pos);

+ 8 - 5
thinkphp/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;
     }
 }

+ 44 - 28
thinkphp/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 @@ class Error
     }
 
     /**
-     * Exception Handler
-     * @param  \Exception|\Throwable $e
+     * 异常处理
+     * @access public
+     * @param  \Exception|\Throwable $e 异常
+     * @return void
      */
     public static function appException($e)
     {
@@ -40,44 +43,50 @@ class Error
             $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 +95,8 @@ class Error
 
     /**
      * 确定错误类型是否致命
-     *
-     * @param  int $type
+     * @access protected
+     * @param  int $type 错误类型
      * @return bool
      */
     protected static function isFatal($type)
@@ -96,25 +105,32 @@ class Error
     }
 
     /**
-     * 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 && 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;
     }
 }

+ 11 - 10
thinkphp/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 @@ namespace think;
 
 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 @@ class Exception extends \Exception
     }
 
     /**
-     * 获取异常额外Debug数据
+     * 获取异常额外 Debug 数据
      * 主要用于输出到异常页面便于调试
-     * @return array 由setData设置的Debug数据
+     * @access public
+     * @return array
      */
     final public function getData()
     {
         return $this->data;
     }
-    
+
 }

+ 156 - 93
thinkphp/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 @@ use SplFileObject;
 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 @@ class File extends SplFileObject
     }
 
     /**
-     * 是否测试
-     * @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 @@ class File extends SplFileObject
 
     /**
      * 获取上传文件的文件名
+     * @access public
      * @return string
      */
     public function getSaveName()
@@ -84,94 +116,101 @@ class File extends SplFileObject
 
     /**
      * 设置上传文件的保存文件名
-     * @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 @@ class File extends SplFileObject
 
         /* 检查文件大小 */
         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 @@ class File extends SplFileObject
 
     /**
      * 检测上传文件后缀
-     * @param  array|string   $ext    允许后缀
+     * @access public
+     * @param  array|string $ext 允许后缀
      * @return bool
      */
     public function checkExt($ext)
@@ -215,77 +255,76 @@ class File extends SplFileObject
         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 @@ class File extends SplFileObject
 
         // 检测合法性
         if (!$this->isValid()) {
-            $this->error = '非法上传文件';
+            $this->error = 'upload illegal files';
             return false;
         }
 
@@ -305,6 +344,7 @@ class File extends SplFileObject
         if (!$this->check()) {
             return false;
         }
+
         $path = rtrim($path, DS) . DS;
         // 文件保存命名规则
         $saveName = $this->buildSaveName($savename);
@@ -315,9 +355,9 @@ class File extends SplFileObject
             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 @@ class File extends SplFileObject
         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 {
@@ -362,52 +404,73 @@ class File extends SplFileObject
                         }
                 }
             }
-        } 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);

+ 37 - 25
thinkphp/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 @@ namespace think;
 
 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 @@ class Hook
 
     /**
      * 批量导入插件
-     * @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,55 +65,59 @@ class Hook
 
     /**
      * 获取插件信息
-     * @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';
@@ -126,10 +136,12 @@ class Hook
             $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;
     }
 

+ 84 - 42
thinkphp/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,114 +13,148 @@ namespace think;
 
 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);
         $value = isset(self::$lang[$range][$key]) ? self::$lang[$range][$key] : $name;
 
@@ -145,45 +179,50 @@ class Lang
             }
 
         }
+
         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'])) {
             // 自动侦测浏览器语言
             preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
             $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 (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 +231,9 @@ class Lang
     }
 
     /**
-     * 设置语言的cookie保存变量
-     * @param string $var 变量名称
+     * 设置语言的 cookie 保存变量
+     * @access public
+     * @param  string $var 变量名称
      * @return void
      */
     public static function setLangCookieVar($var)
@@ -202,8 +242,9 @@ class Lang
     }
 
     /**
-     * 设置语言的cookie的过期时间
-     * @param string $expire 过期时间
+     * 设置语言的 cookie 的过期时间
+     * @access public
+     * @param  string $expire 过期时间
      * @return void
      */
     public static function setLangCookieExpire($expire)
@@ -213,7 +254,8 @@ class Lang
 
     /**
      * 设置允许的语言列表
-     * @param array $list 语言列表
+     * @access public
+     * @param  array $list 语言列表
      * @return void
      */
     public static function setAllowLangList($list)

+ 266 - 179
thinkphp/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 @@ use think\exception\ClassNotFoundException;
 
 class Loader
 {
+    /**
+     * @var array 实例数组
+     */
     protected static $instance = [];
-    // 类名映射
+
+    /**
+     * @var array 类名映射
+     */
     protected static $map = [];
 
-    // 命名空间别名
+    /**
+     * @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 = [];
 
-    // 自动加载的文件
+    /**
+     * @var array 自动加载的文件
+     */
     private static $autoloadFiles = [];
 
-    // 自动加载
+    /**
+     * 自动加载
+     * @access public
+     * @param  string $class 类名
+     * @return bool
+     */
     public static function autoload($class)
     {
         // 检测命名空间别名
@@ -49,33 +80,33 @@ class Loader
         }
 
         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];
         }
 
         // 查找 PSR-4
         $logicalPathPsr4 = strtr($class, '\\', DS) . EXT;
+        $first           = $class[0];
 
-        $first = $class[0];
         if (isset(self::$prefixLengthsPsr4[$first])) {
             foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) {
                 if (0 === strpos($class, $prefix)) {
@@ -97,7 +128,7 @@ class Loader
 
         // 查找 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,10 +155,17 @@ class Loader
             }
         }
 
+        // 找不到则设置映射为 false 并返回
         return self::$map[$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)) {
@@ -137,7 +175,13 @@ class Loader
         }
     }
 
-    // 注册命名空间
+    /**
+     * 注册命名空间
+     * @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 @@ class Loader
         }
     }
 
-    // 添加Ps0空间
+    /**
+     * 添加 PSR-0 命名空间
+     * @access private
+     * @param  array|string $prefix  空间前缀
+     * @param  array        $paths   路径
+     * @param  bool         $prepend 预先设置的优先级更高
+     * @return void
+     */
     private static function addPsr0($prefix, $paths, $prepend = false)
     {
         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]
-            );
+
         } 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,43 @@ class Loader
         }
     }
 
-    // 注册自动加载机制
-    public static function register($autoload = '')
+    /**
+     * 注册自动加载机制
+     * @access public
+     * @param  callable $autoload 自动加载处理方法
+     * @return void
+     */
+    public static function register($autoload = null)
     {
         // 注册系统自动加载
         spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
+
         // 注册命名空间定义
         self::addNamespace([
             'think'    => LIB_PATH . 'think' . DS,
             'behavior' => LIB_PATH . 'behavior' . DS,
             'traits'   => LIB_PATH . 'traits' . DS,
         ]);
+
         // 加载类库映射文件
         if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {
             self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));
         }
 
-        // Composer自动加载支持
+        // Composer 自动加载支持
         if (is_dir(VENDOR_PATH . 'composer')) {
             self::registerComposerLoader();
         }
 
-        // 自动加载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')) {
@@ -283,6 +331,7 @@ class Loader
             if ($classMap) {
                 self::addClassMap($classMap);
             }
+
         }
 
         if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) {
@@ -297,17 +346,19 @@ class Loader
     }
 
     /**
-     * 导入所需的类库 同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);
+
         if (isset($_file[$key])) {
             return true;
         }
@@ -319,7 +370,7 @@ class Loader
                 // 注册的命名空间
                 $baseUrl = self::$prefixDirsPsr4[$name . '\\'];
             } elseif ('@' == $name) {
-                //加载当前模块应用类库
+                // 加载当前模块应用类库
                 $baseUrl = App::$modulePath;
             } elseif (is_dir(EXTEND_PATH . $name)) {
                 $baseUrl = EXTEND_PATH . $name . DS;
@@ -330,11 +381,11 @@ class Loader
         } 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 +393,154 @@ class Loader
             $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,10 +550,11 @@ class Loader
 
     /**
      * 远程调用模块的操作方法 参数格式 [模块/控制器/]操作
-     * @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)
@@ -494,6 +563,7 @@ class Loader
         $action = $info['basename'];
         $module = '.' != $info['dirname'] ? $info['dirname'] : Request::instance()->controller();
         $class  = self::controller($module, $layer, $appendSuffix);
+
         if ($class) {
             if (is_scalar($vars)) {
                 if (strpos($vars, '=')) {
@@ -502,16 +572,20 @@ class Loader
                     $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 +594,38 @@ class Loader
             $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) : '');
+
+        $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;
+
+        return App::$namespace . '\\' .
+            ($module ? $module . '\\' : '') .
+            $layer . '\\' . $path . $class;
     }
 
     /**
      * 初始化类的实例
+     * @access public
      * @return void
      */
     public static function clearInstance()
@@ -553,10 +634,11 @@ class Loader
     }
 }
 
+// 作用范围隔离
+
 /**
- * 作用范围隔离
- *
- * @param $file
+ * include
+ * @param  string $file 文件路径
  * @return mixed
  */
 function __include_file($file)
@@ -564,6 +646,11 @@ function __include_file($file)
     return include $file;
 }
 
+/**
+ * require
+ * @param  string $file 文件路径
+ * @return mixed
+ */
 function __require_file($file)
 {
     return require $file;

+ 102 - 78
thinkphp/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,12 +17,12 @@ use think\exception\ClassNotFoundException;
  * Class Log
  * @package think
  *
- * @method void log($msg) static
- * @method void error($msg) static
- * @method void info($msg) static
- * @method void sql($msg) static
- * @method void notice($msg) static
- * @method void alert($msg) static
+ * @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
 {
@@ -34,41 +34,60 @@ class Log
     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 @@ class Log
 
     /**
      * 记录调试信息
-     * @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 @@ class Log
     }
 
     /**
-     * 当前日志记录的授权key
-     * @param string $key 授权key
+     * 设置当前日志记录的授权 key
+     * @access public
+     * @param  string $key 授权 key
      * @return void
      */
     public static function key($key)
@@ -111,102 +132,105 @@ class Log
 
     /**
      * 检查日志写入权限
-     * @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)) {
+            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)) {
             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);
         }
     }
 

+ 116 - 35
thinkphp/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,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
     protected $type = [];
     // 是否为更新数据
     protected $isUpdate = false;
+    // 是否强制更新所有数据
+    protected $force = false;
     // 更新条件
     protected $updateWhere;
     // 验证失败是否抛出异常
@@ -122,6 +128,16 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
         } 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;
 
@@ -262,7 +278,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
     public function setParent($model)
     {
         $this->parent = $model;
-
         return $this;
     }
 
@@ -338,6 +353,18 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
     }
 
     /**
+     * 更新是否强制写入数据 而不做比较
+     * @access public
+     * @param bool $force
+     * @return $this
+     */
+    public function force($force = true)
+    {
+        $this->force = $force;
+        return $this;
+    }
+
+    /**
      * 修改器 设置数据对象值
      * @access public
      * @param string $name  属性名
@@ -692,7 +719,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
                 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);
                 }
             }
         }
@@ -826,7 +853,29 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
                     $relation   = $this->getAttr($key);
                     $item[$key] = $relation->append([$attr])->toArray();
                 } else {
-                    $item[$name] = $this->getAttr($name);
+                    $relation = Loader::parseName($name, 1, false);
+                    if (method_exists($this, $relation)) {
+                        $modelRelation = $this->$relation();
+                        $value         = $this->getRelationData($modelRelation);
+
+                        if (method_exists($modelRelation, 'getBindAttr')) {
+                            $bindAttr = $modelRelation->getBindAttr();
+                            if ($bindAttr) {
+                                foreach ($bindAttr as $key => $attr) {
+                                    $key = is_numeric($key) ? $attr : $key;
+                                    if (isset($this->data[$key])) {
+                                        throw new Exception('bind attr has exists:' . $key);
+                                    } else {
+                                        $item[$key] = $value ? $value->getAttr($attr) : null;
+                                    }
+                                }
+                                continue;
+                            }
+                        }
+                        $item[$name] = $value;
+                    } else {
+                        $item[$name] = $this->getAttr($name);
+                    }
                 }
             }
         }
@@ -1021,12 +1070,17 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
                 }
             }
 
-            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;
             }
 
             // 检测字段
@@ -1068,16 +1122,17 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
             // 检测字段
             $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, false, false, $sequence);
             } else {
-                $result = $this->getQuery()->insert($this->data);
+                $result = $this->getQuery()->insert($this->data, false, false, $sequence);
             }
 
             // 获取自动增长主键
-            if ($result && is_string($pk) && (!isset($this->data[$pk]) || '' == $this->data[$pk])) {
-                $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;
+                    }
                 }
             }
 
@@ -1114,10 +1169,18 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
             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;
     }
 
@@ -1143,12 +1206,16 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
      */
     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)) {
             // 只读字段不允许更新
@@ -1254,14 +1321,14 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
                 $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;
@@ -1271,7 +1338,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
     /**
      * 设置允许写入的字段
      * @access public
-     * @param mixed $field 允许写入的字段 如果为true只允许写入数据表字段
+     * @param string|array $field 允许写入的字段 如果为true只允许写入数据表字段
      * @return $this
      */
     public function allowField($field)
@@ -1284,6 +1351,21 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
     }
 
     /**
+     * 设置排除写入的字段
+     * @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
      * @param mixed $field 只读字段
@@ -1621,14 +1703,14 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
     {
         $model = new static();
         $query = $model->db();
-        if (is_array($data) && key($data) !== 0) {
+        if (empty($data) && 0 !== $data) {
+            return 0;
+        } elseif (is_array($data) && key($data) !== 0) {
             $query->where($data);
             $data = null;
         } elseif ($data instanceof \Closure) {
             call_user_func_array($data, [ & $query]);
             $data = null;
-        } elseif (empty($data) && 0 !== $data) {
-            return 0;
         }
         $resultSet = $query->select($data);
         $count     = 0;
@@ -1704,13 +1786,14 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
     /**
      * 根据关联条件查询当前模型
      * @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);
     }
 
     /**
@@ -1976,7 +2059,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
             $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 {
@@ -2002,7 +2085,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
             $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 {
@@ -2040,7 +2123,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
     public function __call($method, $args)
     {
         $query = $this->db(true, false);
-
         if (method_exists($this, 'scope' . $method)) {
             // 动态调用命名范围
             $method = 'scope' . $method;
@@ -2056,7 +2138,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
     {
         $model = new static();
         $query = $model->db();
-
         if (method_exists($model, 'scope' . $method)) {
             // 动态调用命名范围
             $method = 'scope' . $method;

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

+ 5 - 2
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -1265,7 +1265,7 @@ class Request
      * @param boolean   $adv 是否进行高级模式获取(有可能被伪装)
      * @return mixed
      */
-    public function ip($type = 0, $adv = false)
+    public function ip($type = 0, $adv = true)
     {
         $type      = $type ? 1 : 0;
         static $ip = null;
@@ -1342,6 +1342,9 @@ class Request
      */
     public function host()
     {
+        if (isset($_SERVER['HTTP_X_REAL_HOST'])) {
+            return $_SERVER['HTTP_X_REAL_HOST'];
+        }
         return $this->server('HTTP_HOST');
     }
 

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

+ 3 - 3
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -335,9 +335,9 @@ class Route
             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;
                     }
                 }

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

+ 7 - 5
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -12,6 +12,7 @@
 namespace think;
 
 use think\exception\TemplateNotFoundException;
+use think\template\TagLib;
 
 /**
  * ThinkPHP分离出来的模板引擎
@@ -121,7 +122,7 @@ class Template
      * 模板引擎配置项
      * @access public
      * @param array|string $config
-     * @return void|array
+     * @return string|void|array
      */
     public function config($config)
     {
@@ -184,7 +185,7 @@ class Template
         }
         $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 +236,7 @@ class Template
      * @access public
      * @param mixed     $name 布局模板名称 false 则关闭布局
      * @param string    $replace 布局模板内容替换标识
-     * @return object
+     * @return Template
      */
     public function layout($name, $replace = '')
     {
@@ -689,6 +690,7 @@ class Template
         } else {
             $className = '\\think\\template\\taglib\\' . ucwords($tagLib);
         }
+        /** @var Taglib $tLib */
         $tLib = new $className($this);
         $tLib->parseTag($content, $hide ? '' : $tagLib);
         return;
@@ -1071,7 +1073,7 @@ class Template
             } 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)) {

+ 5 - 4
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -118,7 +118,7 @@ class Url
             $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);
                 }
             }
@@ -135,7 +135,7 @@ class Url
         if (!empty($vars)) {
             // 添加参数
             if (Config::get('url_common_param')) {
-                $vars = urldecode(http_build_query($vars));
+                $vars = http_build_query($vars);
                 $url .= $suffix . '?' . $vars . $anchor;
             } else {
                 $paramType = Config::get('url_param_type');
@@ -300,9 +300,10 @@ class Url
             if (empty($pattern)) {
                 return [$url, $domain, $suffix];
             }
+            $type = Config::get('url_common_param');
             foreach ($pattern as $key => $val) {
                 if (isset($vars[$key])) {
-                    $url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key . '', '<' . $key . '>'], urlencode($vars[$key]), $url);
+                    $url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key . '', '<' . $key . '>'], $type ? $vars[$key] : urlencode($vars[$key]), $url);
                     unset($vars[$key]);
                     $result = [$url, $domain, $suffix];
                 } elseif (2 == $val) {

+ 141 - 97
thinkphp/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,55 @@ 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',
+        'mobile'      => ':attribute not a valid mobile',
+        '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',
+        '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',
     ];
 
     // 当前验证场景
@@ -341,6 +341,41 @@ class Validate
     }
 
     /**
+     * 根据验证规则验证数据
+     * @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
      * @param string    $field  字段名
@@ -364,25 +399,7 @@ class Validate
                 $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)) {
@@ -419,6 +436,39 @@ class Validate
     }
 
     /**
+     * 获取当前验证类型及规则
+     * @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
      * @param mixed     $value 字段值
@@ -681,21 +731,17 @@ class Validate
      */
     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 +754,17 @@ class Validate
      */
     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 +777,17 @@ class Validate
      */
     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;
         }
     }
 
@@ -846,13 +887,14 @@ class Validate
             $map[$key] = $data[$field];
         }
 
-        $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;
         }
@@ -1233,11 +1275,13 @@ class Validate
         } 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, ':')) {

+ 12 - 9
thinkphp/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,7 +33,7 @@ class View
     public function __construct($engine = [], $replace = [])
     {
         // 初始化模板引擎
-        $this->engine((array) $engine);
+        $this->engine($engine);
         // 基础替换字符串
         $request = Request::instance();
         $base    = $request->root();
@@ -155,18 +155,21 @@ class View
         ob_implicit_flush(0);
 
         // 渲染输出
-        $method = $renderContent ? 'display' : 'fetch';
-        $this->engine->$method($template, $vars, $config);
+        try {
+            $method = $renderContent ? 'display' : 'fetch';
+            // 允许用户自定义模板的字符串替换
+            $replace = array_merge($this->replace, $replace, $this->engine->config('tpl_replace_string'));
+            $this->engine->config('tpl_replace_string', $replace);
+            $this->engine->$method($template, $vars, $config);
+        } catch (\Exception $e) {
+            ob_end_clean();
+            throw $e;
+        }
 
         // 获取并清空缓存
         $content = ob_get_clean();
         // 内容过滤标签
         Hook::listen('view_filter', $content);
-        // 允许用户自定义模板的字符串替换
-        $replace = array_merge($this->replace, $replace);
-        if (!empty($replace)) {
-            $content = strtr($content, $replace);
-        }
         return $content;
     }
 

+ 8 - 2
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -120,8 +120,10 @@ abstract class Driver
     public function remember($name, $value, $expire = null)
     {
         if (!$this->has($name)) {
-            while ($this->has($name . '_lock')) {
+            $time = time();
+            while ($time + 5 > time() && $this->has($name . '_lock')) {
                 // 存在锁定则等待
+                usleep(200000);
             }
 
             try {
@@ -136,6 +138,10 @@ abstract class Driver
             } catch (\Exception $e) {
                 // 解锁
                 $this->rm($name . '_lock');
+                throw $e;
+            } catch (\throwable $e) {
+                $this->rm($name . '_lock');
+                throw $e;
             }
         } else {
             $value = $this->get($name);

+ 7 - 4
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -109,7 +109,7 @@ class File extends Driver
         $content = file_get_contents($filename);
         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;
             }
             $content = substr($content, 32);
@@ -189,7 +189,7 @@ class File extends Driver
         if ($this->has($name)) {
             $value = $this->get($name) - $step;
         } else {
-            $value = $step;
+            $value = -$step;
         }
         return $this->set($name, $value, 0) ? $value : false;
     }
@@ -226,7 +226,10 @@ class File extends Driver
         $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);

+ 2 - 2
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -149,7 +149,7 @@ class Lite extends Driver
         if ($this->has($name)) {
             $value = $this->get($name) - $step;
         } else {
-            $value = $step;
+            $value = -$step;
         }
         return $this->set($name, $value, 0) ? $value : false;
     }

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

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

+ 24 - 15
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -46,9 +46,12 @@ class Redis extends Driver
         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']);
@@ -80,12 +83,17 @@ class Redis extends Driver
     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<xiaobo.sun@qq.com>
-        return (null === $jsonData) ? $value : $jsonData;
+
+        try {
+            $result = 0 === strpos($value, 'think_serialize:') ? unserialize(substr($value, 16)) : $value;
+        } catch (\Exception $e) {
+            $result = $default;
+        }
+
+        return $result;
     }
 
     /**
@@ -107,10 +115,9 @@ class Redis extends Driver
         if ($this->tag && !$this->has($name)) {
             $first = true;
         }
-        $key = $this->getCacheKey($name);
-        //对数组/对象数据进行缓存处理,保证数据完整性  byron sampson<xiaobo.sun@qq.com>
-        $value = (is_object($value) || is_array($value)) ? json_encode($value) : $value;
-        if (is_int($expire) && $expire) {
+        $key   = $this->getCacheKey($name);
+        $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 @@ class Redis extends Driver
     /**
      * 自增缓存(针对数值缓存)
      * @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);
     }
 

+ 2 - 2
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -159,7 +159,7 @@ class Sqlite extends Driver
         if ($this->has($name)) {
             $value = $this->get($name) - $step;
         } else {
-            $value = $step;
+            $value = -$step;
         }
         return $this->set($name, $value, 0) ? $value : false;
     }

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

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

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

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

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

+ 5 - 0
thinkphp/library/think/console/command/optimize/Route.php

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

+ 3 - 3
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -43,7 +43,7 @@ abstract class Rest
         if ('' == $ext) {
             // 自动检测资源类型
             $this->type = $request->type();
-        } elseif (!preg_match('/\(' . $this->restTypeList . '\)$/i', $ext)) {
+        } elseif (!preg_match('/(' . $this->restTypeList . ')$/i', $ext)) {
             // 资源类型非法 则用默认资源类型访问
             $this->type = $this->restDefaultType;
         } else {
@@ -51,7 +51,7 @@ abstract class Rest
         }
         // 请求方式检测
         $method = strtolower($request->method());
-        if (false === stripos($this->restMethodList, $method)) {
+        if (!preg_match('/(' . $this->restMethodList . ')$/i', $method)) {
             // 请求方式非法 则用默认请求方法
             $method = $this->restDefaultMethod;
         }

+ 25 - 13
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -26,7 +26,7 @@ abstract class Builder
     protected $exp = ['eq' => '=', 'neq' => '<>', 'gt' => '>', 'egt' => '>=', 'lt' => '<', 'elt' => '<=', 'notlike' => 'NOT LIKE', 'not like' => 'NOT LIKE', 'like' => 'LIKE', 'in' => 'IN', 'exp' => 'EXP', 'notin' => 'NOT IN', 'not in' => 'NOT IN', 'between' => 'BETWEEN', 'not between' => 'NOT BETWEEN', 'notbetween' => 'NOT BETWEEN', 'exists' => 'EXISTS', 'notexists' => 'NOT EXISTS', 'not exists' => 'NOT EXISTS', 'null' => 'NULL', 'notnull' => 'NOT NULL', 'not null' => 'NOT NULL', '> time' => '> TIME', '< time' => '< TIME', '>= time' => '>= TIME', '<= time' => '<= TIME', 'between time' => 'BETWEEN TIME', 'not between time' => 'NOT BETWEEN TIME', 'notbetween time' => 'NOT BETWEEN TIME'];
 
     // SQL表达式
-    protected $selectSql    = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%';
+    protected $selectSql    = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%UNION%%JOIN%%WHERE%%GROUP%%HAVING%%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%';
@@ -110,8 +110,18 @@ abstract class Builder
                 }
             } 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 ($val[0]) {
+                    case 'exp':
+                        $result[$item] = $val[1];
+                        break;
+                    case 'inc':
+                        $result[$item] = $this->parseKey($val[1]) . '+' . floatval($val[2]);
+                        break;
+                    case 'dec':
+                        $result[$item] = $this->parseKey($val[1]) . '-' . floatval($val[2]);
+                        break;
+                }
             } elseif (is_scalar($val)) {
                 // 过滤非标量数据
                 if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) {
@@ -630,12 +640,12 @@ abstract class Builder
         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);
     }
 
     /**
@@ -749,7 +759,7 @@ abstract class Builder
             $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']) {
@@ -770,19 +780,21 @@ abstract class Builder
             }
             $value    = array_values($data);
             $values[] = 'SELECT ' . implode(',', $value);
+
+            if (!isset($insertFields)) {
+                $insertFields = array_map([$this, 'parseKey'], array_keys($data));
+            }
         }
-        $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;
     }
 
     /**

+ 35 - 15
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -354,15 +354,15 @@ abstract class Connection
             $this->bind = $bind;
         }
 
-        // 释放前次的查询结果
-        if (!empty($this->PDOStatement)) {
-            $this->free();
-        }
-
         Db::$queryTimes++;
         try {
             // 调试开始
             $this->debug(true);
+
+            // 释放前次的查询结果
+            if (!empty($this->PDOStatement)) {
+                $this->free();
+            }
             // 预处理
             if (empty($this->PDOStatement)) {
                 $this->PDOStatement = $this->linkID->prepare($sql);
@@ -386,6 +386,11 @@ abstract class Connection
                 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);
@@ -416,15 +421,15 @@ abstract class Connection
             $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->queryString != $sql) {
+                $this->free();
+            }
             // 预处理
             if (empty($this->PDOStatement)) {
                 $this->PDOStatement = $this->linkID->prepare($sql);
@@ -449,6 +454,11 @@ abstract class Connection
                 return $this->close()->execute($sql, $bind);
             }
             throw new PDOException($e, $this->config, $this->getLastsql());
+        } catch (\Throwable $e) {
+            if ($this->isBreak($e)) {
+                return $this->close()->execute($sql, $bind);
+            }
+            throw $e;
         } catch (\Exception $e) {
             if ($this->isBreak($e)) {
                 return $this->close()->execute($sql, $bind);
@@ -466,6 +476,10 @@ abstract class Connection
      */
     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;
@@ -478,8 +492,8 @@ abstract class Connection
             $sql = is_numeric($key) ?
             substr_replace($sql, $value, strpos($sql, '?'), 1) :
             str_replace(
-                [':' . $key . ')', ':' . $key . ',', ':' . $key . ' '],
-                [$value . ')', $value . ',', $value . ' '],
+                [':' . $key . ')', ':' . $key . ',', ':' . $key . ' ', ':' . $key . PHP_EOL],
+                [$value . ')', $value . ',', $value . ' ', $value . PHP_EOL],
                 $sql . ' ');
         }
         return rtrim($sql);
@@ -648,6 +662,11 @@ abstract class Connection
                 return $this->close()->startTrans();
             }
             throw $e;
+        } catch (\Error $e) {
+            if ($this->isBreak($e)) {
+                return $this->close()->startTrans();
+            }
+            throw $e;
         }
     }
 
@@ -725,7 +744,7 @@ abstract class Connection
      * @param array $sqlArray SQL批处理指令
      * @return boolean
      */
-    public function batchQuery($sqlArray = [])
+    public function batchQuery($sqlArray = [], $bind = [])
     {
         if (!is_array($sqlArray)) {
             return false;
@@ -734,7 +753,7 @@ abstract class Connection
         $this->startTrans();
         try {
             foreach ($sqlArray as $sql) {
-                $this->execute($sql);
+                $this->execute($sql, $bind);
             }
             // 提交事务
             $this->commit();
@@ -742,6 +761,7 @@ abstract class Connection
             $this->rollback();
             throw $e;
         }
+
         return true;
     }
 

+ 116 - 81
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -312,9 +312,9 @@ class Query
      * @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);
     }
 
     /**
@@ -534,22 +534,24 @@ class Query
      * 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->value('MIN(' . $field . ') AS tp_min', 0, $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->value('MAX(' . $field . ') AS tp_max', 0, $force);
     }
 
     /**
@@ -607,7 +609,7 @@ class Query
                 return true;
             }
         }
-        return $this->setField($field, ['exp', $field . '+' . $step]);
+        return $this->setField($field, ['inc', $field, $step]);
     }
 
     /**
@@ -635,8 +637,9 @@ class Query
                 $this->options = [];
                 return true;
             }
+            return $this->setField($field, ['inc', $field, $step]);
         }
-        return $this->setField($field, ['exp', $field . '-' . $step]);
+        return $this->setField($field, ['dec', $field, $step]);
     }
 
     /**
@@ -704,7 +707,8 @@ class Query
     {
         // 传入的表名为数组
         if (is_array($join)) {
-            list($table, $alias) = each($join);
+            $table = key($join);
+            $alias = current($join);
         } else {
             $join = trim($join);
             if (false !== strpos($join, '(')) {
@@ -726,10 +730,13 @@ class Query
                 }
             }
         }
-        if (isset($alias)) {
+        if (isset($alias) && $table != $alias) {
             if (isset($this->options['alias'][$table])) {
                 $table = $table . '@think' . uniqid();
+            } elseif ($this->gettable() == $table) {
+                $table = $table . '@think' . uniqid();
             }
+
             $table = [$table => $alias];
             $this->alias($table);
         }
@@ -828,7 +835,7 @@ class Query
     {
         $fields = is_string($field) ? explode(',', $field) : $field;
         foreach ($fields as $field) {
-            $this->data($field, ['exp', $field . '+' . $step]);
+            $this->data($field, ['inc', $field, $step]);
         }
         return $this;
     }
@@ -844,7 +851,7 @@ class Query
     {
         $fields = is_string($field) ? explode(',', $field) : $field;
         foreach ($fields as $field) {
-            $this->data($field, ['exp', $field . '-' . $step]);
+            $this->data($field, ['dec', $field, $step]);
         }
         return $this;
     }
@@ -1239,6 +1246,7 @@ class Query
         $logic = strtoupper($logic);
         if (isset($this->options['where'][$logic][$field])) {
             unset($this->options['where'][$logic][$field]);
+            unset($this->options['multi'][$logic][$field]);
         }
         return $this;
     }
@@ -1523,7 +1531,12 @@ class Query
     {
         if (is_array($alias)) {
             foreach ($alias as $key => $val) {
-                $this->options['alias'][$key] = $val;
+                if (false !== strpos($key, '__')) {
+                    $table = $this->parseSqlTable($key);
+                } else {
+                    $table = $key;
+                }
+                $this->options['alias'][$table] = $val;
             }
         } else {
             if (isset($this->options['table'])) {
@@ -1651,46 +1664,49 @@ class Query
      * 查询日期或者时间
      * @access public
      * @param string       $field 日期字段名
-     * @param string       $op    比较运算符或者表达式
+     * @param string|array $op    比较运算符或者表达式
      * @param string|array $range 比较范围
      * @return $this
      */
     public function whereTime($field, $op, $range = null)
     {
         if (is_null($range)) {
-            // 使用日期表达式
-            $date = getdate();
-            switch (strtolower($op)) {
-                case 'today':
-                case 'd':
-                    $range = ['today', 'tomorrow'];
-                    break;
-                case 'week':
-                case 'w':
-                    $range = 'this week 00:00:00';
-                    break;
-                case 'month':
-                case 'm':
-                    $range = mktime(0, 0, 0, $date['mon'], 1, $date['year']);
-                    break;
-                case 'year':
-                case 'y':
-                    $range = mktime(0, 0, 0, 1, 1, $date['year']);
-                    break;
-                case 'yesterday':
-                    $range = ['yesterday', 'today'];
-                    break;
-                case 'last week':
-                    $range = ['last week 00:00:00', 'this week 00:00:00'];
-                    break;
-                case 'last month':
-                    $range = [date('y-m-01', strtotime('-1 month')), mktime(0, 0, 0, $date['mon'], 1, $date['year'])];
-                    break;
-                case 'last year':
-                    $range = [mktime(0, 0, 0, 1, 1, $date['year'] - 1), mktime(0, 0, 0, 1, 1, $date['year'])];
-                    break;
-                default:
-                    $range = $op;
+            if (is_array($op)) {
+                $range = $op;
+            } else {
+                // 使用日期表达式
+                switch (strtolower($op)) {
+                    case 'today':
+                    case 'd':
+                        $range = ['today', 'tomorrow'];
+                        break;
+                    case 'week':
+                    case 'w':
+                        $range = ['this week 00:00:00', 'next week 00:00:00'];
+                        break;
+                    case 'month':
+                    case 'm':
+                        $range = ['first Day of this month 00:00:00', 'first Day of next month 00:00:00'];
+                        break;
+                    case 'year':
+                    case 'y':
+                        $range = ['this year 1/1', 'next year 1/1'];
+                        break;
+                    case 'yesterday':
+                        $range = ['yesterday', 'today'];
+                        break;
+                    case 'last week':
+                        $range = ['last week 00:00:00', 'this week 00:00:00'];
+                        break;
+                    case 'last month':
+                        $range = ['first Day of last month 00:00:00', 'first Day of this month 00:00:00'];
+                        break;
+                    case 'last year':
+                        $range = ['last year 1/1', 'this year 1/1'];
+                        break;
+                    default:
+                        $range = $op;
+                }
             }
             $op = is_array($range) ? 'between' : '>';
         }
@@ -1735,7 +1751,7 @@ class Query
                 $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);
@@ -1810,7 +1826,9 @@ class Query
      */
     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;
@@ -2118,24 +2136,37 @@ class Query
     /**
      * 批量插入记录
      * @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);
         } else {
             // 执行操作
             return $this->execute($sql, $bind);
@@ -2574,47 +2605,51 @@ class Query
     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];
-        }
+
+        $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 (!empty($resultSet)) {
+            if ($resultSet instanceof Collection) {
+                $resultSet = $resultSet->all();
+            }
+
             if (false === call_user_func($callback, $resultSet)) {
                 return false;
             }
-            $end       = end($resultSet);
-            $lastId    = is_array($end) ? $end[$key] : $end->$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;
     }

+ 61 - 2
thinkphp/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,13 +12,72 @@
 namespace think\db\builder;
 
 use think\db\Builder;
+use think\Exception;
 
 /**
  * mysql数据库驱动
  */
 class Mysql extends Builder
 {
-    protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
+
+    protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES %DATA% %COMMENT%';
+    protected $updateSql    = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
+
+    /**
+     * 生成insertall SQL
+     * @access public
+     * @param array     $dataSet 数据集
+     * @param array     $options 表达式
+     * @param bool      $replace 是否replace
+     * @return string
+     * @throws Exception
+     */
+    public function insertAll($dataSet, $options = [], $replace = false)
+    {
+        // 获取合法的字段
+        if ('*' == $options['field']) {
+            $fields = array_keys($this->query->getFieldsType($options['table']));
+        } else {
+            $fields = $options['field'];
+        }
+
+        foreach ($dataSet as $data) {
+            foreach ($data as $key => $val) {
+                if (!in_array($key, $fields, true)) {
+                    if ($options['strict']) {
+                        throw new Exception('fields not exists:[' . $key . ']');
+                    }
+                    unset($data[$key]);
+                } elseif (is_null($val)) {
+                    $data[$key] = 'NULL';
+                } elseif (is_scalar($val)) {
+                    $data[$key] = $this->parseValue($val, $key);
+                } elseif (is_object($val) && method_exists($val, '__toString')) {
+                    // 对象数据写入
+                    $data[$key] = $val->__toString();
+                } else {
+                    // 过滤掉非标量数据
+                    unset($data[$key]);
+                }
+            }
+            $value    = array_values($data);
+            $values[] = '( ' . implode(',', $value) . ' )';
+
+            if (!isset($insertFields)) {
+                $insertFields = array_map([$this, 'parseKey'], array_keys($data));
+            }
+        }
+
+        return str_replace(
+            ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
+            [
+                $replace ? 'REPLACE' : 'INSERT',
+                $this->parseTable($options['table'], $options),
+                implode(' , ', $insertFields),
+                implode(' , ', $values),
+                $this->parseComment($options['comment']),
+            ], $this->insertAllSql);
+    }
 
     /**
      * 字段和表名处理

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

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

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

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

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

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

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

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

+ 1 - 1
thinkphp/library/think/debug/Console.php

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

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

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

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

+ 15 - 6
thinkphp/library/think/log/driver/File.php

@@ -12,6 +12,7 @@
 namespace think\log\driver;
 
 use think\App;
+use think\Request;
 
 /**
  * 本地化调试输出到文件
@@ -20,6 +21,7 @@ class File
 {
     protected $config = [
         'time_format' => ' c ',
+        'single'      => false,
         'file_size'   => 2097152,
         'path'        => LOG_PATH,
         'apart_level' => [],
@@ -43,8 +45,12 @@ class File
      */
     public function save(array $log = [])
     {
-        $cli         = IS_CLI ? '_cli' : '';
-        $destination = $this->config['path'] . date('Ym') . DS . date('d') . $cli . '.log';
+        if ($this->config['single']) {
+            $destination = $this->config['path'] . 'single.log';
+        } else {
+            $cli         = IS_CLI ? '_cli' : '';
+            $destination = $this->config['path'] . date('Ym') . DS . date('d') . $cli . '.log';
+        }
 
         $path = dirname($destination);
         !is_dir($path) && mkdir($path, 0755, true);
@@ -60,7 +66,11 @@ class File
             }
             if (in_array($type, $this->config['apart_level'])) {
                 // 独立记录的日志级别
-                $filename = $path . DS . date('d') . '_' . $type . $cli . '.log';
+                if ($this->config['single']) {
+                    $filename = $path . DS . $type . '.log';
+                } else {
+                    $filename = $path . DS . date('d') . '_' . $type . $cli . '.log';
+                }
                 $this->write($level, $filename, true);
             } else {
                 $info .= $level;
@@ -99,11 +109,10 @@ class File
                 $message = '[ info ] ' . $current_uri . $time_str . $memory_str . $file_load . "\r\n" . $message;
             }
             $now     = date($this->config['time_format']);
-            $server  = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '0.0.0.0';
-            $remote  = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0';
+            $ip      = Request::instance()->ip();
             $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;
+            $message = "---------------------------------------------------------------\r\n[{$now}] {$ip} {$method} {$uri}\r\n" . $message;
 
             $this->writed[$destination] = true;
         }

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

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

+ 3 - 5
thinkphp/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 @@ class Pivot extends Model
         }
 
         parent::__construct($data);
-
-        $this->class = $this->name;
     }
 
 }

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

+ 17 - 12
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -79,14 +79,16 @@ class BelongsTo extends OneToOne
     /**
      * 根据关联条件查询当前模型
      * @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)));
         $relation = basename(str_replace('\\', '/', $this->model));
+
         if (is_array($where)) {
             foreach ($where as $key => $val) {
                 if (false === strpos($key, '.')) {
@@ -95,9 +97,11 @@ class BelongsTo extends OneToOne
                 }
             }
         }
+        $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);
     }
 
@@ -124,7 +128,7 @@ class BelongsTo extends OneToOne
         }
 
         if (!empty($range)) {
-            $data = $this->eagerlyWhere($this, [
+            $data = $this->eagerlyWhere($this->query, [
                 $localKey => [
                     'in',
                     $range,
@@ -146,10 +150,10 @@ class BelongsTo extends OneToOne
                 if (!empty($this->bindAttr)) {
                     // 绑定关联属性
                     $this->bindAttr($relationModel, $result, $this->bindAttr);
+                } else {
+                    // 设置关联属性
+                    $result->setRelation($attr, $relationModel);
                 }
-
-                // 设置关联属性
-                $result->setRelation($attr, $relationModel);
             }
         }
     }
@@ -167,7 +171,7 @@ class BelongsTo extends OneToOne
     {
         $localKey   = $this->localKey;
         $foreignKey = $this->foreignKey;
-        $data       = $this->eagerlyWhere($this, [$localKey => $result->$foreignKey], $localKey, $relation, $subRelation, $closure);
+        $data       = $this->eagerlyWhere($this->query, [$localKey => $result->$foreignKey], $localKey, $relation, $subRelation, $closure);
         // 关联模型
         if (!isset($data[$result->$foreignKey])) {
             $relationModel = null;
@@ -179,9 +183,10 @@ class BelongsTo extends OneToOne
         if (!empty($this->bindAttr)) {
             // 绑定关联属性
             $this->bindAttr($relationModel, $result, $this->bindAttr);
+        } else {
+            // 设置关联属性
+            $result->setRelation(Loader::parseName($relation), $relationModel);
         }
-        // 设置关联属性
-        $result->setRelation(Loader::parseName($relation), $relationModel);
     }
 
     /**

+ 19 - 8
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -52,6 +52,10 @@ class BelongsToMany extends Relation
         }
         $this->query = (new $model)->db();
         $this->pivot = $this->newPivot();
+
+        if ('think\model\Pivot' == get_class($this->pivot)) {
+            $this->pivot->name($this->middle);
+        }
     }
 
     /**
@@ -68,12 +72,18 @@ class BelongsToMany extends Relation
     /**
      * 实例化中间表模型
      * @param $data
-     * @return mixed
+     * @return Pivot
+     * @throws Exception
      */
     protected function newPivot($data = [])
     {
-        $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 $pivot;
+        } else {
+            throw new Exception('pivot model must extends: \think\model\Pivot');
+        }
     }
 
     /**
@@ -206,11 +216,12 @@ class BelongsToMany extends Relation
     /**
      * 根据关联条件查询当前模型
      * @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');
     }
@@ -384,7 +395,7 @@ class BelongsToMany extends Relation
 
         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;
@@ -565,7 +576,7 @@ class BelongsToMany extends Relation
         if (empty($this->baseQuery) && $this->parent->getData()) {
             $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;
         }
     }

+ 15 - 9
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -77,7 +77,7 @@ class HasMany extends Relation
         }
 
         if (!empty($range)) {
-            $data = $this->eagerlyOneToMany(new $this->model, [
+            $data = $this->eagerlyOneToMany($this->query, [
                 $this->foreignKey => [
                     'in',
                     $range,
@@ -114,7 +114,7 @@ class HasMany extends Relation
         $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] = [];
@@ -185,7 +185,7 @@ class HasMany extends Relation
         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 = [];
@@ -245,7 +245,7 @@ class HasMany extends Relation
         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 +253,16 @@ class HasMany extends Relation
     /**
      * 根据关联条件查询当前模型
      * @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)));
         $relation = basename(str_replace('\\', '/', $this->model));
+
         if (is_array($where)) {
             foreach ($where as $key => $val) {
                 if (false === strpos($key, '.')) {
@@ -269,9 +271,13 @@ class HasMany extends Relation
                 }
             }
         }
+
+        $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);
     }
 

+ 6 - 7
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -77,10 +77,11 @@ class HasManyThrough extends Relation
     /**
      * 根据关联条件查询当前模型
      * @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 @@ class HasManyThrough extends Relation
      * @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 @@ class HasManyThrough extends Relation
      * @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)
     {}
 
     /**

+ 15 - 10
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -74,21 +74,23 @@ class HasOne extends OneToOne
         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)));
         $relation = basename(str_replace('\\', '/', $this->model));
+
         if (is_array($where)) {
             foreach ($where as $key => $val) {
                 if (false === strpos($key, '.')) {
@@ -97,9 +99,11 @@ class HasOne extends OneToOne
                 }
             }
         }
+        $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);
     }
 
@@ -147,9 +151,10 @@ class HasOne extends OneToOne
                 if (!empty($this->bindAttr)) {
                     // 绑定关联属性
                     $this->bindAttr($relationModel, $result, $this->bindAttr);
+                } else {
+                    // 设置关联属性
+                    $result->setRelation($attr, $relationModel);
                 }
-                // 设置关联属性
-                $result->setRelation($attr, $relationModel);
             }
         }
     }
@@ -180,9 +185,9 @@ class HasOne extends OneToOne
         if (!empty($this->bindAttr)) {
             // 绑定关联属性
             $this->bindAttr($relationModel, $result, $this->bindAttr);
+        } else {
+            $result->setRelation(Loader::parseName($relation), $relationModel);
         }
-
-        $result->setRelation(Loader::parseName($relation), $relationModel);
     }
 
 }

+ 4 - 3
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -82,10 +82,11 @@ class MorphMany extends Relation
     /**
      * 根据关联条件查询当前模型
      * @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');
     }

+ 4 - 3
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -81,10 +81,11 @@ class MorphOne extends Relation
     /**
      * 根据关联条件查询当前模型
      * @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');
     }

+ 8 - 6
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -82,10 +82,11 @@ class MorphTo extends Relation
     /**
      * 根据关联条件查询当前模型
      * @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');
     }
@@ -238,17 +239,18 @@ class MorphTo extends Relation
     /**
      * 添加关联数据
      * @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;
         $morphType = $this->morphType;
         $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);

+ 13 - 3
thinkphp/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 )
 // +----------------------------------------------------------------------
@@ -78,9 +78,9 @@ abstract class OneToOne extends Relation
         $query->via($joinAlias);
 
         if ($this instanceof BelongsTo) {
-            $query->join($joinTable . ' ' . $joinAlias, $alias . '.' . $this->foreignKey . '=' . $joinAlias . '.' . $this->localKey, $this->joinType);
+            $query->join([$joinTable => $joinAlias], $alias . '.' . $this->foreignKey . '=' . $joinAlias . '.' . $this->localKey, $this->joinType);
         } else {
-            $query->join($joinTable . ' ' . $joinAlias, $alias . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey, $this->joinType);
+            $query->join([$joinTable => $joinAlias], $alias . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey, $this->joinType);
         }
 
         if ($closure) {
@@ -215,6 +215,16 @@ abstract class OneToOne extends Relation
     }
 
     /**
+     * 获取绑定属性
+     * @access public
+     * @return array
+     */
+    public function getBindAttr()
+    {
+        return $this->bindAttr;
+    }
+
+    /**
      * 关联统计
      * @access public
      * @param Model    $result  数据对象

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

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

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

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

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

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

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

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

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

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

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

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

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

+ 53 - 34
thinkphp/library/traits/controller/Jump.php

@@ -1,5 +1,4 @@
 <?php
-
 /**
  * 用法:
  * load_trait('controller/Jump');
@@ -27,20 +26,23 @@ trait Jump
     /**
      * 操作成功跳转的快捷方法
      * @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 success($msg = '', $url = null, $data = '', $wait = 3, array $header = [])
     {
         if (is_null($url) && !is_null(Request::instance()->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,
@@ -49,32 +51,39 @@ trait Jump
             '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,
@@ -83,24 +92,29 @@ trait Jump
             '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 = [])
     {
@@ -112,37 +126,42 @@ trait Jump
         ];
         $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;
             $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');
     }
 }

+ 44 - 32
thinkphp/library/traits/model/SoftDelete.php

@@ -3,10 +3,13 @@
 namespace traits\model;
 
 use think\db\Query;
+use think\Model;
 
+/**
+ * @mixin \Think\Model
+ */
 trait SoftDelete
 {
-
     /**
      * 判断当前实例是否被软删除
      * @access public
@@ -15,22 +18,18 @@ trait SoftDelete
     public function trashed()
     {
         $field = $this->getDeleteTimeField();
-        if (!empty($this->data[$field])) {
-            return true;
-        }
-        return false;
+
+        return !empty($this->data[$field]);
     }
 
     /**
-     * 查询软删除数据
+     * 查询包含软删除数据
      * @access public
      * @return Query
      */
     public static function withTrashed()
     {
-        $model = new static();
-        $field = $model->getDeleteTimeField(true);
-        return $model->getQuery();
+        return (new static )->getQuery();
     }
 
     /**
@@ -42,14 +41,14 @@ trait SoftDelete
     {
         $model = new static();
         $field = $model->getDeleteTimeField(true);
-        return $model->getQuery()
-            ->useSoftDelete($field, ['not null', '']);
+
+        return $model->getQuery()->useSoftDelete($field, ['not null', '']);
     }
 
     /**
      * 删除当前的记录
      * @access public
-     * @param bool  $force 是否强制删除
+     * @param bool $force 是否强制删除
      * @return integer
      */
     public function delete($force = false)
@@ -57,44 +56,53 @@ trait SoftDelete
         if (false === $this->trigger('before_delete', $this)) {
             return false;
         }
+
         $name = $this->getDeleteTimeField();
         if (!$force) {
             // 软删除
             $this->data[$name] = $this->autoWriteTimestamp($name);
             $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();
         if (is_array($data) && key($data) !== 0) {
@@ -103,18 +111,16 @@ trait SoftDelete
         } 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;
     }
 
@@ -126,11 +132,13 @@ trait SoftDelete
      */
     public function restore($where = [])
     {
-        $name = $this->getDeleteTimeField();
         if (empty($where)) {
             $pk         = $this->getPk();
             $where[$pk] = $this->getData($pk);
         }
+
+        $name = $this->getDeleteTimeField();
+
         // 恢复删除
         return $this->getQuery()
             ->useSoftDelete($name, ['not null', ''])
@@ -142,30 +150,34 @@ trait SoftDelete
      * 查询默认不包含软删除数据
      * @access protected
      * @param Query $query 查询对象
-     * @return void
+     * @return Query
      */
     protected function base($query)
     {
-        $field = $this->getDeleteTimeField(true);
-        $query->useSoftDelete($field);
+        return $query->useSoftDelete($this->getDeleteTimeField(true));
     }
 
     /**
      * 获取软删除字段
      * @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 (!strpos($field, '.')) {
             $field = '__TABLE__.' . $field;
         }
+
         if (!$read && strpos($field, '.')) {
             $array = explode('.', $field);
             $field = array_pop($array);
         }
+
         return $field;
     }
 }

+ 22 - 13
thinkphp/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 @@ use think\Exception;
 
 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);
     }
 }

+ 4 - 3
thinkphp/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();

+ 1 - 0
thinkphp/tpl/dispatch_jump.tpl

@@ -2,6 +2,7 @@
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
     <title>跳转提示</title>
     <style type="text/css">
         *{ padding: 0; margin: 0; }

+ 1 - 1
thinkphp/tpl/think_exception.tpl

@@ -319,7 +319,7 @@
         
             <div class="info">
                 <div>
-                    <h2>[<?php echo $code; ?>] <?php echo sprintf('%s in %s', parse_class($name), parse_file($file, $line)); ?></h2>
+                    <h2>[<?php echo $code; ?>]&nbsp;<?php echo sprintf('%s in %s', parse_class($name), parse_file($file, $line)); ?></h2>
                 </div>
                 <div><h1><?php echo nl2br(htmlentities($message)); ?></h1></div>
             </div>

+ 1 - 1
vendor/autoload.php

@@ -4,4 +4,4 @@
 
 require_once __DIR__ . '/composer/autoload_real.php';
 
-return ComposerAutoloaderInitf044e82bda67c57e2e97ab8ef5eda062::getLoader();
+return ComposerAutoloaderInite852dc75b22dac3e2b90bb7b0bb60e3a::getLoader();

+ 2 - 0
vendor/composer/autoload_classmap.php

@@ -97,11 +97,13 @@ return array(
     'Qiniu\\Storage\\ResumeUploader' => $vendorDir . '/qiniu/php-sdk/src/Qiniu/Storage/ResumeUploader.php',
     'Qiniu\\Storage\\UploadManager' => $vendorDir . '/qiniu/php-sdk/src/Qiniu/Storage/UploadManager.php',
     'Qiniu\\Zone' => $vendorDir . '/qiniu/php-sdk/src/Qiniu/Zone.php',
+    'Symfony\\Component\\OptionsResolver\\Debug\\OptionsResolverIntrospector' => $vendorDir . '/symfony/options-resolver/Debug/OptionsResolverIntrospector.php',
     'Symfony\\Component\\OptionsResolver\\Exception\\AccessException' => $vendorDir . '/symfony/options-resolver/Exception/AccessException.php',
     'Symfony\\Component\\OptionsResolver\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/options-resolver/Exception/ExceptionInterface.php',
     'Symfony\\Component\\OptionsResolver\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/options-resolver/Exception/InvalidArgumentException.php',
     'Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException' => $vendorDir . '/symfony/options-resolver/Exception/InvalidOptionsException.php',
     'Symfony\\Component\\OptionsResolver\\Exception\\MissingOptionsException' => $vendorDir . '/symfony/options-resolver/Exception/MissingOptionsException.php',
+    'Symfony\\Component\\OptionsResolver\\Exception\\NoConfigurationException' => $vendorDir . '/symfony/options-resolver/Exception/NoConfigurationException.php',
     'Symfony\\Component\\OptionsResolver\\Exception\\NoSuchOptionException' => $vendorDir . '/symfony/options-resolver/Exception/NoSuchOptionException.php',
     'Symfony\\Component\\OptionsResolver\\Exception\\OptionDefinitionException' => $vendorDir . '/symfony/options-resolver/Exception/OptionDefinitionException.php',
     'Symfony\\Component\\OptionsResolver\\Exception\\UndefinedOptionsException' => $vendorDir . '/symfony/options-resolver/Exception/UndefinedOptionsException.php',

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