ayHaru 4 years ago
commit
0596455e96
100 changed files with 27257 additions and 0 deletions
  1. 927 0
      CHANGELOG.md
  2. 32 0
      LICENSE.txt
  3. 180 0
      README.md
  4. 1 0
      application/.htaccess
  5. 12 0
      application/command.php
  6. 217 0
      application/common.php
  7. 5 0
      application/index/config.php
  8. 10 0
      application/index/controller/Index.php
  9. 14 0
      application/provider.php
  10. 28 0
      application/tags.php
  11. 26 0
      build.php
  12. 34 0
      composer.json
  13. 1828 0
      composer.lock
  14. 146 0
      config/app.php
  15. 25 0
      config/cache.php
  16. 20 0
      config/console.php
  17. 30 0
      config/cookie.php
  18. 63 0
      config/database.php
  19. 30 0
      config/log.php
  20. 18 0
      config/middleware.php
  21. 26 0
      config/session.php
  22. 35 0
      config/template.php
  23. 18 0
      config/trace.php
  24. 2 0
      extend/.gitignore
  25. 21 0
      phpunit.xml
  26. 8 0
      public/.htaccess
  27. BIN
      public/favicon.ico
  28. 21 0
      public/index.php
  29. 2 0
      public/robots.txt
  30. 17 0
      public/router.php
  31. 2 0
      public/static/.gitignore
  32. 6 0
      route/route.php
  33. 2 0
      runtime/.gitignore
  34. 20 0
      tests/ExampleTest.php
  35. 11 0
      tests/LoginTest.php
  36. 16 0
      tests/TestCase.php
  37. 22 0
      think
  38. 8 0
      thinkphp/.gitignore
  39. 1 0
      thinkphp/.htaccess
  40. 119 0
      thinkphp/CONTRIBUTING.md
  41. 32 0
      thinkphp/LICENSE.txt
  42. 93 0
      thinkphp/README.md
  43. 52 0
      thinkphp/base.php
  44. 35 0
      thinkphp/composer.json
  45. 327 0
      thinkphp/convention.php
  46. 726 0
      thinkphp/helper.php
  47. 144 0
      thinkphp/lang/zh-cn.php
  48. 981 0
      thinkphp/library/think/App.php
  49. 415 0
      thinkphp/library/think/Build.php
  50. 133 0
      thinkphp/library/think/Cache.php
  51. 552 0
      thinkphp/library/think/Collection.php
  52. 398 0
      thinkphp/library/think/Config.php
  53. 829 0
      thinkphp/library/think/Console.php
  54. 568 0
      thinkphp/library/think/Container.php
  55. 287 0
      thinkphp/library/think/Controller.php
  56. 268 0
      thinkphp/library/think/Cookie.php
  57. 197 0
      thinkphp/library/think/Db.php
  58. 278 0
      thinkphp/library/think/Debug.php
  59. 113 0
      thinkphp/library/think/Env.php
  60. 147 0
      thinkphp/library/think/Error.php
  61. 56 0
      thinkphp/library/think/Exception.php
  62. 125 0
      thinkphp/library/think/Facade.php
  63. 496 0
      thinkphp/library/think/File.php
  64. 220 0
      thinkphp/library/think/Hook.php
  65. 284 0
      thinkphp/library/think/Lang.php
  66. 417 0
      thinkphp/library/think/Loader.php
  67. 389 0
      thinkphp/library/think/Log.php
  68. 205 0
      thinkphp/library/think/Middleware.php
  69. 1116 0
      thinkphp/library/think/Model.php
  70. 445 0
      thinkphp/library/think/Paginator.php
  71. 1268 0
      thinkphp/library/think/Process.php
  72. 2267 0
      thinkphp/library/think/Request.php
  73. 429 0
      thinkphp/library/think/Response.php
  74. 990 0
      thinkphp/library/think/Route.php
  75. 579 0
      thinkphp/library/think/Session.php
  76. 1318 0
      thinkphp/library/think/Template.php
  77. 412 0
      thinkphp/library/think/Url.php
  78. 1554 0
      thinkphp/library/think/Validate.php
  79. 253 0
      thinkphp/library/think/View.php
  80. 366 0
      thinkphp/library/think/cache/Driver.php
  81. 307 0
      thinkphp/library/think/cache/driver/File.php
  82. 209 0
      thinkphp/library/think/cache/driver/Lite.php
  83. 206 0
      thinkphp/library/think/cache/driver/Memcache.php
  84. 279 0
      thinkphp/library/think/cache/driver/Memcached.php
  85. 272 0
      thinkphp/library/think/cache/driver/Redis.php
  86. 233 0
      thinkphp/library/think/cache/driver/Sqlite.php
  87. 175 0
      thinkphp/library/think/cache/driver/Wincache.php
  88. 179 0
      thinkphp/library/think/cache/driver/Xcache.php
  89. 31 0
      thinkphp/library/think/config/driver/Ini.php
  90. 31 0
      thinkphp/library/think/config/driver/Json.php
  91. 40 0
      thinkphp/library/think/config/driver/Xml.php
  92. 482 0
      thinkphp/library/think/console/Command.php
  93. 464 0
      thinkphp/library/think/console/Input.php
  94. 19 0
      thinkphp/library/think/console/LICENSE
  95. 222 0
      thinkphp/library/think/console/Output.php
  96. 281 0
      thinkphp/library/think/console/Table.php
  97. 1 0
      thinkphp/library/think/console/bin/README.md
  98. BIN
      thinkphp/library/think/console/bin/hiddeninput.exe
  99. 59 0
      thinkphp/library/think/console/command/Build.php
  100. 0 0
      thinkphp/library/think/console/command/Clear.php

+ 927 - 0
CHANGELOG.md

@@ -0,0 +1,927 @@
+## V5.1.39 LTS(2019-11-18)
+
+本次更新为常规更新,主要包括:
+
+* 修正`memcached`驱动
+* 改进`HasManyThrough`关联查询
+* 改进`Request`类`isJson`方法
+* 改进关联查询
+* 改进`redis`驱动
+* 增加 Model类`getWhere`方法对复合主键的支持
+* 改进`newQuery`方法
+* 改进闭包查询的参数绑定
+* 修正`Validate`
+* 修复某些情况下URL会多一个冒号
+* 调整composer.json
+* 修复使用`Cache::clear()`时,报错缓存文件不存在问题
+* 使用File类的unlink方法进行文件删除
+* 改进`paraseData`方法
+* 修正image验证方法
+* 改进Url生成
+* 改进空操作对数字的支持
+* 改进一处PHP7.4兼容性问题
+
+## V5.1.38 LTS(2019-8-8)
+
+本次更新为常规更新,主要包括:
+
+* `Request`类增加`isJson`方法
+* 改进浮点型查询
+* 修正关联查询关联外键为空的查询错误
+* 远程一对多支持关联统计和预载入查询
+* 远程一对多关联支持`has`/`hasWhere`查询
+* 优化`parseIn`解析
+* 改进`parseLike`查询
+* 改进Url生成
+* 改进模型的`toArray`方法
+* 修正`notIn`查询
+* 改进`JSON`字段查询
+* 改进Controller类`display`/`fetch`方法返回`ViewResponse`对象
+* 改进`param`方法
+* 改进`mysql`驱动`getExplain`方法
+* 改进时间查询
+* 改进模型关联的`has`/`hasWhere`方法对软删除的支持
+* 修正社区反馈的BUG
+
+## V5.1.37 LTS(2019-5-26)
+
+本次更新为常规更新,主要更新如下:
+
+* 改进关联数据更新
+* 修正关联动态获取器
+* 改进`redis`驱动 
+* 修复验证规则里面出现二维数组时的错误
+* 改进跨域请求支持
+* 完善模型`hidden`方法对关联属性的支持
+* 改进`where`查询方法传入`Query`对象的支持`bind`数据
+* 改进数据集对象的`load`方法
+* 修正缓存类`clear`方法对`tag`的支持
+
+## V5.1.36 LTS(2019-4-28)
+
+本次更新为常规更新,主要更新如下:
+
+* 修正`chunk`方法一处异常抛出的错误
+* 修正模型输出的`visible`
+* 改进环境变量加载
+* 改进命令行日志的`level`配置支持
+* 修复设置有缓存前缀时,无法清空缓存标签的问题
+* HasMony对象`saveAll`方法兼容`Collection`格式参数格式
+* 修正`whereOr`查询使用字符串的问题
+* 改进`dateFormat`设置对写入数据的影响
+* 修正查询缓存
+* 记住指定的跳转地址
+* 改进软删除
+* 改进聚合查询SQL去除limit 1
+* 改进缓存驱动
+
+## V5.1.35 LTS(2019-3-2)
+
+本次主要为常规更新,修正了一些反馈的问题。
+
+* 修正验证类自定义验证方法执行两次的问题
+* 模型增加`isEmpty`方法用于判断是否空模型
+* 改进获取器对`append`的支持
+* 修正一对多关联的`withCount`自关联问题
+* facade类注释调整
+* 改进关联属性的`visible`和`hidden`判断
+* 修正路由分组的`MISS`路由
+* 改进pgsql.sql
+
+## V5.1.34 LTS(2019-1-30)
+
+本次更新为常规更新,修正了一些反馈的问题。
+
+* 改进Request类的`has`方法,支持`patch`
+* 改进`unique`验证的多条件支持
+* 修复自定义上传验证,检测文件大小
+* 改进`in`查询支持表达式
+* 改进路由的`getBind`方法
+* 改进验证类的错误信息获取
+* 改进`response`助手函数默认值
+* 修正mysql的`regexp`查询
+* 改进模型类型强制转换写入对`Expression`对象的支持
+
+## V5.1.33 LTS(2019-1-16)
+
+* 修复路由中存在多个相同替换的正则BUG
+* 修正whereLike查询
+* join方法支持参数绑定
+* 改进union方法
+* 修正多对多关联的attach方法
+* 改进验证类的正则规则自定义
+* 改进Request类method方法
+* 改进File日志类型的CLI日志写入
+* 改进文件日志time_format配置对JSON格式的支持
+
+## V5.1.32 LTS(2018-12-24)
+
+本次主要为常规更新,修正了一些反馈的问题。
+
+
+* 改进多对多关联的`attach`方法 
+* 改进聚合查询的`field`处理
+* 改进关联的`save`方法
+* 修正模型`exists`方法返回值
+* 改进时间字段写入和输出
+* 改进控制器中间件的调用
+* 改进路由变量替换的性能
+* 改进缓存标签的处理机制
+
+## V5.1.31 LTS (2018-12-9)
+
+本次版本包含一个安全更新,建议升级。
+
+* 改进`field`方法
+* 改进`count`方法返回类型
+* `download`函数增加在浏览器中显示文件功能
+* 修正多对多模型的中间表数据写入
+* 改进`sqlsrv`驱动支持多个Schemas模式查询
+* 统一助手函数与\think\response\Download函数文件过期时间
+* 完善关联模型的`save`方法 增加`make`方法仅创建对象不保存
+* 修改条件表达式对静态变量的支持
+* 修正控制器名获取
+* 改进view方法的`field`解析
+
+## V5.1.30 LTS(2018-11-30)
+
+该版本为常规更新,修正了一些社区反馈的问题。
+
+主要更新如下:
+
+* 改进查询类的`execute`方法
+* 判断路由规则定义添加对请求类型的判断
+* 修复`orderRaw`异常
+* 修正 `optimize:autoload`指令
+* 改进软删除的`destroy`方法造成重复执行事件的问题
+* 改进验证类对扩展验证规则 始终验证 不管是否`require`
+* 修复自定义验证`remove`所有规则的异常
+* 改进时间字段的自动写入支持微秒数据
+* 改进`Connection`类的`getrealsql`方法
+* 修正`https`地址的URL生成
+* 修复 `array_walk_recursive` 在低于PHP7.1消耗内部指针问题
+* 改进手动参数绑定使用
+* 改进聚合查询方法的`field`参数支持`Expression`
+
+## V5.1.29 LTS(2018-11-11)
+
+该版本主要改进了参数绑定的解析问题和提升性能,并修正了一些反馈的问题。
+
+* 改进手动参数绑定
+* 修正MISS路由的分组参数无效问题
+* 行为支持对象的方法
+* 修正全局查询范围
+* 改进`belongsto`关联的`has`方法
+* 改进`hasMany`关联
+* 改进模型观察者多次注册的问题
+* 改进`query`类的默认查询参数处理
+* 修正`parseBetween`解析方法
+* 改进路由地址生成的本地域名支持
+* 改进参数绑定的实际URL解析性能
+* 改进`Env`类的`getEnv`和`get`方法
+* 改进模板缓存的生成优化
+* 修复验证类的多语言支持
+* 修复自定义场景验证`remove`规则异常
+* File类添加是否自动补全扩展名的选项
+* 改进`strpos`对子串是否存在的判断
+* 修复`choice`无法用值选择第一个选项问题
+* 验证器支持多维数组取值验证
+* 改进解析`extend`和`block`标签的正则
+
+## V5.1.28 LTS(2018-10-29)
+
+该版本主要修正了上一个版本存在的一些问题,并改进了关联查询
+
+* 改进聚合查询方法的字段支持DISTINCT
+* 改进定义路由后url函数的端口生成
+* 改进控制器中间件对`swoole`等的支持
+* 改进Log类`save`方法
+* 改进验证类的闭包验证参数
+* 多对多关联支持指定中间表数据的名称
+* 关联聚合查询支持闭包方式指定聚合字段
+* 改进Lang类`get`方法
+* 多对多关联增加判断关联数据是否存在的方法
+* 改进关联查询使用`fetchsql`的情况
+* 改进修改器的是否已经执行判断
+* 增加`afterWith`和`beforeWith`验证规则 用于比较日期字段
+
+## V5.1.27 LTS(2018-10-22)
+
+该版本主要修正了路由绑定的参数,改进了修改器的执行多次问题,并正式宣布为LTS版本!
+
+
+* 修正路由绑定的参数丢失问题
+* 修正路由别名的参数获取
+* 改进修改器会执行多次的问题
+
+## V5.1.26(2018-10-12)
+
+该版本主要修正了上一个版本的一些问题,并改进了全局查询范围的支持,同时包含了一个安全更新。
+
+
+* 修正单一模块下注解路由无效的问题
+* 改进数据库的聚合查询的字段处理
+* 模型类增加`globalScope`属性定义 用于指定全局的查询范围
+* 模型的`useGlobalScope`方法支持传入数组 用于指定当前查询需要使用的全局查询范围
+* 改进数据集的`order`方法对数字类型的支持
+* 修正上一个版本`order`方法解析的一处BUG
+* 排序字段不合法或者错误的时候抛出异常
+* 改进`Request`类的`file`方法对上传文件的错误判断
+
+##  V5.1.25(2018-9-21)
+
+该版本主要改进了查询参数绑定的性能和对浮点型的支持,以及一些细节的完善。
+
+* 修正一处命令行问题
+* 改进`Socketlog`日志驱动,支持自定义默认展开日志类别
+* 修正`MorphMany`一处bug
+* 跳转到上次记住的url,并支持默认值
+* 改进模型的异常提示
+* 改进参数绑定对浮点型的支持
+* 改进`order`方法解析
+* 改进`json`字段数据的自动编码
+* 改进日志`log_write`可能造成的日志写入死循环
+* Log类增加`log_level`行为标签位置,用于对某个类型的日志进行处理
+* Route类增加`clear`方法清空路由规则
+* 分布式数据库配置支持使用数组
+* 单日志文件也支持`max_files`参数
+* 改进查询参数绑定的性能
+* 改进别名路由的URL后缀参数检测
+* 控制器前置方法和控制器中间件的`only`和`except`定义不区分大小写
+
+## V5.1.24(2018-9-5)
+
+该版本主要增加了命令行的表格输出功能,并增加了查看路由定义的指令,以及修正了社区的一些反馈问题。
+
+* 修正`Request`类的`file`方法
+* 修正路由的`cache`方法
+* 修正路由缓存的一处问题
+* 改进上传文件获取的异常处理
+* 改进`fetchCollection`方法支持传入数据集类名
+* 修正多级控制器的注解路由生成
+* 改进`Middleware`类`clear`方法
+* 增加`route:list`指令用于[查看定义的路由](752690) 并支持排序
+* 命令行增加`Table`输出类
+* `Command`类增加`table`方法用于输出表格
+* 改进搜索器查询方法支持别名定义
+* 命令行配置增加`auto_path`参数用于定义自动载入的命令类路径
+* 增加`make:command`指令用于[快速生成指令](354146)
+* 改进`make:controller`指令对操作方法后缀的支持
+* 改进命令行的定义文件支持索引数组 用于指令对象的惰性加载
+* 改进`value`和`column`方法对后续查询结果的影响
+* 改进`RuleName`类的`setRule`方法
+
+## V5.1.23(2018-8-23)
+
+该版本主要改进了数据集对象的处理,增加了`findOrEmpty`方法,并且修正了一些社区反馈的BUG。
+
+* 数据集类增加`diff`/`intersect`方法用于获取差集和交集(默认根据主键值比较)
+* 数据集类增加`order`方法支持指定字段排序
+* 数据集类增加`map`方法使用回调函数处理数据并返回新的数据集对象
+* Db增加`allowEmpty`方法允许`find`方法在没有数据的时候返回空数组或者空模型对象而不是null
+* Db增加`findOrEmpty`方法
+* Db增加`fetchCollection`方法用于指定查询返回数据集对象
+* 改进`order`方法的数组方式解析,增强安全性
+* 改进`withSearch`方法,支持第三个参数传入字段前缀标识,用于多表查询字段搜索
+* 修正`optimize:route`指令开启类库后缀后的注解路由生成
+* 修正redis缓存及session驱动
+* 支持指定`Yaconf`的独立配置文件
+* 增加`yaconf`助手函数用于配置文件
+
+
+## V5.1.22(2018-8-9)
+
+该版本主要增加了模型搜索器和`withJoin`方法,完善了模型输出和对`Yaconf`的支持,修正了一些社区反馈的BUG。
+
+* 改进一对一关联的`table`识别问题
+* 改进内置`Facade`类
+* 增加`withJoin`方法支持`join`方式的[一对一关联](一对一关联.md)查询
+* 改进`join`预载入查询的空数据问题
+* 改进`Config`类的`load`方法支持快速加载配置文件
+* 改进`execute`方法和事务的断线重连
+* 改进`memcache`驱动的`has`方法
+* 模型类支持定义[搜索器](搜索器.md)方法
+* 完善`Config`类对`Yaconf`的支持
+* 改进模型的`hidden/visible/append/withAttr`方法,支持在[查询前后调用](数组访问.md),以及支持数据集对象
+* 数据集对象增加`where`方法根据字段或者关联数据[过滤数据](模型数据集.md)
+* 改进AJAX请求的`204`判断
+
+
+## V5.1.21(2018-8-2)
+
+该版本主要增加了下载响应对象和数组查询对象的支持,并修正了一些社区反馈的问题。
+
+* 改进核心对象的无用信息调试输出
+* 改进模型的`isRelationAttr`方法判断
+* 模型类的`get`和`all`方法并入Db类
+* 增加[下载响应对象](文件下载.md)和`download`助手函数
+* 修正别名路由配置定义读取
+* 改进`resultToModel`方法
+* 修正开启类库后缀后的注解路由生成
+* `Response`类增加`noCache`快捷方法
+* 改进路由对象在`Swoole`/`Workerman`下面参数多次合并问题
+* 修正路由`ajax`/`pjax`参数后路由变量无法正确获取的问题
+* 增加清除中间件的方法
+* 改进依赖注入的参数规范自动识别(便于对接前端小写+下划线规范)
+* 改进`hasWhere`的数组条件的字段判断
+* 增加[数组查询对象](高级查询.md)`Where`支持(喜欢数组查询的福音)
+* 改进多对多关联的闭包支持
+
+## V5.1.20(2018-7-25)
+
+该版本主要增加了Db和模型的动态获取器的支持,并修正了一些已知问题。
+
+* Db类添加[获取器支持](703981)
+* 支持模型及关联模型字段[动态定义获取器](354046)
+* 动态获取器支持`JSON`字段
+* 改进路由的`before`行为执行(匹配后执行)
+*  `Config`类支持`Yaconf`
+* 改进Url生成的端口问题
+* Request类增加`setUrl`和`setBaseUrl`方法
+* 改进页面trace的信息显示
+* 修正`MorphOne`关联
+* 命令行添加[查看版本指令](703994)
+
+## V5.1.19 (2018-7-13)
+
+该版本是一个小幅改进版本,针对`Swoole`和`Workerman`的`Cookie`支持做了一些改进,并修正了一些已知的问题。
+
+
+* 改进query类`delete`方法对软删除条件判断
+* 修正分表查询的软删除问题
+* 模型查询的时候同时传入`table`和`name`属性
+* 容器类增加`IteratorAggregate`和`Countable`接口支持
+* 路由分组支持对下面的资源路由统一设置`only/except/vars`参数
+* 改进Cookie类更好支持扩展
+* 改进Request类`post`方法
+* 改进模型自关联的自动识别
+* 改进Request类对`php://input`数据的处理
+
+
+## V5.1.18 (2018-6-30)
+
+该版本主要完善了对`Swoole`和`Workerman`的`HttpServer`运行支持,改进`Request`类,并修正了一些已知的问题。
+
+* 改进关联`append`方法的处理
+* 路由初始化和检测方法分离
+* 修正`destroy`方法强制删除
+* `app_init`钩子位置移入`run`方法
+* `think-swoole`扩展更新到2.0版本
+* `think-worker`扩展更新到2.0版本
+* 改进Url生成的域名自动识别
+* `Request`类增加`setPathinfo`方法和`setHost`方法
+* `Request`类增加`withGet`/`withPost`/`withHeader`/`withServer`/`withCookie`/`withEnv`方法进行赋值操作
+* Route类改进`host`属性的获取
+* 解决注解路由配置不生效的问题
+* 取消Test日志驱动,改为使用`close`设置关闭全局日志写入
+* 修正路由的`response`参数
+* 修正204响应输出的判断
+
+## V5.1.17 (2018-6-18)
+
+该版本主要增加了控制器中间件的支持,改进了路由功能,并且修正了社区反馈的一些问题。
+
+* 修正软删除的`delete`方法
+* 修正Query类`Count`方法
+* 改进多对多`detach`方法
+* 改进Request类`Session`方法
+* 增加控制器中间件支持
+* 模型类增加`jsonAssoc`属性用于定义json数据是否返回数组
+* 修正Request类`method`方法的请求伪装
+* 改进静态路由的匹配
+* 分组首页路由自动完整匹配
+* 改进sqlsrv的`column`方法
+* 日志类的`apart_level`配置支持true自动生成对应类型的日志文件
+* 改进`204`输出判断
+* 修正cli下页面输出的BUG
+* 验证类使用更高效的`ctype`验证机制
+* 改进Request类`cookie`方法
+* 修正软删除的`withTrashed`方法
+* 改进多态一对多的预载入查询
+* 改进Query类`column`方法的缓存读取
+* Query类增加`whereBetweenTimeField`方法
+* 改进分组下多个相同路由规则的合并匹配问题
+* 路由类增加`getRule`/`getRuleList`方法获取定义的路由
+
+## V5.1.16 (2018-6-7)
+
+该版本主要修正了社区反馈的一些问题,并对Request类做了进一步规范和优化。
+
+* 改进Session类的`boot`方法
+* App类的初始化方法可以单独执行
+* 改进Request类的`param`方法
+* 改进资源路由的变量替换
+* Request类增加`__isset`方法
+* 改进`useGlobalScope`方法对软删除的影响
+* 修正命令行调用
+* 改进Cookie类`init`方法
+* 改进多对多关联删除的返回值
+* 一对多关联写入支持`replace`
+* 路由增加`filter`检测方法,用于通过请求参数检测路由是否匹配
+* 取消Request类`session/env/server`方法的`filter`参数
+* 改进关联的指定属性输出
+* 模型删除操作删除后不清空对象数据仅作标记
+* 调整模型的`save`方法返回值为布尔值
+* 修正Request类`isAjax`方法
+* 修正中间件的模块配置读取
+* 取消Request类的请求变量的设置功能
+* 取消请求变量获取的默认修饰符
+* Request类增加`setAction/setModule/setController`方法
+* 关联模型的`delete`方法调用Query类
+* 改进URL生成的域名识别
+* 改进URL检测对已定义路由的域名判断
+* 模型类增加`isExists`和`isForce`方法
+* 软删除的`destroy`和`restore`方法返回值调整为布尔值
+
+## V5.1.15 (2018-6-1)
+
+该版本主要改进了路由缓存的性能和缓存方式设置,增加了JSON格式文件日志的支持,并修正了社区反馈的一些问题。
+
+* 容器类增加`exists`方法 仅判断是否存在对象实例
+* 取消配置类的`autoload`方法
+* 改进路由缓存大小提高性能
+* 改进Dispatch类`init`方法
+* 增加`make:validate`指令生成验证器类
+* Config类`get`方法支持默认值参数
+* 修正字段缓存指令
+* 改进App类对`null`数据的返回
+* 改进模型类的`__isset`方法判断
+* 修正`Query`类的`withAggregate`方法
+* 改进`RuleItem`类的`setRuleName`方法
+* 修正依赖注入和参数的冲突问题
+* 修正Db类对第三方驱动的支持
+* 修正模型类查询对象问题
+* 修正File缓存驱动的`has`方法
+* 修正资源路由嵌套
+* 改进Request类对`$_SERVER`变量的读取
+* 改进请求缓存处理
+* 路由缓存支持指定单独的缓存方式和参数
+* 修正资源路由的中间件多次执行问题
+* 修正`optimize:config`指令
+* 文件日志支持`JSON`格式日志保存
+* 修正Db类`connect`方法
+* 改进Log类`write`方法不会自动写入之前日志
+* 模型的关联操作默认启用事务
+* 改进软删除的事件响应
+
+## V5.1.14 (2018-5-18)
+
+该版本主要对底层容器进行了一些优化改进,并增加了路由缓存功能,可以进一步提升路由性能。
+
+* 依赖注入的对象参数传入改进
+* 改进核心类的容器实例化
+* 改进日期字段的读取
+* 改进验证类的`getScene`方法
+* 模型的`create`方法和`save`方法支持`replace`操作
+* 改进`Db`类的调用机制
+* App类调整为容器类
+* 改进容器默认绑定
+* `Loader`类增加工厂类的实例化方法
+* 增加路由变量默认规则配置参数
+* 增加路由缓存设计
+* 错误处理机制改进
+* 增加清空路由缓存指令
+
+
+## V5.1.13 (2018-5-11)
+
+该版本主要增加了MySQL的XA事务支持,模型事件支持观察者,以及对Facade类的改进。
+
+* 改进自动缓存
+* 改进Url生成
+* 修正数据缓存
+* 修正`value`方法的缓存
+* `join`方法和`view`方法的条件支持使用`Expression`对象
+* 改进驱动的`parseKey`方法
+* 改进Request类`host`方法和`domain`方法对端口的处理
+* 模型增加`withEvent`方法用于控制当前操作是否需要执行模型事件
+* 模型`setInc/setDec`方法支持更新事件
+* 模型添加`before_restore/after_restore`事件
+* 增加模型事件观察者
+* 路由增加`mobile`方法设置是否允许手机访问
+* 数据库XA事务支持
+* 改进索引数组查询对`IN`查询的支持
+* 修正`invokeMethod`方法
+* 修正空数据写入返回值的BUG
+* redis驱动支持`predis`
+* 改进`parseData`方法
+* 改进模块加载
+* App类初始化方法调整
+* 改进数组查询对表达式`Expression`对象支持
+* 改进闭包的依赖注入调用
+* 改进多对多关联的中间表模型更新
+* 增加容器中对象的自定义实例化
+
+## V5.1.12 (2018-4-25)
+
+该版本主要改进了主从查询的及时性,并支持动态设置请求数据。
+
+* 支持动态设置请求数据
+* 改进`comment`方法解析
+* 修正App类`__unset`方法
+* 改进url生成的域名绑定
+* 改进主从查询的及时性
+* 修正`value`的数据缓存功能
+* 改进分页类的集合对象方法调用
+* 改进Db类的代码提示
+* SQL日志增加主从标记
+
+## V5.1.11 (2018-4-19)
+
+该版本为安全和修正版本,改进了JSON查询的参数绑定问题和容器类对象实例获取,并包含一处可能的安全隐患,建议更新。
+
+* 支持指定JSON数据查询的字段类型
+* 修正`selectInsert`方法
+* `whereColumn`方法支持数组方式
+* 改进容器类`make`方法
+* 容器类`delete`方法支持数组
+* 改进`composer`自动加载
+* 改进模板引擎
+* 修正`like`查询的一处安全隐患
+
+## V5.1.10 (2018-4-16)
+
+该版本为修正版本,修正上一个版本的一些BUG,并增强了`think clear`指令。
+
+* 改进`orderField`方法
+* 改进`exists`查询
+* 修改cli模式入口文件位置计算
+* 修正`null`查询
+* 改进`parseTime`方法
+* 修正关联预载入查询
+* 改进`mysql`驱动
+* 改进`think clear`指令 支持 `-c -l -r `选项
+* 改进路由规则对`/`结尾的支持
+
+## V5.1.9 (2018-4-12)
+
+该版本主要是一些改进和修正,并包含一个安全更新,是一个推荐更新版本。
+
+* 默认模板渲染规则支持配置保持操作方法名
+* 改进`Request`类的`ip`方法
+* 支持模型软删除字段的默认值定义
+* 改进路由变量规则对中文的支持
+* 使用闭包查询的时候使用`cache(true)` 抛出异常提示
+* 改进`Loader`类`loadComposerAutoloadFiles`方法
+* 改进查询方法安全性
+* 修正路由地址中控制器名驼峰问题
+* 调整上一个版本的`module_init`和`app_begin`的钩子顺序问题
+* 改进CLI命令行执行的问题
+* 修正社区反馈的其它问题
+
+## V5.1.8 (2018-4-5)
+
+该版本主要改进了中间件的域名和模块支持,并同时修正了几个已知问题。
+
+* 增加`template.auto_rule` 参数设置默认模板渲染的操作名自动转换规则
+* 默认模板渲染规则改由视图驱动实现
+* 修正路由标识定义
+* 修正控制器路由方法
+* 改进Request类`ip`方法支持自定义代理IP参数
+* 路由注册中间件支持数组方式别名
+* 改进命令行执行下的`composer`自动加载
+* 添加域名中间件注册支持
+* 全局中间件支持模块定义文件
+* Log日志配置支持`close`参数可以全局关闭日志写入
+* 中间件方法中捕获`HttpResponseException`异常
+* 改进中间件的闭包参数传入
+* 改进分组路由的延迟解析
+* 改进URL生成对域名绑定的支持
+* 改进文件缓存和文件日志驱动的并发支持
+
+## V5.1.7 (2018-3-28)
+
+该版本主要修正了路由的一些问题,并改进了查询的安全性。
+
+* 支持`middleware`配置文件预先定义中间件别名方便路由调用
+* 修正资源路由
+* 改进`field`方法 自动识别`fieldRaw`
+* 增加`Expression`类
+* Query类增加`raw`方法
+* Query类的`field`/ `order` 和` where`方法都支持使用`raw`表达式查询
+* 改进`inc/dec`查询 支持批量更新
+* 改进路由分组
+* 改进Response类`create`方法
+* 改进composer自动加载
+* 修正域名路由的`append`方法
+* 修正操作方法的初始化方法获取不到问题
+
+## V5.1.6 (2018-3-26)
+
+该版本主要改进了路由规则的匹配算法,大幅提升了路由性能。并正式引入了中间件的支持,可以在路由中定义或者全局定义。另外包含了一个安全更新,是一个建议更新版本。
+
+* 改进URL生成对路由`ext`方法的支持
+* 改进查询缓存对不同数据库相同表名的支持
+* 改进composer自动加载的性能
+* 改进空路由变量对默认参数的影响
+* mysql的`json`字段查询支持多级
+* Query类增加`option`方法
+* 优化路由匹配
+* 修复验证规则数字键名丢失问题
+* 改进路由Url生成
+* 改进一对一关联预载入查询
+* Request类增加`rootDomain`方法
+* 支持API资源控制器生成 `make:controller --api`
+* 优化Template类的标签解析
+* 容器类增加删除和清除对象实例的方法
+* 修正MorphMany关联的`eagerlyMorphToMany`方法一处错误
+* Container类的异常捕获改进
+* Domain对象支持`bind`方法
+* 修正分页参数
+* 默认模板的输出规则不受URL影响
+* 注解路由支持多级控制器
+* Query类增加`getNumRows`方法获取前次操作影响的记录数
+* 改进查询条件的性能
+* 改进模型类`readTransform`方法对序列化类型的处理
+* Log类增加`close`方法可以临时关闭当前请求的日志写入
+* 文件日志方式增加自动清理功能(设置`max_files`参数)
+* 修正Query类的`getPk`方法
+* 修正模板缓存的布局开关问题
+* 修正Query类`select`方法的缓存
+* 改进input助手函数
+* 改进断线重连的信息判断
+* 改进正则验证方法
+* 调整语言包的加载顺序 放到`app_init`之前
+* controller类`fetch`方法改为`final`
+* 路由地址中的变量支持使用`<var>`方式
+* 改进XMLResponse 支持传入编码过的xml内容
+* 修正Query类`view`方法的数组表名支持
+* 改进路由的模型闭包绑定
+* 改进分组变量规则的继承
+* 改进`cli-server`模式下的`composer`自动加载
+* 路由变量规则异常捕获
+* 引入中间件支持
+* 路由定义增加`middleware`方法
+* 增加生成中间件指令`make:middleware` 
+* 增加全局中间件定义支持
+* 改进`optimize:config`指令对全局中间件的支持
+* 改进config类`has`方法
+* 改进时间查询的参数绑定
+* 改进`inc/dec/exp`查询的安全性
+
+
+## V5.1.5 (2018-1-31)
+
+该版本主要增强了数据库的JSON查询,并支持JSON字段的聚合查询,改进了一些性能问题,修正了路由的一些BUG,主要更新如下:
+
+* 改进数据集查询对`JSON`数据的支持
+* 改进聚合查询对`JSON`字段的支持
+* 模型类增加`getOrFail`方法
+* 改进数据库驱动的`parseKey`方法
+* 改进Query类`join`方法的自关联查询
+* 改进数据查询不存在不生成查询缓存
+* 增加`run`命令行指令启动内置服务器
+* `Request`类`pathinfo`方法改进对`cli-server`支持
+* `Session`类增加`use_lock`配置参数设置是否启用锁机制
+* 优化`File`缓存自动生成空目录的问题
+* 域名及分组路由支持`append`方法传递隐式参数
+* 改进日志的并发写入问题
+* 改进`Query`类的`where`方法支持传入`Query`对象
+* 支持设置单个日志文件的文件名
+* 修正路由规则的域名条件约束 
+* `Request`类增加`subDomain`方法用于获取当前子域名
+* `Response`类增加`allowCache`方法控制是否允许请求缓存
+* `Request`类增加`sendData`方法便于扩展
+* 改进`Env`类不依赖`putenv`方法
+* 改进控制台`trace`显示错误
+* 改进`MorphTo`关联
+* 改进完整路由匹配后带斜线访问出错的情况
+* 改进路由的多级分组问题
+* 路由url地址生成支持多级分组
+* 改进路由Url生成的`url_convert`参数的影响
+* 改进`miss`和`auto`路由内部解析
+* 取消预载入关联查询缓存功能
+
+## V5.1.4 (2018-1-19)
+
+该版本主要增强了数据库和模型操作,主要更新如下:
+
+* 支持设置 `deleteTime`属性为`false` 关闭软删除
+* 模型增加`getError`方法
+* 改进Query类的`getTableFields`/`getFieldsType`方法 支持表名自动获取
+* 模型类`toCollection`方法增加参数指定数据集类
+* 改进`union`查询
+* 关联预载入`with`方法增加缓存参数
+* 改进模型类的`get`和`all`方法的缓存 支持关联缓存
+* 支持`order by field`操作
+* 改进`insertAll`分批写入
+* 改进`json`字段数据支持
+* 增加JSON数据的模型对象化操作
+* 改进路由`ext`参数检测 
+* 修正`rule`方法的`method`参数使用 `get|post` 方式注册路由的问题
+
+## V5.1.3 (2018-1-12)
+
+该版本主要改进了路由及调整函数加载顺序,主要更新如下:
+
+* 增加`env`助手函数;
+* 增加`route`助手函数;
+* 增加视图路由方法;
+* 增加路由重定向方法;
+* 路由默认区分最后的目录斜杆(支持设置不区分);
+* 调整公共文件和配置文件的加载顺序(可以在配置文件中直接使用助手函数);
+* 视图类增加`filter`方法设置输出过滤;
+* `view`助手函数增加`filter`参数;
+* 改进缓存生成指令;
+* Session类的`get`方法支持获取多级;
+* Request类`only`方法支持指定默认值;
+* 改进路由分组;
+* 修正使用闭包查询的时候自动数据缓存出错的情况;
+* 废除`view_filter`钩子位置;
+* 修正分组下面的资源路由;
+* 改进session驱动;
+
+## V5.1.2 (2018-1-8)
+
+该版本改进了配置类及数据库类,主要更新如下:
+
+* 修正嵌套路由分组;
+* 修正自定义模板标签界定符后表达式语法出错的情况;
+* 修正自关联的多次调用问题;
+* 修正数组查询的`null`条件查询;
+* 修正Query类的`order`及`field`的一处可能的BUG;
+* 配置参数设置支持三级;
+* 配置对象支持`ArrayAccess`;
+* App类增加`path`方法用于设置应用目录;
+* 关联定义增加`selfRelation`方法用于设置是否为自关联;
+
+## V5.1.1 (2018-1-3)
+
+修正一些反馈的BUG,包括:
+
+* 修正Cookie类存取数组的问题
+* 修正Controller的`fetch`方法
+* 改进跨域请求
+* 修正`insertAll`方法
+* 修正`chunk`方法
+
+## V5.1.0 (2018-1-1)
+
+主要更新如下:
+
+* 增加注解路由支持
+* 路由支持跨域请求设置
+* 增加`app_dispatch`钩子位置
+* 修正多对多关联的`detach`方法
+* 修正软删除的`destroy`方法
+* Cookie类`httponly`参数默认为false
+* 日志File驱动增加`single`参数配置记录同一个文件(不按日期生成)
+* 路由的`ext`和`denyExt`方法支持不传任何参数
+* 改进模型的`save`方法对`oracle`的支持
+* Query类的`insertall`方法支持配合`data`和`limit`方法
+* 增加`whereOr`动态查询支持
+* 日志的ip地址记录改进
+* 模型`saveAll`方法支持`isUpdate`方法
+* 改进`Pivot`模型的实例化操作
+* 改进Model类的`data`方法
+* 改进多对多中间表模型类
+* 模型增加`force`方法强制更新所有数据
+* Hook类支持设置入口方法名称
+* 改进验证类
+* 改进`hasWhere`查询的数据重复问题
+* 模型的`saveall`方法返回数据集对象
+* 改进File缓存的`clear`方法
+* 缓存添加统一的序列化机制
+* 改进泛三级域名的绑定
+* 改进泛域名的传值和取值
+* Request类增加`panDomain`方法
+* 改进废弃字段判断
+* App类增加`create`方法用于实例化应用类库
+* 容器类增加`has`方法
+* 改进多数据库切换连接
+* 改进断线重连的异常捕获
+* 改进模型类`buildQuery`方法
+* Query类增加`unionAll`方法
+* 关联统计功能增强(支持Sum/Max/Min/Avg)
+* 修正延迟写入
+* chunk方法支持复合主键
+* 改进JSON类型的写入
+* 改进Mysql的insertAll方法
+* Model类`save`方法改进复合主键包含自增的情况
+* 改进Query类`inc`和`dec`方法的关键字处理
+* File缓存inc和dec方法保持原来的有效期
+* 改进redis缓存的有效期判断
+* 增加checkRule方法用于单独数据的多个验证规则
+* 修正setDec方法的延迟写入
+* max和min方法增加force参数
+* 二级配置参数区分大小写
+* 改进join方法自关联的问题
+* 修正关联模型自定义表名的情况
+* Query类增加getFieldsType和getTableFields方法
+* 取消视图替换功能及view_replace_str配置参数
+* 改进域名绑定模块后的额外路由规则问题
+* 改进mysql的insertAll方法
+* 改进insertAll方法写入json字段数据的支持
+* 改进redis长连接多编号库的情况
+
+## RC3版本(2017-11-6)
+
+主要更新如下:
+
+* 改进redis驱动的`get`方法
+* 修正Query类的`alias`方法
+* `File`类错误信息支持多语言
+* 修正路由的额外参数解析
+* 改进`whereTime`方法
+* 改进Model类`getAttr`方法
+* 改进App类的`controller`和`validate`方法支持多层
+* 改进`HasManyThrough`类
+* 修正软删除的`restore`方法
+* 改进`MorpthTo`关联
+* 改进数据库驱动类的`parseKey`方法
+* 增加`whereField`动态查询方法
+* 模型增加废弃字段功能
+* 改进路由的`after`行为检查和`before`行为机制
+* 改进路由分组的检查
+* 修正mysql的`json`字段查询
+* 取消Connection类的`quote`方法
+* 改进命令行的支持
+* 验证信息支持多语言
+* 修正路由模型绑定
+* 改进参数绑定类型对枚举类型的支持
+* 修正模板的`{$Think.version} `输出
+* 改进模板`date`函数解析
+* 改进`insertAll`方法支持分批执行
+* Request类`host`方法支持反向代理
+* 改进`JumpResponse`支持区分成功和错误模板
+* 改进开启类库后缀后的关联外键自动识别问题
+* 修正一对一关联的JOIN方式预载入查询问题
+* Query类增加`hidden`方法
+
+## RC2版本(2017-10-17)
+
+主要更新如下:
+
+* 修正视图查询
+* 修正资源路由
+* 修正`HasMany`关联 修正`where`方法的闭包查询
+* 一对一关联绑定属性到父模型后 关联属性不再保留
+* 修正应用的命令行配置文件读取
+* 改进`Connection`类的`getCacheKey`方法
+* 改进文件上传的非法图像异常
+* 改进验证类的`unique`规则
+* Config类`get`方法支持获取一级配置
+* 修正count方法对`fetchSql`的支持
+* 修正mysql驱动对`socket`支持
+* 改进Connection类的`getRealSql`方法
+* 修正`view`助手函数
+* Query类增加`leftJoin` `rightJoin` 和 `fullJoin`方法
+* 改进app_namespace的获取
+* 改进`append`方法对一对一`bind`属性的支持
+* 改进关联的`saveall`方法的返回值
+* 路由标识设置异常修复
+* 改进Route类`rule`方法
+* 改进模型的`table`属性设置
+* 改进composer autofile的加载顺序
+* 改进`exception_handle`配置对闭包的支持
+* 改进app助手函数增加参数
+* 改进composer的加载路径判断
+* 修正路由组合变量的URL生成
+* 修正路由URL生成
+* 改进`whereTime`查询并支持扩展规则
+* File类的`move`方法第二个参数支持`false`
+* 改进Config类
+* 改进缓存类`remember`方法
+* 惯例配置文件调整 Url类当普通模式参数的时候不做`urlencode`处理
+* 取消`ROOT_PATH`和`APP_PATH`常量定义 如需更改应用目录 自己重新定义入口文件
+* 增加`app_debug`的`Env`获取
+* 修正泛域名绑定
+* 改进查询表达式的解析机制
+* mysql增加`regexp`查询表达式 支持正则查询
+* 改进查询表达式的异常判断
+* 改进model类的`destroy`方法
+* 改进Builder类 取消`parseValue`方法
+* 修正like查询的参数绑定问题
+* console和start文件移出核心纳入应用库
+* 改进Db类主键删除方法
+* 改进泛域名绑定模块
+* 取消`BIND_MODULE`常量 改为在入口文件使用`bind`方法设置
+* 改进数组查询
+* 改进模板渲染的异常处理
+* 改进控制器基类的架构方法参数
+* 改进Controller类的`success`和`error`方法
+* 改进对浏览器`JSON-Handle`插件的支持
+* 优化跳转模板的移动端显示
+* 修正模型查询的`chunk`方法对时间字段的支持
+* 改进trace驱动
+* Collection类增加`push`方法
+* 改进Redis Session驱动
+* 增加JumpResponse驱动
+
+
+## RC1(2017-9-8)
+
+主要新特性为:
+
+* 引入容器和Facade支持
+* 依赖注入完善和支持更多场景
+* 重构的(对象化)路由
+* 配置和路由目录独立
+* 取消系统常量
+* 助手函数增强
+* 类库别名机制
+* 模型和数据库增强
+* 验证类增强
+* 模板引擎改进
+* 支持PSR-3日志规范
+* RC1版本取消了5.0多个字段批量数组查询的方式

+ 32 - 0
LICENSE.txt

@@ -0,0 +1,32 @@
+
+ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
+版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn)
+All rights reserved。
+ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+
+Apache Licence是著名的非盈利开源组织Apache采用的协议。
+该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
+允许代码修改,再作为开源或商业软件发布。需要满足
+的条件: 
+1. 需要给代码的用户一份Apache Licence ;
+2. 如果你修改了代码,需要在被修改的文件中说明;
+3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
+带有原来代码中的协议,商标,专利声明和其他原来作者规
+定需要包含的说明;
+4. 如果再发布的产品中包含一个Notice文件,则在Notice文
+件中需要带有本协议内容。你可以在Notice中增加自己的
+许可,但不可以表现为对Apache Licence构成更改。 
+具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.

+ 180 - 0
README.md

@@ -0,0 +1,180 @@
+![](https://box.kancloud.cn/5a0aaa69a5ff42657b5c4715f3d49221) 
+
+ThinkPHP 5.1(LTS版本) —— 12载初心,你值得信赖的PHP框架
+===============
+
+[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/top-think/framework/badges/quality-score.png?b=5.1)](https://scrutinizer-ci.com/g/top-think/framework/?branch=5.1)
+[![Build Status](https://travis-ci.org/top-think/framework.svg?branch=master)](https://travis-ci.org/top-think/framework)
+[![Total Downloads](https://poser.pugx.org/topthink/framework/downloads)](https://packagist.org/packages/topthink/framework)
+[![Latest Stable Version](https://poser.pugx.org/topthink/framework/v/stable)](https://packagist.org/packages/topthink/framework)
+[![PHP Version](https://img.shields.io/badge/php-%3E%3D5.6-8892BF.svg)](http://www.php.net/)
+[![License](https://poser.pugx.org/topthink/framework/license)](https://packagist.org/packages/topthink/framework)
+
+ThinkPHP5.1对底层架构做了进一步的改进,减少依赖,其主要特性包括:
+
+ + 采用容器统一管理对象
+ + 支持Facade
+ + 注解路由支持
+ + 路由跨域请求支持
+ + 配置和路由目录独立
+ + 取消系统常量
+ + 助手函数增强
+ + 类库别名机制
+ + 增加条件查询
+ + 改进查询机制
+ + 配置采用二级
+ + 依赖注入完善
+ + 支持`PSR-3`日志规范
+ + 中间件支持(V5.1.6+)
+ + Swoole/Workerman支持(V5.1.18+)
+
+
+> ThinkPHP5的运行环境要求PHP5.6以上。
+
+## 安装
+
+使用composer安装
+
+~~~
+composer create-project topthink/think tp
+~~~
+
+启动服务
+
+~~~
+cd tp
+php think run
+~~~
+
+然后就可以在浏览器中访问
+
+~~~
+http://localhost:8000
+~~~
+
+更新框架
+~~~
+composer update topthink/framework
+~~~
+
+
+## 在线手册
+
++ [完全开发手册](https://www.kancloud.cn/manual/thinkphp5_1/content)
++ [升级指导](https://www.kancloud.cn/manual/thinkphp5_1/354155) 
+
+## 目录结构
+
+初始的目录结构如下:
+
+~~~
+www  WEB部署目录(或者子目录)
+├─application           应用目录
+│  ├─common             公共模块目录(可以更改)
+│  ├─module_name        模块目录
+│  │  ├─common.php      模块函数文件
+│  │  ├─controller      控制器目录
+│  │  ├─model           模型目录
+│  │  ├─view            视图目录
+│  │  └─ ...            更多类库目录
+│  │
+│  ├─command.php        命令行定义文件
+│  ├─common.php         公共函数文件
+│  └─tags.php           应用行为扩展定义文件
+│
+├─config                应用配置目录
+│  ├─module_name        模块配置目录
+│  │  ├─database.php    数据库配置
+│  │  ├─cache           缓存配置
+│  │  └─ ...            
+│  │
+│  ├─app.php            应用配置
+│  ├─cache.php          缓存配置
+│  ├─cookie.php         Cookie配置
+│  ├─database.php       数据库配置
+│  ├─log.php            日志配置
+│  ├─session.php        Session配置
+│  ├─template.php       模板引擎配置
+│  └─trace.php          Trace配置
+│
+├─route                 路由定义目录
+│  ├─route.php          路由定义
+│  └─...                更多
+│
+├─public                WEB目录(对外访问目录)
+│  ├─index.php          入口文件
+│  ├─router.php         快速测试文件
+│  └─.htaccess          用于apache的重写
+│
+├─thinkphp              框架系统目录
+│  ├─lang               语言文件目录
+│  ├─library            框架类库目录
+│  │  ├─think           Think类库包目录
+│  │  └─traits          系统Trait目录
+│  │
+│  ├─tpl                系统模板目录
+│  ├─base.php           基础定义文件
+│  ├─console.php        控制台入口文件
+│  ├─convention.php     框架惯例配置文件
+│  ├─helper.php         助手函数文件
+│  ├─phpunit.xml        phpunit配置文件
+│  └─start.php          框架入口文件
+│
+├─extend                扩展类库目录
+├─runtime               应用的运行时目录(可写,可定制)
+├─vendor                第三方类库目录(Composer依赖库)
+├─build.php             自动生成定义文件(参考)
+├─composer.json         composer 定义文件
+├─LICENSE.txt           授权说明文件
+├─README.md             README 文件
+├─think                 命令行入口文件
+~~~
+
+> 可以使用php自带webserver快速测试
+> 切换到根目录后,启动命令:php think run
+
+## 命名规范
+
+`ThinkPHP5`遵循PSR-2命名规范和PSR-4自动加载规范,并且注意如下规范:
+
+### 目录和文件
+
+*   目录不强制规范,驼峰和小写+下划线模式均支持;
+*   类库、函数文件统一以`.php`为后缀;
+*   类的文件名均以命名空间定义,并且命名空间的路径和类库文件所在路径一致;
+*   类名和类文件名保持一致,统一采用驼峰法命名(首字母大写);
+
+### 函数和类、属性命名
+
+*   类的命名采用驼峰法,并且首字母大写,例如 `User`、`UserType`,默认不需要添加后缀,例如`UserController`应该直接命名为`User`;
+*   函数的命名使用小写字母和下划线(小写字母开头)的方式,例如 `get_client_ip`;
+*   方法的命名使用驼峰法,并且首字母小写,例如 `getUserName`;
+*   属性的命名使用驼峰法,并且首字母小写,例如 `tableName`、`instance`;
+*   以双下划线“__”打头的函数或方法作为魔法方法,例如 `__call` 和 `__autoload`;
+
+### 常量和配置
+
+*   常量以大写字母和下划线命名,例如 `APP_PATH`和 `THINK_PATH`;
+*   配置参数以小写字母和下划线命名,例如 `url_route_on` 和`url_convert`;
+
+### 数据表和字段
+
+*   数据表和字段采用小写加下划线方式命名,并注意字段名不要以下划线开头,例如 `think_user` 表和 `user_name`字段,不建议使用驼峰和中文作为数据表字段命名。
+
+## 参与开发
+
+请参阅 [ThinkPHP5 核心框架包](https://github.com/top-think/framework)。
+
+## 版权信息
+
+ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
+
+本项目包含的第三方源码和二进制文件之版权信息另行标注。
+
+版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn)
+
+All rights reserved。
+
+ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+
+更多细节参阅 [LICENSE.txt](LICENSE.txt)

+ 1 - 0
application/.htaccess

@@ -0,0 +1 @@
+deny from all

+ 12 - 0
application/command.php

@@ -0,0 +1,12 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+return [];

+ 217 - 0
application/common.php

@@ -0,0 +1,217 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: 流年 <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// 应用公共文件
+
+
+function pre($content, $is_die = true)
+{
+    header('Content-type: text/html; charset=utf-8');
+    echo '<pre>' . print_r($content, true);
+    $is_die && die();
+}
+
+
+
+/**
+ * 成功Json返回函数
+ * @param string $code
+ * @param array  $data
+ * @param string $msg
+ */
+function json_return($code,$data=[],$msg='')
+{
+    exit(json_encode(['code'=>$code,'data'=>$data,'msg'=>$msg],JSON_UNESCAPED_UNICODE ));
+}
+
+/**
+ * php发送http请求
+ * @param array $params 相关请求参数
+ * @param booble $is_json  数据格式,默认false为数组,true为json
+ * @return string
+ */
+function _curl(array $params,$is_json=false )
+{
+    $_data = $params['data'] ?? null;
+    $url = $params['url'];
+    $defaults = [
+        CURLOPT_HEADER => 0,
+        CURLOPT_URL => $params['url'],
+        CURLOPT_FRESH_CONNECT => 1,
+        CURLOPT_RETURNTRANSFER => 1,
+        CURLOPT_FORBID_REUSE => 1,
+        CURLOPT_TIMEOUT => 60,
+        CURLOPT_SSL_VERIFYHOST=>2,
+        CURLOPT_SSL_VERIFYPEER=>0,
+    ];
+    switch ($params['method'])
+    {
+        case 'post':
+            $defaults[CURLOPT_CUSTOMREQUEST] = "POST";
+            break;
+        case 'get':
+            $defaults[CURLOPT_CUSTOMREQUEST] = "GET";
+            break;
+        case 'put':
+            $defaults[CURLOPT_CUSTOMREQUEST] = "PUT";
+            break;
+        case 'delete':
+            $defaults[CURLOPT_CUSTOMREQUEST] = "DELETE";
+            break;
+    }
+    if ($_data && $params['method']!='get') {
+        if($is_json){
+            $defaults[CURLOPT_POSTFIELDS] = json_encode($_data);
+            $defaults[CURLOPT_HTTPHEADER] = [
+                'Content-Type: application/json',
+                'Content-Length: ' . strlen(json_encode($_data))
+            ];
+        }else{
+            $defaults[CURLOPT_POSTFIELDS] = $_data;
+        }
+    }elseif($params['method']=='get'){
+        if(is_array($_data)){
+            $param = '';
+            foreach ($_data as $k=>$v){
+                $param .= $k."=".$v."&";
+            }
+            $param = rtrim($param ,"&");
+            if(!strpos($url,"?")){
+                $url .= "?".$param;
+            }else{
+                $url .= "&".$param;
+            }
+        }
+        $defaults[CURLOPT_URL] = $url;
+    }
+
+
+    $ch = curl_init();
+    curl_setopt_array($ch, ($defaults));
+
+    $result = curl_exec($ch);
+    curl_close($ch);
+
+    return $result;
+}
+
+/**
+ * 整理菜单住方法
+ * @param $param
+ * @return array
+ */
+function prepareMenu($param)
+{
+    $param = objToArray($param);
+    $parent = []; //父类
+    $child = [];  //子类
+
+    foreach($param as $key=>$vo){
+
+        if(0 == $vo['type_id']){
+            $vo['href'] = '#';
+            $parent[] = $vo;
+        }else{
+            $vo['href'] = url($vo['control_name'] .'/'. $vo['action_name']); //跳转地址
+            $child[] = $vo;
+        }
+    }
+
+    foreach($parent as $key=>$vo){
+        foreach($child as $k=>$v){
+
+            if($v['type_id'] == $vo['id']){
+                $parent[$key]['child'][] = $v;
+            }
+        }
+    }
+    unset($child);
+
+    return $parent;
+}
+
+/**
+ * 统一返回信息
+ * @param $code
+ * @param $data
+ * @param $msge
+ */
+function msg($code, $data, $msg)
+{
+    return compact('code', 'data', 'msg');
+}
+
+/**
+ * 整理出tree数据 ---  layui tree
+ * @param $pInfo
+ * @param $spread
+ */
+function getTree($pInfo, $spread = true)
+{
+
+    $res = [];
+    $tree = [];
+    //整理数组
+    foreach($pInfo as $key=>$vo){
+
+        if($spread){
+            $vo['spread'] = true;  //默认展开
+        }
+        $res[$vo['id']] = $vo;
+        $res[$vo['id']]['children'] = [];
+    }
+    unset($pInfo);
+
+    //查找子孙
+    foreach($res as $key=>$vo){
+        if(0 != $vo['pid']){
+            $res[$vo['pid']]['children'][] = &$res[$key];
+        }
+    }
+
+    //过滤杂质
+    foreach( $res as $key=>$vo ){
+        if(0 == $vo['pid']){
+            $tree[] = $vo;
+        }
+    }
+    unset( $res );
+
+    return $tree;
+}
+
+/**
+ * 对象转换成数组
+ * @param $obj
+ */
+function objToArray($obj)
+{
+    return json_decode(json_encode($obj), true);
+}
+
+
+/**
+ * 权限检测
+ * @param $rule
+ */
+function authCheck($rule)
+{
+    $control = explode('/', $rule)['0'];
+    if(in_array($control, ['login', 'index'])){
+        return true;
+    }
+
+    if(in_array($rule, cache(session('role_id')))){
+        return true;
+    }
+
+    return false;
+}

+ 5 - 0
application/index/config.php

@@ -0,0 +1,5 @@
+<?php
+
+return [
+    'user_status'  => ['1' => '已启用' , '2' => '已停用']
+];

+ 10 - 0
application/index/controller/Index.php

@@ -0,0 +1,10 @@
+<?php
+namespace app\index\controller;
+
+class Index
+{
+    public function index()
+    {
+        echo 'index';
+    }
+}

+ 14 - 0
application/provider.php

@@ -0,0 +1,14 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// 应用容器绑定定义
+return [
+];

+ 28 - 0
application/tags.php

@@ -0,0 +1,28 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// 应用行为扩展定义文件
+return [
+    // 应用初始化
+    'app_init'     => [],
+    // 应用开始
+    'app_begin'    => [],
+    // 模块初始化
+    'module_init'  => [],
+    // 操作开始执行
+    'action_begin' => [],
+    // 视图内容过滤
+    'view_filter'  => [],
+    // 日志写入
+    'log_write'    => [],
+    // 应用结束
+    'app_end'      => [],
+];

+ 26 - 0
build.php

@@ -0,0 +1,26 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+return [
+    // 生成应用公共文件
+    '__file__' => ['common.php'],
+
+    // 定义demo模块的自动生成 (按照实际定义的文件名生成)
+    'demo'     => [
+        '__file__'   => ['common.php'],
+        '__dir__'    => ['behavior', 'controller', 'model', 'view'],
+        'controller' => ['Index', 'Test', 'UserType'],
+        'model'      => ['User', 'UserType'],
+        'view'       => ['index/index'],
+    ],
+
+    // 其他更多的模块定义
+];

+ 34 - 0
composer.json

@@ -0,0 +1,34 @@
+{
+    "name": "topthink/think",
+    "description": "the new thinkphp framework",
+    "type": "project",
+    "keywords": [
+        "framework",
+        "thinkphp",
+        "ORM"
+    ],
+    "homepage": "http://thinkphp.cn/",
+    "license": "Apache-2.0",
+    "authors": [
+        {
+            "name": "liu21st",
+            "email": "liu21st@gmail.com"
+        }
+    ],
+    "require": {
+        "php": ">=5.6.0",
+        "topthink/framework": "5.1.*",
+        "topthink/think-testing": "2.0.*"
+    },
+    "autoload": {
+        "psr-4": {
+            "app\\": "application"
+        }
+    },
+    "extra": {
+        "think-path": "thinkphp"
+    },
+    "config": {
+        "preferred-install": "dist"
+    }
+}

File diff suppressed because it is too large
+ 1828 - 0
composer.lock


+ 146 - 0
config/app.php

@@ -0,0 +1,146 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// +----------------------------------------------------------------------
+// | 应用设置
+// +----------------------------------------------------------------------
+
+return [
+    // 应用名称
+    'app_name'               => '',
+    // 应用地址
+    'app_host'               => '',
+    // 应用调试模式
+    'app_debug'              => true,
+    // 应用Trace
+    'app_trace'              => false,
+    // 是否支持多模块
+    'app_multi_module'       => true,
+    // 入口自动绑定模块
+    'auto_bind_module'       => false,
+    // 注册的根命名空间
+    'root_namespace'         => [],
+    // 默认输出类型
+    'default_return_type'    => 'html',
+    // 默认AJAX 数据返回格式,可选json xml ...
+    'default_ajax_return'    => 'json',
+    // 默认JSONP格式返回的处理方法
+    'default_jsonp_handler'  => 'jsonpReturn',
+    // 默认JSONP处理方法
+    'var_jsonp_handler'      => 'callback',
+    // 默认时区
+    'default_timezone'       => 'Asia/Shanghai',
+    // 是否开启多语言
+    'lang_switch_on'         => false,
+    // 默认全局过滤方法 用逗号分隔多个
+    'default_filter'         => '',
+    // 默认语言
+    'default_lang'           => 'zh-cn',
+    // 应用类库后缀
+    'class_suffix'           => false,
+    // 控制器类后缀
+    'controller_suffix'      => false,
+
+    // +----------------------------------------------------------------------
+    // | 模块设置
+    // +----------------------------------------------------------------------
+
+    // 默认模块名
+    'default_module'         => 'index',
+    // 禁止访问模块
+    'deny_module_list'       => ['common'],
+    // 默认控制器名
+    'default_controller'     => 'Index',
+    // 默认操作名
+    'default_action'         => 'index',
+    // 默认验证器
+    'default_validate'       => '',
+    // 默认的空模块名
+    'empty_module'           => '',
+    // 默认的空控制器名
+    'empty_controller'       => 'Error',
+    // 操作方法前缀
+    'use_action_prefix'      => false,
+    // 操作方法后缀
+    'action_suffix'          => '',
+    // 自动搜索控制器
+    'controller_auto_search' => false,
+
+    // +----------------------------------------------------------------------
+    // | URL设置
+    // +----------------------------------------------------------------------
+
+    // PATHINFO变量名 用于兼容模式
+    'var_pathinfo'           => 's',
+    // 兼容PATH_INFO获取
+    'pathinfo_fetch'         => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
+    // pathinfo分隔符
+    'pathinfo_depr'          => '/',
+    // HTTPS代理标识
+    'https_agent_name'       => '',
+    // IP代理获取标识
+    'http_agent_ip'          => 'X-REAL-IP',
+    // URL伪静态后缀
+    'url_html_suffix'        => 'html',
+    // URL普通方式参数 用于自动生成
+    'url_common_param'       => false,
+    // URL参数方式 0 按名称成对解析 1 按顺序解析
+    'url_param_type'         => 0,
+    // 是否开启路由延迟解析
+    'url_lazy_route'         => false,
+    // 是否强制使用路由
+    'url_route_must'         => false,
+    // 合并路由规则
+    'route_rule_merge'       => false,
+    // 路由是否完全匹配
+    'route_complete_match'   => false,
+    // 使用注解路由
+    'route_annotation'       => false,
+    // 域名根,如thinkphp.cn
+    'url_domain_root'        => '',
+    // 是否自动转换URL中的控制器和操作名
+    'url_convert'            => true,
+    // 默认的访问控制器层
+    'url_controller_layer'   => 'controller',
+    // 表单请求类型伪装变量
+    'var_method'             => '_method',
+    // 表单ajax伪装变量
+    'var_ajax'               => '_ajax',
+    // 表单pjax伪装变量
+    'var_pjax'               => '_pjax',
+    // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
+    'request_cache'          => false,
+    // 请求缓存有效期
+    'request_cache_expire'   => null,
+    // 全局请求缓存排除规则
+    'request_cache_except'   => [],
+    // 是否开启路由缓存
+    'route_check_cache'      => false,
+    // 路由缓存的Key自定义设置(闭包),默认为当前URL和请求类型的md5
+    'route_check_cache_key'  => '',
+    // 路由缓存类型及参数
+    'route_cache_option'     => [],
+
+    // 默认跳转页面对应的模板文件
+    'dispatch_success_tmpl'  => Env::get('think_path') . 'tpl/dispatch_jump.tpl',
+    'dispatch_error_tmpl'    => Env::get('think_path') . 'tpl/dispatch_jump.tpl',
+
+    // 异常页面的模板文件
+    'exception_tmpl'         => Env::get('think_path') . 'tpl/think_exception.tpl',
+
+    // 错误显示信息,非调试模式有效
+    'error_message'          => '页面错误!请稍后再试~',
+    // 显示错误信息
+    'show_error_msg'         => false,
+    // 异常处理handle类 留空使用 \think\exception\Handle
+    'exception_handle'       => '',
+
+];

+ 25 - 0
config/cache.php

@@ -0,0 +1,25 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// +----------------------------------------------------------------------
+// | 缓存设置
+// +----------------------------------------------------------------------
+
+return [
+    // 驱动方式
+    'type'   => 'File',
+    // 缓存保存目录
+    'path'   => '',
+    // 缓存前缀
+    'prefix' => '',
+    // 缓存有效期 0表示永久缓存
+    'expire' => 0,
+];

+ 20 - 0
config/console.php

@@ -0,0 +1,20 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// +----------------------------------------------------------------------
+// | 控制台配置
+// +----------------------------------------------------------------------
+return [
+    'name'      => 'Think Console',
+    'version'   => '0.1',
+    'user'      => null,
+    'auto_path' => env('app_path') . 'command' . DIRECTORY_SEPARATOR,
+];

+ 30 - 0
config/cookie.php

@@ -0,0 +1,30 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// +----------------------------------------------------------------------
+// | Cookie设置
+// +----------------------------------------------------------------------
+return [
+    // cookie 名称前缀
+    'prefix'    => '',
+    // cookie 保存时间
+    'expire'    => 0,
+    // cookie 保存路径
+    'path'      => '/',
+    // cookie 有效域名
+    'domain'    => '',
+    //  cookie 启用安全传输
+    'secure'    => false,
+    // httponly设置
+    'httponly'  => '',
+    // 是否使用 setcookie
+    'setcookie' => true,
+];

+ 63 - 0
config/database.php

@@ -0,0 +1,63 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+return [
+    // 数据库类型
+    'type'            => 'mysql',
+    // 服务器地址
+    'hostname'        => '',
+    // 数据库名
+    'database'        => '',
+    // 用户名
+    'username'        => '',
+    // 密码
+    'password'        => '',
+    // 端口
+    'hostport'        => '3306',
+    // 连接dsn
+    'dsn'             => '',
+    // 数据库连接参数
+    'params'          => [],
+    // 数据库编码默认采用utf8
+    'charset'         => 'utf8',
+    // 数据库表前缀
+    'prefix'          => '',
+    // 数据库调试模式
+    'debug'           => true,
+    // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
+    'deploy'          => 0,
+    // 数据库读写是否分离 主从式有效
+    'rw_separate'     => false,
+    // 读写分离后 主服务器数量
+    'master_num'      => 1,
+    // 指定从服务器序号
+    'slave_no'        => '',
+    // 自动读取主库数据
+    'read_master'     => false,
+    // 是否严格检查字段是否存在
+    'fields_strict'   => true,
+    // 数据集返回类型
+    'resultset_type'  => 'array',
+    // 自动写入时间戳字段
+    'auto_timestamp'  => false,
+    // 时间字段取出后的默认时间格式
+    'datetime_format' => 'Y-m-d H:i:s',
+    // 是否需要进行SQL性能分析
+    'sql_explain'     => false,
+    // Builder类
+    'builder'         => '',
+    // Query类
+    'query'           => '\\think\\db\\Query',
+    // 是否需要断线重连
+    'break_reconnect' => false,
+    // 断线标识字符串
+    'break_match_str' => [],
+];

+ 30 - 0
config/log.php

@@ -0,0 +1,30 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// +----------------------------------------------------------------------
+// | 日志设置
+// +----------------------------------------------------------------------
+return [
+    // 日志记录方式,内置 file socket 支持扩展
+    'type'        => 'File',
+    // 日志保存目录
+    'path'        => '',
+    // 日志记录级别
+    'level'       => [],
+    // 单文件日志写入
+    'single'      => false,
+    // 独立日志级别
+    'apart_level' => [],
+    // 最大日志文件数量
+    'max_files'   => 0,
+    // 是否关闭日志写入
+    'close'       => false,
+];

+ 18 - 0
config/middleware.php

@@ -0,0 +1,18 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// +----------------------------------------------------------------------
+// | 中间件配置
+// +----------------------------------------------------------------------
+return [
+    // 默认中间件命名空间
+    'default_namespace' => 'app\\http\\middleware\\',
+];

+ 26 - 0
config/session.php

@@ -0,0 +1,26 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// +----------------------------------------------------------------------
+// | 会话设置
+// +----------------------------------------------------------------------
+
+return [
+    'id'             => '',
+    // SESSION_ID的提交变量,解决flash上传跨域
+    'var_session_id' => '',
+    // SESSION 前缀
+    'prefix'         => 'think',
+    // 驱动方式 支持redis memcache memcached
+    'type'           => '',
+    // 是否自动开启 SESSION
+    'auto_start'     => true,
+];

+ 35 - 0
config/template.php

@@ -0,0 +1,35 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// +----------------------------------------------------------------------
+// | 模板设置
+// +----------------------------------------------------------------------
+
+return [
+    // 模板引擎类型 支持 php think 支持扩展
+    'type'         => 'Think',
+    // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
+    'auto_rule'    => 1,
+    // 模板路径
+    'view_path'    => '',
+    // 模板后缀
+    'view_suffix'  => 'html',
+    // 模板文件名分隔符
+    'view_depr'    => DIRECTORY_SEPARATOR,
+    // 模板引擎普通标签开始标记
+    'tpl_begin'    => '{',
+    // 模板引擎普通标签结束标记
+    'tpl_end'      => '}',
+    // 标签库标签开始标记
+    'taglib_begin' => '{',
+    // 标签库标签结束标记
+    'taglib_end'   => '}',
+];

+ 18 - 0
config/trace.php

@@ -0,0 +1,18 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// +----------------------------------------------------------------------
+// | Trace设置 开启 app_trace 后 有效
+// +----------------------------------------------------------------------
+return [
+    // 内置Html Console 支持扩展
+    'type' => 'Html',
+];

+ 2 - 0
extend/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 21 - 0
phpunit.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false">
+    <testsuites>
+        <testsuite name="Application Test Suite">
+            <directory>./tests/</directory>
+        </testsuite>
+    </testsuites>
+    <filter>
+        <whitelist>
+            <directory suffix=".php">application/</directory>
+        </whitelist>
+    </filter>
+</phpunit>

+ 8 - 0
public/.htaccess

@@ -0,0 +1,8 @@
+<IfModule mod_rewrite.c>
+  Options +FollowSymlinks -Multiviews
+  RewriteEngine On
+
+  RewriteCond %{REQUEST_FILENAME} !-d
+  RewriteCond %{REQUEST_FILENAME} !-f
+  RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
+</IfModule>

BIN
public/favicon.ico


+ 21 - 0
public/index.php

@@ -0,0 +1,21 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// [ 应用入口文件 ]
+namespace think;
+
+// 加载基础文件
+require __DIR__ . '/../thinkphp/base.php';
+
+// 支持事先使用静态方法设置Request对象和Config对象
+
+// 执行应用并响应
+Container::get('app')->run()->send();

+ 2 - 0
public/robots.txt

@@ -0,0 +1,2 @@
+User-agent: *
+Disallow:

+ 17 - 0
public/router.php

@@ -0,0 +1,17 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+// $Id$
+
+if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SCRIPT_NAME"])) {
+    return false;
+} else {
+    require __DIR__ . "/index.php";
+}

+ 2 - 0
public/static/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 6 - 0
route/route.php

@@ -0,0 +1,6 @@
+<?php
+
+
+return [
+
+];

+ 2 - 0
runtime/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 20 - 0
tests/ExampleTest.php

@@ -0,0 +1,20 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace tests;
+
+class ExampleTest extends TestCase
+{
+
+    public function testBasicExample()
+    {
+        $this->visit('/')->see('ThinkPHP');
+    }
+}

+ 11 - 0
tests/LoginTest.php

@@ -0,0 +1,11 @@
+<?php
+
+namespace tests;
+
+use PHPUnit\Framework\TestCase;
+
+class LoginTest extends TestCase
+{
+
+
+}

+ 16 - 0
tests/TestCase.php

@@ -0,0 +1,16 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+namespace tests;
+
+class TestCase extends \think\testing\TestCase
+{
+    protected $baseUrl = 'http://localhost';
+}

+ 22 - 0
think

@@ -0,0 +1,22 @@
+#!/usr/bin/env php
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+// 加载基础文件
+require __DIR__ . '/thinkphp/base.php';
+
+// 应用初始化
+Container::get('app')->path(__DIR__ . '/application/')->initialize();
+
+// 控制台初始化
+Console::init();

+ 8 - 0
thinkphp/.gitignore

@@ -0,0 +1,8 @@
+/vendor
+composer.phar
+composer.lock
+.DS_Store
+Thumbs.db
+/phpunit.xml
+/.idea
+/.vscode

+ 1 - 0
thinkphp/.htaccess

@@ -0,0 +1 @@
+deny from all

+ 119 - 0
thinkphp/CONTRIBUTING.md

@@ -0,0 +1,119 @@
+如何贡献我的源代码
+===
+
+此文档介绍了 ThinkPHP 团队的组成以及运转机制,您提交的代码将给 ThinkPHP 项目带来什么好处,以及如何才能加入我们的行列。
+
+## 通过 Github 贡献代码
+
+ThinkPHP 目前使用 Git 来控制程序版本,如果你想为 ThinkPHP 贡献源代码,请先大致了解 Git 的使用方法。我们目前把项目托管在 GitHub 上,任何 GitHub 用户都可以向我们贡献代码。
+
+参与的方式很简单,`fork`一份 ThinkPHP 的代码到你的仓库中,修改后提交,并向我们发起`pull request`申请,我们会及时对代码进行审查并处理你的申请并。审查通过后,你的代码将被`merge`进我们的仓库中,这样你就会自动出现在贡献者名单里了,非常方便。
+
+我们希望你贡献的代码符合:
+
+* ThinkPHP 的编码规范
+* 适当的注释,能让其他人读懂
+* 遵循 Apache2 开源协议
+
+**如果想要了解更多细节或有任何疑问,请继续阅读下面的内容**
+
+### 注意事项
+
+* 本项目代码格式化标准选用 [**PSR-2**](http://www.kancloud.cn/thinkphp/php-fig-psr/3141);
+* 类名和类文件名遵循 [**PSR-4**](http://www.kancloud.cn/thinkphp/php-fig-psr/3144);
+* 对于 Issues 的处理,请使用诸如 `fix #xxx(Issue ID)` 的 commit title 直接关闭 issue。
+* 系统会自动在 PHP 5.4 5.5 5.6 7.0 和 HHVM 上测试修改,其中 HHVM 下的测试容许报错,请确保你的修改符合 PHP 5.4 ~ 5.6 和 PHP 7.0 的语法规范;
+* 管理员不会合并造成 CI faild 的修改,若出现 CI faild 请检查自己的源代码或修改相应的[单元测试文件](tests);
+
+## GitHub Issue
+
+GitHub 提供了 Issue 功能,该功能可以用于:
+
+* 提出 bug
+* 提出功能改进
+* 反馈使用体验
+
+该功能不应该用于:
+
+ * 提出修改意见(涉及代码署名和修订追溯问题)
+ * 不友善的言论
+
+## 快速修改
+
+**GitHub 提供了快速编辑文件的功能**
+
+1. 登录 GitHub 帐号;
+2. 浏览项目文件,找到要进行修改的文件;
+3. 点击右上角铅笔图标进行修改;
+4. 填写 `Commit changes` 相关内容(Title 必填);
+5. 提交修改,等待 CI 验证和管理员合并。
+
+**若您需要一次提交大量修改,请继续阅读下面的内容**
+
+## 完整流程
+
+1. `fork`本项目;
+2. 克隆(`clone`)你 `fork` 的项目到本地;
+3. 新建分支(`branch`)并检出(`checkout`)新分支;
+4. 添加本项目到你的本地 git 仓库作为上游(`upstream`);
+5. 进行修改,若你的修改包含方法或函数的增减,请记得修改[单元测试文件](tests);
+6. 变基(衍合 `rebase`)你的分支到上游 master 分支;
+7. `push` 你的本地仓库到 GitHub;
+8. 提交 `pull request`;
+9. 等待 CI 验证(若不通过则重复 5~7,GitHub 会自动更新你的 `pull request`);
+10. 等待管理员处理,并及时 `rebase` 你的分支到上游 master 分支(若上游 master 分支有修改)。
+
+*若有必要,可以 `git push -f` 强行推送 rebase 后的分支到自己的 `fork`*
+
+*绝对不可以使用 `git push -f` 强行推送修改到上游*
+
+### 注意事项
+
+* 若对上述流程有任何不清楚的地方,请查阅 GIT 教程,如 [这个](http://backlogtool.com/git-guide/cn/);
+* 对于代码**不同方面**的修改,请在自己 `fork` 的项目中**创建不同的分支**(原因参见`完整流程`第9条备注部分);
+* 变基及交互式变基操作参见 [Git 交互式变基](http://pakchoi.me/2015/03/17/git-interactive-rebase/)
+
+## 推荐资源
+
+### 开发环境
+
+* XAMPP for Windows 5.5.x
+* WampServer (for Windows)
+* upupw Apache PHP5.4 ( for Windows)
+
+或自行安装
+
+- Apache / Nginx
+- PHP 5.4 ~ 5.6
+- MySQL / MariaDB
+
+*Windows 用户推荐添加 PHP bin 目录到 PATH,方便使用 composer*
+
+*Linux 用户自行配置环境, Mac 用户推荐使用内置 Apache 配合 Homebrew 安装 PHP 和 MariaDB*
+
+### 编辑器
+
+Sublime Text 3 + phpfmt 插件
+
+phpfmt 插件参数
+
+```json
+{
+	"autocomplete": true,
+	"enable_auto_align": true,
+	"format_on_save": true,
+	"indent_with_space": true,
+	"psr1_naming": false,
+	"psr2": true,
+	"version": 4
+}
+```
+
+或其他 编辑器 / IDE 配合 PSR2 自动格式化工具
+
+### Git GUI
+
+* SourceTree
+* GitHub Desktop
+
+或其他 Git 图形界面客户端

+ 32 - 0
thinkphp/LICENSE.txt

@@ -0,0 +1,32 @@
+
+ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
+版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn)
+All rights reserved。
+ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+
+Apache Licence是著名的非盈利开源组织Apache采用的协议。
+该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
+允许代码修改,再作为开源或商业软件发布。需要满足
+的条件: 
+1. 需要给代码的用户一份Apache Licence ;
+2. 如果你修改了代码,需要在被修改的文件中说明;
+3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
+带有原来代码中的协议,商标,专利声明和其他原来作者规
+定需要包含的说明;
+4. 如果再发布的产品中包含一个Notice文件,则在Notice文
+件中需要带有本协议内容。你可以在Notice中增加自己的
+许可,但不可以表现为对Apache Licence构成更改。 
+具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.

+ 93 - 0
thinkphp/README.md

@@ -0,0 +1,93 @@
+![](https://box.kancloud.cn/5a0aaa69a5ff42657b5c4715f3d49221) 
+
+ThinkPHP 5.1(LTS) —— 12载初心,你值得信赖的PHP框架
+===============
+
+[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/top-think/framework/badges/quality-score.png?b=5.1)](https://scrutinizer-ci.com/g/top-think/framework/?branch=5.1)
+[![Build Status](https://travis-ci.org/top-think/framework.svg?branch=master)](https://travis-ci.org/top-think/framework)
+[![Total Downloads](https://poser.pugx.org/topthink/framework/downloads)](https://packagist.org/packages/topthink/framework)
+[![Latest Stable Version](https://poser.pugx.org/topthink/framework/v/stable)](https://packagist.org/packages/topthink/framework)
+[![PHP Version](https://img.shields.io/badge/php-%3E%3D5.6-8892BF.svg)](http://www.php.net/)
+[![License](https://poser.pugx.org/topthink/framework/license)](https://packagist.org/packages/topthink/framework)
+
+ThinkPHP5.1对底层架构做了进一步的改进,减少依赖,其主要特性包括:
+
+ + 采用容器统一管理对象
+ + 支持Facade
+ + 更易用的路由
+ + 注解路由支持
+ + 路由跨域请求支持
+ + 验证类增强
+ + 配置和路由目录独立
+ + 取消系统常量
+ + 类库别名机制
+ + 模型和数据库增强
+ + 依赖注入完善
+ + 支持PSR-3日志规范
+ + 中间件支持(`V5.1.6+`)
+ + 支持`Swoole`/`Workerman`运行(`V5.1.18+`)
+
+官方已经正式宣布`5.1.27`版本为LTS版本。
+
+### 废除的功能:
+
+ + 聚合模型
+ + 内置控制器扩展类
+ + 模型自动验证
+
+> ThinkPHP5.1的运行环境要求PHP5.6+。
+
+
+## 安装
+
+使用composer安装
+
+~~~
+composer create-project topthink/think tp
+~~~
+
+启动服务
+
+~~~
+cd tp
+php think run
+~~~
+
+然后就可以在浏览器中访问
+
+~~~
+http://localhost:8000
+~~~
+
+更新框架
+~~~
+composer update topthink/framework
+~~~
+
+
+## 在线手册
+
++ [完全开发手册](https://www.kancloud.cn/manual/thinkphp5_1/content)
++ [升级指导](https://www.kancloud.cn/manual/thinkphp5_1/354155) 
+
+## 命名规范
+
+`ThinkPHP5.1`遵循PSR-2命名规范和PSR-4自动加载规范。
+
+## 参与开发
+
+请参阅 [ThinkPHP5 核心框架包](https://github.com/top-think/framework)。
+
+## 版权信息
+
+ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
+
+本项目包含的第三方源码和二进制文件之版权信息另行标注。
+
+版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn)
+
+All rights reserved。
+
+ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+
+更多细节参阅 [LICENSE.txt](LICENSE.txt)

+ 52 - 0
thinkphp/base.php

@@ -0,0 +1,52 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+namespace think;
+
+// 载入Loader类
+require __DIR__ . '/library/think/Loader.php';
+
+// 注册自动加载
+Loader::register();
+
+// 注册错误和异常处理机制
+Error::register();
+
+// 实现日志接口
+if (interface_exists('Psr\Log\LoggerInterface')) {
+    interface LoggerInterface extends \Psr\Log\LoggerInterface
+    {}
+} else {
+    interface LoggerInterface
+    {}
+}
+
+// 注册类库别名
+Loader::addClassAlias([
+    'App'      => facade\App::class,
+    'Build'    => facade\Build::class,
+    'Cache'    => facade\Cache::class,
+    'Config'   => facade\Config::class,
+    'Cookie'   => facade\Cookie::class,
+    'Db'       => Db::class,
+    'Debug'    => facade\Debug::class,
+    'Env'      => facade\Env::class,
+    'Facade'   => Facade::class,
+    'Hook'     => facade\Hook::class,
+    'Lang'     => facade\Lang::class,
+    'Log'      => facade\Log::class,
+    'Request'  => facade\Request::class,
+    'Response' => facade\Response::class,
+    'Route'    => facade\Route::class,
+    'Session'  => facade\Session::class,
+    'Url'      => facade\Url::class,
+    'Validate' => facade\Validate::class,
+    'View'     => facade\View::class,
+]);

+ 35 - 0
thinkphp/composer.json

@@ -0,0 +1,35 @@
+{
+    "name": "topthink/framework",
+    "description": "the new thinkphp framework",
+    "type": "think-framework",
+    "keywords": [
+        "framework",
+        "thinkphp",
+        "ORM"
+    ],
+    "homepage": "http://thinkphp.cn/",
+    "license": "Apache-2.0",
+    "authors": [
+        {
+            "name": "liu21st",
+            "email": "liu21st@gmail.com"
+        },
+        {
+            "name": "yunwuxin",
+            "email": "448901948@qq.com"
+        }
+    ],
+    "require": {
+        "php": ">=5.6.0",
+        "topthink/think-installer": "2.*"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^5.0|^6.0",
+        "johnkary/phpunit-speedtrap": "^1.0",
+        "mikey179/vfsstream": "~1.6",
+        "phploc/phploc": "2.*",
+        "sebastian/phpcpd": "2.*",
+        "squizlabs/php_codesniffer": "2.*",
+        "phpdocumentor/reflection-docblock": "^2.0"
+    }
+}

+ 327 - 0
thinkphp/convention.php

@@ -0,0 +1,327 @@
+<?php
+
+return [
+    // +----------------------------------------------------------------------
+    // | 应用设置
+    // +----------------------------------------------------------------------
+    'app'        => [
+        // 应用名称
+        'app_name'               => '',
+        // 应用地址
+        'app_host'               => '',
+        // 应用调试模式
+        'app_debug'              => false,
+        // 应用Trace
+        'app_trace'              => false,
+        // 应用模式状态
+        'app_status'             => '',
+        // 是否HTTPS
+        'is_https'               => false,
+        // 入口自动绑定模块
+        'auto_bind_module'       => false,
+        // 注册的根命名空间
+        'root_namespace'         => [],
+        // 默认输出类型
+        'default_return_type'    => 'html',
+        // 默认AJAX 数据返回格式,可选json xml ...
+        'default_ajax_return'    => 'json',
+        // 默认JSONP格式返回的处理方法
+        'default_jsonp_handler'  => 'jsonpReturn',
+        // 默认JSONP处理方法
+        'var_jsonp_handler'      => 'callback',
+        // 默认时区
+        'default_timezone'       => 'Asia/Shanghai',
+        // 是否开启多语言
+        'lang_switch_on'         => false,
+        // 默认验证器
+        'default_validate'       => '',
+        // 默认语言
+        'default_lang'           => 'zh-cn',
+
+        // +----------------------------------------------------------------------
+        // | 模块设置
+        // +----------------------------------------------------------------------
+
+        // 自动搜索控制器
+        'controller_auto_search' => false,
+        // 操作方法前缀
+        'use_action_prefix'      => false,
+        // 操作方法后缀
+        'action_suffix'          => '',
+        // 默认的空控制器名
+        'empty_controller'       => 'Error',
+        // 默认的空模块名
+        'empty_module'           => '',
+        // 默认模块名
+        'default_module'         => 'index',
+        // 是否支持多模块
+        'app_multi_module'       => true,
+        // 禁止访问模块
+        'deny_module_list'       => ['common'],
+        // 默认控制器名
+        'default_controller'     => 'Index',
+        // 默认操作名
+        'default_action'         => 'index',
+        // 是否自动转换URL中的控制器和操作名
+        'url_convert'            => true,
+        // 默认的访问控制器层
+        'url_controller_layer'   => 'controller',
+        // 应用类库后缀
+        'class_suffix'           => false,
+        // 控制器类后缀
+        'controller_suffix'      => false,
+
+        // +----------------------------------------------------------------------
+        // | URL请求设置
+        // +----------------------------------------------------------------------
+
+        // 默认全局过滤方法 用逗号分隔多个
+        'default_filter'         => '',
+        // PATHINFO变量名 用于兼容模式
+        'var_pathinfo'           => 's',
+        // 兼容PATH_INFO获取
+        'pathinfo_fetch'         => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
+        // HTTPS代理标识
+        'https_agent_name'       => '',
+        // IP代理获取标识
+        'http_agent_ip'          => 'HTTP_X_REAL_IP',
+        // URL伪静态后缀
+        'url_html_suffix'        => 'html',
+        // 域名根,如thinkphp.cn
+        'url_domain_root'        => '',
+        // 表单请求类型伪装变量
+        'var_method'             => '_method',
+        // 表单ajax伪装变量
+        'var_ajax'               => '_ajax',
+        // 表单pjax伪装变量
+        'var_pjax'               => '_pjax',
+        // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
+        'request_cache'          => false,
+        // 请求缓存有效期
+        'request_cache_expire'   => null,
+        // 全局请求缓存排除规则
+        'request_cache_except'   => [],
+
+        // +----------------------------------------------------------------------
+        // | 路由设置
+        // +----------------------------------------------------------------------
+
+        // pathinfo分隔符
+        'pathinfo_depr'          => '/',
+        // URL普通方式参数 用于自动生成
+        'url_common_param'       => false,
+        // URL参数方式 0 按名称成对解析 1 按顺序解析
+        'url_param_type'         => 0,
+        // 是否开启路由延迟解析
+        'url_lazy_route'         => false,
+        // 是否强制使用路由
+        'url_route_must'         => false,
+        // 合并路由规则
+        'route_rule_merge'       => false,
+        // 路由是否完全匹配
+        'route_complete_match'   => false,
+        // 使用注解路由
+        'route_annotation'       => false,
+        // 默认的路由变量规则
+        'default_route_pattern'  => '\w+',
+        // 是否开启路由缓存
+        'route_check_cache'      => false,
+        // 路由缓存的Key自定义设置(闭包),默认为当前URL和请求类型的md5
+        'route_check_cache_key'  => '',
+        // 路由缓存的设置
+        'route_cache_option'     => [],
+
+        // +----------------------------------------------------------------------
+        // | 异常及错误设置
+        // +----------------------------------------------------------------------
+
+        // 默认跳转页面对应的模板文件
+        'dispatch_success_tmpl'  => __DIR__ . '/tpl/dispatch_jump.tpl',
+        'dispatch_error_tmpl'    => __DIR__ . '/tpl/dispatch_jump.tpl',
+        // 异常页面的模板文件
+        'exception_tmpl'         => __DIR__ . '/tpl/think_exception.tpl',
+        // 错误显示信息,非调试模式有效
+        'error_message'          => '页面错误!请稍后再试~',
+        // 显示错误信息
+        'show_error_msg'         => false,
+        // 异常处理handle类 留空使用 \think\exception\Handle
+        'exception_handle'       => '',
+    ],
+
+    // +----------------------------------------------------------------------
+    // | 模板设置
+    // +----------------------------------------------------------------------
+
+    'template'   => [
+        // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写
+        'auto_rule'    => 1,
+        // 模板引擎类型 支持 php think 支持扩展
+        'type'         => 'Think',
+        // 视图基础目录,配置目录为所有模块的视图起始目录
+        'view_base'    => '',
+        // 当前模板的视图目录 留空为自动获取
+        'view_path'    => '',
+        // 模板后缀
+        'view_suffix'  => 'html',
+        // 模板文件名分隔符
+        'view_depr'    => DIRECTORY_SEPARATOR,
+        // 模板引擎普通标签开始标记
+        'tpl_begin'    => '{',
+        // 模板引擎普通标签结束标记
+        'tpl_end'      => '}',
+        // 标签库标签开始标记
+        'taglib_begin' => '{',
+        // 标签库标签结束标记
+        'taglib_end'   => '}',
+    ],
+
+    // +----------------------------------------------------------------------
+    // | 日志设置
+    // +----------------------------------------------------------------------
+
+    'log'        => [
+        // 日志记录方式,内置 file socket 支持扩展
+        'type'         => 'File',
+        // 日志保存目录
+        //'path'  => LOG_PATH,
+        // 日志记录级别
+        'level'        => [],
+        // 是否记录trace信息到日志
+        'record_trace' => false,
+        // 是否JSON格式记录
+        'json'         => false,
+    ],
+
+    // +----------------------------------------------------------------------
+    // | Trace设置 开启 app_trace 后 有效
+    // +----------------------------------------------------------------------
+
+    'trace'      => [
+        // 内置Html Console 支持扩展
+        'type' => 'Html',
+        'file' => __DIR__ . '/tpl/page_trace.tpl',
+    ],
+
+    // +----------------------------------------------------------------------
+    // | 缓存设置
+    // +----------------------------------------------------------------------
+
+    'cache'      => [
+        // 驱动方式
+        'type'   => 'File',
+        // 缓存保存目录
+        //'path'   => CACHE_PATH,
+        // 缓存前缀
+        'prefix' => '',
+        // 缓存有效期 0表示永久缓存
+        'expire' => 0,
+    ],
+
+    // +----------------------------------------------------------------------
+    // | 会话设置
+    // +----------------------------------------------------------------------
+
+    'session'    => [
+        'id'             => '',
+        // SESSION_ID的提交变量,解决flash上传跨域
+        'var_session_id' => '',
+        // SESSION 前缀
+        'prefix'         => 'think',
+        // 驱动方式 支持redis memcache memcached
+        'type'           => '',
+        // 是否自动开启 SESSION
+        'auto_start'     => true,
+        'httponly'       => true,
+        'secure'         => false,
+    ],
+
+    // +----------------------------------------------------------------------
+    // | Cookie设置
+    // +----------------------------------------------------------------------
+
+    'cookie'     => [
+        // cookie 名称前缀
+        'prefix'    => '',
+        // cookie 保存时间
+        'expire'    => 0,
+        // cookie 保存路径
+        'path'      => '/',
+        // cookie 有效域名
+        'domain'    => '',
+        //  cookie 启用安全传输
+        'secure'    => false,
+        // httponly设置
+        'httponly'  => '',
+        // 是否使用 setcookie
+        'setcookie' => true,
+    ],
+
+    // +----------------------------------------------------------------------
+    // | 数据库设置
+    // +----------------------------------------------------------------------
+
+    'database'   => [
+        // 数据库类型
+        'type'            => 'mysql',
+        // 数据库连接DSN配置
+        'dsn'             => '',
+        // 服务器地址
+        'hostname'        => '127.0.0.1',
+        // 数据库名
+        'database'        => '',
+        // 数据库用户名
+        'username'        => 'root',
+        // 数据库密码
+        'password'        => '',
+        // 数据库连接端口
+        'hostport'        => '',
+        // 数据库连接参数
+        'params'          => [],
+        // 数据库编码默认采用utf8
+        'charset'         => 'utf8',
+        // 数据库表前缀
+        'prefix'          => '',
+        // 数据库调试模式
+        'debug'           => false,
+        // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
+        'deploy'          => 0,
+        // 数据库读写是否分离 主从式有效
+        'rw_separate'     => false,
+        // 读写分离后 主服务器数量
+        'master_num'      => 1,
+        // 指定从服务器序号
+        'slave_no'        => '',
+        // 是否严格检查字段是否存在
+        'fields_strict'   => true,
+        // 数据集返回类型
+        'resultset_type'  => 'array',
+        // 自动写入时间戳字段
+        'auto_timestamp'  => false,
+        // 时间字段取出后的默认时间格式
+        'datetime_format' => 'Y-m-d H:i:s',
+        // 是否需要进行SQL性能分析
+        'sql_explain'     => false,
+        // 查询对象
+        'query'           => '\\think\\db\\Query',
+    ],
+
+    //分页配置
+    'paginate'   => [
+        'type'      => 'bootstrap',
+        'var_page'  => 'page',
+        'list_rows' => 15,
+    ],
+
+    //控制台配置
+    'console'    => [
+        'name'      => 'Think Console',
+        'version'   => '0.1',
+        'user'      => null,
+        'auto_path' => '',
+    ],
+
+    // 中间件配置
+    'middleware' => [
+        'default_namespace' => 'app\\http\\middleware\\',
+    ],
+];

+ 726 - 0
thinkphp/helper.php

@@ -0,0 +1,726 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+//------------------------
+// ThinkPHP 助手函数
+//-------------------------
+
+use think\Container;
+use think\Db;
+use think\exception\HttpException;
+use think\exception\HttpResponseException;
+use think\facade\Cache;
+use think\facade\Config;
+use think\facade\Cookie;
+use think\facade\Debug;
+use think\facade\Env;
+use think\facade\Hook;
+use think\facade\Lang;
+use think\facade\Log;
+use think\facade\Request;
+use think\facade\Route;
+use think\facade\Session;
+use think\facade\Url;
+use think\Response;
+use think\route\RuleItem;
+
+if (!function_exists('abort')) {
+    /**
+     * 抛出HTTP异常
+     * @param integer|Response      $code 状态码 或者 Response对象实例
+     * @param string                $message 错误信息
+     * @param array                 $header 参数
+     */
+    function abort($code, $message = null, $header = [])
+    {
+        if ($code instanceof Response) {
+            throw new HttpResponseException($code);
+        } else {
+            throw new HttpException($code, $message, null, $header);
+        }
+    }
+}
+
+if (!function_exists('action')) {
+    /**
+     * 调用模块的操作方法 参数格式 [模块/控制器/]操作
+     * @param string        $url 调用地址
+     * @param string|array  $vars 调用参数 支持字符串和数组
+     * @param string        $layer 要调用的控制层名称
+     * @param bool          $appendSuffix 是否添加类名后缀
+     * @return mixed
+     */
+    function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
+    {
+        return app()->action($url, $vars, $layer, $appendSuffix);
+    }
+}
+
+if (!function_exists('app')) {
+    /**
+     * 快速获取容器中的实例 支持依赖注入
+     * @param string    $name 类名或标识 默认获取当前应用实例
+     * @param array     $args 参数
+     * @param bool      $newInstance    是否每次创建新的实例
+     * @return mixed|\think\App
+     */
+    function app($name = 'think\App', $args = [], $newInstance = false)
+    {
+        return Container::get($name, $args, $newInstance);
+    }
+}
+
+if (!function_exists('behavior')) {
+    /**
+     * 执行某个行为(run方法) 支持依赖注入
+     * @param mixed $behavior   行为类名或者别名
+     * @param mixed $args       参数
+     * @return mixed
+     */
+    function behavior($behavior, $args = null)
+    {
+        return Hook::exec($behavior, $args);
+    }
+}
+
+if (!function_exists('bind')) {
+    /**
+     * 绑定一个类到容器
+     * @access public
+     * @param string  $abstract    类标识、接口
+     * @param mixed   $concrete    要绑定的类、闭包或者实例
+     * @return Container
+     */
+    function bind($abstract, $concrete = null)
+    {
+        return Container::getInstance()->bindTo($abstract, $concrete);
+    }
+}
+
+if (!function_exists('cache')) {
+    /**
+     * 缓存管理
+     * @param mixed     $name 缓存名称,如果为数组表示进行缓存设置
+     * @param mixed     $value 缓存值
+     * @param mixed     $options 缓存参数
+     * @param string    $tag 缓存标签
+     * @return mixed
+     */
+    function cache($name, $value = '', $options = null, $tag = null)
+    {
+        if (is_array($options)) {
+            // 缓存操作的同时初始化
+            Cache::connect($options);
+        } elseif (is_array($name)) {
+            // 缓存初始化
+            return Cache::connect($name);
+        }
+
+        if ('' === $value) {
+            // 获取缓存
+            return 0 === strpos($name, '?') ? Cache::has(substr($name, 1)) : Cache::get($name);
+        } elseif (is_null($value)) {
+            // 删除缓存
+            return Cache::rm($name);
+        }
+
+        // 缓存数据
+        if (is_array($options)) {
+            $expire = isset($options['expire']) ? $options['expire'] : null; //修复查询缓存无法设置过期时间
+        } else {
+            $expire = is_numeric($options) ? $options : null; //默认快捷缓存设置过期时间
+        }
+
+        if (is_null($tag)) {
+            return Cache::set($name, $value, $expire);
+        } else {
+            return Cache::tag($tag)->set($name, $value, $expire);
+        }
+    }
+}
+
+if (!function_exists('call')) {
+    /**
+     * 调用反射执行callable 支持依赖注入
+     * @param mixed $callable   支持闭包等callable写法
+     * @param array $args       参数
+     * @return mixed
+     */
+    function call($callable, $args = [])
+    {
+        return Container::getInstance()->invoke($callable, $args);
+    }
+}
+
+if (!function_exists('class_basename')) {
+    /**
+     * 获取类名(不包含命名空间)
+     *
+     * @param  string|object $class
+     * @return string
+     */
+    function class_basename($class)
+    {
+        $class = is_object($class) ? get_class($class) : $class;
+        return basename(str_replace('\\', '/', $class));
+    }
+}
+
+if (!function_exists('class_uses_recursive')) {
+    /**
+     *获取一个类里所有用到的trait,包括父类的
+     *
+     * @param $class
+     * @return array
+     */
+    function class_uses_recursive($class)
+    {
+        if (is_object($class)) {
+            $class = get_class($class);
+        }
+
+        $results = [];
+        $classes = array_merge([$class => $class], class_parents($class));
+        foreach ($classes as $class) {
+            $results += trait_uses_recursive($class);
+        }
+
+        return array_unique($results);
+    }
+}
+
+if (!function_exists('config')) {
+    /**
+     * 获取和设置配置参数
+     * @param string|array  $name 参数名
+     * @param mixed         $value 参数值
+     * @return mixed
+     */
+    function config($name = '', $value = null)
+    {
+        if (is_null($value) && is_string($name)) {
+            if ('.' == substr($name, -1)) {
+                return Config::pull(substr($name, 0, -1));
+            }
+
+            return 0 === strpos($name, '?') ? Config::has(substr($name, 1)) : Config::get($name);
+        } else {
+            return Config::set($name, $value);
+        }
+    }
+}
+
+if (!function_exists('container')) {
+    /**
+     * 获取容器对象实例
+     * @return Container
+     */
+    function container()
+    {
+        return Container::getInstance();
+    }
+}
+
+if (!function_exists('controller')) {
+    /**
+     * 实例化控制器 格式:[模块/]控制器
+     * @param string    $name 资源地址
+     * @param string    $layer 控制层名称
+     * @param bool      $appendSuffix 是否添加类名后缀
+     * @return \think\Controller
+     */
+    function controller($name, $layer = 'controller', $appendSuffix = false)
+    {
+        return app()->controller($name, $layer, $appendSuffix);
+    }
+}
+
+if (!function_exists('cookie')) {
+    /**
+     * Cookie管理
+     * @param string|array  $name cookie名称,如果为数组表示进行cookie设置
+     * @param mixed         $value cookie值
+     * @param mixed         $option 参数
+     * @return mixed
+     */
+    function cookie($name, $value = '', $option = null)
+    {
+        if (is_array($name)) {
+            // 初始化
+            Cookie::init($name);
+        } elseif (is_null($name)) {
+            // 清除
+            Cookie::clear($value);
+        } elseif ('' === $value) {
+            // 获取
+            return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1), $option) : Cookie::get($name);
+        } elseif (is_null($value)) {
+            // 删除
+            return Cookie::delete($name);
+        } else {
+            // 设置
+            return Cookie::set($name, $value, $option);
+        }
+    }
+}
+
+if (!function_exists('db')) {
+    /**
+     * 实例化数据库类
+     * @param string        $name 操作的数据表名称(不含前缀)
+     * @param array|string  $config 数据库配置参数
+     * @param bool          $force 是否强制重新连接
+     * @return \think\db\Query
+     */
+    function db($name = '', $config = [], $force = true)
+    {
+        return Db::connect($config, $force)->name($name);
+    }
+}
+
+if (!function_exists('debug')) {
+    /**
+     * 记录时间(微秒)和内存使用情况
+     * @param string            $start 开始标签
+     * @param string            $end 结束标签
+     * @param integer|string    $dec 小数位 如果是m 表示统计内存占用
+     * @return mixed
+     */
+    function debug($start, $end = '', $dec = 6)
+    {
+        if ('' == $end) {
+            Debug::remark($start);
+        } else {
+            return 'm' == $dec ? Debug::getRangeMem($start, $end) : Debug::getRangeTime($start, $end, $dec);
+        }
+    }
+}
+
+if (!function_exists('download')) {
+    /**
+     * 获取\think\response\Download对象实例
+     * @param string  $filename 要下载的文件
+     * @param string  $name 显示文件名
+     * @param bool    $content 是否为内容
+     * @param integer $expire 有效期(秒)
+     * @return \think\response\Download
+     */
+    function download($filename, $name = '', $content = false, $expire = 360, $openinBrowser = false)
+    {
+        return Response::create($filename, 'download')->name($name)->isContent($content)->expire($expire)->openinBrowser($openinBrowser);
+    }
+}
+
+if (!function_exists('dump')) {
+    /**
+     * 浏览器友好的变量输出
+     * @param mixed     $var 变量
+     * @param boolean   $echo 是否输出 默认为true 如果为false 则返回输出字符串
+     * @param string    $label 标签 默认为空
+     * @return void|string
+     */
+    function dump($var, $echo = true, $label = null)
+    {
+        return Debug::dump($var, $echo, $label);
+    }
+}
+
+if (!function_exists('env')) {
+    /**
+     * 获取环境变量值
+     * @access public
+     * @param  string    $name 环境变量名(支持二级 .号分割)
+     * @param  string    $default  默认值
+     * @return mixed
+     */
+    function env($name = null, $default = null)
+    {
+        return Env::get($name, $default);
+    }
+}
+
+if (!function_exists('exception')) {
+    /**
+     * 抛出异常处理
+     *
+     * @param string    $msg  异常消息
+     * @param integer   $code 异常代码 默认为0
+     * @param string    $exception 异常类
+     *
+     * @throws Exception
+     */
+    function exception($msg, $code = 0, $exception = '')
+    {
+        $e = $exception ?: '\think\Exception';
+        throw new $e($msg, $code);
+    }
+}
+
+if (!function_exists('halt')) {
+    /**
+     * 调试变量并且中断输出
+     * @param mixed      $var 调试变量或者信息
+     */
+    function halt($var)
+    {
+        dump($var);
+
+        throw new HttpResponseException(new Response);
+    }
+}
+
+if (!function_exists('input')) {
+    /**
+     * 获取输入数据 支持默认值和过滤
+     * @param string    $key 获取的变量名
+     * @param mixed     $default 默认值
+     * @param string    $filter 过滤方法
+     * @return mixed
+     */
+    function input($key = '', $default = null, $filter = '')
+    {
+        if (0 === strpos($key, '?')) {
+            $key = substr($key, 1);
+            $has = true;
+        }
+
+        if ($pos = strpos($key, '.')) {
+            // 指定参数来源
+            $method = substr($key, 0, $pos);
+            if (in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'route', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) {
+                $key = substr($key, $pos + 1);
+            } else {
+                $method = 'param';
+            }
+        } else {
+            // 默认为自动判断
+            $method = 'param';
+        }
+
+        if (isset($has)) {
+            return request()->has($key, $method, $default);
+        } else {
+            return request()->$method($key, $default, $filter);
+        }
+    }
+}
+
+if (!function_exists('json')) {
+    /**
+     * 获取\think\response\Json对象实例
+     * @param mixed   $data 返回的数据
+     * @param integer $code 状态码
+     * @param array   $header 头部
+     * @param array   $options 参数
+     * @return \think\response\Json
+     */
+    function json($data = [], $code = 200, $header = [], $options = [])
+    {
+        return Response::create($data, 'json', $code, $header, $options);
+    }
+}
+
+if (!function_exists('jsonp')) {
+    /**
+     * 获取\think\response\Jsonp对象实例
+     * @param mixed   $data    返回的数据
+     * @param integer $code    状态码
+     * @param array   $header 头部
+     * @param array   $options 参数
+     * @return \think\response\Jsonp
+     */
+    function jsonp($data = [], $code = 200, $header = [], $options = [])
+    {
+        return Response::create($data, 'jsonp', $code, $header, $options);
+    }
+}
+
+if (!function_exists('lang')) {
+    /**
+     * 获取语言变量值
+     * @param string    $name 语言变量名
+     * @param array     $vars 动态变量值
+     * @param string    $lang 语言
+     * @return mixed
+     */
+    function lang($name, $vars = [], $lang = '')
+    {
+        return Lang::get($name, $vars, $lang);
+    }
+}
+
+if (!function_exists('model')) {
+    /**
+     * 实例化Model
+     * @param string    $name Model名称
+     * @param string    $layer 业务层名称
+     * @param bool      $appendSuffix 是否添加类名后缀
+     * @return \think\Model
+     */
+    function model($name = '', $layer = 'model', $appendSuffix = false)
+    {
+        return app()->model($name, $layer, $appendSuffix);
+    }
+}
+
+if (!function_exists('parse_name')) {
+    /**
+     * 字符串命名风格转换
+     * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
+     * @param string  $name 字符串
+     * @param integer $type 转换类型
+     * @param bool    $ucfirst 首字母是否大写(驼峰规则)
+     * @return string
+     */
+    function parse_name($name, $type = 0, $ucfirst = true)
+    {
+        if ($type) {
+            $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
+                return strtoupper($match[1]);
+            }, $name);
+
+            return $ucfirst ? ucfirst($name) : lcfirst($name);
+        } else {
+            return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
+        }
+    }
+}
+
+if (!function_exists('redirect')) {
+    /**
+     * 获取\think\response\Redirect对象实例
+     * @param mixed         $url 重定向地址 支持Url::build方法的地址
+     * @param array|integer $params 额外参数
+     * @param integer       $code 状态码
+     * @return \think\response\Redirect
+     */
+    function redirect($url = [], $params = [], $code = 302)
+    {
+        if (is_integer($params)) {
+            $code   = $params;
+            $params = [];
+        }
+
+        return Response::create($url, 'redirect', $code)->params($params);
+    }
+}
+
+if (!function_exists('request')) {
+    /**
+     * 获取当前Request对象实例
+     * @return Request
+     */
+    function request()
+    {
+        return app('request');
+    }
+}
+
+if (!function_exists('response')) {
+    /**
+     * 创建普通 Response 对象实例
+     * @param mixed      $data   输出数据
+     * @param int|string $code   状态码
+     * @param array      $header 头信息
+     * @param string     $type
+     * @return Response
+     */
+    function response($data = '', $code = 200, $header = [], $type = 'html')
+    {
+        return Response::create($data, $type, $code, $header);
+    }
+}
+
+if (!function_exists('route')) {
+    /**
+     * 路由注册
+     * @param  string    $rule       路由规则
+     * @param  mixed     $route      路由地址
+     * @param  array     $option     路由参数
+     * @param  array     $pattern    变量规则
+     * @return RuleItem
+     */
+    function route($rule, $route, $option = [], $pattern = [])
+    {
+        return Route::rule($rule, $route, '*', $option, $pattern);
+    }
+}
+
+if (!function_exists('session')) {
+    /**
+     * Session管理
+     * @param string|array  $name session名称,如果为数组表示进行session设置
+     * @param mixed         $value session值
+     * @param string        $prefix 前缀
+     * @return mixed
+     */
+    function session($name, $value = '', $prefix = null)
+    {
+        if (is_array($name)) {
+            // 初始化
+            Session::init($name);
+        } elseif (is_null($name)) {
+            // 清除
+            Session::clear($value);
+        } elseif ('' === $value) {
+            // 判断或获取
+            return 0 === strpos($name, '?') ? Session::has(substr($name, 1), $prefix) : Session::get($name, $prefix);
+        } elseif (is_null($value)) {
+            // 删除
+            return Session::delete($name, $prefix);
+        } else {
+            // 设置
+            return Session::set($name, $value, $prefix);
+        }
+    }
+}
+
+if (!function_exists('token')) {
+    /**
+     * 生成表单令牌
+     * @param string $name 令牌名称
+     * @param mixed  $type 令牌生成方法
+     * @return string
+     */
+    function token($name = '__token__', $type = 'md5')
+    {
+        $token = Request::token($name, $type);
+
+        return '<input type="hidden" name="' . $name . '" value="' . $token . '" />';
+    }
+}
+
+if (!function_exists('trace')) {
+    /**
+     * 记录日志信息
+     * @param mixed     $log log信息 支持字符串和数组
+     * @param string    $level 日志级别
+     * @return array|void
+     */
+    function trace($log = '[think]', $level = 'log')
+    {
+        if ('[think]' === $log) {
+            return Log::getLog();
+        } else {
+            Log::record($log, $level);
+        }
+    }
+}
+
+if (!function_exists('trait_uses_recursive')) {
+    /**
+     * 获取一个trait里所有引用到的trait
+     *
+     * @param  string $trait
+     * @return array
+     */
+    function trait_uses_recursive($trait)
+    {
+        $traits = class_uses($trait);
+        foreach ($traits as $trait) {
+            $traits += trait_uses_recursive($trait);
+        }
+
+        return $traits;
+    }
+}
+
+if (!function_exists('url')) {
+    /**
+     * Url生成
+     * @param string        $url 路由地址
+     * @param string|array  $vars 变量
+     * @param bool|string   $suffix 生成的URL后缀
+     * @param bool|string   $domain 域名
+     * @return string
+     */
+    function url($url = '', $vars = '', $suffix = true, $domain = false)
+    {
+        return Url::build($url, $vars, $suffix, $domain);
+    }
+}
+
+if (!function_exists('validate')) {
+    /**
+     * 实例化验证器
+     * @param string    $name 验证器名称
+     * @param string    $layer 业务层名称
+     * @param bool      $appendSuffix 是否添加类名后缀
+     * @return \think\Validate
+     */
+    function validate($name = '', $layer = 'validate', $appendSuffix = false)
+    {
+        return app()->validate($name, $layer, $appendSuffix);
+    }
+}
+
+if (!function_exists('view')) {
+    /**
+     * 渲染模板输出
+     * @param string    $template 模板文件
+     * @param array     $vars 模板变量
+     * @param integer   $code 状态码
+     * @param callable  $filter 内容过滤
+     * @return \think\response\View
+     */
+    function view($template = '', $vars = [], $code = 200, $filter = null)
+    {
+        return Response::create($template, 'view', $code)->assign($vars)->filter($filter);
+    }
+}
+
+if (!function_exists('widget')) {
+    /**
+     * 渲染输出Widget
+     * @param string    $name Widget名称
+     * @param array     $data 传入的参数
+     * @return mixed
+     */
+    function widget($name, $data = [])
+    {
+        $result = app()->action($name, $data, 'widget');
+
+        if (is_object($result)) {
+            $result = $result->getContent();
+        }
+
+        return $result;
+    }
+}
+
+if (!function_exists('xml')) {
+    /**
+     * 获取\think\response\Xml对象实例
+     * @param mixed   $data    返回的数据
+     * @param integer $code    状态码
+     * @param array   $header  头部
+     * @param array   $options 参数
+     * @return \think\response\Xml
+     */
+    function xml($data = [], $code = 200, $header = [], $options = [])
+    {
+        return Response::create($data, 'xml', $code, $header, $options);
+    }
+}
+
+if (!function_exists('yaconf')) {
+    /**
+     * 获取yaconf配置
+     *
+     * @param  string    $name 配置参数名
+     * @param  mixed     $default   默认值
+     * @return mixed
+     */
+    function yaconf($name, $default = null)
+    {
+        return Config::yaconf($name, $default);
+    }
+}

+ 144 - 0
thinkphp/lang/zh-cn.php

@@ -0,0 +1,144 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+// 核心中文语言包
+return [
+    // 系统错误提示
+    'Undefined variable'                                        => '未定义变量',
+    'Undefined index'                                           => '未定义数组索引',
+    'Undefined offset'                                          => '未定义数组下标',
+    'Parse error'                                               => '语法解析错误',
+    'Type error'                                                => '类型错误',
+    'Fatal error'                                               => '致命错误',
+    'syntax error'                                              => '语法错误',
+
+    // 框架核心错误提示
+    'dispatch type not support'                                 => '不支持的调度类型',
+    'method param miss'                                         => '方法参数错误',
+    'method not exists'                                         => '方法不存在',
+    'function not exists'                                       => '函数不存在',
+    'file 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'                                       => '查询表达式错误',
+    'order express error'                                       => '排序表达式错误',
+    'no data to update'                                         => '没有任何数据需要更新',
+    'miss data to insert'                                       => '缺少需要写入的数据',
+    'not support data'                                          => '不支持的数据表达式',
+    '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方法',
+    'route pattern error'                                       => '路由变量规则定义错误',
+    'route behavior will not support'                           => '路由行为废弃(使用中间件替代)',
+    'closure not support cache(true)'                           => '使用闭包查询不支持cache(true),请指定缓存Key',
+
+    // 上传错误信息
+    'unknown upload error'                                      => '未知上传错误!',
+    'file write error'                                          => '文件写入失败!',
+    'upload temp dir not found'                                 => '找不到临时文件夹!',
+    'no file to uploaded'                                       => '没有文件被上传!',
+    'only the portion of file is uploaded'                      => '文件只有部分被上传!',
+    'upload File size exceeds the maximum value'                => '上传文件大小超过了最大值!',
+    'upload write error'                                        => '文件上传保存错误!',
+    'has the same filename: {:filename}'                        => '存在同名文件:{:filename}',
+    'upload illegal files'                                      => '非法上传文件',
+    'illegal image files'                                       => '非法图片文件',
+    'extensions to upload is not allowed'                       => '上传文件后缀不允许',
+    'mimetype to upload is not allowed'                         => '上传文件MIME类型不允许!',
+    'filesize not match'                                        => '上传文件大小不符!',
+    'directory {:path} creation failed'                         => '目录 {:path} 创建失败!',
+
+    'The middleware must return Response instance'              => '中间件方法必须返回Response对象实例',
+    'The queue was exhausted, with no response returned'        => '中间件队列为空',
+    // Validate Error Message
+    ':attribute require'                                        => ':attribute不能为空',
+    ':attribute must'                                           => ':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'                                  => '规则错误',
+];

+ 981 - 0
thinkphp/library/think/App.php

@@ -0,0 +1,981 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use think\exception\ClassNotFoundException;
+use think\exception\HttpResponseException;
+use think\route\Dispatch;
+
+/**
+ * App 应用管理
+ */
+class App extends Container
+{
+    const VERSION = '5.1.39 LTS';
+
+    /**
+     * 当前模块路径
+     * @var string
+     */
+    protected $modulePath;
+
+    /**
+     * 应用调试模式
+     * @var bool
+     */
+    protected $appDebug = true;
+
+    /**
+     * 应用开始时间
+     * @var float
+     */
+    protected $beginTime;
+
+    /**
+     * 应用内存初始占用
+     * @var integer
+     */
+    protected $beginMem;
+
+    /**
+     * 应用类库命名空间
+     * @var string
+     */
+    protected $namespace = 'app';
+
+    /**
+     * 应用类库后缀
+     * @var bool
+     */
+    protected $suffix = false;
+
+    /**
+     * 严格路由检测
+     * @var bool
+     */
+    protected $routeMust;
+
+    /**
+     * 应用类库目录
+     * @var string
+     */
+    protected $appPath;
+
+    /**
+     * 框架目录
+     * @var string
+     */
+    protected $thinkPath;
+
+    /**
+     * 应用根目录
+     * @var string
+     */
+    protected $rootPath;
+
+    /**
+     * 运行时目录
+     * @var string
+     */
+    protected $runtimePath;
+
+    /**
+     * 配置目录
+     * @var string
+     */
+    protected $configPath;
+
+    /**
+     * 路由目录
+     * @var string
+     */
+    protected $routePath;
+
+    /**
+     * 配置后缀
+     * @var string
+     */
+    protected $configExt;
+
+    /**
+     * 应用调度实例
+     * @var Dispatch
+     */
+    protected $dispatch;
+
+    /**
+     * 绑定模块(控制器)
+     * @var string
+     */
+    protected $bindModule;
+
+    /**
+     * 初始化
+     * @var bool
+     */
+    protected $initialized = false;
+
+    public function __construct($appPath = '')
+    {
+        $this->thinkPath = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
+        $this->path($appPath);
+    }
+
+    /**
+     * 绑定模块或者控制器
+     * @access public
+     * @param  string $bind
+     * @return $this
+     */
+    public function bind($bind)
+    {
+        $this->bindModule = $bind;
+        return $this;
+    }
+
+    /**
+     * 设置应用类库目录
+     * @access public
+     * @param  string $path 路径
+     * @return $this
+     */
+    public function path($path)
+    {
+        $this->appPath = $path ? realpath($path) . DIRECTORY_SEPARATOR : $this->getAppPath();
+
+        return $this;
+    }
+
+    /**
+     * 初始化应用
+     * @access public
+     * @return void
+     */
+    public function initialize()
+    {
+        if ($this->initialized) {
+            return;
+        }
+
+        $this->initialized = true;
+        $this->beginTime   = microtime(true);
+        $this->beginMem    = memory_get_usage();
+
+        $this->rootPath    = dirname($this->appPath) . DIRECTORY_SEPARATOR;
+        $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
+        $this->routePath   = $this->rootPath . 'route' . DIRECTORY_SEPARATOR;
+        $this->configPath  = $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
+
+        static::setInstance($this);
+
+        $this->instance('app', $this);
+
+        // 加载环境变量配置文件
+        if (is_file($this->rootPath . '.env')) {
+            $this->env->load($this->rootPath . '.env');
+        }
+
+        $this->configExt = $this->env->get('config_ext', '.php');
+
+        // 加载惯例配置文件
+        $this->config->set(include $this->thinkPath . 'convention.php');
+
+        // 设置路径环境变量
+        $this->env->set([
+            'think_path'   => $this->thinkPath,
+            'root_path'    => $this->rootPath,
+            'app_path'     => $this->appPath,
+            'config_path'  => $this->configPath,
+            'route_path'   => $this->routePath,
+            'runtime_path' => $this->runtimePath,
+            'extend_path'  => $this->rootPath . 'extend' . DIRECTORY_SEPARATOR,
+            'vendor_path'  => $this->rootPath . 'vendor' . DIRECTORY_SEPARATOR,
+        ]);
+
+        $this->namespace = $this->env->get('app_namespace', $this->namespace);
+        $this->env->set('app_namespace', $this->namespace);
+
+        // 注册应用命名空间
+        Loader::addNamespace($this->namespace, $this->appPath);
+
+        // 初始化应用
+        $this->init();
+
+        // 开启类名后缀
+        $this->suffix = $this->config('app.class_suffix');
+
+        // 应用调试模式
+        $this->appDebug = $this->env->get('app_debug', $this->config('app.app_debug'));
+        $this->env->set('app_debug', $this->appDebug);
+
+        if (!$this->appDebug) {
+            ini_set('display_errors', 'Off');
+        } elseif (PHP_SAPI != 'cli') {
+            //重新申请一块比较大的buffer
+            if (ob_get_level() > 0) {
+                $output = ob_get_clean();
+            }
+            ob_start();
+            if (!empty($output)) {
+                echo $output;
+            }
+        }
+
+        // 注册异常处理类
+        if ($this->config('app.exception_handle')) {
+            Error::setExceptionHandler($this->config('app.exception_handle'));
+        }
+
+        // 注册根命名空间
+        if (!empty($this->config('app.root_namespace'))) {
+            Loader::addNamespace($this->config('app.root_namespace'));
+        }
+
+        // 加载composer autofile文件
+        Loader::loadComposerAutoloadFiles();
+
+        // 注册类库别名
+        Loader::addClassAlias($this->config->pull('alias'));
+
+        // 数据库配置初始化
+        Db::init($this->config->pull('database'));
+
+        // 设置系统时区
+        date_default_timezone_set($this->config('app.default_timezone'));
+
+        // 读取语言包
+        $this->loadLangPack();
+
+        // 路由初始化
+        $this->routeInit();
+    }
+
+    /**
+     * 初始化应用或模块
+     * @access public
+     * @param  string $module 模块名
+     * @return void
+     */
+    public function init($module = '')
+    {
+        // 定位模块目录
+        $module = $module ? $module . DIRECTORY_SEPARATOR : '';
+        $path   = $this->appPath . $module;
+
+        // 加载初始化文件
+        if (is_file($path . 'init.php')) {
+            include $path . 'init.php';
+        } elseif (is_file($this->runtimePath . $module . 'init.php')) {
+            include $this->runtimePath . $module . 'init.php';
+        } else {
+            // 加载行为扩展文件
+            if (is_file($path . 'tags.php')) {
+                $tags = include $path . 'tags.php';
+                if (is_array($tags)) {
+                    $this->hook->import($tags);
+                }
+            }
+
+            // 加载公共文件
+            if (is_file($path . 'common.php')) {
+                include_once $path . 'common.php';
+            }
+
+            if ('' == $module) {
+                // 加载系统助手函数
+                include $this->thinkPath . 'helper.php';
+            }
+
+            // 加载中间件
+            if (is_file($path . 'middleware.php')) {
+                $middleware = include $path . 'middleware.php';
+                if (is_array($middleware)) {
+                    $this->middleware->import($middleware);
+                }
+            }
+
+            // 注册服务的容器对象实例
+            if (is_file($path . 'provider.php')) {
+                $provider = include $path . 'provider.php';
+                if (is_array($provider)) {
+                    $this->bindTo($provider);
+                }
+            }
+
+            // 自动读取配置文件
+            if (is_dir($path . 'config')) {
+                $dir = $path . 'config' . DIRECTORY_SEPARATOR;
+            } elseif (is_dir($this->configPath . $module)) {
+                $dir = $this->configPath . $module;
+            }
+
+            $files = isset($dir) ? scandir($dir) : [];
+
+            foreach ($files as $file) {
+                if ('.' . pathinfo($file, PATHINFO_EXTENSION) === $this->configExt) {
+                    $this->config->load($dir . $file, pathinfo($file, PATHINFO_FILENAME));
+                }
+            }
+        }
+
+        $this->setModulePath($path);
+
+        if ($module) {
+            // 对容器中的对象实例进行配置更新
+            $this->containerConfigUpdate($module);
+        }
+    }
+
+    protected function containerConfigUpdate($module)
+    {
+        $config = $this->config->get();
+
+        // 注册异常处理类
+        if ($config['app']['exception_handle']) {
+            Error::setExceptionHandler($config['app']['exception_handle']);
+        }
+
+        Db::init($config['database']);
+        $this->middleware->setConfig($config['middleware']);
+        $this->route->setConfig($config['app']);
+        $this->request->init($config['app']);
+        $this->cookie->init($config['cookie']);
+        $this->view->init($config['template']);
+        $this->log->init($config['log']);
+        $this->session->setConfig($config['session']);
+        $this->debug->setConfig($config['trace']);
+        $this->cache->init($config['cache'], true);
+
+        // 加载当前模块语言包
+        $this->lang->load($this->appPath . $module . DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php');
+
+        // 模块请求缓存检查
+        $this->checkRequestCache(
+            $config['app']['request_cache'],
+            $config['app']['request_cache_expire'],
+            $config['app']['request_cache_except']
+        );
+    }
+
+    /**
+     * 执行应用程序
+     * @access public
+     * @return Response
+     * @throws Exception
+     */
+    public function run()
+    {
+        try {
+            // 初始化应用
+            $this->initialize();
+
+            // 监听app_init
+            $this->hook->listen('app_init');
+
+            if ($this->bindModule) {
+                // 模块/控制器绑定
+                $this->route->bind($this->bindModule);
+            } elseif ($this->config('app.auto_bind_module')) {
+                // 入口自动绑定
+                $name = pathinfo($this->request->baseFile(), PATHINFO_FILENAME);
+                if ($name && 'index' != $name && is_dir($this->appPath . $name)) {
+                    $this->route->bind($name);
+                }
+            }
+
+            // 监听app_dispatch
+            $this->hook->listen('app_dispatch');
+
+            $dispatch = $this->dispatch;
+
+            if (empty($dispatch)) {
+                // 路由检测
+                $dispatch = $this->routeCheck()->init();
+            }
+
+            // 记录当前调度信息
+            $this->request->dispatch($dispatch);
+
+            // 记录路由和请求信息
+            if ($this->appDebug) {
+                $this->log('[ ROUTE ] ' . var_export($this->request->routeInfo(), true));
+                $this->log('[ HEADER ] ' . var_export($this->request->header(), true));
+                $this->log('[ PARAM ] ' . var_export($this->request->param(), true));
+            }
+
+            // 监听app_begin
+            $this->hook->listen('app_begin');
+
+            // 请求缓存检查
+            $this->checkRequestCache(
+                $this->config('request_cache'),
+                $this->config('request_cache_expire'),
+                $this->config('request_cache_except')
+            );
+
+            $data = null;
+        } catch (HttpResponseException $exception) {
+            $dispatch = null;
+            $data     = $exception->getResponse();
+        }
+
+        $this->middleware->add(function (Request $request, $next) use ($dispatch, $data) {
+            return is_null($data) ? $dispatch->run() : $data;
+        });
+
+        $response = $this->middleware->dispatch($this->request);
+
+        // 监听app_end
+        $this->hook->listen('app_end', $response);
+
+        return $response;
+    }
+
+    protected function getRouteCacheKey()
+    {
+        if ($this->config->get('route_check_cache_key')) {
+            $closure  = $this->config->get('route_check_cache_key');
+            $routeKey = $closure($this->request);
+        } else {
+            $routeKey = md5($this->request->baseUrl(true) . ':' . $this->request->method());
+        }
+
+        return $routeKey;
+    }
+
+    protected function loadLangPack()
+    {
+        // 读取默认语言
+        $this->lang->range($this->config('app.default_lang'));
+
+        if ($this->config('app.lang_switch_on')) {
+            // 开启多语言机制 检测当前语言
+            $this->lang->detect();
+        }
+
+        $this->request->setLangset($this->lang->range());
+
+        // 加载系统语言包
+        $this->lang->load([
+            $this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php',
+            $this->appPath . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php',
+        ]);
+    }
+
+    /**
+     * 设置当前地址的请求缓存
+     * @access public
+     * @param  string $key 缓存标识,支持变量规则 ,例如 item/:name/:id
+     * @param  mixed  $expire 缓存有效期
+     * @param  array  $except 缓存排除
+     * @param  string $tag    缓存标签
+     * @return void
+     */
+    public function checkRequestCache($key, $expire = null, $except = [], $tag = null)
+    {
+        $cache = $this->request->cache($key, $expire, $except, $tag);
+
+        if ($cache) {
+            $this->setResponseCache($cache);
+        }
+    }
+
+    public function setResponseCache($cache)
+    {
+        list($key, $expire, $tag) = $cache;
+
+        if (strtotime($this->request->server('HTTP_IF_MODIFIED_SINCE')) + $expire > $this->request->server('REQUEST_TIME')) {
+            // 读取缓存
+            $response = Response::create()->code(304);
+            throw new HttpResponseException($response);
+        } elseif ($this->cache->has($key)) {
+            list($content, $header) = $this->cache->get($key);
+
+            $response = Response::create($content)->header($header);
+            throw new HttpResponseException($response);
+        }
+    }
+
+    /**
+     * 设置当前请求的调度信息
+     * @access public
+     * @param  Dispatch  $dispatch 调度信息
+     * @return $this
+     */
+    public function dispatch(Dispatch $dispatch)
+    {
+        $this->dispatch = $dispatch;
+        return $this;
+    }
+
+    /**
+     * 记录调试信息
+     * @access public
+     * @param  mixed  $msg  调试信息
+     * @param  string $type 信息类型
+     * @return void
+     */
+    public function log($msg, $type = 'info')
+    {
+        $this->appDebug && $this->log->record($msg, $type);
+    }
+
+    /**
+     * 获取配置参数 为空则获取所有配置
+     * @access public
+     * @param  string    $name 配置参数名(支持二级配置 .号分割)
+     * @return mixed
+     */
+    public function config($name = '')
+    {
+        return $this->config->get($name);
+    }
+
+    /**
+     * 路由初始化 导入路由定义规则
+     * @access public
+     * @return void
+     */
+    public function routeInit()
+    {
+        // 路由检测
+        $files = scandir($this->routePath);
+        foreach ($files as $file) {
+            if (strpos($file, '.php')) {
+                $filename = $this->routePath . $file;
+                // 导入路由配置
+                $rules = include $filename;
+                if (is_array($rules)) {
+                    $this->route->import($rules);
+                }
+            }
+        }
+
+        if ($this->route->config('route_annotation')) {
+            // 自动生成路由定义
+            if ($this->appDebug) {
+                $suffix = $this->route->config('controller_suffix') || $this->route->config('class_suffix');
+                $this->build->buildRoute($suffix);
+            }
+
+            $filename = $this->runtimePath . 'build_route.php';
+
+            if (is_file($filename)) {
+                include $filename;
+            }
+        }
+    }
+
+    /**
+     * URL路由检测(根据PATH_INFO)
+     * @access public
+     * @return Dispatch
+     */
+    public function routeCheck()
+    {
+        // 检测路由缓存
+        if (!$this->appDebug && $this->config->get('route_check_cache')) {
+            $routeKey = $this->getRouteCacheKey();
+            $option   = $this->config->get('route_cache_option');
+
+            if ($option && $this->cache->connect($option)->has($routeKey)) {
+                return $this->cache->connect($option)->get($routeKey);
+            } elseif ($this->cache->has($routeKey)) {
+                return $this->cache->get($routeKey);
+            }
+        }
+
+        // 获取应用调度信息
+        $path = $this->request->path();
+
+        // 是否强制路由模式
+        $must = !is_null($this->routeMust) ? $this->routeMust : $this->route->config('url_route_must');
+
+        // 路由检测 返回一个Dispatch对象
+        $dispatch = $this->route->check($path, $must);
+
+        if (!empty($routeKey)) {
+            try {
+                if ($option) {
+                    $this->cache->connect($option)->tag('route_cache')->set($routeKey, $dispatch);
+                } else {
+                    $this->cache->tag('route_cache')->set($routeKey, $dispatch);
+                }
+            } catch (\Exception $e) {
+                // 存在闭包的时候缓存无效
+            }
+        }
+
+        return $dispatch;
+    }
+
+    /**
+     * 设置应用的路由检测机制
+     * @access public
+     * @param  bool $must  是否强制检测路由
+     * @return $this
+     */
+    public function routeMust($must = false)
+    {
+        $this->routeMust = $must;
+        return $this;
+    }
+
+    /**
+     * 解析模块和类名
+     * @access protected
+     * @param  string $name         资源地址
+     * @param  string $layer        验证层名称
+     * @param  bool   $appendSuffix 是否添加类名后缀
+     * @return array
+     */
+    protected function parseModuleAndClass($name, $layer, $appendSuffix)
+    {
+        if (false !== strpos($name, '\\')) {
+            $class  = $name;
+            $module = $this->request->module();
+        } else {
+            if (strpos($name, '/')) {
+                list($module, $name) = explode('/', $name, 2);
+            } else {
+                $module = $this->request->module();
+            }
+
+            $class = $this->parseClass($module, $layer, $name, $appendSuffix);
+        }
+
+        return [$module, $class];
+    }
+
+    /**
+     * 实例化应用类库
+     * @access public
+     * @param  string $name         类名称
+     * @param  string $layer        业务层名称
+     * @param  bool   $appendSuffix 是否添加类名后缀
+     * @param  string $common       公共模块名
+     * @return object
+     * @throws ClassNotFoundException
+     */
+    public function create($name, $layer, $appendSuffix = false, $common = 'common')
+    {
+        $guid = $name . $layer;
+
+        if ($this->__isset($guid)) {
+            return $this->__get($guid);
+        }
+
+        list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);
+
+        if (class_exists($class)) {
+            $object = $this->__get($class);
+        } else {
+            $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
+            if (class_exists($class)) {
+                $object = $this->__get($class);
+            } else {
+                throw new ClassNotFoundException('class not exists:' . $class, $class);
+            }
+        }
+
+        $this->__set($guid, $class);
+
+        return $object;
+    }
+
+    /**
+     * 实例化(分层)模型
+     * @access public
+     * @param  string $name         Model名称
+     * @param  string $layer        业务层名称
+     * @param  bool   $appendSuffix 是否添加类名后缀
+     * @param  string $common       公共模块名
+     * @return Model
+     * @throws ClassNotFoundException
+     */
+    public function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common')
+    {
+        return $this->create($name, $layer, $appendSuffix, $common);
+    }
+
+    /**
+     * 实例化(分层)控制器 格式:[模块名/]控制器名
+     * @access public
+     * @param  string $name              资源地址
+     * @param  string $layer             控制层名称
+     * @param  bool   $appendSuffix      是否添加类名后缀
+     * @param  string $empty             空控制器名称
+     * @return object
+     * @throws ClassNotFoundException
+     */
+    public function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
+    {
+        list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);
+
+        if (class_exists($class)) {
+            return $this->make($class, true);
+        } elseif ($empty && class_exists($emptyClass = $this->parseClass($module, $layer, $empty, $appendSuffix))) {
+            return $this->make($emptyClass, true);
+        }
+
+        throw new ClassNotFoundException('class not exists:' . $class, $class);
+    }
+
+    /**
+     * 实例化验证类 格式:[模块名/]验证器名
+     * @access public
+     * @param  string $name         资源地址
+     * @param  string $layer        验证层名称
+     * @param  bool   $appendSuffix 是否添加类名后缀
+     * @param  string $common       公共模块名
+     * @return Validate
+     * @throws ClassNotFoundException
+     */
+    public function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common')
+    {
+        $name = $name ?: $this->config('default_validate');
+
+        if (empty($name)) {
+            return new Validate;
+        }
+
+        return $this->create($name, $layer, $appendSuffix, $common);
+    }
+
+    /**
+     * 数据库初始化
+     * @access public
+     * @param  mixed         $config 数据库配置
+     * @param  bool|string   $name 连接标识 true 强制重新连接
+     * @return \think\db\Query
+     */
+    public function db($config = [], $name = false)
+    {
+        return Db::connect($config, $name);
+    }
+
+    /**
+     * 远程调用模块的操作方法 参数格式 [模块/控制器/]操作
+     * @access public
+     * @param  string       $url          调用地址
+     * @param  string|array $vars         调用参数 支持字符串和数组
+     * @param  string       $layer        要调用的控制层名称
+     * @param  bool         $appendSuffix 是否添加类名后缀
+     * @return mixed
+     * @throws ClassNotFoundException
+     */
+    public function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
+    {
+        $info   = pathinfo($url);
+        $action = $info['basename'];
+        $module = '.' != $info['dirname'] ? $info['dirname'] : $this->request->controller();
+        $class  = $this->controller($module, $layer, $appendSuffix);
+
+        if (is_scalar($vars)) {
+            if (strpos($vars, '=')) {
+                parse_str($vars, $vars);
+            } else {
+                $vars = [$vars];
+            }
+        }
+
+        return $this->invokeMethod([$class, $action . $this->config('action_suffix')], $vars);
+    }
+
+    /**
+     * 解析应用类的类名
+     * @access public
+     * @param  string $module 模块名
+     * @param  string $layer  层名 controller model ...
+     * @param  string $name   类名
+     * @param  bool   $appendSuffix
+     * @return string
+     */
+    public function parseClass($module, $layer, $name, $appendSuffix = false)
+    {
+        $name  = str_replace(['/', '.'], '\\', $name);
+        $array = explode('\\', $name);
+        $class = Loader::parseName(array_pop($array), 1) . ($this->suffix || $appendSuffix ? ucfirst($layer) : '');
+        $path  = $array ? implode('\\', $array) . '\\' : '';
+
+        return $this->namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $path . $class;
+    }
+
+    /**
+     * 获取框架版本
+     * @access public
+     * @return string
+     */
+    public function version()
+    {
+        return static::VERSION;
+    }
+
+    /**
+     * 是否为调试模式
+     * @access public
+     * @return bool
+     */
+    public function isDebug()
+    {
+        return $this->appDebug;
+    }
+
+    /**
+     * 获取模块路径
+     * @access public
+     * @return string
+     */
+    public function getModulePath()
+    {
+        return $this->modulePath;
+    }
+
+    /**
+     * 设置模块路径
+     * @access public
+     * @param  string $path 路径
+     * @return void
+     */
+    public function setModulePath($path)
+    {
+        $this->modulePath = $path;
+        $this->env->set('module_path', $path);
+    }
+
+    /**
+     * 获取应用根目录
+     * @access public
+     * @return string
+     */
+    public function getRootPath()
+    {
+        return $this->rootPath;
+    }
+
+    /**
+     * 获取应用类库目录
+     * @access public
+     * @return string
+     */
+    public function getAppPath()
+    {
+        if (is_null($this->appPath)) {
+            $this->appPath = Loader::getRootPath() . 'application' . DIRECTORY_SEPARATOR;
+        }
+
+        return $this->appPath;
+    }
+
+    /**
+     * 获取应用运行时目录
+     * @access public
+     * @return string
+     */
+    public function getRuntimePath()
+    {
+        return $this->runtimePath;
+    }
+
+    /**
+     * 获取核心框架目录
+     * @access public
+     * @return string
+     */
+    public function getThinkPath()
+    {
+        return $this->thinkPath;
+    }
+
+    /**
+     * 获取路由目录
+     * @access public
+     * @return string
+     */
+    public function getRoutePath()
+    {
+        return $this->routePath;
+    }
+
+    /**
+     * 获取应用配置目录
+     * @access public
+     * @return string
+     */
+    public function getConfigPath()
+    {
+        return $this->configPath;
+    }
+
+    /**
+     * 获取配置后缀
+     * @access public
+     * @return string
+     */
+    public function getConfigExt()
+    {
+        return $this->configExt;
+    }
+
+    /**
+     * 获取应用类库命名空间
+     * @access public
+     * @return string
+     */
+    public function getNamespace()
+    {
+        return $this->namespace;
+    }
+
+    /**
+     * 设置应用类库命名空间
+     * @access public
+     * @param  string $namespace 命名空间名称
+     * @return $this
+     */
+    public function setNamespace($namespace)
+    {
+        $this->namespace = $namespace;
+        return $this;
+    }
+
+    /**
+     * 是否启用类库后缀
+     * @access public
+     * @return bool
+     */
+    public function getSuffix()
+    {
+        return $this->suffix;
+    }
+
+    /**
+     * 获取应用开启时间
+     * @access public
+     * @return float
+     */
+    public function getBeginTime()
+    {
+        return $this->beginTime;
+    }
+
+    /**
+     * 获取应用初始内存占用
+     * @access public
+     * @return integer
+     */
+    public function getBeginMem()
+    {
+        return $this->beginMem;
+    }
+
+}

+ 415 - 0
thinkphp/library/think/Build.php

@@ -0,0 +1,415 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+class Build
+{
+    /**
+     * 应用对象
+     * @var App
+     */
+    protected $app;
+
+    /**
+     * 应用目录
+     * @var string
+     */
+    protected $basePath;
+
+    public function __construct(App $app)
+    {
+        $this->app      = $app;
+        $this->basePath = $this->app->getAppPath();
+    }
+
+    /**
+     * 根据传入的build资料创建目录和文件
+     * @access public
+     * @param  array  $build build列表
+     * @param  string $namespace 应用类库命名空间
+     * @param  bool   $suffix 类库后缀
+     * @return void
+     */
+    public function run(array $build = [], $namespace = 'app', $suffix = false)
+    {
+        // 锁定
+        $lockfile = $this->basePath . 'build.lock';
+
+        if (is_writable($lockfile)) {
+            return;
+        } elseif (!touch($lockfile)) {
+            throw new Exception('应用目录[' . $this->basePath . ']不可写,目录无法自动生成!<BR>请手动生成项目目录~', 10006);
+        }
+
+        foreach ($build as $module => $list) {
+            if ('__dir__' == $module) {
+                // 创建目录列表
+                $this->buildDir($list);
+            } elseif ('__file__' == $module) {
+                // 创建文件列表
+                $this->buildFile($list);
+            } else {
+                // 创建模块
+                $this->module($module, $list, $namespace, $suffix);
+            }
+        }
+
+        // 解除锁定
+        unlink($lockfile);
+    }
+
+    /**
+     * 创建目录
+     * @access protected
+     * @param  array $list 目录列表
+     * @return void
+     */
+    protected function buildDir($list)
+    {
+        foreach ($list as $dir) {
+            $this->checkDirBuild($this->basePath . $dir);
+        }
+    }
+
+    /**
+     * 创建文件
+     * @access protected
+     * @param  array $list 文件列表
+     * @return void
+     */
+    protected function buildFile($list)
+    {
+        foreach ($list as $file) {
+            if (!is_dir($this->basePath . dirname($file))) {
+                // 创建目录
+                mkdir($this->basePath . dirname($file), 0755, true);
+            }
+
+            if (!is_file($this->basePath . $file)) {
+                file_put_contents($this->basePath . $file, 'php' == pathinfo($file, PATHINFO_EXTENSION) ? "<?php\n" : '');
+            }
+        }
+    }
+
+    /**
+     * 创建模块
+     * @access public
+     * @param  string $module 模块名
+     * @param  array  $list build列表
+     * @param  string $namespace 应用类库命名空间
+     * @param  bool   $suffix 类库后缀
+     * @return void
+     */
+    public function module($module = '', $list = [], $namespace = 'app', $suffix = false)
+    {
+        $module = $module ? $module : '';
+
+        if (!is_dir($this->basePath . $module)) {
+            // 创建模块目录
+            mkdir($this->basePath . $module);
+        }
+
+        if (basename($this->app->getRuntimePath()) != $module) {
+            // 创建配置文件和公共文件
+            $this->buildCommon($module);
+            // 创建模块的默认页面
+            $this->buildHello($module, $namespace, $suffix);
+        }
+
+        if (empty($list)) {
+            // 创建默认的模块目录和文件
+            $list = [
+                '__file__' => ['common.php'],
+                '__dir__'  => ['controller', 'model', 'view', 'config'],
+            ];
+        }
+
+        // 创建子目录和文件
+        foreach ($list as $path => $file) {
+            $modulePath = $this->basePath . $module . DIRECTORY_SEPARATOR;
+            if ('__dir__' == $path) {
+                // 生成子目录
+                foreach ($file as $dir) {
+                    $this->checkDirBuild($modulePath . $dir);
+                }
+            } elseif ('__file__' == $path) {
+                // 生成(空白)文件
+                foreach ($file as $name) {
+                    if (!is_file($modulePath . $name)) {
+                        file_put_contents($modulePath . $name, 'php' == pathinfo($name, PATHINFO_EXTENSION) ? "<?php\n" : '');
+                    }
+                }
+            } else {
+                // 生成相关MVC文件
+                foreach ($file as $val) {
+                    $val      = trim($val);
+                    $filename = $modulePath . $path . DIRECTORY_SEPARATOR . $val . ($suffix ? ucfirst($path) : '') . '.php';
+                    $space    = $namespace . '\\' . ($module ? $module . '\\' : '') . $path;
+                    $class    = $val . ($suffix ? ucfirst($path) : '');
+                    switch ($path) {
+                        case 'controller': // 控制器
+                            $content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
+                            break;
+                        case 'model': // 模型
+                            $content = "<?php\nnamespace {$space};\n\nuse think\Model;\n\nclass {$class} extends Model\n{\n\n}";
+                            break;
+                        case 'view': // 视图
+                            $filename = $modulePath . $path . DIRECTORY_SEPARATOR . $val . '.html';
+                            $this->checkDirBuild(dirname($filename));
+                            $content = '';
+                            break;
+                        default:
+                            // 其他文件
+                            $content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
+                    }
+
+                    if (!is_file($filename)) {
+                        file_put_contents($filename, $content);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 根据注释自动生成路由规则
+     * @access public
+     * @param  bool   $suffix 类库后缀
+     * @param  string $layer  控制器层目录名
+     * @return string
+     */
+    public function buildRoute($suffix = false, $layer = '')
+    {
+        $namespace = $this->app->getNameSpace();
+        $content   = '<?php ' . PHP_EOL . '//根据 Annotation 自动生成的路由规则';
+
+        if (!$layer) {
+            $layer = $this->app->config('app.url_controller_layer');
+        }
+
+        if ($this->app->config('app.app_multi_module')) {
+            $modules = glob($this->basePath . '*', GLOB_ONLYDIR);
+
+            foreach ($modules as $module) {
+                $module = basename($module);
+
+                if (in_array($module, $this->app->config('app.deny_module_list'))) {
+                    continue;
+                }
+
+                $path = $this->basePath . $module . DIRECTORY_SEPARATOR . $layer . DIRECTORY_SEPARATOR;
+                $content .= $this->buildDirRoute($path, $namespace, $module, $suffix, $layer);
+            }
+        } else {
+            $path = $this->basePath . $layer . DIRECTORY_SEPARATOR;
+            $content .= $this->buildDirRoute($path, $namespace, '', $suffix, $layer);
+        }
+
+        $filename = $this->app->getRuntimePath() . 'build_route.php';
+        file_put_contents($filename, $content);
+
+        return $filename;
+    }
+
+    /**
+     * 生成子目录控制器类的路由规则
+     * @access protected
+     * @param  string $path  控制器目录
+     * @param  string $namespace 应用命名空间
+     * @param  string $module 模块
+     * @param  bool   $suffix 类库后缀
+     * @param  string $layer 控制器层目录名
+     * @return string
+     */
+    protected function buildDirRoute($path, $namespace, $module, $suffix, $layer)
+    {
+        $content     = '';
+        $controllers = glob($path . '*.php');
+
+        foreach ($controllers as $controller) {
+            $controller = basename($controller, '.php');
+
+            $class = new \ReflectionClass($namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $controller);
+
+            if (strpos($layer, '\\')) {
+                // 多级控制器
+                $level      = str_replace(DIRECTORY_SEPARATOR, '.', substr($layer, 11));
+                $controller = $level . '.' . $controller;
+                $length     = strlen(strstr($layer, '\\', true));
+            } else {
+                $length = strlen($layer);
+            }
+
+            if ($suffix) {
+                $controller = substr($controller, 0, -$length);
+            }
+
+            $content .= $this->getControllerRoute($class, $module, $controller);
+        }
+
+        $subDir = glob($path . '*', GLOB_ONLYDIR);
+
+        foreach ($subDir as $dir) {
+            $content .= $this->buildDirRoute($dir . DIRECTORY_SEPARATOR, $namespace, $module, $suffix, $layer . '\\' . basename($dir));
+        }
+
+        return $content;
+    }
+
+    /**
+     * 生成控制器类的路由规则
+     * @access protected
+     * @param  string $class        控制器完整类名
+     * @param  string $module       模块名
+     * @param  string $controller   控制器名
+     * @return string
+     */
+    protected function getControllerRoute($class, $module, $controller)
+    {
+        $content = '';
+        $comment = $class->getDocComment();
+
+        if (false !== strpos($comment, '@route(')) {
+            $comment = $this->parseRouteComment($comment);
+            $route   = ($module ? $module . '/' : '') . $controller;
+            $comment = preg_replace('/route\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\)/is', 'Route::resource(\1,\'' . $route . '\')', $comment);
+            $content .= PHP_EOL . $comment;
+        } elseif (false !== strpos($comment, '@alias(')) {
+            $comment = $this->parseRouteComment($comment, '@alias(');
+            $route   = ($module ? $module . '/' : '') . $controller;
+            $comment = preg_replace('/alias\(\s?([\'\"][\-\_\/\w]+[\'\"])\s?\)/is', 'Route::alias(\1,\'' . $route . '\')', $comment);
+            $content .= PHP_EOL . $comment;
+        }
+
+        $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC);
+
+        foreach ($methods as $method) {
+            $comment = $this->getMethodRouteComment($module, $controller, $method);
+            if ($comment) {
+                $content .= PHP_EOL . $comment;
+            }
+        }
+
+        return $content;
+    }
+
+    /**
+     * 解析路由注释
+     * @access protected
+     * @param  string $comment
+     * @param  string $tag
+     * @return string
+     */
+    protected function parseRouteComment($comment, $tag = '@route(')
+    {
+        $comment = substr($comment, 3, -2);
+        $comment = explode(PHP_EOL, substr(strstr(trim($comment), $tag), 1));
+        $comment = array_map(function ($item) {return trim(trim($item), ' \t*');}, $comment);
+
+        if (count($comment) > 1) {
+            $key     = array_search('', $comment);
+            $comment = array_slice($comment, 0, false === $key ? 1 : $key);
+        }
+
+        $comment = implode(PHP_EOL . "\t", $comment) . ';';
+
+        if (strpos($comment, '{')) {
+            $comment = preg_replace_callback('/\{\s?.*?\s?\}/s', function ($matches) {
+                return false !== strpos($matches[0], '"') ? '[' . substr(var_export(json_decode($matches[0], true), true), 7, -1) . ']' : $matches[0];
+            }, $comment);
+        }
+        return $comment;
+    }
+
+    /**
+     * 获取方法的路由注释
+     * @access protected
+     * @param  string           $module 模块
+     * @param  string           $controller 控制器名
+     * @param  \ReflectMethod   $reflectMethod
+     * @return string|void
+     */
+    protected function getMethodRouteComment($module, $controller, $reflectMethod)
+    {
+        $comment = $reflectMethod->getDocComment();
+
+        if (false !== strpos($comment, '@route(')) {
+            $comment = $this->parseRouteComment($comment);
+            $action  = $reflectMethod->getName();
+
+            if ($suffix = $this->app->config('app.action_suffix')) {
+                $action = substr($action, 0, -strlen($suffix));
+            }
+
+            $route   = ($module ? $module . '/' : '') . $controller . '/' . $action;
+            $comment = preg_replace('/route\s?\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\,?\s?[\'\"]?(\w+?)[\'\"]?\s?\)/is', 'Route::\2(\1,\'' . $route . '\')', $comment);
+            $comment = preg_replace('/route\s?\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\)/is', 'Route::rule(\1,\'' . $route . '\')', $comment);
+
+            return $comment;
+        }
+    }
+
+    /**
+     * 创建模块的欢迎页面
+     * @access protected
+     * @param  string $module 模块名
+     * @param  string $namespace 应用类库命名空间
+     * @param  bool   $suffix 类库后缀
+     * @return void
+     */
+    protected function buildHello($module, $namespace, $suffix = false)
+    {
+        $filename = $this->basePath . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'controller' . DIRECTORY_SEPARATOR . 'Index' . ($suffix ? 'Controller' : '') . '.php';
+        if (!is_file($filename)) {
+            $content = file_get_contents($this->app->getThinkPath() . 'tpl' . DIRECTORY_SEPARATOR . 'default_index.tpl');
+            $content = str_replace(['{$app}', '{$module}', '{layer}', '{$suffix}'], [$namespace, $module ? $module . '\\' : '', 'controller', $suffix ? 'Controller' : ''], $content);
+            $this->checkDirBuild(dirname($filename));
+
+            file_put_contents($filename, $content);
+        }
+    }
+
+    /**
+     * 创建模块的公共文件
+     * @access protected
+     * @param  string $module 模块名
+     * @return void
+     */
+    protected function buildCommon($module)
+    {
+        $filename = $this->app->getConfigPath() . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'app.php';
+        $this->checkDirBuild(dirname($filename));
+
+        if (!is_file($filename)) {
+            file_put_contents($filename, "<?php\n//配置文件\nreturn [\n\n];");
+        }
+
+        $filename = $this->basePath . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'common.php';
+
+        if (!is_file($filename)) {
+            file_put_contents($filename, "<?php\n");
+        }
+    }
+
+    /**
+     * 创建目录
+     * @access protected
+     * @param  string $dirname 目录名称
+     * @return void
+     */
+    protected function checkDirBuild($dirname)
+    {
+        if (!is_dir($dirname)) {
+            mkdir($dirname, 0755, true);
+        }
+    }
+}

+ 133 - 0
thinkphp/library/think/Cache.php

@@ -0,0 +1,133 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use think\cache\Driver;
+
+/**
+ * Class Cache
+ *
+ * @package think
+ *
+ * @mixin Driver
+ * @mixin \think\cache\driver\File
+ */
+class Cache
+{
+    /**
+     * 缓存实例
+     * @var array
+     */
+    protected $instance = [];
+
+    /**
+     * 缓存配置
+     * @var array
+     */
+    protected $config = [];
+
+    /**
+     * 操作句柄
+     * @var object
+     */
+    protected $handler;
+
+    public function __construct(array $config = [])
+    {
+        $this->config = $config;
+        $this->init($config);
+    }
+
+    /**
+     * 连接缓存
+     * @access public
+     * @param  array         $options  配置数组
+     * @param  bool|string   $name 缓存连接标识 true 强制重新连接
+     * @return Driver
+     */
+    public function connect(array $options = [], $name = false)
+    {
+        if (false === $name) {
+            $name = md5(serialize($options));
+        }
+
+        if (true === $name || !isset($this->instance[$name])) {
+            $type = !empty($options['type']) ? $options['type'] : 'File';
+
+            if (true === $name) {
+                $name = md5(serialize($options));
+            }
+
+            $this->instance[$name] = Loader::factory($type, '\\think\\cache\\driver\\', $options);
+        }
+
+        return $this->instance[$name];
+    }
+
+    /**
+     * 自动初始化缓存
+     * @access public
+     * @param  array         $options  配置数组
+     * @param  bool          $force    强制更新
+     * @return Driver
+     */
+    public function init(array $options = [], $force = false)
+    {
+        if (is_null($this->handler) || $force) {
+
+            if ('complex' == $options['type']) {
+                $default = $options['default'];
+                $options = isset($options[$default['type']]) ? $options[$default['type']] : $default;
+            }
+
+            $this->handler = $this->connect($options);
+        }
+
+        return $this->handler;
+    }
+
+    public static function __make(Config $config)
+    {
+        return new static($config->pull('cache'));
+    }
+
+    public function getConfig()
+    {
+        return $this->config;
+    }
+
+    public function setConfig(array $config)
+    {
+        $this->config = array_merge($this->config, $config);
+    }
+
+    /**
+     * 切换缓存类型 需要配置 cache.type 为 complex
+     * @access public
+     * @param  string $name 缓存标识
+     * @return Driver
+     */
+    public function store($name = '')
+    {
+        if ('' !== $name && 'complex' == $this->config['type']) {
+            return $this->connect($this->config[$name], strtolower($name));
+        }
+
+        return $this->init();
+    }
+
+    public function __call($method, $args)
+    {
+        return call_user_func_array([$this->init(), $method], $args);
+    }
+
+}

+ 552 - 0
thinkphp/library/think/Collection.php

@@ -0,0 +1,552 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: zhangyajun <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use ArrayAccess;
+use ArrayIterator;
+use Countable;
+use IteratorAggregate;
+use JsonSerializable;
+
+class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
+{
+    /**
+     * 数据集数据
+     * @var array
+     */
+    protected $items = [];
+
+    public function __construct($items = [])
+    {
+        $this->items = $this->convertToArray($items);
+    }
+
+    public static function make($items = [])
+    {
+        return new static($items);
+    }
+
+    /**
+     * 是否为空
+     * @access public
+     * @return bool
+     */
+    public function isEmpty()
+    {
+        return empty($this->items);
+    }
+
+    public function toArray()
+    {
+        return array_map(function ($value) {
+            return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value;
+        }, $this->items);
+    }
+
+    public function all()
+    {
+        return $this->items;
+    }
+
+    /**
+     * 合并数组
+     *
+     * @access public
+     * @param  mixed $items
+     * @return static
+     */
+    public function merge($items)
+    {
+        return new static(array_merge($this->items, $this->convertToArray($items)));
+    }
+
+    /**
+     * 交换数组中的键和值
+     *
+     * @access public
+     * @return static
+     */
+    public function flip()
+    {
+        return new static(array_flip($this->items));
+    }
+
+    /**
+     * 按指定键整理数据
+     *
+     * @access public
+     * @param  mixed    $items      数据
+     * @param  string   $indexKey   键名
+     * @return array
+     */
+    public function dictionary($items = null, &$indexKey = null)
+    {
+        if ($items instanceof self || $items instanceof Paginator) {
+            $items = $items->all();
+        }
+
+        $items = is_null($items) ? $this->items : $items;
+
+        if ($items && empty($indexKey)) {
+            $indexKey = is_array($items[0]) ? 'id' : $items[0]->getPk();
+        }
+
+        if (isset($indexKey) && is_string($indexKey)) {
+            return array_column($items, null, $indexKey);
+        }
+
+        return $items;
+    }
+
+    /**
+     * 比较数组,返回差集
+     *
+     * @access public
+     * @param  mixed    $items      数据
+     * @param  string   $indexKey   指定比较的键名
+     * @return static
+     */
+    public function diff($items, $indexKey = null)
+    {
+        if ($this->isEmpty() || is_scalar($this->items[0])) {
+            return new static(array_diff($this->items, $this->convertToArray($items)));
+        }
+
+        $diff       = [];
+        $dictionary = $this->dictionary($items, $indexKey);
+
+        if (is_string($indexKey)) {
+            foreach ($this->items as $item) {
+                if (!isset($dictionary[$item[$indexKey]])) {
+                    $diff[] = $item;
+                }
+            }
+        }
+
+        return new static($diff);
+    }
+
+    /**
+     * 比较数组,返回交集
+     *
+     * @access public
+     * @param  mixed    $items      数据
+     * @param  string   $indexKey   指定比较的键名
+     * @return static
+     */
+    public function intersect($items, $indexKey = null)
+    {
+        if ($this->isEmpty() || is_scalar($this->items[0])) {
+            return new static(array_diff($this->items, $this->convertToArray($items)));
+        }
+
+        $intersect  = [];
+        $dictionary = $this->dictionary($items, $indexKey);
+
+        if (is_string($indexKey)) {
+            foreach ($this->items as $item) {
+                if (isset($dictionary[$item[$indexKey]])) {
+                    $intersect[] = $item;
+                }
+            }
+        }
+
+        return new static($intersect);
+    }
+
+    /**
+     * 返回数组中所有的键名
+     *
+     * @access public
+     * @return array
+     */
+    public function keys()
+    {
+        $current = current($this->items);
+
+        if (is_scalar($current)) {
+            $array = $this->items;
+        } elseif (is_array($current)) {
+            $array = $current;
+        } else {
+            $array = $current->toArray();
+        }
+
+        return array_keys($array);
+    }
+
+    /**
+     * 删除数组的最后一个元素(出栈)
+     *
+     * @access public
+     * @return mixed
+     */
+    public function pop()
+    {
+        return array_pop($this->items);
+    }
+
+    /**
+     * 通过使用用户自定义函数,以字符串返回数组
+     *
+     * @access public
+     * @param  callable $callback
+     * @param  mixed    $initial
+     * @return mixed
+     */
+    public function reduce(callable $callback, $initial = null)
+    {
+        return array_reduce($this->items, $callback, $initial);
+    }
+
+    /**
+     * 以相反的顺序返回数组。
+     *
+     * @access public
+     * @return static
+     */
+    public function reverse()
+    {
+        return new static(array_reverse($this->items));
+    }
+
+    /**
+     * 删除数组中首个元素,并返回被删除元素的值
+     *
+     * @access public
+     * @return mixed
+     */
+    public function shift()
+    {
+        return array_shift($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  int  $size
+     * @param  bool $preserveKeys
+     * @return static
+     */
+    public function chunk($size, $preserveKeys = false)
+    {
+        $chunks = [];
+
+        foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) {
+            $chunks[] = new static($chunk);
+        }
+
+        return new static($chunks);
+    }
+
+    /**
+     * 在数组开头插入一个元素
+     * @access public
+     * @param mixed  $value
+     * @param mixed  $key
+     * @return void
+     */
+    public function unshift($value, $key = null)
+    {
+        if (is_null($key)) {
+            array_unshift($this->items, $value);
+        } else {
+            $this->items = [$key => $value] + $this->items;
+        }
+    }
+
+    /**
+     * 给每个元素执行个回调
+     *
+     * @access public
+     * @param  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)) {
+                $this->items[$key] = $result;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 用回调函数处理数组中的元素
+     * @access public
+     * @param  callable|null $callback
+     * @return static
+     */
+    public function map(callable $callback)
+    {
+        return new static(array_map($callback, $this->items));
+    }
+
+    /**
+     * 用回调函数过滤数组中的元素
+     * @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));
+    }
+
+    /**
+     * 根据字段条件过滤数组中的元素
+     * @access public
+     * @param  string   $field 字段名
+     * @param  mixed    $operator 操作符
+     * @param  mixed    $value 数据
+     * @return static
+     */
+    public function where($field, $operator, $value = null)
+    {
+        if (is_null($value)) {
+            $value    = $operator;
+            $operator = '=';
+        }
+
+        return $this->filter(function ($data) use ($field, $operator, $value) {
+            if (strpos($field, '.')) {
+                list($field, $relation) = explode('.', $field);
+
+                $result = isset($data[$field][$relation]) ? $data[$field][$relation] : null;
+            } else {
+                $result = isset($data[$field]) ? $data[$field] : null;
+            }
+
+            switch (strtolower($operator)) {
+                case '===':
+                    return $result === $value;
+                case '!==':
+                    return $result !== $value;
+                case '!=':
+                case '<>':
+                    return $result != $value;
+                case '>':
+                    return $result > $value;
+                case '>=':
+                    return $result >= $value;
+                case '<':
+                    return $result < $value;
+                case '<=':
+                    return $result <= $value;
+                case 'like':
+                    return is_string($result) && false !== strpos($result, $value);
+                case 'not like':
+                    return is_string($result) && false === strpos($result, $value);
+                case 'in':
+                    return is_scalar($result) && in_array($result, $value, true);
+                case 'not in':
+                    return is_scalar($result) && !in_array($result, $value, true);
+                case 'between':
+                    list($min, $max) = is_string($value) ? explode(',', $value) : $value;
+                    return is_scalar($result) && $result >= $min && $result <= $max;
+                case 'not between':
+                    list($min, $max) = is_string($value) ? explode(',', $value) : $value;
+                    return is_scalar($result) && $result > $max || $result < $min;
+                case '==':
+                case '=':
+                default:
+                    return $result == $value;
+            }
+        });
+    }
+
+    /**
+     * 返回数据中指定的一列
+     * @access public
+     * @param mixed $columnKey 键名
+     * @param mixed $indexKey  作为索引值的列
+     * @return array
+     */
+    public function column($columnKey, $indexKey = null)
+    {
+        return array_column($this->items, $columnKey, $indexKey);
+    }
+
+    /**
+     * 对数组排序
+     *
+     * @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);
+
+        };
+
+        uasort($items, $callback);
+
+        return new static($items);
+    }
+
+    /**
+     * 指定字段排序
+     * @access public
+     * @param  string       $field 排序字段
+     * @param  string       $order 排序
+     * @param  bool         $intSort 是否为数字排序
+     * @return $this
+     */
+    public function order($field, $order = null, $intSort = true)
+    {
+        return $this->sort(function ($a, $b) use ($field, $order, $intSort) {
+            $fieldA = isset($a[$field]) ? $a[$field] : null;
+            $fieldB = isset($b[$field]) ? $b[$field] : null;
+
+            if ($intSort) {
+                return 'desc' == strtolower($order) ? $fieldB >= $fieldA : $fieldA >= $fieldB;
+            } else {
+                return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB);
+            }
+        });
+    }
+
+    /**
+     * 将数组打乱
+     *
+     * @access public
+     * @return static
+     */
+    public function shuffle()
+    {
+        $items = $this->items;
+
+        shuffle($items);
+
+        return new static($items);
+    }
+
+    /**
+     * 截取数组
+     *
+     * @access public
+     * @param  int  $offset
+     * @param  int  $length
+     * @param  bool $preserveKeys
+     * @return static
+     */
+    public function slice($offset, $length = null, $preserveKeys = false)
+    {
+        return new static(array_slice($this->items, $offset, $length, $preserveKeys));
+    }
+
+    // ArrayAccess
+    public function offsetExists($offset)
+    {
+        return array_key_exists($offset, $this->items);
+    }
+
+    public function offsetGet($offset)
+    {
+        return $this->items[$offset];
+    }
+
+    public function offsetSet($offset, $value)
+    {
+        if (is_null($offset)) {
+            $this->items[] = $value;
+        } else {
+            $this->items[$offset] = $value;
+        }
+    }
+
+    public function offsetUnset($offset)
+    {
+        unset($this->items[$offset]);
+    }
+
+    //Countable
+    public function count()
+    {
+        return count($this->items);
+    }
+
+    //IteratorAggregate
+    public function getIterator()
+    {
+        return new ArrayIterator($this->items);
+    }
+
+    //JsonSerializable
+    public function jsonSerialize()
+    {
+        return $this->toArray();
+    }
+
+    /**
+     * 转换当前数据集为JSON字符串
+     * @access public
+     * @param  integer $options json参数
+     * @return string
+     */
+    public function toJson($options = JSON_UNESCAPED_UNICODE)
+    {
+        return json_encode($this->toArray(), $options);
+    }
+
+    public function __toString()
+    {
+        return $this->toJson();
+    }
+
+    /**
+     * 转换成数组
+     *
+     * @access public
+     * @param  mixed $items
+     * @return array
+     */
+    protected function convertToArray($items)
+    {
+        if ($items instanceof self) {
+            return $items->all();
+        }
+
+        return (array) $items;
+    }
+}

+ 398 - 0
thinkphp/library/think/Config.php

@@ -0,0 +1,398 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use Yaconf;
+
+class Config implements \ArrayAccess
+{
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $config = [];
+
+    /**
+     * 配置前缀
+     * @var string
+     */
+    protected $prefix = 'app';
+
+    /**
+     * 配置文件目录
+     * @var string
+     */
+    protected $path;
+
+    /**
+     * 配置文件后缀
+     * @var string
+     */
+    protected $ext;
+
+    /**
+     * 是否支持Yaconf
+     * @var bool
+     */
+    protected $yaconf;
+
+    /**
+     * 构造方法
+     * @access public
+     */
+    public function __construct($path = '', $ext = '.php')
+    {
+        $this->path   = $path;
+        $this->ext    = $ext;
+        $this->yaconf = class_exists('Yaconf');
+    }
+
+    public static function __make(App $app)
+    {
+        $path = $app->getConfigPath();
+        $ext  = $app->getConfigExt();
+        return new static($path, $ext);
+    }
+
+    /**
+     * 设置开启Yaconf
+     * @access public
+     * @param  bool|string    $yaconf  是否使用Yaconf
+     * @return void
+     */
+    public function setYaconf($yaconf)
+    {
+        if ($this->yaconf) {
+            $this->yaconf = $yaconf;
+        }
+    }
+
+    /**
+     * 设置配置参数默认前缀
+     * @access public
+     * @param string    $prefix 前缀
+     * @return void
+     */
+    public function setDefaultPrefix($prefix)
+    {
+        $this->prefix = $prefix;
+    }
+
+    /**
+     * 解析配置文件或内容
+     * @access public
+     * @param  string    $config 配置文件路径或内容
+     * @param  string    $type 配置解析类型
+     * @param  string    $name 配置名(如设置即表示二级配置)
+     * @return mixed
+     */
+    public function parse($config, $type = '', $name = '')
+    {
+        if (empty($type)) {
+            $type = pathinfo($config, PATHINFO_EXTENSION);
+        }
+
+        $object = Loader::factory($type, '\\think\\config\\driver\\', $config);
+
+        return $this->set($object->parse(), $name);
+    }
+
+    /**
+     * 加载配置文件(多种格式)
+     * @access public
+     * @param  string    $file 配置文件名
+     * @param  string    $name 一级配置名
+     * @return mixed
+     */
+    public function load($file, $name = '')
+    {
+        if (is_file($file)) {
+            $filename = $file;
+        } elseif (is_file($this->path . $file . $this->ext)) {
+            $filename = $this->path . $file . $this->ext;
+        }
+
+        if (isset($filename)) {
+            return $this->loadFile($filename, $name);
+        } elseif ($this->yaconf && Yaconf::has($file)) {
+            return $this->set(Yaconf::get($file), $name);
+        }
+
+        return $this->config;
+    }
+
+    /**
+     * 获取实际的yaconf配置参数
+     * @access protected
+     * @param  string    $name 配置参数名
+     * @return string
+     */
+    protected function getYaconfName($name)
+    {
+        if ($this->yaconf && is_string($this->yaconf)) {
+            return $this->yaconf . '.' . $name;
+        }
+
+        return $name;
+    }
+
+    /**
+     * 获取yaconf配置
+     * @access public
+     * @param  string    $name 配置参数名
+     * @param  mixed     $default   默认值
+     * @return mixed
+     */
+    public function yaconf($name, $default = null)
+    {
+        if ($this->yaconf) {
+            $yaconfName = $this->getYaconfName($name);
+
+            if (Yaconf::has($yaconfName)) {
+                return Yaconf::get($yaconfName);
+            }
+        }
+
+        return $default;
+    }
+
+    protected function loadFile($file, $name)
+    {
+        $name = strtolower($name);
+        $type = pathinfo($file, PATHINFO_EXTENSION);
+
+        if ('php' == $type) {
+            return $this->set(include $file, $name);
+        } elseif ('yaml' == $type && function_exists('yaml_parse_file')) {
+            return $this->set(yaml_parse_file($file), $name);
+        }
+
+        return $this->parse($file, $type, $name);
+    }
+
+    /**
+     * 检测配置是否存在
+     * @access public
+     * @param  string    $name 配置参数名(支持多级配置 .号分割)
+     * @return bool
+     */
+    public function has($name)
+    {
+        if (false === strpos($name, '.')) {
+            $name = $this->prefix . '.' . $name;
+        }
+
+        return !is_null($this->get($name));
+    }
+
+    /**
+     * 获取一级配置
+     * @access public
+     * @param  string    $name 一级配置名
+     * @return array
+     */
+    public function pull($name)
+    {
+        $name = strtolower($name);
+
+        if ($this->yaconf) {
+            $yaconfName = $this->getYaconfName($name);
+
+            if (Yaconf::has($yaconfName)) {
+                $config = Yaconf::get($yaconfName);
+                return isset($this->config[$name]) ? array_merge($this->config[$name], $config) : $config;
+            }
+        }
+
+        return isset($this->config[$name]) ? $this->config[$name] : [];
+    }
+
+    /**
+     * 获取配置参数 为空则获取所有配置
+     * @access public
+     * @param  string    $name      配置参数名(支持多级配置 .号分割)
+     * @param  mixed     $default   默认值
+     * @return mixed
+     */
+    public function get($name = null, $default = null)
+    {
+        if ($name && false === strpos($name, '.')) {
+            $name = $this->prefix . '.' . $name;
+        }
+
+        // 无参数时获取所有
+        if (empty($name)) {
+            return $this->config;
+        }
+
+        if ('.' == substr($name, -1)) {
+            return $this->pull(substr($name, 0, -1));
+        }
+
+        if ($this->yaconf) {
+            $yaconfName = $this->getYaconfName($name);
+
+            if (Yaconf::has($yaconfName)) {
+                return Yaconf::get($yaconfName);
+            }
+        }
+
+        $name    = explode('.', $name);
+        $name[0] = strtolower($name[0]);
+        $config  = $this->config;
+
+        // 按.拆分成多维数组进行判断
+        foreach ($name as $val) {
+            if (isset($config[$val])) {
+                $config = $config[$val];
+            } else {
+                return $default;
+            }
+        }
+
+        return $config;
+    }
+
+    /**
+     * 设置配置参数 name为数组则为批量设置
+     * @access public
+     * @param  string|array  $name 配置参数名(支持三级配置 .号分割)
+     * @param  mixed         $value 配置值
+     * @return mixed
+     */
+    public function set($name, $value = null)
+    {
+        if (is_string($name)) {
+            if (false === strpos($name, '.')) {
+                $name = $this->prefix . '.' . $name;
+            }
+
+            $name = explode('.', $name, 3);
+
+            if (count($name) == 2) {
+                $this->config[strtolower($name[0])][$name[1]] = $value;
+            } else {
+                $this->config[strtolower($name[0])][$name[1]][$name[2]] = $value;
+            }
+
+            return $value;
+        } elseif (is_array($name)) {
+            // 批量设置
+            if (!empty($value)) {
+                if (isset($this->config[$value])) {
+                    $result = array_merge($this->config[$value], $name);
+                } else {
+                    $result = $name;
+                }
+
+                $this->config[$value] = $result;
+            } else {
+                $result = $this->config = array_merge($this->config, $name);
+            }
+        } else {
+            // 为空直接返回 已有配置
+            $result = $this->config;
+        }
+
+        return $result;
+    }
+
+    /**
+     * 移除配置
+     * @access public
+     * @param  string  $name 配置参数名(支持三级配置 .号分割)
+     * @return void
+     */
+    public function remove($name)
+    {
+        if (false === strpos($name, '.')) {
+            $name = $this->prefix . '.' . $name;
+        }
+
+        $name = explode('.', $name, 3);
+
+        if (count($name) == 2) {
+            unset($this->config[strtolower($name[0])][$name[1]]);
+        } else {
+            unset($this->config[strtolower($name[0])][$name[1]][$name[2]]);
+        }
+    }
+
+    /**
+     * 重置配置参数
+     * @access public
+     * @param  string    $prefix  配置前缀名
+     * @return void
+     */
+    public function reset($prefix = '')
+    {
+        if ('' === $prefix) {
+            $this->config = [];
+        } else {
+            $this->config[$prefix] = [];
+        }
+    }
+
+    /**
+     * 设置配置
+     * @access public
+     * @param  string    $name  参数名
+     * @param  mixed     $value 值
+     */
+    public function __set($name, $value)
+    {
+        return $this->set($name, $value);
+    }
+
+    /**
+     * 获取配置参数
+     * @access public
+     * @param  string $name 参数名
+     * @return mixed
+     */
+    public function __get($name)
+    {
+        return $this->get($name);
+    }
+
+    /**
+     * 检测是否存在参数
+     * @access public
+     * @param  string $name 参数名
+     * @return bool
+     */
+    public function __isset($name)
+    {
+        return $this->has($name);
+    }
+
+    // ArrayAccess
+    public function offsetSet($name, $value)
+    {
+        $this->set($name, $value);
+    }
+
+    public function offsetExists($name)
+    {
+        return $this->has($name);
+    }
+
+    public function offsetUnset($name)
+    {
+        $this->remove($name);
+    }
+
+    public function offsetGet($name)
+    {
+        return $this->get($name);
+    }
+}

+ 829 - 0
thinkphp/library/think/Console.php

@@ -0,0 +1,829 @@
+<?php
+// +----------------------------------------------------------------------
+// | TopThink [ WE CAN DO IT JUST THINK IT ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2015 http://www.topthink.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Author: zhangyajun <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use think\console\Command;
+use think\console\command\Help as HelpCommand;
+use think\console\Input;
+use think\console\input\Argument as InputArgument;
+use think\console\input\Definition as InputDefinition;
+use think\console\input\Option as InputOption;
+use think\console\Output;
+use think\console\output\driver\Buffer;
+
+class Console
+{
+
+    private $name;
+    private $version;
+
+    /** @var Command[] */
+    private $commands = [];
+
+    private $wantHelps = false;
+
+    private $catchExceptions = true;
+    private $autoExit        = true;
+    private $definition;
+    private $defaultCommand;
+
+    private static $defaultCommands = [
+        'help'              => "think\\console\\command\\Help",
+        'list'              => "think\\console\\command\\Lists",
+        'build'             => "think\\console\\command\\Build",
+        'clear'             => "think\\console\\command\\Clear",
+        'make:command'      => "think\\console\\command\\make\\Command",
+        'make:controller'   => "think\\console\\command\\make\\Controller",
+        'make:model'        => "think\\console\\command\\make\\Model",
+        'make:middleware'   => "think\\console\\command\\make\\Middleware",
+        'make:validate'     => "think\\console\\command\\make\\Validate",
+        'optimize:autoload' => "think\\console\\command\\optimize\\Autoload",
+        'optimize:config'   => "think\\console\\command\\optimize\\Config",
+        'optimize:schema'   => "think\\console\\command\\optimize\\Schema",
+        'optimize:route'    => "think\\console\\command\\optimize\\Route",
+        'run'               => "think\\console\\command\\RunServer",
+        'version'           => "think\\console\\command\\Version",
+        'route:list'        => "think\\console\\command\\RouteList",
+    ];
+
+    /**
+     * Console constructor.
+     * @access public
+     * @param  string     $name    名称
+     * @param  string     $version 版本
+     * @param null|string $user    执行用户
+     */
+    public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN', $user = null)
+    {
+        $this->name    = $name;
+        $this->version = $version;
+
+        if ($user) {
+            $this->setUser($user);
+        }
+
+        $this->defaultCommand = 'list';
+        $this->definition     = $this->getDefaultInputDefinition();
+    }
+
+    /**
+     * 设置执行用户
+     * @param $user
+     */
+    public function setUser($user)
+    {
+        if (DIRECTORY_SEPARATOR == '\\') {
+            return;
+        }
+
+        $user = posix_getpwnam($user);
+        if ($user) {
+            posix_setuid($user['uid']);
+            posix_setgid($user['gid']);
+        }
+    }
+
+    /**
+     * 初始化 Console
+     * @access public
+     * @param  bool $run 是否运行 Console
+     * @return int|Console
+     */
+    public static function init($run = true)
+    {
+        static $console;
+
+        if (!$console) {
+            $config  = Container::get('config')->pull('console');
+            $console = new self($config['name'], $config['version'], $config['user']);
+
+            $commands = $console->getDefinedCommands($config);
+
+            // 添加指令集
+            $console->addCommands($commands);
+        }
+
+        if ($run) {
+            // 运行
+            return $console->run();
+        } else {
+            return $console;
+        }
+    }
+
+    /**
+     * @access public
+     * @param  array $config
+     * @return array
+     */
+    public function getDefinedCommands(array $config = [])
+    {
+        $commands = self::$defaultCommands;
+
+        if (!empty($config['auto_path']) && is_dir($config['auto_path'])) {
+            // 自动加载指令类
+            $files = scandir($config['auto_path']);
+
+            if (count($files) > 2) {
+                $beforeClass = get_declared_classes();
+
+                foreach ($files as $file) {
+                    if (pathinfo($file, PATHINFO_EXTENSION) == 'php') {
+                        include $config['auto_path'] . $file;
+                    }
+                }
+
+                $afterClass = get_declared_classes();
+                $commands   = array_merge($commands, array_diff($afterClass, $beforeClass));
+            }
+        }
+
+        $file = Container::get('env')->get('app_path') . 'command.php';
+
+        if (is_file($file)) {
+            $appCommands = include $file;
+
+            if (is_array($appCommands)) {
+                $commands = array_merge($commands, $appCommands);
+            }
+        }
+
+        return $commands;
+    }
+
+    /**
+     * @access public
+     * @param  string $command
+     * @param  array  $parameters
+     * @param  string $driver
+     * @return Output|Buffer
+     */
+    public static function call($command, array $parameters = [], $driver = 'buffer')
+    {
+        $console = self::init(false);
+
+        array_unshift($parameters, $command);
+
+        $input  = new Input($parameters);
+        $output = new Output($driver);
+
+        $console->setCatchExceptions(false);
+        $console->find($command)->run($input, $output);
+
+        return $output;
+    }
+
+    /**
+     * 执行当前的指令
+     * @access public
+     * @return int
+     * @throws \Exception
+     * @api
+     */
+    public function run()
+    {
+        $input  = new Input();
+        $output = new Output();
+
+        $this->configureIO($input, $output);
+
+        try {
+            $exitCode = $this->doRun($input, $output);
+        } catch (\Exception $e) {
+            if (!$this->catchExceptions) {
+                throw $e;
+            }
+
+            $output->renderException($e);
+
+            $exitCode = $e->getCode();
+            if (is_numeric($exitCode)) {
+                $exitCode = (int) $exitCode;
+                if (0 === $exitCode) {
+                    $exitCode = 1;
+                }
+            } else {
+                $exitCode = 1;
+            }
+        }
+
+        if ($this->autoExit) {
+            if ($exitCode > 255) {
+                $exitCode = 255;
+            }
+
+            exit($exitCode);
+        }
+
+        return $exitCode;
+    }
+
+    /**
+     * 执行指令
+     * @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());
+
+            return 0;
+        }
+
+        $name = $this->getCommandName($input);
+
+        if (true === $input->hasParameterOption(['--help', '-h'])) {
+            if (!$name) {
+                $name  = 'help';
+                $input = new Input(['help']);
+            } else {
+                $this->wantHelps = true;
+            }
+        }
+
+        if (!$name) {
+            $name  = $this->defaultCommand;
+            $input = new Input([$this->defaultCommand]);
+        }
+
+        $command = $this->find($name);
+
+        $exitCode = $this->doRunCommand($command, $input, $output);
+
+        return $exitCode;
+    }
+
+    /**
+     * 设置输入参数定义
+     * @access public
+     * @param  InputDefinition $definition
+     */
+    public function setDefinition(InputDefinition $definition)
+    {
+        $this->definition = $definition;
+    }
+
+    /**
+     * 获取输入参数定义
+     * @access public
+     * @return InputDefinition The InputDefinition instance
+     */
+    public function getDefinition()
+    {
+        return $this->definition;
+    }
+
+    /**
+     * Gets the help message.
+     * @access public
+     * @return string A help message.
+     */
+    public function getHelp()
+    {
+        return $this->getLongVersion();
+    }
+
+    /**
+     * 是否捕获异常
+     * @access public
+     * @param  bool $boolean
+     * @api
+     */
+    public function setCatchExceptions($boolean)
+    {
+        $this->catchExceptions = (bool) $boolean;
+    }
+
+    /**
+     * 是否自动退出
+     * @access public
+     * @param  bool $boolean
+     * @api
+     */
+    public function setAutoExit($boolean)
+    {
+        $this->autoExit = (bool) $boolean;
+    }
+
+    /**
+     * 获取名称
+     * @access public
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * 设置名称
+     * @access public
+     * @param  string $name
+     */
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * 获取版本
+     * @access public
+     * @return string
+     * @api
+     */
+    public function getVersion()
+    {
+        return $this->version;
+    }
+
+    /**
+     * 设置版本
+     * @access public
+     * @param  string $version
+     */
+    public function setVersion($version)
+    {
+        $this->version = $version;
+    }
+
+    /**
+     * 获取完整的版本号
+     * @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 '<info>Console Tool</info>';
+    }
+
+    /**
+     * 注册一个指令 (便于动态创建指令)
+     * @access public
+     * @param  string $name     指令名
+     * @return Command
+     */
+    public function register($name)
+    {
+        return $this->add(new Command($name));
+    }
+
+    /**
+     * 添加指令集
+     * @access public
+     * @param  array $commands
+     */
+    public function addCommands(array $commands)
+    {
+        foreach ($commands as $key => $command) {
+            if (is_subclass_of($command, "\\think\\console\\Command")) {
+                // 注册指令
+                $this->add($command, is_numeric($key) ? '' : $key);
+            }
+        }
+    }
+
+    /**
+     * 注册一个指令(对象)
+     * @access public
+     * @param  mixed    $command    指令对象或者指令类名
+     * @param  string   $name       指令名 留空则自动获取
+     * @return mixed
+     */
+    public function add($command, $name)
+    {
+        if ($name) {
+            $this->commands[$name] = $command;
+            return;
+        }
+
+        if (is_string($command)) {
+            $command = new $command();
+        }
+
+        $command->setConsole($this);
+
+        if (!$command->isEnabled()) {
+            $command->setConsole(null);
+            return;
+        }
+
+        if (null === $command->getDefinition()) {
+            throw new \LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command)));
+        }
+
+        $this->commands[$command->getName()] = $command;
+
+        foreach ($command->getAliases() as $alias) {
+            $this->commands[$alias] = $command;
+        }
+
+        return $command;
+    }
+
+    /**
+     * 获取指令
+     * @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));
+        }
+
+        $command = $this->commands[$name];
+
+        if (is_string($command)) {
+            $command = new $command();
+        }
+
+        $command->setConsole($this);
+
+        if ($this->wantHelps) {
+            $this->wantHelps = false;
+
+            /** @var HelpCommand $helpCommand */
+            $helpCommand = $this->get('help');
+            $helpCommand->setCommand($command);
+
+            return $helpCommand;
+        }
+
+        return $command;
+    }
+
+    /**
+     * 某个指令是否存在
+     * @access public
+     * @param  string $name 指令名称
+     * @return bool
+     */
+    public function has($name)
+    {
+        return isset($this->commands[$name]);
+    }
+
+    /**
+     * 获取所有的命名空间
+     * @access public
+     * @return array
+     */
+    public function getNamespaces()
+    {
+        $namespaces = [];
+        foreach ($this->commands as $name => $command) {
+            if (is_string($command)) {
+                $namespaces = array_merge($namespaces, $this->extractAllNamespaces($name));
+            } else {
+                $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
+
+                foreach ($command->getAliases() as $alias) {
+                    $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
+                }
+            }
+
+        }
+
+        return array_values(array_unique(array_filter($namespaces)));
+    }
+
+    /**
+     * 查找注册命名空间中的名称或缩写。
+     * @access public
+     * @param  string $namespace
+     * @return string
+     * @throws \InvalidArgumentException
+     */
+    public function findNamespace($namespace)
+    {
+        $allNamespaces = $this->getNamespaces();
+        $expr          = preg_replace_callback('{([^:]+|)}', function ($matches) {
+            return preg_quote($matches[1]) . '[^:]*';
+        }, $namespace);
+        $namespaces = preg_grep('{^' . $expr . '}', $allNamespaces);
+
+        if (empty($namespaces)) {
+            $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
+
+            if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
+                if (1 == count($alternatives)) {
+                    $message .= "\n\nDid you mean this?\n    ";
+                } else {
+                    $message .= "\n\nDid you mean one of these?\n    ";
+                }
+
+                $message .= implode("\n    ", $alternatives);
+            }
+
+            throw new \InvalidArgumentException($message);
+        }
+
+        $exact = in_array($namespace, $namespaces, true);
+        if (count($namespaces) > 1 && !$exact) {
+            throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))));
+        }
+
+        return $exact ? $namespace : reset($namespaces);
+    }
+
+    /**
+     * 查找指令
+     * @access public
+     * @param  string $name 名称或者别名
+     * @return Command
+     * @throws \InvalidArgumentException
+     */
+    public function find($name)
+    {
+        $allCommands = array_keys($this->commands);
+
+        $expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
+            return preg_quote($matches[1]) . '[^:]*';
+        }, $name);
+
+        $commands = preg_grep('{^' . $expr . '}', $allCommands);
+
+        if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) {
+            if (false !== $pos = strrpos($name, ':')) {
+                $this->findNamespace(substr($name, 0, $pos));
+            }
+
+            $message = sprintf('Command "%s" is not defined.', $name);
+
+            if ($alternatives = $this->findAlternatives($name, $allCommands)) {
+                if (1 == count($alternatives)) {
+                    $message .= "\n\nDid you mean this?\n    ";
+                } else {
+                    $message .= "\n\nDid you mean one of these?\n    ";
+                }
+                $message .= implode("\n    ", $alternatives);
+            }
+
+            throw new \InvalidArgumentException($message);
+        }
+
+        $exact = in_array($name, $commands, true);
+        if (count($commands) > 1 && !$exact) {
+            $suggestions = $this->getAbbreviationSuggestions(array_values($commands));
+
+            throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions));
+        }
+
+        return $this->get($exact ? $name : reset($commands));
+    }
+
+    /**
+     * 获取所有的指令
+     * @access public
+     * @param  string $namespace 命名空间
+     * @return Command[]
+     * @api
+     */
+    public function all($namespace = null)
+    {
+        if (null === $namespace) {
+            return $this->commands;
+        }
+
+        $commands = [];
+        foreach ($this->commands as $name => $command) {
+            if ($this->extractNamespace($name, substr_count($namespace, ':') + 1) === $namespace) {
+                $commands[$name] = $command;
+            }
+        }
+
+        return $commands;
+    }
+
+    /**
+     * 获取可能的指令名
+     * @access public
+     * @param  array $names
+     * @return array
+     */
+    public static function getAbbreviations($names)
+    {
+        $abbrevs = [];
+        foreach ($names as $name) {
+            for ($len = strlen($name); $len > 0; --$len) {
+                $abbrev             = substr($name, 0, $len);
+                $abbrevs[$abbrev][] = $name;
+            }
+        }
+
+        return $abbrevs;
+    }
+
+    /**
+     * 配置基于用户的参数和选项的输入和输出实例。
+     * @access protected
+     * @param  Input  $input  输入实例
+     * @param  Output $output 输出实例
+     */
+    protected function configureIO(Input $input, Output $output)
+    {
+        if (true === $input->hasParameterOption(['--ansi'])) {
+            $output->setDecorated(true);
+        } elseif (true === $input->hasParameterOption(['--no-ansi'])) {
+            $output->setDecorated(false);
+        }
+
+        if (true === $input->hasParameterOption(['--no-interaction', '-n'])) {
+            $input->setInteractive(false);
+        }
+
+        if (true === $input->hasParameterOption(['--quiet', '-q'])) {
+            $output->setVerbosity(Output::VERBOSITY_QUIET);
+        } else {
+            if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) {
+                $output->setVerbosity(Output::VERBOSITY_DEBUG);
+            } elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) {
+                $output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE);
+            } elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) {
+                $output->setVerbosity(Output::VERBOSITY_VERBOSE);
+            }
+        }
+    }
+
+    /**
+     * 执行指令
+     * @access protected
+     * @param  Command $command 指令实例
+     * @param  Input   $input   输入实例
+     * @param  Output  $output  输出实例
+     * @return int
+     * @throws \Exception
+     */
+    protected function doRunCommand(Command $command, Input $input, Output $output)
+    {
+        return $command->run($input, $output);
+    }
+
+    /**
+     * 获取指令的基础名称
+     * @access protected
+     * @param  Input $input
+     * @return string
+     */
+    protected function getCommandName(Input $input)
+    {
+        return $input->getFirstArgument();
+    }
+
+    /**
+     * 获取默认输入定义
+     * @access protected
+     * @return InputDefinition
+     */
+    protected function getDefaultInputDefinition()
+    {
+        return new InputDefinition([
+            new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
+            new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'),
+            new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this console version'),
+            new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
+            new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
+            new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
+            new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
+            new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
+        ]);
+    }
+
+    public static function addDefaultCommands(array $classnames)
+    {
+        self::$defaultCommands = array_merge(self::$defaultCommands, $classnames);
+    }
+
+    /**
+     * 获取可能的建议
+     * @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) : '');
+    }
+
+    /**
+     * 返回命名空间部分
+     * @access public
+     * @param  string $name  指令
+     * @param  string $limit 部分的命名空间的最大数量
+     * @return string
+     */
+    public function extractNamespace($name, $limit = null)
+    {
+        $parts = explode(':', $name);
+        array_pop($parts);
+
+        return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit));
+    }
+
+    /**
+     * 查找可替代的建议
+     * @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);
+        }
+
+        foreach (explode(':', $name) as $i => $subname) {
+            foreach ($collectionParts as $collectionName => $parts) {
+                $exists = isset($alternatives[$collectionName]);
+                if (!isset($parts[$i]) && $exists) {
+                    $alternatives[$collectionName] += $threshold;
+                    continue;
+                } elseif (!isset($parts[$i])) {
+                    continue;
+                }
+
+                $lev = levenshtein($subname, $parts[$i]);
+                if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
+                    $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
+                } elseif ($exists) {
+                    $alternatives[$collectionName] += $threshold;
+                }
+            }
+        }
+
+        foreach ($collection as $item) {
+            $lev = levenshtein($name, $item);
+            if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
+                $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
+            }
+        }
+
+        $alternatives = array_filter($alternatives, function ($lev) use ($threshold) {
+            return $lev < 2 * $threshold;
+        });
+        asort($alternatives);
+
+        return array_keys($alternatives);
+    }
+
+    /**
+     * 设置默认的指令
+     * @access public
+     * @param  string $commandName The Command name
+     */
+    public function setDefaultCommand($commandName)
+    {
+        $this->defaultCommand = $commandName;
+    }
+
+    /**
+     * 返回所有的命名空间
+     * @access private
+     * @param  string $name
+     * @return array
+     */
+    private function extractAllNamespaces($name)
+    {
+        $parts      = explode(':', $name, -1);
+        $namespaces = [];
+
+        foreach ($parts as $part) {
+            if (count($namespaces)) {
+                $namespaces[] = end($namespaces) . ':' . $part;
+            } else {
+                $namespaces[] = $part;
+            }
+        }
+
+        return $namespaces;
+    }
+
+    public function __debugInfo()
+    {
+        $data = get_object_vars($this);
+        unset($data['commands'], $data['definition']);
+
+        return $data;
+    }
+}

+ 568 - 0
thinkphp/library/think/Container.php

@@ -0,0 +1,568 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use ArrayAccess;
+use ArrayIterator;
+use Closure;
+use Countable;
+use InvalidArgumentException;
+use IteratorAggregate;
+use ReflectionClass;
+use ReflectionException;
+use ReflectionFunction;
+use ReflectionMethod;
+use think\exception\ClassNotFoundException;
+
+/**
+ * @package think
+ * @property Build          $build
+ * @property Cache          $cache
+ * @property Config         $config
+ * @property Cookie         $cookie
+ * @property Debug          $debug
+ * @property Env            $env
+ * @property Hook           $hook
+ * @property Lang           $lang
+ * @property Middleware     $middleware
+ * @property Request        $request
+ * @property Response       $response
+ * @property Route          $route
+ * @property Session        $session
+ * @property Template       $template
+ * @property Url            $url
+ * @property Validate       $validate
+ * @property View           $view
+ * @property route\RuleName $rule_name
+ * @property Log            $log
+ */
+class Container implements ArrayAccess, IteratorAggregate, Countable
+{
+    /**
+     * 容器对象实例
+     * @var Container
+     */
+    protected static $instance;
+
+    /**
+     * 容器中的对象实例
+     * @var array
+     */
+    protected $instances = [];
+
+    /**
+     * 容器绑定标识
+     * @var array
+     */
+    protected $bind = [
+        'app'                   => App::class,
+        'build'                 => Build::class,
+        'cache'                 => Cache::class,
+        'config'                => Config::class,
+        'cookie'                => Cookie::class,
+        'debug'                 => Debug::class,
+        'env'                   => Env::class,
+        'hook'                  => Hook::class,
+        'lang'                  => Lang::class,
+        'log'                   => Log::class,
+        'middleware'            => Middleware::class,
+        'request'               => Request::class,
+        'response'              => Response::class,
+        'route'                 => Route::class,
+        'session'               => Session::class,
+        'template'              => Template::class,
+        'url'                   => Url::class,
+        'validate'              => Validate::class,
+        'view'                  => View::class,
+        'rule_name'             => route\RuleName::class,
+        // 接口依赖注入
+        'think\LoggerInterface' => Log::class,
+    ];
+
+    /**
+     * 容器标识别名
+     * @var array
+     */
+    protected $name = [];
+
+    /**
+     * 获取当前容器的实例(单例)
+     * @access public
+     * @return static
+     */
+    public static function getInstance()
+    {
+        if (is_null(static::$instance)) {
+            static::$instance = new static;
+        }
+
+        return static::$instance;
+    }
+
+    /**
+     * 设置当前容器的实例
+     * @access public
+     * @param  object        $instance
+     * @return void
+     */
+    public static function setInstance($instance)
+    {
+        static::$instance = $instance;
+    }
+
+    /**
+     * 获取容器中的对象实例
+     * @access public
+     * @param  string        $abstract       类名或者标识
+     * @param  array|true    $vars           变量
+     * @param  bool          $newInstance    是否每次创建新的实例
+     * @return object
+     */
+    public static function get($abstract, $vars = [], $newInstance = false)
+    {
+        return static::getInstance()->make($abstract, $vars, $newInstance);
+    }
+
+    /**
+     * 绑定一个类、闭包、实例、接口实现到容器
+     * @access public
+     * @param  string  $abstract    类标识、接口
+     * @param  mixed   $concrete    要绑定的类、闭包或者实例
+     * @return Container
+     */
+    public static function set($abstract, $concrete = null)
+    {
+        return static::getInstance()->bindTo($abstract, $concrete);
+    }
+
+    /**
+     * 移除容器中的对象实例
+     * @access public
+     * @param  string  $abstract    类标识、接口
+     * @return void
+     */
+    public static function remove($abstract)
+    {
+        return static::getInstance()->delete($abstract);
+    }
+
+    /**
+     * 清除容器中的对象实例
+     * @access public
+     * @return void
+     */
+    public static function clear()
+    {
+        return static::getInstance()->flush();
+    }
+
+    /**
+     * 绑定一个类、闭包、实例、接口实现到容器
+     * @access public
+     * @param  string|array  $abstract    类标识、接口
+     * @param  mixed         $concrete    要绑定的类、闭包或者实例
+     * @return $this
+     */
+    public function bindTo($abstract, $concrete = null)
+    {
+        if (is_array($abstract)) {
+            $this->bind = array_merge($this->bind, $abstract);
+        } elseif ($concrete instanceof Closure) {
+            $this->bind[$abstract] = $concrete;
+        } elseif (is_object($concrete)) {
+            if (isset($this->bind[$abstract])) {
+                $abstract = $this->bind[$abstract];
+            }
+            $this->instances[$abstract] = $concrete;
+        } else {
+            $this->bind[$abstract] = $concrete;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 绑定一个类实例当容器
+     * @access public
+     * @param  string           $abstract    类名或者标识
+     * @param  object|\Closure  $instance    类的实例
+     * @return $this
+     */
+    public function instance($abstract, $instance)
+    {
+        if ($instance instanceof \Closure) {
+            $this->bind[$abstract] = $instance;
+        } else {
+            if (isset($this->bind[$abstract])) {
+                $abstract = $this->bind[$abstract];
+            }
+
+            $this->instances[$abstract] = $instance;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 判断容器中是否存在类及标识
+     * @access public
+     * @param  string    $abstract    类名或者标识
+     * @return bool
+     */
+    public function bound($abstract)
+    {
+        return isset($this->bind[$abstract]) || isset($this->instances[$abstract]);
+    }
+
+    /**
+     * 判断容器中是否存在对象实例
+     * @access public
+     * @param  string    $abstract    类名或者标识
+     * @return bool
+     */
+    public function exists($abstract)
+    {
+        if (isset($this->bind[$abstract])) {
+            $abstract = $this->bind[$abstract];
+        }
+
+        return isset($this->instances[$abstract]);
+    }
+
+    /**
+     * 判断容器中是否存在类及标识
+     * @access public
+     * @param  string    $name    类名或者标识
+     * @return bool
+     */
+    public function has($name)
+    {
+        return $this->bound($name);
+    }
+
+    /**
+     * 创建类的实例
+     * @access public
+     * @param  string        $abstract       类名或者标识
+     * @param  array|true    $vars           变量
+     * @param  bool          $newInstance    是否每次创建新的实例
+     * @return object
+     */
+    public function make($abstract, $vars = [], $newInstance = false)
+    {
+        if (true === $vars) {
+            // 总是创建新的实例化对象
+            $newInstance = true;
+            $vars        = [];
+        }
+
+        $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract;
+
+        if (isset($this->instances[$abstract]) && !$newInstance) {
+            return $this->instances[$abstract];
+        }
+
+        if (isset($this->bind[$abstract])) {
+            $concrete = $this->bind[$abstract];
+
+            if ($concrete instanceof Closure) {
+                $object = $this->invokeFunction($concrete, $vars);
+            } else {
+                $this->name[$abstract] = $concrete;
+                return $this->make($concrete, $vars, $newInstance);
+            }
+        } else {
+            $object = $this->invokeClass($abstract, $vars);
+        }
+
+        if (!$newInstance) {
+            $this->instances[$abstract] = $object;
+        }
+
+        return $object;
+    }
+
+    /**
+     * 删除容器中的对象实例
+     * @access public
+     * @param  string|array    $abstract    类名或者标识
+     * @return void
+     */
+    public function delete($abstract)
+    {
+        foreach ((array) $abstract as $name) {
+            $name = isset($this->name[$name]) ? $this->name[$name] : $name;
+
+            if (isset($this->instances[$name])) {
+                unset($this->instances[$name]);
+            }
+        }
+    }
+
+    /**
+     * 获取容器中的对象实例
+     * @access public
+     * @return array
+     */
+    public function all()
+    {
+        return $this->instances;
+    }
+
+    /**
+     * 清除容器中的对象实例
+     * @access public
+     * @return void
+     */
+    public function flush()
+    {
+        $this->instances = [];
+        $this->bind      = [];
+        $this->name      = [];
+    }
+
+    /**
+     * 执行函数或者闭包方法 支持参数调用
+     * @access public
+     * @param  mixed  $function 函数或者闭包
+     * @param  array  $vars     参数
+     * @return mixed
+     */
+    public function invokeFunction($function, $vars = [])
+    {
+        try {
+            $reflect = new ReflectionFunction($function);
+
+            $args = $this->bindParams($reflect, $vars);
+
+            return call_user_func_array($function, $args);
+        } catch (ReflectionException $e) {
+            throw new Exception('function not exists: ' . $function . '()');
+        }
+    }
+
+    /**
+     * 调用反射执行类的方法 支持参数绑定
+     * @access public
+     * @param  mixed   $method 方法
+     * @param  array   $vars   参数
+     * @return mixed
+     */
+    public function invokeMethod($method, $vars = [])
+    {
+        try {
+            if (is_array($method)) {
+                $class   = is_object($method[0]) ? $method[0] : $this->invokeClass($method[0]);
+                $reflect = new ReflectionMethod($class, $method[1]);
+            } else {
+                // 静态方法
+                $reflect = new ReflectionMethod($method);
+            }
+
+            $args = $this->bindParams($reflect, $vars);
+
+            return $reflect->invokeArgs(isset($class) ? $class : null, $args);
+        } catch (ReflectionException $e) {
+            if (is_array($method) && is_object($method[0])) {
+                $method[0] = get_class($method[0]);
+            }
+
+            throw new Exception('method not exists: ' . (is_array($method) ? $method[0] . '::' . $method[1] : $method) . '()');
+        }
+    }
+
+    /**
+     * 调用反射执行类的方法 支持参数绑定
+     * @access public
+     * @param  object  $instance 对象实例
+     * @param  mixed   $reflect 反射类
+     * @param  array   $vars   参数
+     * @return mixed
+     */
+    public function invokeReflectMethod($instance, $reflect, $vars = [])
+    {
+        $args = $this->bindParams($reflect, $vars);
+
+        return $reflect->invokeArgs($instance, $args);
+    }
+
+    /**
+     * 调用反射执行callable 支持参数绑定
+     * @access public
+     * @param  mixed $callable
+     * @param  array $vars   参数
+     * @return mixed
+     */
+    public function invoke($callable, $vars = [])
+    {
+        if ($callable instanceof Closure) {
+            return $this->invokeFunction($callable, $vars);
+        }
+
+        return $this->invokeMethod($callable, $vars);
+    }
+
+    /**
+     * 调用反射执行类的实例化 支持依赖注入
+     * @access public
+     * @param  string    $class 类名
+     * @param  array     $vars  参数
+     * @return mixed
+     */
+    public function invokeClass($class, $vars = [])
+    {
+        try {
+            $reflect = new ReflectionClass($class);
+
+            if ($reflect->hasMethod('__make')) {
+                $method = new ReflectionMethod($class, '__make');
+
+                if ($method->isPublic() && $method->isStatic()) {
+                    $args = $this->bindParams($method, $vars);
+                    return $method->invokeArgs(null, $args);
+                }
+            }
+
+            $constructor = $reflect->getConstructor();
+
+            $args = $constructor ? $this->bindParams($constructor, $vars) : [];
+
+            return $reflect->newInstanceArgs($args);
+
+        } catch (ReflectionException $e) {
+            throw new ClassNotFoundException('class not exists: ' . $class, $class);
+        }
+    }
+
+    /**
+     * 绑定参数
+     * @access protected
+     * @param  \ReflectionMethod|\ReflectionFunction $reflect 反射类
+     * @param  array                                 $vars    参数
+     * @return array
+     */
+    protected function bindParams($reflect, $vars = [])
+    {
+        if ($reflect->getNumberOfParameters() == 0) {
+            return [];
+        }
+
+        // 判断数组类型 数字数组时按顺序绑定参数
+        reset($vars);
+        $type   = key($vars) === 0 ? 1 : 0;
+        $params = $reflect->getParameters();
+
+        foreach ($params as $param) {
+            $name      = $param->getName();
+            $lowerName = Loader::parseName($name);
+            $class     = $param->getClass();
+
+            if ($class) {
+                $args[] = $this->getObjectParam($class->getName(), $vars);
+            } elseif (1 == $type && !empty($vars)) {
+                $args[] = array_shift($vars);
+            } elseif (0 == $type && isset($vars[$name])) {
+                $args[] = $vars[$name];
+            } elseif (0 == $type && isset($vars[$lowerName])) {
+                $args[] = $vars[$lowerName];
+            } elseif ($param->isDefaultValueAvailable()) {
+                $args[] = $param->getDefaultValue();
+            } else {
+                throw new InvalidArgumentException('method param miss:' . $name);
+            }
+        }
+
+        return $args;
+    }
+
+    /**
+     * 获取对象类型的参数值
+     * @access protected
+     * @param  string   $className  类名
+     * @param  array    $vars       参数
+     * @return mixed
+     */
+    protected function getObjectParam($className, &$vars)
+    {
+        $array = $vars;
+        $value = array_shift($array);
+
+        if ($value instanceof $className) {
+            $result = $value;
+            array_shift($vars);
+        } else {
+            $result = $this->make($className);
+        }
+
+        return $result;
+    }
+
+    public function __set($name, $value)
+    {
+        $this->bindTo($name, $value);
+    }
+
+    public function __get($name)
+    {
+        return $this->make($name);
+    }
+
+    public function __isset($name)
+    {
+        return $this->bound($name);
+    }
+
+    public function __unset($name)
+    {
+        $this->delete($name);
+    }
+
+    public function offsetExists($key)
+    {
+        return $this->__isset($key);
+    }
+
+    public function offsetGet($key)
+    {
+        return $this->__get($key);
+    }
+
+    public function offsetSet($key, $value)
+    {
+        $this->__set($key, $value);
+    }
+
+    public function offsetUnset($key)
+    {
+        $this->__unset($key);
+    }
+
+    //Countable
+    public function count()
+    {
+        return count($this->instances);
+    }
+
+    //IteratorAggregate
+    public function getIterator()
+    {
+        return new ArrayIterator($this->instances);
+    }
+
+    public function __debugInfo()
+    {
+        $data = get_object_vars($this);
+        unset($data['instances'], $data['instance']);
+
+        return $data;
+    }
+}

+ 287 - 0
thinkphp/library/think/Controller.php

@@ -0,0 +1,287 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use think\exception\ValidateException;
+use traits\controller\Jump;
+
+class Controller
+{
+    use Jump;
+
+    /**
+     * 视图类实例
+     * @var \think\View
+     */
+    protected $view;
+
+    /**
+     * Request实例
+     * @var \think\Request
+     */
+    protected $request;
+
+    /**
+     * 验证失败是否抛出异常
+     * @var bool
+     */
+    protected $failException = false;
+
+    /**
+     * 是否批量验证
+     * @var bool
+     */
+    protected $batchValidate = false;
+
+    /**
+     * 前置操作方法列表(即将废弃)
+     * @var array $beforeActionList
+     */
+    protected $beforeActionList = [];
+
+    /**
+     * 控制器中间件
+     * @var array
+     */
+    protected $middleware = [];
+
+    /**
+     * 构造方法
+     * @access public
+     */
+    public function __construct(App $app = null)
+    {
+        $this->app     = $app ?: Container::get('app');
+        $this->request = $this->app['request'];
+        $this->view    = $this->app['view'];
+
+        // 控制器初始化
+        $this->initialize();
+
+        $this->registerMiddleware();
+
+        // 前置操作方法 即将废弃
+        foreach ((array) $this->beforeActionList as $method => $options) {
+            is_numeric($method) ?
+            $this->beforeAction($options) :
+            $this->beforeAction($method, $options);
+        }
+    }
+
+    // 初始化
+    protected function initialize()
+    {}
+
+    // 注册控制器中间件
+    public function registerMiddleware()
+    {
+        foreach ($this->middleware as $key => $val) {
+            if (!is_int($key)) {
+                $only = $except = null;
+
+                if (isset($val['only'])) {
+                    $only = array_map(function ($item) {
+                        return strtolower($item);
+                    }, $val['only']);
+                } elseif (isset($val['except'])) {
+                    $except = array_map(function ($item) {
+                        return strtolower($item);
+                    }, $val['except']);
+                }
+
+                if (isset($only) && !in_array($this->request->action(), $only)) {
+                    continue;
+                } elseif (isset($except) && in_array($this->request->action(), $except)) {
+                    continue;
+                } else {
+                    $val = $key;
+                }
+            }
+
+            $this->app['middleware']->controller($val);
+        }
+    }
+
+    /**
+     * 前置操作
+     * @access protected
+     * @param  string $method  前置操作方法名
+     * @param  array  $options 调用参数 ['only'=>[...]] 或者['except'=>[...]]
+     */
+    protected function beforeAction($method, $options = [])
+    {
+        if (isset($options['only'])) {
+            if (is_string($options['only'])) {
+                $options['only'] = explode(',', $options['only']);
+            }
+
+            $only = array_map(function ($val) {
+                return strtolower($val);
+            }, $options['only']);
+
+            if (!in_array($this->request->action(), $only)) {
+                return;
+            }
+        } elseif (isset($options['except'])) {
+            if (is_string($options['except'])) {
+                $options['except'] = explode(',', $options['except']);
+            }
+
+            $except = array_map(function ($val) {
+                return strtolower($val);
+            }, $options['except']);
+
+            if (in_array($this->request->action(), $except)) {
+                return;
+            }
+        }
+
+        call_user_func([$this, $method]);
+    }
+
+    /**
+     * 加载模板输出
+     * @access protected
+     * @param  string $template 模板文件名
+     * @param  array  $vars     模板输出变量
+     * @param  array  $config   模板参数
+     * @return mixed
+     */
+    protected function fetch($template = '', $vars = [], $config = [])
+    {
+        return Response::create($template, 'view')->assign($vars)->config($config);
+    }
+
+    /**
+     * 渲染内容输出
+     * @access protected
+     * @param  string $content 模板内容
+     * @param  array  $vars    模板输出变量
+     * @param  array  $config  模板参数
+     * @return mixed
+     */
+    protected function display($content = '', $vars = [], $config = [])
+    {
+        return Response::create($content, 'view')->assign($vars)->config($config)->isContent(true);
+    }
+
+    /**
+     * 模板变量赋值
+     * @access protected
+     * @param  mixed $name  要显示的模板变量
+     * @param  mixed $value 变量的值
+     * @return $this
+     */
+    protected function assign($name, $value = '')
+    {
+        $this->view->assign($name, $value);
+
+        return $this;
+    }
+
+    /**
+     * 视图过滤
+     * @access protected
+     * @param  Callable $filter 过滤方法或闭包
+     * @return $this
+     */
+    protected function filter($filter)
+    {
+        $this->view->filter($filter);
+
+        return $this;
+    }
+
+    /**
+     * 初始化模板引擎
+     * @access protected
+     * @param  array|string $engine 引擎参数
+     * @return $this
+     */
+    protected function engine($engine)
+    {
+        $this->view->engine($engine);
+
+        return $this;
+    }
+
+    /**
+     * 设置验证失败后是否抛出异常
+     * @access protected
+     * @param  bool $fail 是否抛出异常
+     * @return $this
+     */
+    protected function validateFailException($fail = true)
+    {
+        $this->failException = $fail;
+
+        return $this;
+    }
+
+    /**
+     * 验证数据
+     * @access protected
+     * @param  array        $data     数据
+     * @param  string|array $validate 验证器名或者验证规则数组
+     * @param  array        $message  提示信息
+     * @param  bool         $batch    是否批量验证
+     * @param  mixed        $callback 回调方法(闭包)
+     * @return array|string|true
+     * @throws ValidateException
+     */
+    protected function validate($data, $validate, $message = [], $batch = false, $callback = null)
+    {
+        if (is_array($validate)) {
+            $v = $this->app->validate();
+            $v->rule($validate);
+        } else {
+            if (strpos($validate, '.')) {
+                // 支持场景
+                list($validate, $scene) = explode('.', $validate);
+            }
+            $v = $this->app->validate($validate);
+            if (!empty($scene)) {
+                $v->scene($scene);
+            }
+        }
+
+        // 是否批量验证
+        if ($batch || $this->batchValidate) {
+            $v->batch(true);
+        }
+
+        if (is_array($message)) {
+            $v->message($message);
+        }
+
+        if ($callback && is_callable($callback)) {
+            call_user_func_array($callback, [$v, &$data]);
+        }
+
+        if (!$v->check($data)) {
+            if ($this->failException) {
+                throw new ValidateException($v->getError());
+            }
+            return $v->getError();
+        }
+
+        return true;
+    }
+
+    public function __debugInfo()
+    {
+        $data = get_object_vars($this);
+        unset($data['app'], $data['request']);
+
+        return $data;
+    }
+}

+ 268 - 0
thinkphp/library/think/Cookie.php

@@ -0,0 +1,268 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+class Cookie
+{
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $config = [
+        // cookie 名称前缀
+        'prefix'    => '',
+        // cookie 保存时间
+        'expire'    => 0,
+        // cookie 保存路径
+        'path'      => '/',
+        // cookie 有效域名
+        'domain'    => '',
+        //  cookie 启用安全传输
+        'secure'    => false,
+        // httponly设置
+        'httponly'  => false,
+        // 是否使用 setcookie
+        'setcookie' => true,
+    ];
+
+    /**
+     * 构造方法
+     * @access public
+     */
+    public function __construct(array $config = [])
+    {
+        $this->init($config);
+    }
+
+    /**
+     * Cookie初始化
+     * @access public
+     * @param  array $config
+     * @return void
+     */
+    public function init(array $config = [])
+    {
+        $this->config = array_merge($this->config, array_change_key_case($config));
+
+        if (!empty($this->config['httponly']) && PHP_SESSION_ACTIVE != session_status()) {
+            ini_set('session.cookie_httponly', 1);
+        }
+    }
+
+    public static function __make(Config $config)
+    {
+        return new static($config->pull('cookie'));
+    }
+
+    /**
+     * 设置或者获取cookie作用域(前缀)
+     * @access public
+     * @param  string $prefix
+     * @return string|void
+     */
+    public function prefix($prefix = '')
+    {
+        if (empty($prefix)) {
+            return $this->config['prefix'];
+        }
+
+        $this->config['prefix'] = $prefix;
+    }
+
+    /**
+     * Cookie 设置、获取、删除
+     *
+     * @access public
+     * @param  string $name  cookie名称
+     * @param  mixed  $value cookie值
+     * @param  mixed  $option 可选参数 可能会是 null|integer|string
+     * @return void
+     */
+    public function set($name, $value = '', $option = null)
+    {
+        // 参数设置(会覆盖黙认设置)
+        if (!is_null($option)) {
+            if (is_numeric($option)) {
+                $option = ['expire' => $option];
+            } elseif (is_string($option)) {
+                parse_str($option, $option);
+            }
+
+            $config = array_merge($this->config, array_change_key_case($option));
+        } else {
+            $config = $this->config;
+        }
+
+        $name = $config['prefix'] . $name;
+
+        // 设置cookie
+        if (is_array($value)) {
+            array_walk_recursive($value, [$this, 'jsonFormatProtect'], 'encode');
+            $value = 'think:' . json_encode($value);
+        }
+
+        $expire = !empty($config['expire']) ? $_SERVER['REQUEST_TIME'] + intval($config['expire']) : 0;
+
+        if ($config['setcookie']) {
+            $this->setCookie($name, $value, $expire, $config);
+        }
+
+        $_COOKIE[$name] = $value;
+    }
+
+    /**
+     * Cookie 设置保存
+     *
+     * @access public
+     * @param  string $name  cookie名称
+     * @param  mixed  $value cookie值
+     * @param  array  $option 可选参数
+     * @return void
+     */
+    protected function setCookie($name, $value, $expire, $option = [])
+    {
+        setcookie($name, $value, $expire, $option['path'], $option['domain'], $option['secure'], $option['httponly']);
+    }
+
+    /**
+     * 永久保存Cookie数据
+     * @access public
+     * @param  string $name  cookie名称
+     * @param  mixed  $value cookie值
+     * @param  mixed  $option 可选参数 可能会是 null|integer|string
+     * @return void
+     */
+    public function forever($name, $value = '', $option = null)
+    {
+        if (is_null($option) || is_numeric($option)) {
+            $option = [];
+        }
+
+        $option['expire'] = 315360000;
+
+        $this->set($name, $value, $option);
+    }
+
+    /**
+     * 判断Cookie数据
+     * @access public
+     * @param  string        $name cookie名称
+     * @param  string|null   $prefix cookie前缀
+     * @return bool
+     */
+    public function has($name, $prefix = null)
+    {
+        $prefix = !is_null($prefix) ? $prefix : $this->config['prefix'];
+        $name   = $prefix . $name;
+
+        return isset($_COOKIE[$name]);
+    }
+
+    /**
+     * Cookie获取
+     * @access public
+     * @param  string        $name cookie名称 留空获取全部
+     * @param  string|null   $prefix cookie前缀
+     * @return mixed
+     */
+    public function get($name = '', $prefix = null)
+    {
+        $prefix = !is_null($prefix) ? $prefix : $this->config['prefix'];
+        $key    = $prefix . $name;
+
+        if ('' == $name) {
+            if ($prefix) {
+                $value = [];
+                foreach ($_COOKIE as $k => $val) {
+                    if (0 === strpos($k, $prefix)) {
+                        $value[$k] = $val;
+                    }
+                }
+            } else {
+                $value = $_COOKIE;
+            }
+        } elseif (isset($_COOKIE[$key])) {
+            $value = $_COOKIE[$key];
+
+            if (0 === strpos($value, 'think:')) {
+                $value = substr($value, 6);
+                $value = json_decode($value, true);
+                array_walk_recursive($value, [$this, 'jsonFormatProtect'], 'decode');
+            }
+        } else {
+            $value = null;
+        }
+
+        return $value;
+    }
+
+    /**
+     * Cookie删除
+     * @access public
+     * @param  string        $name cookie名称
+     * @param  string|null   $prefix cookie前缀
+     * @return void
+     */
+    public function delete($name, $prefix = null)
+    {
+        $config = $this->config;
+        $prefix = !is_null($prefix) ? $prefix : $config['prefix'];
+        $name   = $prefix . $name;
+
+        if ($config['setcookie']) {
+            $this->setcookie($name, '', $_SERVER['REQUEST_TIME'] - 3600, $config);
+        }
+
+        // 删除指定cookie
+        unset($_COOKIE[$name]);
+    }
+
+    /**
+     * Cookie清空
+     * @access public
+     * @param  string|null $prefix cookie前缀
+     * @return void
+     */
+    public function clear($prefix = null)
+    {
+        // 清除指定前缀的所有cookie
+        if (empty($_COOKIE)) {
+            return;
+        }
+
+        // 要删除的cookie前缀,不指定则删除config设置的指定前缀
+        $config = $this->config;
+        $prefix = !is_null($prefix) ? $prefix : $config['prefix'];
+
+        if ($prefix) {
+            // 如果前缀为空字符串将不作处理直接返回
+            foreach ($_COOKIE as $key => $val) {
+                if (0 === strpos($key, $prefix)) {
+                    if ($config['setcookie']) {
+                        $this->setcookie($key, '', $_SERVER['REQUEST_TIME'] - 3600, $config);
+                    }
+                    unset($_COOKIE[$key]);
+                }
+            }
+        }
+
+        return;
+    }
+
+    private function jsonFormatProtect(&$val, $key, $type = 'encode')
+    {
+        if (!empty($val) && true !== $val) {
+            $val = 'decode' == $type ? urldecode($val) : urlencode($val);
+        }
+    }
+
+}

+ 197 - 0
thinkphp/library/think/Db.php

@@ -0,0 +1,197 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use think\db\Connection;
+
+/**
+ * Class Db
+ * @package think
+ * @method \think\db\Query master() static 从主服务器读取数据
+ * @method \think\db\Query readMaster(bool $all = false) static 后续从主服务器读取数据
+ * @method \think\db\Query table(string $table) static 指定数据表(含前缀)
+ * @method \think\db\Query name(string $name) static 指定数据表(不含前缀)
+ * @method \think\db\Expression raw(string $value) static 使用表达式设置数据
+ * @method \think\db\Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件
+ * @method \think\db\Query whereRaw(string $where, array $bind = []) static 表达式查询
+ * @method \think\db\Query whereExp(string $field, string $condition, array $bind = []) static 字段表达式查询
+ * @method \think\db\Query when(mixed $condition, mixed $query, mixed $otherwise = null) static 条件查询
+ * @method \think\db\Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询
+ * @method \think\db\Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询
+ * @method \think\db\Query field(mixed $field, boolean $except = false) static 指定查询字段
+ * @method \think\db\Query fieldRaw(string $field, array $bind = []) static 指定查询字段
+ * @method \think\db\Query union(mixed $union, boolean $all = false) static UNION查询
+ * @method \think\db\Query limit(mixed $offset, integer $length = null) static 查询LIMIT
+ * @method \think\db\Query order(mixed $field, string $order = null) static 查询ORDER
+ * @method \think\db\Query orderRaw(string $field, array $bind = []) static 查询ORDER
+ * @method \think\db\Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存
+ * @method \think\db\Query withAttr(string $name,callable $callback = null) static 使用获取器获取数据
+ * @method mixed value(string $field) static 获取某个字段的值
+ * @method array column(string $field, string $key = '') static 获取某个列的值
+ * @method mixed find(mixed $data = null) static 查询单个记录
+ * @method mixed select(mixed $data = null) static 查询多个记录
+ * @method integer insert(array $data, boolean $replace = false, boolean $getLastInsID = false, string $sequence = null) static 插入一条记录
+ * @method integer insertGetId(array $data, boolean $replace = false, string $sequence = null) static 插入一条记录并返回自增ID
+ * @method integer insertAll(array $dataSet) static 插入多条记录
+ * @method integer update(array $data) static 更新记录
+ * @method integer delete(mixed $data = null) static 删除记录
+ * @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据
+ * @method \Generator cursor(mixed $data = null) static 使用游标查找记录
+ * @method mixed query(string $sql, array $bind = [], boolean $master = false, bool $pdo = false) static SQL查询
+ * @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行
+ * @method \think\Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询
+ * @method mixed transaction(callable $callback) static 执行数据库事务
+ * @method void startTrans() static 启动事务
+ * @method void commit() static 用于非自动提交状态下面的查询提交
+ * @method void rollback() static 事务回滚
+ * @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句
+ * @method string getLastInsID(string $sequence = null) static 获取最近插入的ID
+ */
+class Db
+{
+    /**
+     * 当前数据库连接对象
+     * @var Connection
+     */
+    protected static $connection;
+
+    /**
+     * 数据库配置
+     * @var array
+     */
+    protected static $config = [];
+
+    /**
+     * 查询次数
+     * @var integer
+     */
+    public static $queryTimes = 0;
+
+    /**
+     * 执行次数
+     * @var integer
+     */
+    public static $executeTimes = 0;
+
+    /**
+     * 配置
+     * @access public
+     * @param  mixed $config
+     * @return void
+     */
+    public static function init($config = [])
+    {
+        self::$config = $config;
+
+        if (empty($config['query'])) {
+            self::$config['query'] = '\\think\\db\\Query';
+        }
+    }
+
+    /**
+     * 获取数据库配置
+     * @access public
+     * @param  string $config 配置名称
+     * @return mixed
+     */
+    public static function getConfig($name = '')
+    {
+        if ('' === $name) {
+            return self::$config;
+        }
+
+        return isset(self::$config[$name]) ? self::$config[$name] : null;
+    }
+
+    /**
+     * 切换数据库连接
+     * @access public
+     * @param  mixed         $config 连接配置
+     * @param  bool|string   $name 连接标识 true 强制重新连接
+     * @param  string        $query 查询对象类名
+     * @return mixed 返回查询对象实例
+     * @throws Exception
+     */
+    public static function connect($config = [], $name = false, $query = '')
+    {
+        // 解析配置参数
+        $options = self::parseConfig($config ?: self::$config);
+
+        $query = $query ?: $options['query'];
+
+        // 创建数据库连接对象实例
+        self::$connection = Connection::instance($options, $name);
+
+        return new $query(self::$connection);
+    }
+
+    /**
+     * 数据库连接参数解析
+     * @access private
+     * @param  mixed $config
+     * @return array
+     */
+    private static function parseConfig($config)
+    {
+        if (is_string($config) && false === strpos($config, '/')) {
+            // 支持读取配置参数
+            $config = isset(self::$config[$config]) ? self::$config[$config] : self::$config;
+        }
+
+        $result = is_string($config) ? self::parseDsnConfig($config) : $config;
+
+        if (empty($result['query'])) {
+            $result['query'] = self::$config['query'];
+        }
+
+        return $result;
+    }
+
+    /**
+     * DSN解析
+     * 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1&param2=val2#utf8
+     * @access private
+     * @param  string $dsnStr
+     * @return array
+     */
+    private static function parseDsnConfig($dsnStr)
+    {
+        $info = parse_url($dsnStr);
+
+        if (!$info) {
+            return [];
+        }
+
+        $dsn = [
+            'type'     => $info['scheme'],
+            'username' => isset($info['user']) ? $info['user'] : '',
+            'password' => isset($info['pass']) ? $info['pass'] : '',
+            'hostname' => isset($info['host']) ? $info['host'] : '',
+            'hostport' => isset($info['port']) ? $info['port'] : '',
+            'database' => !empty($info['path']) ? ltrim($info['path'], '/') : '',
+            'charset'  => isset($info['fragment']) ? $info['fragment'] : 'utf8',
+        ];
+
+        if (isset($info['query'])) {
+            parse_str($info['query'], $dsn['params']);
+        } else {
+            $dsn['params'] = [];
+        }
+
+        return $dsn;
+    }
+
+    public static function __callStatic($method, $args)
+    {
+        return call_user_func_array([static::connect(), $method], $args);
+    }
+}

+ 278 - 0
thinkphp/library/think/Debug.php

@@ -0,0 +1,278 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use think\model\Collection as ModelCollection;
+use think\response\Redirect;
+
+class Debug
+{
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $config = [];
+
+    /**
+     * 区间时间信息
+     * @var array
+     */
+    protected $info = [];
+
+    /**
+     * 区间内存信息
+     * @var array
+     */
+    protected $mem = [];
+
+    /**
+     * 应用对象
+     * @var App
+     */
+    protected $app;
+
+    public function __construct(App $app, array $config = [])
+    {
+        $this->app    = $app;
+        $this->config = $config;
+    }
+
+    public static function __make(App $app, Config $config)
+    {
+        return new static($app, $config->pull('trace'));
+    }
+
+    public function setConfig(array $config)
+    {
+        $this->config = array_merge($this->config, $config);
+    }
+
+    /**
+     * 记录时间(微秒)和内存使用情况
+     * @access public
+     * @param  string    $name 标记位置
+     * @param  mixed     $value 标记值 留空则取当前 time 表示仅记录时间 否则同时记录时间和内存
+     * @return void
+     */
+    public function remark($name, $value = '')
+    {
+        // 记录时间和内存使用
+        $this->info[$name] = is_float($value) ? $value : microtime(true);
+
+        if ('time' != $value) {
+            $this->mem['mem'][$name]  = is_float($value) ? $value : memory_get_usage();
+            $this->mem['peak'][$name] = memory_get_peak_usage();
+        }
+    }
+
+    /**
+     * 统计某个区间的时间(微秒)使用情况
+     * @access public
+     * @param  string            $start 开始标签
+     * @param  string            $end 结束标签
+     * @param  integer|string    $dec 小数位
+     * @return integer
+     */
+    public function getRangeTime($start, $end, $dec = 6)
+    {
+        if (!isset($this->info[$end])) {
+            $this->info[$end] = microtime(true);
+        }
+
+        return number_format(($this->info[$end] - $this->info[$start]), $dec);
+    }
+
+    /**
+     * 统计从开始到统计时的时间(微秒)使用情况
+     * @access public
+     * @param  integer|string $dec 小数位
+     * @return integer
+     */
+    public function getUseTime($dec = 6)
+    {
+        return number_format((microtime(true) - $this->app->getBeginTime()), $dec);
+    }
+
+    /**
+     * 获取当前访问的吞吐率情况
+     * @access public
+     * @return string
+     */
+    public function getThroughputRate()
+    {
+        return number_format(1 / $this->getUseTime(), 2) . 'req/s';
+    }
+
+    /**
+     * 记录区间的内存使用情况
+     * @access public
+     * @param  string            $start 开始标签
+     * @param  string            $end 结束标签
+     * @param  integer|string    $dec 小数位
+     * @return string
+     */
+    public function getRangeMem($start, $end, $dec = 2)
+    {
+        if (!isset($this->mem['mem'][$end])) {
+            $this->mem['mem'][$end] = memory_get_usage();
+        }
+
+        $size = $this->mem['mem'][$end] - $this->mem['mem'][$start];
+        $a    = ['B', 'KB', 'MB', 'GB', 'TB'];
+        $pos  = 0;
+
+        while ($size >= 1024) {
+            $size /= 1024;
+            $pos++;
+        }
+
+        return round($size, $dec) . " " . $a[$pos];
+    }
+
+    /**
+     * 统计从开始到统计时的内存使用情况
+     * @access public
+     * @param  integer|string $dec 小数位
+     * @return string
+     */
+    public function getUseMem($dec = 2)
+    {
+        $size = memory_get_usage() - $this->app->getBeginMem();
+        $a    = ['B', 'KB', 'MB', 'GB', 'TB'];
+        $pos  = 0;
+
+        while ($size >= 1024) {
+            $size /= 1024;
+            $pos++;
+        }
+
+        return round($size, $dec) . " " . $a[$pos];
+    }
+
+    /**
+     * 统计区间的内存峰值情况
+     * @access public
+     * @param  string            $start 开始标签
+     * @param  string            $end 结束标签
+     * @param  integer|string    $dec 小数位
+     * @return string
+     */
+    public function getMemPeak($start, $end, $dec = 2)
+    {
+        if (!isset($this->mem['peak'][$end])) {
+            $this->mem['peak'][$end] = memory_get_peak_usage();
+        }
+
+        $size = $this->mem['peak'][$end] - $this->mem['peak'][$start];
+        $a    = ['B', 'KB', 'MB', 'GB', 'TB'];
+        $pos  = 0;
+
+        while ($size >= 1024) {
+            $size /= 1024;
+            $pos++;
+        }
+
+        return round($size, $dec) . " " . $a[$pos];
+    }
+
+    /**
+     * 获取文件加载信息
+     * @access public
+     * @param  bool  $detail 是否显示详细
+     * @return integer|array
+     */
+    public function getFile($detail = false)
+    {
+        if ($detail) {
+            $files = get_included_files();
+            $info  = [];
+
+            foreach ($files as $key => $file) {
+                $info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )';
+            }
+
+            return $info;
+        }
+
+        return count(get_included_files());
+    }
+
+    /**
+     * 浏览器友好的变量输出
+     * @access public
+     * @param  mixed         $var 变量
+     * @param  boolean       $echo 是否输出 默认为true 如果为false 则返回输出字符串
+     * @param  string        $label 标签 默认为空
+     * @param  integer       $flags htmlspecialchars flags
+     * @return void|string
+     */
+    public function dump($var, $echo = true, $label = null, $flags = ENT_SUBSTITUTE)
+    {
+        $label = (null === $label) ? '' : rtrim($label) . ':';
+        if ($var instanceof Model || $var instanceof ModelCollection) {
+            $var = $var->toArray();
+        }
+
+        ob_start();
+        var_dump($var);
+
+        $output = ob_get_clean();
+        $output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output);
+
+        if (PHP_SAPI == '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;
+        }
+        return $output;
+    }
+
+    public function inject(Response $response, &$content)
+    {
+        $config = $this->config;
+        $type   = isset($config['type']) ? $config['type'] : 'Html';
+
+        unset($config['type']);
+
+        $trace = Loader::factory($type, '\\think\\debug\\', $config);
+
+        if ($response instanceof Redirect) {
+            //TODO 记录
+        } else {
+            $output = $trace->output($response, $this->app['log']->getLog());
+            if (is_string($output)) {
+                // trace调试信息注入
+                $pos = strripos($content, '</body>');
+                if (false !== $pos) {
+                    $content = substr($content, 0, $pos) . $output . substr($content, $pos);
+                } else {
+                    $content = $content . $output;
+                }
+            }
+        }
+    }
+
+    public function __debugInfo()
+    {
+        $data = get_object_vars($this);
+        unset($data['app']);
+
+        return $data;
+    }
+}

+ 113 - 0
thinkphp/library/think/Env.php

@@ -0,0 +1,113 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+class Env
+{
+    /**
+     * 环境变量数据
+     * @var array
+     */
+    protected $data = [];
+
+    public function __construct()
+    {
+        $this->data = $_ENV;
+    }
+
+    /**
+     * 读取环境变量定义文件
+     * @access public
+     * @param  string    $file  环境变量定义文件
+     * @return void
+     */
+    public function load($file)
+    {
+        $env = parse_ini_file($file, true);
+        $this->set($env);
+    }
+
+    /**
+     * 获取环境变量值
+     * @access public
+     * @param  string    $name 环境变量名
+     * @param  mixed     $default  默认值
+     * @return mixed
+     */
+    public function get($name = null, $default = null, $php_prefix = true)
+    {
+        if (is_null($name)) {
+            return $this->data;
+        }
+
+        $name = strtoupper(str_replace('.', '_', $name));
+
+        if (isset($this->data[$name])) {
+            return $this->data[$name];
+        }
+
+        return $this->getEnv($name, $default, $php_prefix);
+    }
+
+    protected function getEnv($name, $default = null, $php_prefix = true)
+    {
+        if ($php_prefix) {
+            $name = 'PHP_' . $name;
+        }
+
+        $result = getenv($name);
+
+        if (false === $result) {
+            return $default;
+        }
+
+        if ('false' === $result) {
+            $result = false;
+        } elseif ('true' === $result) {
+            $result = true;
+        }
+
+        if (!isset($this->data[$name])) {
+            $this->data[$name] = $result;
+        }
+
+        return $result;
+    }
+
+    /**
+     * 设置环境变量值
+     * @access public
+     * @param  string|array  $env   环境变量
+     * @param  mixed         $value  值
+     * @return void
+     */
+    public function set($env, $value = null)
+    {
+        if (is_array($env)) {
+            $env = array_change_key_case($env, CASE_UPPER);
+
+            foreach ($env as $key => $val) {
+                if (is_array($val)) {
+                    foreach ($val as $k => $v) {
+                        $this->data[$key . '_' . strtoupper($k)] = $v;
+                    }
+                } else {
+                    $this->data[$key] = $val;
+                }
+            }
+        } else {
+            $name = strtoupper(str_replace('.', '_', $env));
+
+            $this->data[$name] = $value;
+        }
+    }
+}

+ 147 - 0
thinkphp/library/think/Error.php

@@ -0,0 +1,147 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use think\console\Output as ConsoleOutput;
+use think\exception\ErrorException;
+use think\exception\Handle;
+use think\exception\ThrowableError;
+
+class Error
+{
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected static $exceptionHandler;
+
+    /**
+     * 注册异常处理
+     * @access public
+     * @return void
+     */
+    public static function register()
+    {
+        error_reporting(E_ALL);
+        set_error_handler([__CLASS__, 'appError']);
+        set_exception_handler([__CLASS__, 'appException']);
+        register_shutdown_function([__CLASS__, 'appShutdown']);
+    }
+
+    /**
+     * Exception Handler
+     * @access public
+     * @param  \Exception|\Throwable $e
+     */
+    public static function appException($e)
+    {
+        if (!$e instanceof \Exception) {
+            $e = new ThrowableError($e);
+        }
+
+        self::getExceptionHandler()->report($e);
+
+        if (PHP_SAPI == 'cli') {
+            self::getExceptionHandler()->renderForConsole(new ConsoleOutput, $e);
+        } else {
+            self::getExceptionHandler()->render($e)->send();
+        }
+    }
+
+    /**
+     * Error Handler
+     * @access public
+     * @param  integer $errno   错误编号
+     * @param  integer $errstr  详细错误信息
+     * @param  string  $errfile 出错的文件
+     * @param  integer $errline 出错行号
+     * @throws ErrorException
+     */
+    public static function appError($errno, $errstr, $errfile = '', $errline = 0)
+    {
+        $exception = new ErrorException($errno, $errstr, $errfile, $errline);
+        if (error_reporting() & $errno) {
+            // 将错误信息托管至 think\exception\ErrorException
+            throw $exception;
+        }
+
+        self::getExceptionHandler()->report($exception);
+    }
+
+    /**
+     * Shutdown Handler
+     * @access public
+     */
+    public static function appShutdown()
+    {
+        if (!is_null($error = error_get_last()) && self::isFatal($error['type'])) {
+            // 将错误信息托管至think\ErrorException
+            $exception = new ErrorException($error['type'], $error['message'], $error['file'], $error['line']);
+
+            self::appException($exception);
+        }
+
+        // 写入日志
+        Container::get('log')->save();
+    }
+
+    /**
+     * 确定错误类型是否致命
+     *
+     * @access protected
+     * @param  int $type
+     * @return bool
+     */
+    protected static function isFatal($type)
+    {
+        return in_array($type, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]);
+    }
+
+    /**
+     * 设置异常处理类
+     *
+     * @access public
+     * @param  mixed $handle
+     * @return void
+     */
+    public static function setExceptionHandler($handle)
+    {
+        self::$exceptionHandler = $handle;
+    }
+
+    /**
+     * Get an instance of the exception handler.
+     *
+     * @access public
+     * @return Handle
+     */
+    public static function getExceptionHandler()
+    {
+        static $handle;
+
+        if (!$handle) {
+            // 异常处理handle
+            $class = self::$exceptionHandler;
+
+            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;
+    }
+}

+ 56 - 0
thinkphp/library/think/Exception.php

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

+ 125 - 0
thinkphp/library/think/Facade.php

@@ -0,0 +1,125 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+class Facade
+{
+    /**
+     * 绑定对象
+     * @var array
+     */
+    protected static $bind = [];
+
+    /**
+     * 始终创建新的对象实例
+     * @var bool
+     */
+    protected static $alwaysNewInstance;
+
+    /**
+     * 绑定类的静态代理
+     * @static
+     * @access public
+     * @param  string|array  $name    类标识
+     * @param  string        $class   类名
+     * @return object
+     */
+    public static function bind($name, $class = null)
+    {
+        if (__CLASS__ != static::class) {
+            return self::__callStatic('bind', func_get_args());
+        }
+
+        if (is_array($name)) {
+            self::$bind = array_merge(self::$bind, $name);
+        } else {
+            self::$bind[$name] = $class;
+        }
+    }
+
+    /**
+     * 创建Facade实例
+     * @static
+     * @access protected
+     * @param  string    $class          类名或标识
+     * @param  array     $args           变量
+     * @param  bool      $newInstance    是否每次创建新的实例
+     * @return object
+     */
+    protected static function createFacade($class = '', $args = [], $newInstance = false)
+    {
+        $class = $class ?: static::class;
+
+        $facadeClass = static::getFacadeClass();
+
+        if ($facadeClass) {
+            $class = $facadeClass;
+        } elseif (isset(self::$bind[$class])) {
+            $class = self::$bind[$class];
+        }
+
+        if (static::$alwaysNewInstance) {
+            $newInstance = true;
+        }
+
+        return Container::getInstance()->make($class, $args, $newInstance);
+    }
+
+    /**
+     * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+     * @access protected
+     * @return string
+     */
+    protected static function getFacadeClass()
+    {}
+
+    /**
+     * 带参数实例化当前Facade类
+     * @access public
+     * @return mixed
+     */
+    public static function instance(...$args)
+    {
+        if (__CLASS__ != static::class) {
+            return self::createFacade('', $args);
+        }
+    }
+
+    /**
+     * 调用类的实例
+     * @access public
+     * @param  string        $class          类名或者标识
+     * @param  array|true    $args           变量
+     * @param  bool          $newInstance    是否每次创建新的实例
+     * @return mixed
+     */
+    public static function make($class, $args = [], $newInstance = false)
+    {
+        if (__CLASS__ != static::class) {
+            return self::__callStatic('make', func_get_args());
+        }
+
+        if (true === $args) {
+            // 总是创建新的实例化对象
+            $newInstance = true;
+            $args        = [];
+        }
+
+        return self::createFacade($class, $args, $newInstance);
+    }
+
+    // 调用实际类的方法
+    public static function __callStatic($method, $params)
+    {
+        return call_user_func_array([static::createFacade(), $method], $params);
+    }
+}

+ 496 - 0
thinkphp/library/think/File.php

@@ -0,0 +1,496 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use SplFileObject;
+
+class File extends SplFileObject
+{
+    /**
+     * 错误信息
+     * @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
+     */
+    protected $hash = [];
+
+    public function __construct($filename, $mode = 'r')
+    {
+        parent::__construct($filename, $mode);
+
+        $this->filename = $this->getRealPath() ?: $this->getPathname();
+    }
+
+    /**
+     * 是否测试
+     * @access public
+     * @param  bool   $test 是否测试
+     * @return $this
+     */
+    public function isTest($test = false)
+    {
+        $this->isTest = $test;
+
+        return $this;
+    }
+
+    /**
+     * 设置上传信息
+     * @access public
+     * @param  array   $info 上传文件信息
+     * @return $this
+     */
+    public function setUploadInfo($info)
+    {
+        $this->info = $info;
+
+        return $this;
+    }
+
+    /**
+     * 获取上传文件的信息
+     * @access public
+     * @param  string   $name
+     * @return array|string
+     */
+    public function getInfo($name = '')
+    {
+        return isset($this->info[$name]) ? $this->info[$name] : $this->info;
+    }
+
+    /**
+     * 获取上传文件的文件名
+     * @access public
+     * @return string
+     */
+    public function getSaveName()
+    {
+        return $this->saveName;
+    }
+
+    /**
+     * 设置上传文件的保存文件名
+     * @access public
+     * @param  string   $saveName
+     * @return $this
+     */
+    public function setSaveName($saveName)
+    {
+        $this->saveName = $saveName;
+
+        return $this;
+    }
+
+    /**
+     * 获取文件的哈希散列值
+     * @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];
+    }
+
+    /**
+     * 检查目录是否可写
+     * @access protected
+     * @param  string   $path    目录
+     * @return boolean
+     */
+    protected function checkPath($path)
+    {
+        if (is_dir($path)) {
+            return true;
+        }
+
+        if (mkdir($path, 0755, true)) {
+            return true;
+        }
+
+        $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);
+    }
+
+    /**
+     * 设置文件的命名规则
+     * @access public
+     * @param  string   $rule    文件命名规则
+     * @return $this
+     */
+    public function rule($rule)
+    {
+        $this->rule = $rule;
+
+        return $this;
+    }
+
+    /**
+     * 设置上传文件的验证规则
+     * @access public
+     * @param  array   $rule    验证规则
+     * @return $this
+     */
+    public function validate($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);
+    }
+
+    /**
+     * 检测上传文件
+     * @access public
+     * @param  array   $rule    验证规则
+     * @return bool
+     */
+    public function check($rule = [])
+    {
+        $rule = $rule ?: $this->validate;
+
+        if ((isset($rule['size']) && !$this->checkSize($rule['size']))
+            || (isset($rule['type']) && !$this->checkMime($rule['type']))
+            || (isset($rule['ext']) && !$this->checkExt($rule['ext']))
+            || !$this->checkImg()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * 检测上传文件后缀
+     * @access public
+     * @param  array|string   $ext    允许后缀
+     * @return bool
+     */
+    public function checkExt($ext)
+    {
+        if (is_string($ext)) {
+            $ext = explode(',', $ext);
+        }
+
+        $extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
+
+        if (!in_array($extension, $ext)) {
+            $this->error = 'extensions to upload is not allowed';
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * 检测图像文件
+     * @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])) {
+            $this->error = 'illegal image files';
+            return false;
+        }
+
+        return true;
+    }
+
+    // 判断图像类型
+    protected function getImageType($image)
+    {
+        if (function_exists('exif_imagetype')) {
+            return exif_imagetype($image);
+        }
+
+        try {
+            $info = getimagesize($image);
+            return $info ? $info[2] : false;
+        } catch (\Exception $e) {
+            return false;
+        }
+    }
+
+    /**
+     * 检测上传文件大小
+     * @access public
+     * @param  integer   $size    最大大小
+     * @return bool
+     */
+    public function checkSize($size)
+    {
+        if ($this->getSize() > (int) $size) {
+            $this->error = 'filesize not match';
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * 检测上传文件类型
+     * @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)) {
+            $this->error = 'mimetype to upload is not allowed';
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * 移动文件
+     * @access public
+     * @param  string           $path    保存路径
+     * @param  string|bool      $savename    保存的文件名 默认自动生成
+     * @param  boolean          $replace 同名文件是否覆盖
+     * @param  bool             $autoAppendExt     自动补充扩展名
+     * @return false|File       false-失败 否则返回File实例
+     */
+    public function move($path, $savename = true, $replace = true, $autoAppendExt = true)
+    {
+        // 文件上传失败,捕获错误代码
+        if (!empty($this->info['error'])) {
+            $this->error($this->info['error']);
+            return false;
+        }
+
+        // 检测合法性
+        if (!$this->isValid()) {
+            $this->error = 'upload illegal files';
+            return false;
+        }
+
+        // 验证上传
+        if (!$this->check()) {
+            return false;
+        }
+
+        $path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
+        // 文件保存命名规则
+        $saveName = $this->buildSaveName($savename, $autoAppendExt);
+        $filename = $path . $saveName;
+
+        // 检测目录
+        if (false === $this->checkPath(dirname($filename))) {
+            return false;
+        }
+
+        /* 不覆盖同名文件 */
+        if (!$replace && is_file($filename)) {
+            $this->error = ['has the same filename: {:filename}', ['filename' => $filename]];
+            return false;
+        }
+
+        /* 移动文件 */
+        if ($this->isTest) {
+            rename($this->filename, $filename);
+        } elseif (!move_uploaded_file($this->filename, $filename)) {
+            $this->error = 'upload write error';
+            return false;
+        }
+
+        // 返回 File对象实例
+        $file = new self($filename);
+        $file->setSaveName($saveName);
+        $file->setUploadInfo($this->info);
+
+        return $file;
+    }
+
+    /**
+     * 获取保存文件名
+     * @access protected
+     * @param  string|bool   $savename    保存的文件名 默认自动生成
+     * @param  bool          $autoAppendExt     自动补充扩展名
+     * @return string
+     */
+    protected function buildSaveName($savename, $autoAppendExt = true)
+    {
+        if (true === $savename) {
+            // 自动生成文件名
+            $savename = $this->autoBuildName();
+        } elseif ('' === $savename || false === $savename) {
+            // 保留原文件名
+            $savename = $this->getInfo('name');
+        }
+
+        if ($autoAppendExt && false === strpos($savename, '.')) {
+            $savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION);
+        }
+
+        return $savename;
+    }
+
+    /**
+     * 自动生成文件名
+     * @access protected
+     * @return string
+     */
+    protected function autoBuildName()
+    {
+        if ($this->rule instanceof \Closure) {
+            $savename = call_user_func_array($this->rule, [$this]);
+        } else {
+            switch ($this->rule) {
+                case 'date':
+                    $savename = date('Ymd') . DIRECTORY_SEPARATOR . md5(microtime(true));
+                    break;
+                default:
+                    if (in_array($this->rule, hash_algos())) {
+                        $hash     = $this->hash($this->rule);
+                        $savename = substr($hash, 0, 2) . DIRECTORY_SEPARATOR . substr($hash, 2);
+                    } elseif (is_callable($this->rule)) {
+                        $savename = call_user_func($this->rule);
+                    } else {
+                        $savename = date('Ymd') . DIRECTORY_SEPARATOR . md5(microtime(true));
+                    }
+            }
+        }
+
+        return $savename;
+    }
+
+    /**
+     * 获取错误代码信息
+     * @access private
+     * @param  int $errorNo  错误号
+     */
+    private function error($errorNo)
+    {
+        switch ($errorNo) {
+            case 1:
+            case 2:
+                $this->error = 'upload File size exceeds the maximum value';
+                break;
+            case 3:
+                $this->error = 'only the portion of file is uploaded';
+                break;
+            case 4:
+                $this->error = 'no file to uploaded';
+                break;
+            case 6:
+                $this->error = 'upload temp dir not found';
+                break;
+            case 7:
+                $this->error = 'file write error';
+                break;
+            default:
+                $this->error = 'unknown upload error';
+        }
+    }
+
+    /**
+     * 获取错误信息(支持多语言)
+     * @access public
+     * @return string
+     */
+    public function getError()
+    {
+        $lang = Container::get('lang');
+
+        if (is_array($this->error)) {
+            list($msg, $vars) = $this->error;
+        } else {
+            $msg  = $this->error;
+            $vars = [];
+        }
+
+        return $lang->has($msg) ? $lang->get($msg, $vars) : $msg;
+    }
+
+    public function __call($method, $args)
+    {
+        return $this->hash($method);
+    }
+}

+ 220 - 0
thinkphp/library/think/Hook.php

@@ -0,0 +1,220 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+class Hook
+{
+    /**
+     * 钩子行为定义
+     * @var array
+     */
+    private $tags = [];
+
+    /**
+     * 绑定行为列表
+     * @var array
+     */
+    protected $bind = [];
+
+    /**
+     * 入口方法名称
+     * @var string
+     */
+    private static $portal = 'run';
+
+    /**
+     * 应用对象
+     * @var App
+     */
+    protected $app;
+
+    public function __construct(App $app)
+    {
+        $this->app = $app;
+    }
+
+    /**
+     * 指定入口方法名称
+     * @access public
+     * @param  string  $name     方法名
+     * @return $this
+     */
+    public function portal($name)
+    {
+        self::$portal = $name;
+        return $this;
+    }
+
+    /**
+     * 指定行为标识 便于调用
+     * @access public
+     * @param  string|array  $name     行为标识
+     * @param  mixed         $behavior 行为
+     * @return $this
+     */
+    public function alias($name, $behavior = null)
+    {
+        if (is_array($name)) {
+            $this->bind = array_merge($this->bind, $name);
+        } else {
+            $this->bind[$name] = $behavior;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 动态添加行为扩展到某个标签
+     * @access public
+     * @param  string    $tag 标签名称
+     * @param  mixed     $behavior 行为名称
+     * @param  bool      $first 是否放到开头执行
+     * @return void
+     */
+    public function add($tag, $behavior, $first = false)
+    {
+        isset($this->tags[$tag]) || $this->tags[$tag] = [];
+
+        if (is_array($behavior) && !is_callable($behavior)) {
+            if (!array_key_exists('_overlay', $behavior)) {
+                $this->tags[$tag] = array_merge($this->tags[$tag], $behavior);
+            } else {
+                unset($behavior['_overlay']);
+                $this->tags[$tag] = $behavior;
+            }
+        } elseif ($first) {
+            array_unshift($this->tags[$tag], $behavior);
+        } else {
+            $this->tags[$tag][] = $behavior;
+        }
+    }
+
+    /**
+     * 批量导入插件
+     * @access public
+     * @param  array     $tags 插件信息
+     * @param  bool      $recursive 是否递归合并
+     * @return void
+     */
+    public function import(array $tags, $recursive = true)
+    {
+        if ($recursive) {
+            foreach ($tags as $tag => $behavior) {
+                $this->add($tag, $behavior);
+            }
+        } else {
+            $this->tags = $tags + $this->tags;
+        }
+    }
+
+    /**
+     * 获取插件信息
+     * @access public
+     * @param  string $tag 插件位置 留空获取全部
+     * @return array
+     */
+    public function get($tag = '')
+    {
+        if (empty($tag)) {
+            //获取全部的插件信息
+            return $this->tags;
+        }
+
+        return array_key_exists($tag, $this->tags) ? $this->tags[$tag] : [];
+    }
+
+    /**
+     * 监听标签的行为
+     * @access public
+     * @param  string $tag    标签名称
+     * @param  mixed  $params 传入参数
+     * @param  bool   $once   只获取一个有效返回值
+     * @return mixed
+     */
+    public function listen($tag, $params = null, $once = false)
+    {
+        $results = [];
+        $tags    = $this->get($tag);
+
+        foreach ($tags as $key => $name) {
+            $results[$key] = $this->execTag($name, $tag, $params);
+
+            if (false === $results[$key] || (!is_null($results[$key]) && $once)) {
+                break;
+            }
+        }
+
+        return $once ? end($results) : $results;
+    }
+
+    /**
+     * 执行行为
+     * @access public
+     * @param  mixed     $class  行为
+     * @param  mixed     $params 参数
+     * @return mixed
+     */
+    public function exec($class, $params = null)
+    {
+        if ($class instanceof \Closure || is_array($class)) {
+            $method = $class;
+        } else {
+            if (isset($this->bind[$class])) {
+                $class = $this->bind[$class];
+            }
+            $method = [$class, self::$portal];
+        }
+
+        return $this->app->invoke($method, [$params]);
+    }
+
+    /**
+     * 执行某个标签的行为
+     * @access protected
+     * @param  mixed     $class  要执行的行为
+     * @param  string    $tag    方法名(标签名)
+     * @param  mixed     $params 参数
+     * @return mixed
+     */
+    protected function execTag($class, $tag = '', $params = null)
+    {
+        $method = Loader::parseName($tag, 1, false);
+
+        if ($class instanceof \Closure) {
+            $call  = $class;
+            $class = 'Closure';
+        } elseif (is_array($class) || strpos($class, '::')) {
+            $call = $class;
+        } else {
+            $obj = Container::get($class);
+
+            if (!is_callable([$obj, $method])) {
+                $method = self::$portal;
+            }
+
+            $call  = [$class, $method];
+            $class = $class . '->' . $method;
+        }
+
+        $result = $this->app->invoke($call, [$params]);
+
+        return $result;
+    }
+
+    public function __debugInfo()
+    {
+        $data = get_object_vars($this);
+        unset($data['app']);
+
+        return $data;
+    }
+}

+ 284 - 0
thinkphp/library/think/Lang.php

@@ -0,0 +1,284 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+class Lang
+{
+    /**
+     * 多语言信息
+     * @var array
+     */
+    private $lang = [];
+
+    /**
+     * 当前语言
+     * @var string
+     */
+    private $range = 'zh-cn';
+
+    /**
+     * 多语言自动侦测变量名
+     * @var string
+     */
+    protected $langDetectVar = 'lang';
+
+    /**
+     * 多语言cookie变量
+     * @var string
+     */
+    protected $langCookieVar = 'think_var';
+
+    /**
+     * 允许的多语言列表
+     * @var array
+     */
+    protected $allowLangList = [];
+
+    /**
+     * Accept-Language转义为对应语言包名称 系统默认配置
+     * @var string
+     */
+    protected $acceptLanguage = [
+        'zh-hans-cn' => 'zh-cn',
+    ];
+
+    /**
+     * 应用对象
+     * @var App
+     */
+    protected $app;
+
+    public function __construct(App $app)
+    {
+        $this->app = $app;
+    }
+
+    // 设定当前的语言
+    public function range($range = '')
+    {
+        if ('' == $range) {
+            return $this->range;
+        } else {
+            $this->range = $range;
+        }
+    }
+
+    /**
+     * 设置语言定义(不区分大小写)
+     * @access public
+     * @param  string|array  $name 语言变量
+     * @param  string        $value 语言值
+     * @param  string        $range 语言作用域
+     * @return mixed
+     */
+    public function set($name, $value = null, $range = '')
+    {
+        $range = $range ?: $this->range;
+        // 批量定义
+        if (!isset($this->lang[$range])) {
+            $this->lang[$range] = [];
+        }
+
+        if (is_array($name)) {
+            return $this->lang[$range] = array_change_key_case($name) + $this->lang[$range];
+        }
+
+        return $this->lang[$range][strtolower($name)] = $value;
+    }
+
+    /**
+     * 加载语言定义(不区分大小写)
+     * @access public
+     * @param  string|array  $file   语言文件
+     * @param  string        $range  语言作用域
+     * @return array
+     */
+    public function load($file, $range = '')
+    {
+        $range = $range ?: $this->range;
+        if (!isset($this->lang[$range])) {
+            $this->lang[$range] = [];
+        }
+
+        // 批量定义
+        if (is_string($file)) {
+            $file = [$file];
+        }
+
+        $lang = [];
+
+        foreach ($file as $_file) {
+            if (is_file($_file)) {
+                // 记录加载信息
+                $this->app->log('[ LANG ] ' . $_file);
+                $_lang = include $_file;
+                if (is_array($_lang)) {
+                    $lang = array_change_key_case($_lang) + $lang;
+                }
+            }
+        }
+
+        if (!empty($lang)) {
+            $this->lang[$range] = $lang + $this->lang[$range];
+        }
+
+        return $this->lang[$range];
+    }
+
+    /**
+     * 获取语言定义(不区分大小写)
+     * @access public
+     * @param  string|null   $name 语言变量
+     * @param  string        $range 语言作用域
+     * @return bool
+     */
+    public function has($name, $range = '')
+    {
+        $range = $range ?: $this->range;
+
+        return isset($this->lang[$range][strtolower($name)]);
+    }
+
+    /**
+     * 获取语言定义(不区分大小写)
+     * @access public
+     * @param  string|null   $name 语言变量
+     * @param  array         $vars 变量替换
+     * @param  string        $range 语言作用域
+     * @return mixed
+     */
+    public function get($name = null, $vars = [], $range = '')
+    {
+        $range = $range ?: $this->range;
+
+        // 空参数返回所有定义
+        if (is_null($name)) {
+            return $this->lang[$range];
+        }
+
+        $key   = strtolower($name);
+        $value = isset($this->lang[$range][$key]) ? $this->lang[$range][$key] : $name;
+
+        // 变量解析
+        if (!empty($vars) && is_array($vars)) {
+            /**
+             * Notes:
+             * 为了检测的方便,数字索引的判断仅仅是参数数组的第一个元素的key为数字0
+             * 数字索引采用的是系统的 sprintf 函数替换,用法请参考 sprintf 函数
+             */
+            if (key($vars) === 0) {
+                // 数字索引解析
+                array_unshift($vars, $value);
+                $value = call_user_func_array('sprintf', $vars);
+            } else {
+                // 关联索引解析
+                $replace = array_keys($vars);
+                foreach ($replace as &$v) {
+                    $v = "{:{$v}}";
+                }
+                $value = str_replace($replace, $vars, $value);
+            }
+        }
+
+        return $value;
+    }
+
+    /**
+     * 自动侦测设置获取语言选择
+     * @access public
+     * @return string
+     */
+    public function detect()
+    {
+        // 自动侦测设置获取语言选择
+        $langSet = '';
+
+        if (isset($_GET[$this->langDetectVar])) {
+            // url中设置了语言变量
+            $langSet = strtolower($_GET[$this->langDetectVar]);
+        } elseif (isset($_COOKIE[$this->langCookieVar])) {
+            // Cookie中设置了语言变量
+            $langSet = strtolower($_COOKIE[$this->langCookieVar]);
+        } elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
+            // 自动侦测浏览器语言
+            preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
+            $langSet = strtolower($matches[1]);
+            if (isset($this->acceptLanguage[$langSet])) {
+                $langSet = $this->acceptLanguage[$langSet];
+            }
+        }
+
+        if (empty($this->allowLangList) || in_array($langSet, $this->allowLangList)) {
+            // 合法的语言
+            $this->range = $langSet ?: $this->range;
+        }
+
+        return $this->range;
+    }
+
+    /**
+     * 设置当前语言到Cookie
+     * @access public
+     * @param  string $lang 语言
+     * @return void
+     */
+    public function saveToCookie($lang = null)
+    {
+        $range = $lang ?: $this->range;
+
+        $_COOKIE[$this->langCookieVar] = $range;
+    }
+
+    /**
+     * 设置语言自动侦测的变量
+     * @access public
+     * @param  string $var 变量名称
+     * @return void
+     */
+    public function setLangDetectVar($var)
+    {
+        $this->langDetectVar = $var;
+    }
+
+    /**
+     * 设置语言的cookie保存变量
+     * @access public
+     * @param  string $var 变量名称
+     * @return void
+     */
+    public function setLangCookieVar($var)
+    {
+        $this->langCookieVar = $var;
+    }
+
+    /**
+     * 设置允许的语言列表
+     * @access public
+     * @param  array $list 语言列表
+     * @return void
+     */
+    public function setAllowLangList(array $list)
+    {
+        $this->allowLangList = $list;
+    }
+
+    /**
+     * 设置转义的语言列表
+     * @access public
+     * @param  array $list 语言列表
+     * @return void
+     */
+    public function setAcceptLanguage(array $list)
+    {
+        $this->acceptLanguage = array_merge($this->acceptLanguage, $list);
+    }
+}

+ 417 - 0
thinkphp/library/think/Loader.php

@@ -0,0 +1,417 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use think\exception\ClassNotFoundException;
+
+class Loader
+{
+    /**
+     * 类名映射信息
+     * @var array
+     */
+    protected static $classMap = [];
+
+    /**
+     * 类库别名
+     * @var array
+     */
+    protected static $classAlias = [];
+
+    /**
+     * PSR-4
+     * @var array
+     */
+    private static $prefixLengthsPsr4 = [];
+    private static $prefixDirsPsr4    = [];
+    private static $fallbackDirsPsr4  = [];
+
+    /**
+     * PSR-0
+     * @var array
+     */
+    private static $prefixesPsr0     = [];
+    private static $fallbackDirsPsr0 = [];
+
+    /**
+     * 需要加载的文件
+     * @var array
+     */
+    private static $files = [];
+
+    /**
+     * Composer安装路径
+     * @var string
+     */
+    private static $composerPath;
+
+    // 获取应用根目录
+    public static function getRootPath()
+    {
+        if ('cli' == PHP_SAPI) {
+            $scriptName = realpath($_SERVER['argv'][0]);
+        } else {
+            $scriptName = $_SERVER['SCRIPT_FILENAME'];
+        }
+
+        $path = realpath(dirname($scriptName));
+
+        if (!is_file($path . DIRECTORY_SEPARATOR . 'think')) {
+            $path = dirname($path);
+        }
+
+        return $path . DIRECTORY_SEPARATOR;
+    }
+
+    // 注册自动加载机制
+    public static function register($autoload = '')
+    {
+        // 注册系统自动加载
+        spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
+
+        $rootPath = self::getRootPath();
+
+        self::$composerPath = $rootPath . 'vendor' . DIRECTORY_SEPARATOR . 'composer' . DIRECTORY_SEPARATOR;
+
+        // Composer自动加载支持
+        if (is_dir(self::$composerPath)) {
+            if (is_file(self::$composerPath . 'autoload_static.php')) {
+                require self::$composerPath . 'autoload_static.php';
+
+                $declaredClass = get_declared_classes();
+                $composerClass = array_pop($declaredClass);
+
+                foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) {
+                    if (property_exists($composerClass, $attr)) {
+                        self::${$attr} = $composerClass::${$attr};
+                    }
+                }
+            } else {
+                self::registerComposerLoader(self::$composerPath);
+            }
+        }
+
+        // 注册命名空间定义
+        self::addNamespace([
+            'think'  => __DIR__,
+            'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits',
+        ]);
+
+        // 加载类库映射文件
+        if (is_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php')) {
+            self::addClassMap(__include_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php'));
+        }
+
+        // 自动加载extend目录
+        self::addAutoLoadDir($rootPath . 'extend');
+    }
+
+    // 自动加载
+    public static function autoload($class)
+    {
+        if (isset(self::$classAlias[$class])) {
+            return class_alias(self::$classAlias[$class], $class);
+        }
+
+        if ($file = self::findFile($class)) {
+
+            // Win环境严格区分大小写
+            if (strpos(PHP_OS, 'WIN') !== false && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) {
+                return false;
+            }
+
+            __include_file($file);
+            return true;
+        }
+    }
+
+    /**
+     * 查找文件
+     * @access private
+     * @param  string $class
+     * @return string|false
+     */
+    private static function findFile($class)
+    {
+        if (!empty(self::$classMap[$class])) {
+            // 类库映射
+            return self::$classMap[$class];
+        }
+
+        // 查找 PSR-4
+        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php';
+
+        $first = $class[0];
+        if (isset(self::$prefixLengthsPsr4[$first])) {
+            foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach (self::$prefixDirsPsr4[$prefix] as $dir) {
+                        if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // 查找 PSR-4 fallback dirs
+        foreach (self::$fallbackDirsPsr4 as $dir) {
+            if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+                return $file;
+            }
+        }
+
+        // 查找 PSR-0
+        if (false !== $pos = strrpos($class, '\\')) {
+            // namespaced class name
+            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+            . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+        } else {
+            // PEAR-like class name
+            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php';
+        }
+
+        if (isset(self::$prefixesPsr0[$first])) {
+            foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($dirs as $dir) {
+                        if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // 查找 PSR-0 fallback dirs
+        foreach (self::$fallbackDirsPsr0 as $dir) {
+            if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                return $file;
+            }
+        }
+
+        return self::$classMap[$class] = false;
+    }
+
+    // 注册classmap
+    public static function addClassMap($class, $map = '')
+    {
+        if (is_array($class)) {
+            self::$classMap = array_merge(self::$classMap, $class);
+        } else {
+            self::$classMap[$class] = $map;
+        }
+    }
+
+    // 注册命名空间
+    public static function addNamespace($namespace, $path = '')
+    {
+        if (is_array($namespace)) {
+            foreach ($namespace as $prefix => $paths) {
+                self::addPsr4($prefix . '\\', rtrim($paths, DIRECTORY_SEPARATOR), true);
+            }
+        } else {
+            self::addPsr4($namespace . '\\', rtrim($path, DIRECTORY_SEPARATOR), true);
+        }
+    }
+
+    // 添加Ps0空间
+    private static function addPsr0($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            if ($prepend) {
+                self::$fallbackDirsPsr0 = array_merge(
+                    (array) $paths,
+                    self::$fallbackDirsPsr0
+                );
+            } else {
+                self::$fallbackDirsPsr0 = array_merge(
+                    self::$fallbackDirsPsr0,
+                    (array) $paths
+                );
+            }
+
+            return;
+        }
+
+        $first = $prefix[0];
+        if (!isset(self::$prefixesPsr0[$first][$prefix])) {
+            self::$prefixesPsr0[$first][$prefix] = (array) $paths;
+
+            return;
+        }
+
+        if ($prepend) {
+            self::$prefixesPsr0[$first][$prefix] = array_merge(
+                (array) $paths,
+                self::$prefixesPsr0[$first][$prefix]
+            );
+        } else {
+            self::$prefixesPsr0[$first][$prefix] = array_merge(
+                self::$prefixesPsr0[$first][$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    // 添加Psr4空间
+    private static function addPsr4($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            // Register directories for the root namespace.
+            if ($prepend) {
+                self::$fallbackDirsPsr4 = array_merge(
+                    (array) $paths,
+                    self::$fallbackDirsPsr4
+                );
+            } else {
+                self::$fallbackDirsPsr4 = array_merge(
+                    self::$fallbackDirsPsr4,
+                    (array) $paths
+                );
+            }
+        } elseif (!isset(self::$prefixDirsPsr4[$prefix])) {
+            // Register directories for a new namespace.
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+
+            self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            self::$prefixDirsPsr4[$prefix]                = (array) $paths;
+        } elseif ($prepend) {
+            // Prepend directories for an already registered namespace.
+            self::$prefixDirsPsr4[$prefix] = array_merge(
+                (array) $paths,
+                self::$prefixDirsPsr4[$prefix]
+            );
+        } else {
+            // Append directories for an already registered namespace.
+            self::$prefixDirsPsr4[$prefix] = array_merge(
+                self::$prefixDirsPsr4[$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    // 注册自动加载类库目录
+    public static function addAutoLoadDir($path)
+    {
+        self::$fallbackDirsPsr4[] = $path;
+    }
+
+    // 注册类别名
+    public static function addClassAlias($alias, $class = null)
+    {
+        if (is_array($alias)) {
+            self::$classAlias = array_merge(self::$classAlias, $alias);
+        } else {
+            self::$classAlias[$alias] = $class;
+        }
+    }
+
+    // 注册composer自动加载
+    public static function registerComposerLoader($composerPath)
+    {
+        if (is_file($composerPath . 'autoload_namespaces.php')) {
+            $map = require $composerPath . 'autoload_namespaces.php';
+            foreach ($map as $namespace => $path) {
+                self::addPsr0($namespace, $path);
+            }
+        }
+
+        if (is_file($composerPath . 'autoload_psr4.php')) {
+            $map = require $composerPath . 'autoload_psr4.php';
+            foreach ($map as $namespace => $path) {
+                self::addPsr4($namespace, $path);
+            }
+        }
+
+        if (is_file($composerPath . 'autoload_classmap.php')) {
+            $classMap = require $composerPath . 'autoload_classmap.php';
+            if ($classMap) {
+                self::addClassMap($classMap);
+            }
+        }
+
+        if (is_file($composerPath . 'autoload_files.php')) {
+            self::$files = require $composerPath . 'autoload_files.php';
+        }
+    }
+
+    // 加载composer autofile文件
+    public static function loadComposerAutoloadFiles()
+    {
+        foreach (self::$files as $fileIdentifier => $file) {
+            if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+                __require_file($file);
+
+                $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+            }
+        }
+    }
+
+    /**
+     * 字符串命名风格转换
+     * 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)
+    {
+        if ($type) {
+            $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
+                return strtoupper($match[1]);
+            }, $name);
+            return $ucfirst ? ucfirst($name) : lcfirst($name);
+        }
+
+        return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
+    }
+
+    /**
+     * 创建工厂对象实例
+     * @access public
+     * @param  string $name         工厂类名
+     * @param  string $namespace    默认命名空间
+     * @return mixed
+     */
+    public static function factory($name, $namespace = '', ...$args)
+    {
+        $class = false !== strpos($name, '\\') ? $name : $namespace . ucwords($name);
+
+        if (class_exists($class)) {
+            return Container::getInstance()->invokeClass($class, $args);
+        } else {
+            throw new ClassNotFoundException('class not exists:' . $class, $class);
+        }
+    }
+}
+
+/**
+ * 作用范围隔离
+ *
+ * @param $file
+ * @return mixed
+ */
+function __include_file($file)
+{
+    return include $file;
+}
+
+function __require_file($file)
+{
+    return require $file;
+}

+ 389 - 0
thinkphp/library/think/Log.php

@@ -0,0 +1,389 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+class Log implements LoggerInterface
+{
+    const EMERGENCY = 'emergency';
+    const ALERT     = 'alert';
+    const CRITICAL  = 'critical';
+    const ERROR     = 'error';
+    const WARNING   = 'warning';
+    const NOTICE    = 'notice';
+    const INFO      = 'info';
+    const DEBUG     = 'debug';
+    const SQL       = 'sql';
+
+    /**
+     * 日志信息
+     * @var array
+     */
+    protected $log = [];
+
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $config = [];
+
+    /**
+     * 日志写入驱动
+     * @var object
+     */
+    protected $driver;
+
+    /**
+     * 日志授权key
+     * @var string
+     */
+    protected $key;
+
+    /**
+     * 是否允许日志写入
+     * @var bool
+     */
+    protected $allowWrite = true;
+
+    /**
+     * 应用对象
+     * @var App
+     */
+    protected $app;
+
+    public function __construct(App $app)
+    {
+        $this->app = $app;
+    }
+
+    public static function __make(App $app, Config $config)
+    {
+        return (new static($app))->init($config->pull('log'));
+    }
+
+    /**
+     * 日志初始化
+     * @access public
+     * @param  array $config
+     * @return $this
+     */
+    public function init($config = [])
+    {
+        $type = isset($config['type']) ? $config['type'] : 'File';
+
+        $this->config = $config;
+
+        unset($config['type']);
+
+        if (!empty($config['close'])) {
+            $this->allowWrite = false;
+        }
+
+        $this->driver = Loader::factory($type, '\\think\\log\\driver\\', $config);
+
+        return $this;
+    }
+
+    /**
+     * 获取日志信息
+     * @access public
+     * @param  string $type 信息类型
+     * @return array
+     */
+    public function getLog($type = '')
+    {
+        return $type ? $this->log[$type] : $this->log;
+    }
+
+    /**
+     * 记录日志信息
+     * @access public
+     * @param  mixed  $msg       日志信息
+     * @param  string $type      日志级别
+     * @param  array  $context   替换内容
+     * @return $this
+     */
+    public function record($msg, $type = 'info', array $context = [])
+    {
+        if (!$this->allowWrite) {
+            return;
+        }
+
+        if (is_string($msg) && !empty($context)) {
+            $replace = [];
+            foreach ($context as $key => $val) {
+                $replace['{' . $key . '}'] = $val;
+            }
+
+            $msg = strtr($msg, $replace);
+        }
+
+        if (PHP_SAPI == 'cli') {
+            if (empty($this->config['level']) || in_array($type, $this->config['level'])) {
+                // 命令行日志实时写入
+                $this->write($msg, $type, true);
+            }
+        } else {
+            $this->log[$type][] = $msg;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 清空日志信息
+     * @access public
+     * @return $this
+     */
+    public function clear()
+    {
+        $this->log = [];
+
+        return $this;
+    }
+
+    /**
+     * 当前日志记录的授权key
+     * @access public
+     * @param  string  $key  授权key
+     * @return $this
+     */
+    public function key($key)
+    {
+        $this->key = $key;
+
+        return $this;
+    }
+
+    /**
+     * 检查日志写入权限
+     * @access public
+     * @param  array  $config  当前日志配置参数
+     * @return bool
+     */
+    public function check($config)
+    {
+        if ($this->key && !empty($config['allow_key']) && !in_array($this->key, $config['allow_key'])) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * 关闭本次请求日志写入
+     * @access public
+     * @return $this
+     */
+    public function close()
+    {
+        $this->allowWrite = false;
+        $this->log        = [];
+
+        return $this;
+    }
+
+    /**
+     * 保存调试信息
+     * @access public
+     * @return bool
+     */
+    public function save()
+    {
+        if (empty($this->log) || !$this->allowWrite) {
+            return true;
+        }
+
+        if (!$this->check($this->config)) {
+            // 检测日志写入权限
+            return false;
+        }
+
+        $log = [];
+
+        foreach ($this->log as $level => $info) {
+            if (!$this->app->isDebug() && 'debug' == $level) {
+                continue;
+            }
+
+            if (empty($this->config['level']) || in_array($level, $this->config['level'])) {
+                $log[$level] = $info;
+
+                $this->app['hook']->listen('log_level', [$level, $info]);
+            }
+        }
+
+        $result = $this->driver->save($log, true);
+
+        if ($result) {
+            $this->log = [];
+        }
+
+        return $result;
+    }
+
+    /**
+     * 实时写入日志信息 并支持行为
+     * @access public
+     * @param  mixed  $msg   调试信息
+     * @param  string $type  日志级别
+     * @param  bool   $force 是否强制写入
+     * @return bool
+     */
+    public function write($msg, $type = 'info', $force = false)
+    {
+        // 封装日志信息
+        if (empty($this->config['level'])) {
+            $force = true;
+        }
+
+        if (true === $force || in_array($type, $this->config['level'])) {
+            $log[$type][] = $msg;
+        } else {
+            return false;
+        }
+
+        // 监听log_write
+        $this->app['hook']->listen('log_write', $log);
+
+        // 写入日志
+        return $this->driver->save($log, false);
+    }
+
+    /**
+     * 记录日志信息
+     * @access public
+     * @param  string $level     日志级别
+     * @param  mixed  $message   日志信息
+     * @param  array  $context   替换内容
+     * @return void
+     */
+    public function log($level, $message, array $context = [])
+    {
+        $this->record($message, $level, $context);
+    }
+
+    /**
+     * 记录emergency信息
+     * @access public
+     * @param  mixed  $message   日志信息
+     * @param  array  $context   替换内容
+     * @return void
+     */
+    public function emergency($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录警报信息
+     * @access public
+     * @param  mixed  $message   日志信息
+     * @param  array  $context   替换内容
+     * @return void
+     */
+    public function alert($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录紧急情况
+     * @access public
+     * @param  mixed  $message   日志信息
+     * @param  array  $context   替换内容
+     * @return void
+     */
+    public function critical($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录错误信息
+     * @access public
+     * @param  mixed  $message   日志信息
+     * @param  array  $context   替换内容
+     * @return void
+     */
+    public function error($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录warning信息
+     * @access public
+     * @param  mixed  $message   日志信息
+     * @param  array  $context   替换内容
+     * @return void
+     */
+    public function warning($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录notice信息
+     * @access public
+     * @param  mixed  $message   日志信息
+     * @param  array  $context   替换内容
+     * @return void
+     */
+    public function notice($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录一般信息
+     * @access public
+     * @param  mixed  $message   日志信息
+     * @param  array  $context   替换内容
+     * @return void
+     */
+    public function info($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录调试信息
+     * @access public
+     * @param  mixed  $message   日志信息
+     * @param  array  $context   替换内容
+     * @return void
+     */
+    public function debug($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    /**
+     * 记录sql信息
+     * @access public
+     * @param  mixed  $message   日志信息
+     * @param  array  $context   替换内容
+     * @return void
+     */
+    public function sql($message, array $context = [])
+    {
+        $this->log(__FUNCTION__, $message, $context);
+    }
+
+    public function __debugInfo()
+    {
+        $data = get_object_vars($this);
+        unset($data['app']);
+
+        return $data;
+    }
+}

+ 205 - 0
thinkphp/library/think/Middleware.php

@@ -0,0 +1,205 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: Slince <taosikai@yeah.net>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use InvalidArgumentException;
+use LogicException;
+use think\exception\HttpResponseException;
+
+class Middleware
+{
+    protected $queue = [];
+    protected $app;
+    protected $config = [
+        'default_namespace' => 'app\\http\\middleware\\',
+    ];
+
+    public function __construct(App $app, array $config = [])
+    {
+        $this->app    = $app;
+        $this->config = array_merge($this->config, $config);
+    }
+
+    public static function __make(App $app, Config $config)
+    {
+        return new static($app, $config->pull('middleware'));
+    }
+
+    public function setConfig(array $config)
+    {
+        $this->config = array_merge($this->config, $config);
+    }
+
+    /**
+     * 导入中间件
+     * @access public
+     * @param  array  $middlewares
+     * @param  string $type  中间件类型
+     */
+    public function import(array $middlewares = [], $type = 'route')
+    {
+        foreach ($middlewares as $middleware) {
+            $this->add($middleware, $type);
+        }
+    }
+
+    /**
+     * 注册中间件
+     * @access public
+     * @param  mixed  $middleware
+     * @param  string $type  中间件类型
+     */
+    public function add($middleware, $type = 'route')
+    {
+        if (is_null($middleware)) {
+            return;
+        }
+
+        $middleware = $this->buildMiddleware($middleware, $type);
+
+        if ($middleware) {
+            $this->queue[$type][] = $middleware;
+        }
+    }
+
+    /**
+     * 注册控制器中间件
+     * @access public
+     * @param  mixed  $middleware
+     */
+    public function controller($middleware)
+    {
+        return $this->add($middleware, 'controller');
+    }
+
+    /**
+     * 移除中间件
+     * @access public
+     * @param  mixed  $middleware
+     * @param  string $type  中间件类型
+     */
+    public function unshift($middleware, $type = 'route')
+    {
+        if (is_null($middleware)) {
+            return;
+        }
+
+        $middleware = $this->buildMiddleware($middleware, $type);
+
+        if ($middleware) {
+            array_unshift($this->queue[$type], $middleware);
+        }
+    }
+
+    /**
+     * 获取注册的中间件
+     * @access public
+     * @param  string $type  中间件类型
+     */
+    public function all($type = 'route')
+    {
+        return $this->queue[$type] ?: [];
+    }
+
+    /**
+     * 清除中间件
+     * @access public
+     */
+    public function clear()
+    {
+        $this->queue = [];
+    }
+
+    /**
+     * 中间件调度
+     * @access public
+     * @param  Request  $request
+     * @param  string   $type  中间件类型
+     */
+    public function dispatch(Request $request, $type = 'route')
+    {
+        return call_user_func($this->resolve($type), $request);
+    }
+
+    /**
+     * 解析中间件
+     * @access protected
+     * @param  mixed  $middleware
+     * @param  string $type  中间件类型
+     */
+    protected function buildMiddleware($middleware, $type = 'route')
+    {
+        if (is_array($middleware)) {
+            list($middleware, $param) = $middleware;
+        }
+
+        if ($middleware instanceof \Closure) {
+            return [$middleware, isset($param) ? $param : null];
+        }
+
+        if (!is_string($middleware)) {
+            throw new InvalidArgumentException('The middleware is invalid');
+        }
+
+        if (false === strpos($middleware, '\\')) {
+            if (isset($this->config[$middleware])) {
+                $middleware = $this->config[$middleware];
+            } else {
+                $middleware = $this->config['default_namespace'] . $middleware;
+            }
+        }
+
+        if (is_array($middleware)) {
+            return $this->import($middleware, $type);
+        }
+
+        if (strpos($middleware, ':')) {
+            list($middleware, $param) = explode(':', $middleware, 2);
+        }
+
+        return [[$this->app->make($middleware), 'handle'], isset($param) ? $param : null];
+    }
+
+    protected function resolve($type = 'route')
+    {
+        return function (Request $request) use ($type) {
+
+            $middleware = array_shift($this->queue[$type]);
+
+            if (null === $middleware) {
+                throw new InvalidArgumentException('The queue was exhausted, with no response returned');
+            }
+
+            list($call, $param) = $middleware;
+
+            try {
+                $response = call_user_func_array($call, [$request, $this->resolve($type), $param]);
+            } catch (HttpResponseException $exception) {
+                $response = $exception->getResponse();
+            }
+
+            if (!$response instanceof Response) {
+                throw new LogicException('The middleware must return Response instance');
+            }
+
+            return $response;
+        };
+    }
+
+    public function __debugInfo()
+    {
+        $data = get_object_vars($this);
+        unset($data['app']);
+
+        return $data;
+    }
+}

File diff suppressed because it is too large
+ 1116 - 0
thinkphp/library/think/Model.php


+ 445 - 0
thinkphp/library/think/Paginator.php

@@ -0,0 +1,445 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: zhangyajun <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use ArrayAccess;
+use ArrayIterator;
+use Countable;
+use IteratorAggregate;
+use JsonSerializable;
+use Traversable;
+
+abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
+{
+    /**
+     * 是否简洁模式
+     * @var bool
+     */
+    protected $simple = false;
+
+    /**
+     * 数据集
+     * @var Collection
+     */
+    protected $items;
+
+    /**
+     * 当前页
+     * @var integer
+     */
+    protected $currentPage;
+
+    /**
+     * 最后一页
+     * @var integer
+     */
+    protected $lastPage;
+
+    /**
+     * 数据总数
+     * @var integer|null
+     */
+    protected $total;
+
+    /**
+     * 每页数量
+     * @var integer
+     */
+    protected $listRows;
+
+    /**
+     * 是否有下一页
+     * @var bool
+     */
+    protected $hasMore;
+
+    /**
+     * 分页配置
+     * @var array
+     */
+    protected $options = [
+        'var_page' => 'page',
+        'path'     => '/',
+        'query'    => [],
+        'fragment' => '',
+    ];
+
+    public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
+    {
+        $this->options = array_merge($this->options, $options);
+
+        $this->options['path'] = '/' != $this->options['path'] ? rtrim($this->options['path'], '/') : $this->options['path'];
+
+        $this->simple   = $simple;
+        $this->listRows = $listRows;
+
+        if (!$items instanceof Collection) {
+            $items = Collection::make($items);
+        }
+
+        if ($simple) {
+            $this->currentPage = $this->setCurrentPage($currentPage);
+            $this->hasMore     = count($items) > ($this->listRows);
+            $items             = $items->slice(0, $this->listRows);
+        } else {
+            $this->total       = $total;
+            $this->lastPage    = (int) ceil($total / $listRows);
+            $this->currentPage = $this->setCurrentPage($currentPage);
+            $this->hasMore     = $this->currentPage < $this->lastPage;
+        }
+        $this->items = $items;
+    }
+
+    /**
+     * @access public
+     * @param       $items
+     * @param       $listRows
+     * @param null  $currentPage
+     * @param null  $total
+     * @param bool  $simple
+     * @param array $options
+     * @return Paginator
+     */
+    public static function make($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
+    {
+        return new static($items, $listRows, $currentPage, $total, $simple, $options);
+    }
+
+    protected function setCurrentPage($currentPage)
+    {
+        if (!$this->simple && $currentPage > $this->lastPage) {
+            return $this->lastPage > 0 ? $this->lastPage : 1;
+        }
+
+        return $currentPage;
+    }
+
+    /**
+     * 获取页码对应的链接
+     *
+     * @access protected
+     * @param  $page
+     * @return string
+     */
+    protected function url($page)
+    {
+        if ($page <= 0) {
+            $page = 1;
+        }
+
+        if (strpos($this->options['path'], '[PAGE]') === false) {
+            $parameters = [$this->options['var_page'] => $page];
+            $path       = $this->options['path'];
+        } else {
+            $parameters = [];
+            $path       = str_replace('[PAGE]', $page, $this->options['path']);
+        }
+
+        if (count($this->options['query']) > 0) {
+            $parameters = array_merge($this->options['query'], $parameters);
+        }
+
+        $url = $path;
+        if (!empty($parameters)) {
+            $url .= '?' . http_build_query($parameters, null, '&');
+        }
+
+        return $url . $this->buildFragment();
+    }
+
+    /**
+     * 自动获取当前页码
+     * @access public
+     * @param  string $varPage
+     * @param  int    $default
+     * @return int
+     */
+    public static function getCurrentPage($varPage = 'page', $default = 1)
+    {
+        $page = Container::get('request')->param($varPage);
+
+        if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) {
+            return $page;
+        }
+
+        return $default;
+    }
+
+    /**
+     * 自动获取当前的path
+     * @access public
+     * @return string
+     */
+    public static function getCurrentPath()
+    {
+        return Container::get('request')->baseUrl();
+    }
+
+    public function total()
+    {
+        if ($this->simple) {
+            throw new \DomainException('not support total');
+        }
+
+        return $this->total;
+    }
+
+    public function listRows()
+    {
+        return $this->listRows;
+    }
+
+    public function currentPage()
+    {
+        return $this->currentPage;
+    }
+
+    public function lastPage()
+    {
+        if ($this->simple) {
+            throw new \DomainException('not support last');
+        }
+
+        return $this->lastPage;
+    }
+
+    /**
+     * 数据是否足够分页
+     * @access public
+     * @return boolean
+     */
+    public function hasPages()
+    {
+        return !(1 == $this->currentPage && !$this->hasMore);
+    }
+
+    /**
+     * 创建一组分页链接
+     *
+     * @access public
+     * @param  int $start
+     * @param  int $end
+     * @return array
+     */
+    public function getUrlRange($start, $end)
+    {
+        $urls = [];
+
+        for ($page = $start; $page <= $end; $page++) {
+            $urls[$page] = $this->url($page);
+        }
+
+        return $urls;
+    }
+
+    /**
+     * 设置URL锚点
+     *
+     * @access public
+     * @param  string|null $fragment
+     * @return $this
+     */
+    public function fragment($fragment)
+    {
+        $this->options['fragment'] = $fragment;
+
+        return $this;
+    }
+
+    /**
+     * 添加URL参数
+     *
+     * @access public
+     * @param  array|string $key
+     * @param  string|null  $value
+     * @return $this
+     */
+    public function appends($key, $value = null)
+    {
+        if (!is_array($key)) {
+            $queries = [$key => $value];
+        } else {
+            $queries = $key;
+        }
+
+        foreach ($queries as $k => $v) {
+            if ($k !== $this->options['var_page']) {
+                $this->options['query'][$k] = $v;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 构造锚点字符串
+     *
+     * @access public
+     * @return string
+     */
+    protected function buildFragment()
+    {
+        return $this->options['fragment'] ? '#' . $this->options['fragment'] : '';
+    }
+
+    /**
+     * 渲染分页html
+     * @access public
+     * @return mixed
+     */
+    abstract public function render();
+
+    public function items()
+    {
+        return $this->items->all();
+    }
+
+    public function getCollection()
+    {
+        return $this->items;
+    }
+
+    public function isEmpty()
+    {
+        return $this->items->isEmpty();
+    }
+
+    /**
+     * 给每个元素执行个回调
+     *
+     * @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)) {
+                $this->items[$key] = $result;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Retrieve an external iterator
+     * @access public
+     * @return Traversable An instance of an object implementing <b>Iterator</b> or
+     * <b>Traversable</b>
+     */
+    public function getIterator()
+    {
+        return new ArrayIterator($this->items->all());
+    }
+
+    /**
+     * Whether a offset exists
+     * @access public
+     * @param  mixed $offset
+     * @return bool
+     */
+    public function offsetExists($offset)
+    {
+        return $this->items->offsetExists($offset);
+    }
+
+    /**
+     * Offset to retrieve
+     * @access public
+     * @param  mixed $offset
+     * @return mixed
+     */
+    public function offsetGet($offset)
+    {
+        return $this->items->offsetGet($offset);
+    }
+
+    /**
+     * Offset to set
+     * @access public
+     * @param  mixed $offset
+     * @param  mixed $value
+     */
+    public function offsetSet($offset, $value)
+    {
+        $this->items->offsetSet($offset, $value);
+    }
+
+    /**
+     * Offset to unset
+     * @access public
+     * @param  mixed $offset
+     * @return void
+     * @since  5.0.0
+     */
+    public function offsetUnset($offset)
+    {
+        $this->items->offsetUnset($offset);
+    }
+
+    /**
+     * Count elements of an object
+     */
+    public function count()
+    {
+        return $this->items->count();
+    }
+
+    public function __toString()
+    {
+        return (string) $this->render();
+    }
+
+    public function toArray()
+    {
+        try {
+            $total = $this->total();
+        } catch (\DomainException $e) {
+            $total = null;
+        }
+
+        return [
+            'total'        => $total,
+            'per_page'     => $this->listRows(),
+            'current_page' => $this->currentPage(),
+            'last_page'    => $this->lastPage,
+            'data'         => $this->items->toArray(),
+        ];
+    }
+
+    /**
+     * Specify data which should be serialized to JSON
+     */
+    public function jsonSerialize()
+    {
+        return $this->toArray();
+    }
+
+    public function __call($name, $arguments)
+    {
+        $collection = $this->getCollection();
+
+        $result = call_user_func_array([$collection, $name], $arguments);
+
+        if ($result === $collection) {
+            return $this;
+        }
+
+        return $result;
+    }
+
+}

File diff suppressed because it is too large
+ 1268 - 0
thinkphp/library/think/Process.php


File diff suppressed because it is too large
+ 2267 - 0
thinkphp/library/think/Request.php


+ 429 - 0
thinkphp/library/think/Response.php

@@ -0,0 +1,429 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use think\response\Redirect as RedirectResponse;
+
+class Response
+{
+    /**
+     * 原始数据
+     * @var mixed
+     */
+    protected $data;
+
+    /**
+     * 应用对象实例
+     * @var App
+     */
+    protected $app;
+
+    /**
+     * 当前contentType
+     * @var string
+     */
+    protected $contentType = 'text/html';
+
+    /**
+     * 字符集
+     * @var string
+     */
+    protected $charset = 'utf-8';
+
+    /**
+     * 状态码
+     * @var integer
+     */
+    protected $code = 200;
+
+    /**
+     * 是否允许请求缓存
+     * @var bool
+     */
+    protected $allowCache = true;
+
+    /**
+     * 输出参数
+     * @var array
+     */
+    protected $options = [];
+
+    /**
+     * header参数
+     * @var array
+     */
+    protected $header = [];
+
+    /**
+     * 输出内容
+     * @var string
+     */
+    protected $content = null;
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  mixed $data    输出数据
+     * @param  int   $code
+     * @param  array $header
+     * @param  array $options 输出参数
+     */
+    public function __construct($data = '', $code = 200, array $header = [], $options = [])
+    {
+        $this->data($data);
+
+        if (!empty($options)) {
+            $this->options = array_merge($this->options, $options);
+        }
+
+        $this->contentType($this->contentType, $this->charset);
+
+        $this->code   = $code;
+        $this->app    = Container::get('app');
+        $this->header = array_merge($this->header, $header);
+    }
+
+    /**
+     * 创建Response对象
+     * @access public
+     * @param  mixed  $data    输出数据
+     * @param  string $type    输出类型
+     * @param  int    $code
+     * @param  array  $header
+     * @param  array  $options 输出参数
+     * @return Response
+     */
+    public static function create($data = '', $type = '', $code = 200, array $header = [], $options = [])
+    {
+        $class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst(strtolower($type));
+
+        if (class_exists($class)) {
+            return new $class($data, $code, $header, $options);
+        }
+
+        return new static($data, $code, $header, $options);
+    }
+
+    /**
+     * 发送数据到客户端
+     * @access public
+     * @return void
+     * @throws \InvalidArgumentException
+     */
+    public function send()
+    {
+        // 监听response_send
+        $this->app['hook']->listen('response_send', $this);
+
+        // 处理输出数据
+        $data = $this->getContent();
+
+        // Trace调试注入
+        if ('cli' != PHP_SAPI && $this->app['env']->get('app_trace', $this->app->config('app.app_trace'))) {
+            $this->app['debug']->inject($this, $data);
+        }
+
+        if (200 == $this->code && $this->allowCache) {
+            $cache = $this->app['request']->getCache();
+            if ($cache) {
+                $this->header['Cache-Control'] = 'max-age=' . $cache[1] . ',must-revalidate';
+                $this->header['Last-Modified'] = gmdate('D, d M Y H:i:s') . ' GMT';
+                $this->header['Expires']       = gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME'] + $cache[1]) . ' GMT';
+
+                $this->app['cache']->tag($cache[2])->set($cache[0], [$data, $this->header], $cache[1]);
+            }
+        }
+
+        if (!headers_sent() && !empty($this->header)) {
+            // 发送状态码
+            http_response_code($this->code);
+            // 发送头部信息
+            foreach ($this->header as $name => $val) {
+                header($name . (!is_null($val) ? ':' . $val : ''));
+            }
+        }
+
+        $this->sendData($data);
+
+        if (function_exists('fastcgi_finish_request')) {
+            // 提高页面响应
+            fastcgi_finish_request();
+        }
+
+        // 监听response_end
+        $this->app['hook']->listen('response_end', $this);
+
+        // 清空当次请求有效的数据
+        if (!($this instanceof RedirectResponse)) {
+            $this->app['session']->flush();
+        }
+    }
+
+    /**
+     * 处理数据
+     * @access protected
+     * @param  mixed $data 要处理的数据
+     * @return mixed
+     */
+    protected function output($data)
+    {
+        return $data;
+    }
+
+    /**
+     * 输出数据
+     * @access protected
+     * @param string $data 要处理的数据
+     * @return void
+     */
+    protected function sendData($data)
+    {
+        echo $data;
+    }
+
+    /**
+     * 输出的参数
+     * @access public
+     * @param  mixed $options 输出参数
+     * @return $this
+     */
+    public function options($options = [])
+    {
+        $this->options = array_merge($this->options, $options);
+
+        return $this;
+    }
+
+    /**
+     * 输出数据设置
+     * @access public
+     * @param  mixed $data 输出数据
+     * @return $this
+     */
+    public function data($data)
+    {
+        $this->data = $data;
+
+        return $this;
+    }
+
+    /**
+     * 是否允许请求缓存
+     * @access public
+     * @param  bool $cache 允许请求缓存
+     * @return $this
+     */
+    public function allowCache($cache)
+    {
+        $this->allowCache = $cache;
+
+        return $this;
+    }
+
+    /**
+     * 设置响应头
+     * @access public
+     * @param  string|array $name  参数名
+     * @param  string       $value 参数值
+     * @return $this
+     */
+    public function header($name, $value = null)
+    {
+        if (is_array($name)) {
+            $this->header = array_merge($this->header, $name);
+        } else {
+            $this->header[$name] = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 设置页面输出内容
+     * @access public
+     * @param  mixed $content
+     * @return $this
+     */
+    public function content($content)
+    {
+        if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([
+            $content,
+            '__toString',
+        ])
+        ) {
+            throw new \InvalidArgumentException(sprintf('variable type error: %s', gettype($content)));
+        }
+
+        $this->content = (string) $content;
+
+        return $this;
+    }
+
+    /**
+     * 发送HTTP状态
+     * @access public
+     * @param  integer $code 状态码
+     * @return $this
+     */
+    public function code($code)
+    {
+        $this->code = $code;
+
+        return $this;
+    }
+
+    /**
+     * LastModified
+     * @access public
+     * @param  string $time
+     * @return $this
+     */
+    public function lastModified($time)
+    {
+        $this->header['Last-Modified'] = $time;
+
+        return $this;
+    }
+
+    /**
+     * Expires
+     * @access public
+     * @param  string $time
+     * @return $this
+     */
+    public function expires($time)
+    {
+        $this->header['Expires'] = $time;
+
+        return $this;
+    }
+
+    /**
+     * ETag
+     * @access public
+     * @param  string $eTag
+     * @return $this
+     */
+    public function eTag($eTag)
+    {
+        $this->header['ETag'] = $eTag;
+
+        return $this;
+    }
+
+    /**
+     * 页面缓存控制
+     * @access public
+     * @param  string $cache 缓存设置
+     * @return $this
+     */
+    public function cacheControl($cache)
+    {
+        $this->header['Cache-control'] = $cache;
+
+        return $this;
+    }
+
+    /**
+     * 设置页面不做任何缓存
+     * @access public
+     * @return $this
+     */
+    public function noCache()
+    {
+        $this->header['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0';
+        $this->header['Pragma']        = 'no-cache';
+
+        return $this;
+    }
+
+    /**
+     * 页面输出类型
+     * @access public
+     * @param  string $contentType 输出类型
+     * @param  string $charset     输出编码
+     * @return $this
+     */
+    public function contentType($contentType, $charset = 'utf-8')
+    {
+        $this->header['Content-Type'] = $contentType . '; charset=' . $charset;
+
+        return $this;
+    }
+
+    /**
+     * 获取头部信息
+     * @access public
+     * @param  string $name 头部名称
+     * @return mixed
+     */
+    public function getHeader($name = '')
+    {
+        if (!empty($name)) {
+            return isset($this->header[$name]) ? $this->header[$name] : null;
+        }
+
+        return $this->header;
+    }
+
+    /**
+     * 获取原始数据
+     * @access public
+     * @return mixed
+     */
+    public function getData()
+    {
+        return $this->data;
+    }
+
+    /**
+     * 获取输出数据
+     * @access public
+     * @return mixed
+     */
+    public function getContent()
+    {
+        if (null == $this->content) {
+            $content = $this->output($this->data);
+
+            if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([
+                $content,
+                '__toString',
+            ])
+            ) {
+                throw new \InvalidArgumentException(sprintf('variable type error: %s', gettype($content)));
+            }
+
+            $this->content = (string) $content;
+        }
+
+        return $this->content;
+    }
+
+    /**
+     * 获取状态码
+     * @access public
+     * @return integer
+     */
+    public function getCode()
+    {
+        return $this->code;
+    }
+
+    public function __debugInfo()
+    {
+        $data = get_object_vars($this);
+        unset($data['app']);
+
+        return $data;
+    }
+}

+ 990 - 0
thinkphp/library/think/Route.php

@@ -0,0 +1,990 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use think\exception\RouteNotFoundException;
+use think\route\AliasRule;
+use think\route\dispatch\Url as UrlDispatch;
+use think\route\Domain;
+use think\route\Resource;
+use think\route\RuleGroup;
+use think\route\RuleItem;
+
+class Route
+{
+    /**
+     * REST定义
+     * @var array
+     */
+    protected $rest = [
+        'index'  => ['get', '', 'index'],
+        'create' => ['get', '/create', 'create'],
+        'edit'   => ['get', '/<id>/edit', 'edit'],
+        'read'   => ['get', '/<id>', 'read'],
+        'save'   => ['post', '', 'save'],
+        'update' => ['put', '/<id>', 'update'],
+        'delete' => ['delete', '/<id>', 'delete'],
+    ];
+
+    /**
+     * 请求方法前缀定义
+     * @var array
+     */
+    protected $methodPrefix = [
+        'get'    => 'get',
+        'post'   => 'post',
+        'put'    => 'put',
+        'delete' => 'delete',
+        'patch'  => 'patch',
+    ];
+
+    /**
+     * 应用对象
+     * @var App
+     */
+    protected $app;
+
+    /**
+     * 请求对象
+     * @var Request
+     */
+    protected $request;
+
+    /**
+     * 当前HOST
+     * @var string
+     */
+    protected $host;
+
+    /**
+     * 当前域名
+     * @var string
+     */
+    protected $domain;
+
+    /**
+     * 当前分组对象
+     * @var RuleGroup
+     */
+    protected $group;
+
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $config = [];
+
+    /**
+     * 路由绑定
+     * @var array
+     */
+    protected $bind = [];
+
+    /**
+     * 域名对象
+     * @var array
+     */
+    protected $domains = [];
+
+    /**
+     * 跨域路由规则
+     * @var RuleGroup
+     */
+    protected $cross;
+
+    /**
+     * 路由别名
+     * @var array
+     */
+    protected $alias = [];
+
+    /**
+     * 路由是否延迟解析
+     * @var bool
+     */
+    protected $lazy = true;
+
+    /**
+     * 路由是否测试模式
+     * @var bool
+     */
+    protected $isTest;
+
+    /**
+     * (分组)路由规则是否合并解析
+     * @var bool
+     */
+    protected $mergeRuleRegex = true;
+
+    /**
+     * 路由解析自动搜索多级控制器
+     * @var bool
+     */
+    protected $autoSearchController = true;
+
+    public function __construct(App $app, array $config = [])
+    {
+        $this->app     = $app;
+        $this->request = $app['request'];
+        $this->config  = $config;
+
+        $this->host = $this->request->host(true) ?: $config['app_host'];
+
+        $this->setDefaultDomain();
+    }
+
+    public function config($name = null)
+    {
+        if (is_null($name)) {
+            return $this->config;
+        }
+
+        return isset($this->config[$name]) ? $this->config[$name] : null;
+    }
+
+    /**
+     * 配置
+     * @access public
+     * @param  array $config
+     * @return void
+     */
+    public function setConfig(array $config = [])
+    {
+        $this->config = array_merge($this->config, array_change_key_case($config));
+    }
+
+    public static function __make(App $app, Config $config)
+    {
+        $config = $config->pull('app');
+        $route  = new static($app, $config);
+
+        $route->lazy($config['url_lazy_route'])
+            ->autoSearchController($config['controller_auto_search'])
+            ->mergeRuleRegex($config['route_rule_merge']);
+
+        return $route;
+    }
+
+    /**
+     * 设置路由的请求对象实例
+     * @access public
+     * @param  Request     $request   请求对象实例
+     * @return void
+     */
+    public function setRequest($request)
+    {
+        $this->request = $request;
+    }
+
+    /**
+     * 设置路由域名及分组(包括资源路由)是否延迟解析
+     * @access public
+     * @param  bool     $lazy   路由是否延迟解析
+     * @return $this
+     */
+    public function lazy($lazy = true)
+    {
+        $this->lazy = $lazy;
+        return $this;
+    }
+
+    /**
+     * 设置路由为测试模式
+     * @access public
+     * @param  bool     $test   路由是否测试模式
+     * @return void
+     */
+    public function setTestMode($test)
+    {
+        $this->isTest = $test;
+    }
+
+    /**
+     * 检查路由是否为测试模式
+     * @access public
+     * @return bool
+     */
+    public function isTest()
+    {
+        return $this->isTest;
+    }
+
+    /**
+     * 设置路由域名及分组(包括资源路由)是否合并解析
+     * @access public
+     * @param  bool     $merge   路由是否合并解析
+     * @return $this
+     */
+    public function mergeRuleRegex($merge = true)
+    {
+        $this->mergeRuleRegex = $merge;
+        $this->group->mergeRuleRegex($merge);
+
+        return $this;
+    }
+
+    /**
+     * 设置路由自动解析是否搜索多级控制器
+     * @access public
+     * @param  bool     $auto   是否自动搜索多级控制器
+     * @return $this
+     */
+    public function autoSearchController($auto = true)
+    {
+        $this->autoSearchController = $auto;
+        return $this;
+    }
+
+    /**
+     * 初始化默认域名
+     * @access protected
+     * @return void
+     */
+    protected function setDefaultDomain()
+    {
+        // 默认域名
+        $this->domain = $this->host;
+
+        // 注册默认域名
+        $domain = new Domain($this, $this->host);
+
+        $this->domains[$this->host] = $domain;
+
+        // 默认分组
+        $this->group = $domain;
+    }
+
+    /**
+     * 设置当前域名
+     * @access public
+     * @param  RuleGroup    $group 域名
+     * @return void
+     */
+    public function setGroup(RuleGroup $group)
+    {
+        $this->group = $group;
+    }
+
+    /**
+     * 获取当前分组
+     * @access public
+     * @return RuleGroup
+     */
+    public function getGroup()
+    {
+        return $this->group;
+    }
+
+    /**
+     * 注册变量规则
+     * @access public
+     * @param  string|array  $name 变量名
+     * @param  string        $rule 变量规则
+     * @return $this
+     */
+    public function pattern($name, $rule = '')
+    {
+        $this->group->pattern($name, $rule);
+
+        return $this;
+    }
+
+    /**
+     * 注册路由参数
+     * @access public
+     * @param  string|array  $name  参数名
+     * @param  mixed         $value 值
+     * @return $this
+     */
+    public function option($name, $value = '')
+    {
+        $this->group->option($name, $value);
+
+        return $this;
+    }
+
+    /**
+     * 注册域名路由
+     * @access public
+     * @param  string|array  $name 子域名
+     * @param  mixed         $rule 路由规则
+     * @param  array         $option 路由参数
+     * @param  array         $pattern 变量规则
+     * @return Domain
+     */
+    public function domain($name, $rule = '', $option = [], $pattern = [])
+    {
+        // 支持多个域名使用相同路由规则
+        $domainName = is_array($name) ? array_shift($name) : $name;
+
+        if ('*' != $domainName && false === strpos($domainName, '.')) {
+            $domainName .= '.' . $this->request->rootDomain();
+        }
+
+        if (!isset($this->domains[$domainName])) {
+            $domain = (new Domain($this, $domainName, $rule, $option, $pattern))
+                ->lazy($this->lazy)
+                ->mergeRuleRegex($this->mergeRuleRegex);
+
+            $this->domains[$domainName] = $domain;
+        } else {
+            $domain = $this->domains[$domainName];
+            $domain->parseGroupRule($rule);
+        }
+
+        if (is_array($name) && !empty($name)) {
+            $root = $this->request->rootDomain();
+            foreach ($name as $item) {
+                if (false === strpos($item, '.')) {
+                    $item .= '.' . $root;
+                }
+
+                $this->domains[$item] = $domainName;
+            }
+        }
+
+        // 返回域名对象
+        return $domain;
+    }
+
+    /**
+     * 获取域名
+     * @access public
+     * @return array
+     */
+    public function getDomains()
+    {
+        return $this->domains;
+    }
+
+    /**
+     * 设置路由绑定
+     * @access public
+     * @param  string     $bind 绑定信息
+     * @param  string     $domain 域名
+     * @return $this
+     */
+    public function bind($bind, $domain = null)
+    {
+        $domain = is_null($domain) ? $this->domain : $domain;
+
+        $this->bind[$domain] = $bind;
+
+        return $this;
+    }
+
+    /**
+     * 读取路由绑定
+     * @access public
+     * @param  string    $domain 域名
+     * @return string|null
+     */
+    public function getBind($domain = null)
+    {
+        if (is_null($domain)) {
+            $domain = $this->domain;
+        } elseif (true === $domain) {
+            return $this->bind;
+        } elseif (false === strpos($domain, '.')) {
+            $domain .= '.' . $this->request->rootDomain();
+        }
+
+        $subDomain = $this->request->subDomain();
+
+        if (strpos($subDomain, '.')) {
+            $name = '*' . strstr($subDomain, '.');
+        }
+
+        if (isset($this->bind[$domain])) {
+            $result = $this->bind[$domain];
+        } elseif (isset($name) && isset($this->bind[$name])) {
+            $result = $this->bind[$name];
+        } elseif (!empty($subDomain) && isset($this->bind['*'])) {
+            $result = $this->bind['*'];
+        } else {
+            $result = null;
+        }
+
+        return $result;
+    }
+
+    /**
+     * 读取路由标识
+     * @access public
+     * @param  string    $name 路由标识
+     * @param  string    $domain 域名
+     * @return mixed
+     */
+    public function getName($name = null, $domain = null, $method = '*')
+    {
+        return $this->app['rule_name']->get($name, $domain, $method);
+    }
+
+    /**
+     * 读取路由
+     * @access public
+     * @param  string    $rule 路由规则
+     * @param  string    $domain 域名
+     * @return array
+     */
+    public function getRule($rule, $domain = null)
+    {
+        if (is_null($domain)) {
+            $domain = $this->domain;
+        }
+
+        return $this->app['rule_name']->getRule($rule, $domain);
+    }
+
+    /**
+     * 读取路由
+     * @access public
+     * @param  string    $domain 域名
+     * @return array
+     */
+    public function getRuleList($domain = null)
+    {
+        return $this->app['rule_name']->getRuleList($domain);
+    }
+
+    /**
+     * 批量导入路由标识
+     * @access public
+     * @param  array    $name 路由标识
+     * @return $this
+     */
+    public function setName($name)
+    {
+        $this->app['rule_name']->import($name);
+        return $this;
+    }
+
+    /**
+     * 导入配置文件的路由规则
+     * @access public
+     * @param  array     $rules 路由规则
+     * @param  string    $type  请求类型
+     * @return void
+     */
+    public function import(array $rules, $type = '*')
+    {
+        // 检查域名部署
+        if (isset($rules['__domain__'])) {
+            foreach ($rules['__domain__'] as $key => $rule) {
+                $this->domain($key, $rule);
+            }
+            unset($rules['__domain__']);
+        }
+
+        // 检查变量规则
+        if (isset($rules['__pattern__'])) {
+            $this->pattern($rules['__pattern__']);
+            unset($rules['__pattern__']);
+        }
+
+        // 检查路由别名
+        if (isset($rules['__alias__'])) {
+            foreach ($rules['__alias__'] as $key => $val) {
+                $this->alias($key, $val);
+            }
+            unset($rules['__alias__']);
+        }
+
+        // 检查资源路由
+        if (isset($rules['__rest__'])) {
+            foreach ($rules['__rest__'] as $key => $rule) {
+                $this->resource($key, $rule);
+            }
+            unset($rules['__rest__']);
+        }
+
+        // 检查路由规则(包含分组)
+        foreach ($rules as $key => $val) {
+            if (is_numeric($key)) {
+                $key = array_shift($val);
+            }
+
+            if (empty($val)) {
+                continue;
+            }
+
+            if (is_string($key) && 0 === strpos($key, '[')) {
+                $key = substr($key, 1, -1);
+                $this->group($key, $val);
+            } elseif (is_array($val)) {
+                $this->rule($key, $val[0], $type, $val[1], isset($val[2]) ? $val[2] : []);
+            } else {
+                $this->rule($key, $val, $type);
+            }
+        }
+    }
+
+    /**
+     * 注册路由规则
+     * @access public
+     * @param  string    $rule       路由规则
+     * @param  mixed     $route      路由地址
+     * @param  string    $method     请求类型
+     * @param  array     $option     路由参数
+     * @param  array     $pattern    变量规则
+     * @return RuleItem
+     */
+    public function rule($rule, $route, $method = '*', array $option = [], array $pattern = [])
+    {
+        return $this->group->addRule($rule, $route, $method, $option, $pattern);
+    }
+
+    /**
+     * 设置跨域有效路由规则
+     * @access public
+     * @param  Rule      $rule      路由规则
+     * @param  string    $method    请求类型
+     * @return $this
+     */
+    public function setCrossDomainRule($rule, $method = '*')
+    {
+        if (!isset($this->cross)) {
+            $this->cross = (new RuleGroup($this))->mergeRuleRegex($this->mergeRuleRegex);
+        }
+
+        $this->cross->addRuleItem($rule, $method);
+
+        return $this;
+    }
+
+    /**
+     * 批量注册路由规则
+     * @access public
+     * @param  array     $rules      路由规则
+     * @param  string    $method     请求类型
+     * @param  array     $option     路由参数
+     * @param  array     $pattern    变量规则
+     * @return void
+     */
+    public function rules($rules, $method = '*', array $option = [], array $pattern = [])
+    {
+        $this->group->addRules($rules, $method, $option, $pattern);
+    }
+
+    /**
+     * 注册路由分组
+     * @access public
+     * @param  string|array      $name       分组名称或者参数
+     * @param  array|\Closure    $route      分组路由
+     * @param  array             $option     路由参数
+     * @param  array             $pattern    变量规则
+     * @return RuleGroup
+     */
+    public function group($name, $route, array $option = [], array $pattern = [])
+    {
+        if (is_array($name)) {
+            $option = $name;
+            $name   = isset($option['name']) ? $option['name'] : '';
+        }
+
+        return (new RuleGroup($this, $this->group, $name, $route, $option, $pattern))
+            ->lazy($this->lazy)
+            ->mergeRuleRegex($this->mergeRuleRegex);
+    }
+
+    /**
+     * 注册路由
+     * @access public
+     * @param  string    $rule 路由规则
+     * @param  mixed     $route 路由地址
+     * @param  array     $option 路由参数
+     * @param  array     $pattern 变量规则
+     * @return RuleItem
+     */
+    public function any($rule, $route = '', array $option = [], array $pattern = [])
+    {
+        return $this->rule($rule, $route, '*', $option, $pattern);
+    }
+
+    /**
+     * 注册GET路由
+     * @access public
+     * @param  string    $rule 路由规则
+     * @param  mixed     $route 路由地址
+     * @param  array     $option 路由参数
+     * @param  array     $pattern 变量规则
+     * @return RuleItem
+     */
+    public function get($rule, $route = '', array $option = [], array $pattern = [])
+    {
+        return $this->rule($rule, $route, 'GET', $option, $pattern);
+    }
+
+    /**
+     * 注册POST路由
+     * @access public
+     * @param  string    $rule 路由规则
+     * @param  mixed     $route 路由地址
+     * @param  array     $option 路由参数
+     * @param  array     $pattern 变量规则
+     * @return RuleItem
+     */
+    public function post($rule, $route = '', array $option = [], array $pattern = [])
+    {
+        return $this->rule($rule, $route, 'POST', $option, $pattern);
+    }
+
+    /**
+     * 注册PUT路由
+     * @access public
+     * @param  string    $rule 路由规则
+     * @param  mixed     $route 路由地址
+     * @param  array     $option 路由参数
+     * @param  array     $pattern 变量规则
+     * @return RuleItem
+     */
+    public function put($rule, $route = '', array $option = [], array $pattern = [])
+    {
+        return $this->rule($rule, $route, 'PUT', $option, $pattern);
+    }
+
+    /**
+     * 注册DELETE路由
+     * @access public
+     * @param  string    $rule 路由规则
+     * @param  mixed     $route 路由地址
+     * @param  array     $option 路由参数
+     * @param  array     $pattern 变量规则
+     * @return RuleItem
+     */
+    public function delete($rule, $route = '', array $option = [], array $pattern = [])
+    {
+        return $this->rule($rule, $route, 'DELETE', $option, $pattern);
+    }
+
+    /**
+     * 注册PATCH路由
+     * @access public
+     * @param  string    $rule 路由规则
+     * @param  mixed     $route 路由地址
+     * @param  array     $option 路由参数
+     * @param  array     $pattern 变量规则
+     * @return RuleItem
+     */
+    public function patch($rule, $route = '', array $option = [], array $pattern = [])
+    {
+        return $this->rule($rule, $route, 'PATCH', $option, $pattern);
+    }
+
+    /**
+     * 注册资源路由
+     * @access public
+     * @param  string    $rule 路由规则
+     * @param  string    $route 路由地址
+     * @param  array     $option 路由参数
+     * @param  array     $pattern 变量规则
+     * @return Resource
+     */
+    public function resource($rule, $route = '', array $option = [], array $pattern = [])
+    {
+        return (new Resource($this, $this->group, $rule, $route, $option, $pattern, $this->rest))
+            ->lazy($this->lazy);
+    }
+
+    /**
+     * 注册控制器路由 操作方法对应不同的请求前缀
+     * @access public
+     * @param  string    $rule 路由规则
+     * @param  string    $route 路由地址
+     * @param  array     $option 路由参数
+     * @param  array     $pattern 变量规则
+     * @return RuleGroup
+     */
+    public function controller($rule, $route = '', array $option = [], array $pattern = [])
+    {
+        $group = new RuleGroup($this, $this->group, $rule, null, $option, $pattern);
+
+        foreach ($this->methodPrefix as $type => $val) {
+            $group->addRule('<action>', $val . '<action>', $type);
+        }
+
+        return $group->prefix($route . '/');
+    }
+
+    /**
+     * 注册视图路由
+     * @access public
+     * @param  string|array $rule 路由规则
+     * @param  string       $template 路由模板地址
+     * @param  array        $vars 模板变量
+     * @param  array        $option 路由参数
+     * @param  array        $pattern 变量规则
+     * @return RuleItem
+     */
+    public function view($rule, $template = '', array $vars = [], array $option = [], array $pattern = [])
+    {
+        return $this->rule($rule, $template, 'GET', $option, $pattern)->view($vars);
+    }
+
+    /**
+     * 注册重定向路由
+     * @access public
+     * @param  string|array $rule 路由规则
+     * @param  string       $route 路由地址
+     * @param  array        $status 状态码
+     * @param  array        $option 路由参数
+     * @param  array        $pattern 变量规则
+     * @return RuleItem
+     */
+    public function redirect($rule, $route = '', $status = 301, array $option = [], array $pattern = [])
+    {
+        return $this->rule($rule, $route, '*', $option, $pattern)->redirect()->status($status);
+    }
+
+    /**
+     * 注册别名路由
+     * @access public
+     * @param  string  $rule 路由别名
+     * @param  string  $route 路由地址
+     * @param  array   $option 路由参数
+     * @return AliasRule
+     */
+    public function alias($rule, $route, array $option = [])
+    {
+        $aliasRule = new AliasRule($this, $this->group, $rule, $route, $option);
+
+        $this->alias[$rule] = $aliasRule;
+
+        return $aliasRule;
+    }
+
+    /**
+     * 获取别名路由定义
+     * @access public
+     * @param  string    $name 路由别名
+     * @return string|array|null
+     */
+    public function getAlias($name = null)
+    {
+        if (is_null($name)) {
+            return $this->alias;
+        }
+
+        return isset($this->alias[$name]) ? $this->alias[$name] : null;
+    }
+
+    /**
+     * 设置不同请求类型下面的方法前缀
+     * @access public
+     * @param  string|array  $method 请求类型
+     * @param  string        $prefix 类型前缀
+     * @return $this
+     */
+    public function setMethodPrefix($method, $prefix = '')
+    {
+        if (is_array($method)) {
+            $this->methodPrefix = array_merge($this->methodPrefix, array_change_key_case($method));
+        } else {
+            $this->methodPrefix[strtolower($method)] = $prefix;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 获取请求类型的方法前缀
+     * @access public
+     * @param  string    $method 请求类型
+     * @param  string    $prefix 类型前缀
+     * @return string|null
+     */
+    public function getMethodPrefix($method)
+    {
+        $method = strtolower($method);
+
+        return isset($this->methodPrefix[$method]) ? $this->methodPrefix[$method] : null;
+    }
+
+    /**
+     * rest方法定义和修改
+     * @access public
+     * @param  string        $name 方法名称
+     * @param  array|bool    $resource 资源
+     * @return $this
+     */
+    public function rest($name, $resource = [])
+    {
+        if (is_array($name)) {
+            $this->rest = $resource ? $name : array_merge($this->rest, $name);
+        } else {
+            $this->rest[$name] = $resource;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 获取rest方法定义的参数
+     * @access public
+     * @param  string        $name 方法名称
+     * @return array|null
+     */
+    public function getRest($name = null)
+    {
+        if (is_null($name)) {
+            return $this->rest;
+        }
+
+        return isset($this->rest[$name]) ? $this->rest[$name] : null;
+    }
+
+    /**
+     * 注册未匹配路由规则后的处理
+     * @access public
+     * @param  string    $route 路由地址
+     * @param  string    $method 请求类型
+     * @param  array     $option 路由参数
+     * @return RuleItem
+     */
+    public function miss($route, $method = '*', array $option = [])
+    {
+        return $this->group->addMissRule($route, $method, $option);
+    }
+
+    /**
+     * 注册一个自动解析的URL路由
+     * @access public
+     * @param  string    $route 路由地址
+     * @return RuleItem
+     */
+    public function auto($route)
+    {
+        return $this->group->addAutoRule($route);
+    }
+
+    /**
+     * 检测URL路由
+     * @access public
+     * @param  string    $url URL地址
+     * @param  bool      $must 是否强制路由
+     * @return Dispatch
+     * @throws RouteNotFoundException
+     */
+    public function check($url, $must = false)
+    {
+        // 自动检测域名路由
+        $domain = $this->checkDomain();
+        $url    = str_replace($this->config['pathinfo_depr'], '|', $url);
+
+        $completeMatch = $this->config['route_complete_match'];
+
+        $result = $domain->check($this->request, $url, $completeMatch);
+
+        if (false === $result && !empty($this->cross)) {
+            // 检测跨域路由
+            $result = $this->cross->check($this->request, $url, $completeMatch);
+        }
+
+        if (false !== $result) {
+            // 路由匹配
+            return $result;
+        } elseif ($must) {
+            // 强制路由不匹配则抛出异常
+            throw new RouteNotFoundException();
+        }
+
+        // 默认路由解析
+        return new UrlDispatch($this->request, $this->group, $url, [
+            'auto_search' => $this->autoSearchController,
+        ]);
+    }
+
+    /**
+     * 检测域名的路由规则
+     * @access protected
+     * @return Domain
+     */
+    protected function checkDomain()
+    {
+        // 获取当前子域名
+        $subDomain = $this->request->subDomain();
+
+        $item = false;
+
+        if ($subDomain && count($this->domains) > 1) {
+            $domain  = explode('.', $subDomain);
+            $domain2 = array_pop($domain);
+
+            if ($domain) {
+                // 存在三级域名
+                $domain3 = array_pop($domain);
+            }
+
+            if ($subDomain && isset($this->domains[$subDomain])) {
+                // 子域名配置
+                $item = $this->domains[$subDomain];
+            } elseif (isset($this->domains['*.' . $domain2]) && !empty($domain3)) {
+                // 泛三级域名
+                $item      = $this->domains['*.' . $domain2];
+                $panDomain = $domain3;
+            } elseif (isset($this->domains['*']) && !empty($domain2)) {
+                // 泛二级域名
+                if ('www' != $domain2) {
+                    $item      = $this->domains['*'];
+                    $panDomain = $domain2;
+                }
+            }
+
+            if (isset($panDomain)) {
+                // 保存当前泛域名
+                $this->request->setPanDomain($panDomain);
+            }
+        }
+
+        if (false === $item) {
+            // 检测当前完整域名
+            $item = $this->domains[$this->host];
+        }
+
+        if (is_string($item)) {
+            $item = $this->domains[$item];
+        }
+
+        return $item;
+    }
+
+    /**
+     * 清空路由规则
+     * @access public
+     * @return void
+     */
+    public function clear()
+    {
+        $this->app['rule_name']->clear();
+        $this->group->clear();
+    }
+
+    /**
+     * 设置全局的路由分组参数
+     * @access public
+     * @param  string    $method     方法名
+     * @param  array     $args       调用参数
+     * @return RuleGroup
+     */
+    public function __call($method, $args)
+    {
+        return call_user_func_array([$this->group, $method], $args);
+    }
+
+    public function __debugInfo()
+    {
+        $data = get_object_vars($this);
+        unset($data['app'], $data['request']);
+
+        return $data;
+    }
+}

+ 579 - 0
thinkphp/library/think/Session.php

@@ -0,0 +1,579 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+use think\exception\ClassNotFoundException;
+
+class Session
+{
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $config = [];
+
+    /**
+     * 前缀
+     * @var string
+     */
+    protected $prefix = '';
+
+    /**
+     * 是否初始化
+     * @var bool
+     */
+    protected $init = null;
+
+    /**
+     * 锁驱动
+     * @var object
+     */
+    protected $lockDriver = null;
+
+    /**
+     * 锁key
+     * @var string
+     */
+    protected $sessKey = 'PHPSESSID';
+
+    /**
+     * 锁超时时间
+     * @var integer
+     */
+    protected $lockTimeout = 3;
+
+    /**
+     * 是否启用锁机制
+     * @var bool
+     */
+    protected $lock = false;
+
+    public function __construct(array $config = [])
+    {
+        $this->config = $config;
+    }
+
+    /**
+     * 设置或者获取session作用域(前缀)
+     * @access public
+     * @param  string $prefix
+     * @return string|void
+     */
+    public function prefix($prefix = '')
+    {
+        empty($this->init) && $this->boot();
+
+        if (empty($prefix) && null !== $prefix) {
+            return $this->prefix;
+        } else {
+            $this->prefix = $prefix;
+        }
+    }
+
+    public static function __make(Config $config)
+    {
+        return new static($config->pull('session'));
+    }
+
+    /**
+     * 配置
+     * @access public
+     * @param  array $config
+     * @return void
+     */
+    public function setConfig(array $config = [])
+    {
+        $this->config = array_merge($this->config, array_change_key_case($config));
+
+        if (isset($config['prefix'])) {
+            $this->prefix = $config['prefix'];
+        }
+
+        if (isset($config['use_lock'])) {
+            $this->lock = $config['use_lock'];
+        }
+    }
+
+    /**
+     * 设置已经初始化
+     * @access public
+     * @return void
+     */
+    public function inited()
+    {
+        $this->init = true;
+    }
+
+    /**
+     * session初始化
+     * @access public
+     * @param  array $config
+     * @return void
+     * @throws \think\Exception
+     */
+    public function init(array $config = [])
+    {
+        $config = $config ?: $this->config;
+
+        $isDoStart = false;
+        if (isset($config['use_trans_sid'])) {
+            ini_set('session.use_trans_sid', $config['use_trans_sid'] ? 1 : 0);
+        }
+
+        // 启动session
+        if (!empty($config['auto_start']) && PHP_SESSION_ACTIVE != session_status()) {
+            ini_set('session.auto_start', 0);
+            $isDoStart = true;
+        }
+
+        if (isset($config['prefix'])) {
+            $this->prefix = $config['prefix'];
+        }
+
+        if (isset($config['use_lock'])) {
+            $this->lock = $config['use_lock'];
+        }
+
+        if (isset($config['var_session_id']) && isset($_REQUEST[$config['var_session_id']])) {
+            session_id($_REQUEST[$config['var_session_id']]);
+        } elseif (isset($config['id']) && !empty($config['id'])) {
+            session_id($config['id']);
+        }
+
+        if (isset($config['name'])) {
+            session_name($config['name']);
+        }
+
+        if (isset($config['path'])) {
+            session_save_path($config['path']);
+        }
+
+        if (isset($config['domain'])) {
+            ini_set('session.cookie_domain', $config['domain']);
+        }
+
+        if (isset($config['expire'])) {
+            ini_set('session.gc_maxlifetime', $config['expire']);
+            ini_set('session.cookie_lifetime', $config['expire']);
+        }
+
+        if (isset($config['secure'])) {
+            ini_set('session.cookie_secure', $config['secure']);
+        }
+
+        if (isset($config['httponly'])) {
+            ini_set('session.cookie_httponly', $config['httponly']);
+        }
+
+        if (isset($config['use_cookies'])) {
+            ini_set('session.use_cookies', $config['use_cookies'] ? 1 : 0);
+        }
+
+        if (isset($config['cache_limiter'])) {
+            session_cache_limiter($config['cache_limiter']);
+        }
+
+        if (isset($config['cache_expire'])) {
+            session_cache_expire($config['cache_expire']);
+        }
+
+        if (!empty($config['type'])) {
+            // 读取session驱动
+            $class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\session\\driver\\' . ucwords($config['type']);
+
+            // 检查驱动类
+            if (!class_exists($class) || !session_set_save_handler(new $class($config))) {
+                throw new ClassNotFoundException('error session handler:' . $class, $class);
+            }
+        }
+
+        if ($isDoStart) {
+            $this->start();
+        } else {
+            $this->init = false;
+        }
+
+        return $this;
+    }
+
+    /**
+     * session自动启动或者初始化
+     * @access public
+     * @return void
+     */
+    public function boot()
+    {
+        if (is_null($this->init)) {
+            $this->init();
+        }
+
+        if (false === $this->init) {
+            if (PHP_SESSION_ACTIVE != session_status()) {
+                $this->start();
+            }
+            $this->init = true;
+        }
+    }
+
+    /**
+     * session设置
+     * @access public
+     * @param  string        $name session名称
+     * @param  mixed         $value session值
+     * @param  string|null   $prefix 作用域(前缀)
+     * @return void
+     */
+    public function set($name, $value, $prefix = null)
+    {
+        $this->lock();
+
+        empty($this->init) && $this->boot();
+
+        $prefix = !is_null($prefix) ? $prefix : $this->prefix;
+
+        if (strpos($name, '.')) {
+            // 二维数组赋值
+            list($name1, $name2) = explode('.', $name);
+            if ($prefix) {
+                $_SESSION[$prefix][$name1][$name2] = $value;
+            } else {
+                $_SESSION[$name1][$name2] = $value;
+            }
+        } elseif ($prefix) {
+            $_SESSION[$prefix][$name] = $value;
+        } else {
+            $_SESSION[$name] = $value;
+        }
+
+        $this->unlock();
+    }
+
+    /**
+     * session获取
+     * @access public
+     * @param  string        $name session名称
+     * @param  string|null   $prefix 作用域(前缀)
+     * @return mixed
+     */
+    public function get($name = '', $prefix = null)
+    {
+        $this->lock();
+
+        empty($this->init) && $this->boot();
+
+        $prefix = !is_null($prefix) ? $prefix : $this->prefix;
+
+        $value = $prefix ? (!empty($_SESSION[$prefix]) ? $_SESSION[$prefix] : []) : $_SESSION;
+
+        if ('' != $name) {
+            $name = explode('.', $name);
+
+            foreach ($name as $val) {
+                if (isset($value[$val])) {
+                    $value = $value[$val];
+                } else {
+                    $value = null;
+                    break;
+                }
+            }
+        }
+
+        $this->unlock();
+
+        return $value;
+    }
+
+    /**
+     * session 读写锁驱动实例化
+     */
+    protected function initDriver()
+    {
+        $config = $this->config;
+
+        if (!empty($config['type']) && isset($config['use_lock']) && $config['use_lock']) {
+            // 读取session驱动
+            $class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\session\\driver\\' . ucwords($config['type']);
+
+            // 检查驱动类及类中是否存在 lock 和 unlock 函数
+            if (class_exists($class) && method_exists($class, 'lock') && method_exists($class, 'unlock')) {
+                $this->lockDriver = new $class($config);
+            }
+        }
+
+        // 通过cookie获得session_id
+        if (isset($config['name']) && $config['name']) {
+            $this->sessKey = $config['name'];
+        }
+
+        if (isset($config['lock_timeout']) && $config['lock_timeout'] > 0) {
+            $this->lockTimeout = $config['lock_timeout'];
+        }
+    }
+
+    /**
+     * session 读写加锁
+     * @access protected
+     * @return void
+     */
+    protected function lock()
+    {
+        if (empty($this->lock)) {
+            return;
+        }
+
+        $this->initDriver();
+
+        if (null !== $this->lockDriver && method_exists($this->lockDriver, 'lock')) {
+            $t = time();
+            // 使用 session_id 作为互斥条件,即只对同一 session_id 的会话互斥。第一次请求没有 session_id
+            $sessID = isset($_COOKIE[$this->sessKey]) ? $_COOKIE[$this->sessKey] : '';
+
+            do {
+                if (time() - $t > $this->lockTimeout) {
+                    $this->unlock();
+                }
+            } while (!$this->lockDriver->lock($sessID, $this->lockTimeout));
+        }
+    }
+
+    /**
+     * session 读写解锁
+     * @access protected
+     * @return void
+     */
+    protected function unlock()
+    {
+        if (empty($this->lock)) {
+            return;
+        }
+
+        $this->pause();
+
+        if ($this->lockDriver && method_exists($this->lockDriver, 'unlock')) {
+            $sessID = isset($_COOKIE[$this->sessKey]) ? $_COOKIE[$this->sessKey] : '';
+            $this->lockDriver->unlock($sessID);
+        }
+    }
+
+    /**
+     * session获取并删除
+     * @access public
+     * @param  string        $name session名称
+     * @param  string|null   $prefix 作用域(前缀)
+     * @return mixed
+     */
+    public function pull($name, $prefix = null)
+    {
+        $result = $this->get($name, $prefix);
+
+        if ($result) {
+            $this->delete($name, $prefix);
+            return $result;
+        } else {
+            return;
+        }
+    }
+
+    /**
+     * session设置 下一次请求有效
+     * @access public
+     * @param  string        $name session名称
+     * @param  mixed         $value session值
+     * @param  string|null   $prefix 作用域(前缀)
+     * @return void
+     */
+    public function flash($name, $value)
+    {
+        $this->set($name, $value);
+
+        if (!$this->has('__flash__.__time__')) {
+            $this->set('__flash__.__time__', $_SERVER['REQUEST_TIME_FLOAT']);
+        }
+
+        $this->push('__flash__', $name);
+    }
+
+    /**
+     * 清空当前请求的session数据
+     * @access public
+     * @return void
+     */
+    public function flush()
+    {
+        if (!$this->init) {
+            return;
+        }
+
+        $item = $this->get('__flash__');
+
+        if (!empty($item)) {
+            $time = $item['__time__'];
+
+            if ($_SERVER['REQUEST_TIME_FLOAT'] > $time) {
+                unset($item['__time__']);
+                $this->delete($item);
+                $this->set('__flash__', []);
+            }
+        }
+    }
+
+    /**
+     * 删除session数据
+     * @access public
+     * @param  string|array  $name session名称
+     * @param  string|null   $prefix 作用域(前缀)
+     * @return void
+     */
+    public function delete($name, $prefix = null)
+    {
+        empty($this->init) && $this->boot();
+
+        $prefix = !is_null($prefix) ? $prefix : $this->prefix;
+
+        if (is_array($name)) {
+            foreach ($name as $key) {
+                $this->delete($key, $prefix);
+            }
+        } elseif (strpos($name, '.')) {
+            list($name1, $name2) = explode('.', $name);
+            if ($prefix) {
+                unset($_SESSION[$prefix][$name1][$name2]);
+            } else {
+                unset($_SESSION[$name1][$name2]);
+            }
+        } else {
+            if ($prefix) {
+                unset($_SESSION[$prefix][$name]);
+            } else {
+                unset($_SESSION[$name]);
+            }
+        }
+    }
+
+    /**
+     * 清空session数据
+     * @access public
+     * @param  string|null   $prefix 作用域(前缀)
+     * @return void
+     */
+    public function clear($prefix = null)
+    {
+        empty($this->init) && $this->boot();
+        $prefix = !is_null($prefix) ? $prefix : $this->prefix;
+
+        if ($prefix) {
+            unset($_SESSION[$prefix]);
+        } else {
+            $_SESSION = [];
+        }
+    }
+
+    /**
+     * 判断session数据
+     * @access public
+     * @param  string        $name session名称
+     * @param  string|null   $prefix
+     * @return bool
+     */
+    public function has($name, $prefix = null)
+    {
+        empty($this->init) && $this->boot();
+
+        $prefix = !is_null($prefix) ? $prefix : $this->prefix;
+        $value  = $prefix ? (!empty($_SESSION[$prefix]) ? $_SESSION[$prefix] : []) : $_SESSION;
+
+        $name = explode('.', $name);
+
+        foreach ($name as $val) {
+            if (!isset($value[$val])) {
+                return false;
+            } else {
+                $value = $value[$val];
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * 添加数据到一个session数组
+     * @access public
+     * @param  string  $key
+     * @param  mixed   $value
+     * @return void
+     */
+    public function push($key, $value)
+    {
+        $array = $this->get($key);
+
+        if (is_null($array)) {
+            $array = [];
+        }
+
+        $array[] = $value;
+
+        $this->set($key, $array);
+    }
+
+    /**
+     * 启动session
+     * @access public
+     * @return void
+     */
+    public function start()
+    {
+        session_start();
+
+        $this->init = true;
+    }
+
+    /**
+     * 销毁session
+     * @access public
+     * @return void
+     */
+    public function destroy()
+    {
+        if (!empty($_SESSION)) {
+            $_SESSION = [];
+        }
+
+        session_unset();
+        session_destroy();
+
+        $this->init       = null;
+        $this->lockDriver = null;
+    }
+
+    /**
+     * 重新生成session_id
+     * @access public
+     * @param  bool $delete 是否删除关联会话文件
+     * @return void
+     */
+    public function regenerate($delete = false)
+    {
+        session_regenerate_id($delete);
+    }
+
+    /**
+     * 暂停session
+     * @access public
+     * @return void
+     */
+    public function pause()
+    {
+        // 暂停session
+        session_write_close();
+        $this->init = false;
+    }
+}

File diff suppressed because it is too large
+ 1318 - 0
thinkphp/library/think/Template.php


+ 412 - 0
thinkphp/library/think/Url.php

@@ -0,0 +1,412 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+class Url
+{
+    /**
+     * 配置参数
+     * @var array
+     */
+    protected $config = [];
+
+    /**
+     * ROOT地址
+     * @var string
+     */
+    protected $root;
+
+    /**
+     * 绑定检查
+     * @var bool
+     */
+    protected $bindCheck;
+
+    /**
+     * 应用对象
+     * @var App
+     */
+    protected $app;
+
+    public function __construct(App $app, array $config = [])
+    {
+        $this->app    = $app;
+        $this->config = $config;
+
+        if (is_file($app->getRuntimePath() . 'route.php')) {
+            // 读取路由映射文件
+            $app['route']->setName(include $app->getRuntimePath() . 'route.php');
+        }
+    }
+
+    /**
+     * 初始化
+     * @access public
+     * @param  array $config
+     * @return void
+     */
+    public function init(array $config = [])
+    {
+        $this->config = array_merge($this->config, array_change_key_case($config));
+    }
+
+    public static function __make(App $app, Config $config)
+    {
+        return new static($app, $config->pull('app'));
+    }
+
+    /**
+     * URL生成 支持路由反射
+     * @access public
+     * @param  string            $url 路由地址
+     * @param  string|array      $vars 参数(支持数组和字符串)a=val&b=val2... ['a'=>'val1', 'b'=>'val2']
+     * @param  string|bool       $suffix 伪静态后缀,默认为true表示获取配置值
+     * @param  boolean|string    $domain 是否显示域名 或者直接传入域名
+     * @return string
+     */
+    public function build($url = '', $vars = '', $suffix = true, $domain = false)
+    {
+        // 解析URL
+        if (0 === strpos($url, '[') && $pos = strpos($url, ']')) {
+            // [name] 表示使用路由命名标识生成URL
+            $name = substr($url, 1, $pos - 1);
+            $url  = 'name' . substr($url, $pos + 1);
+        }
+
+        if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
+            $info = parse_url($url);
+            $url  = !empty($info['path']) ? $info['path'] : '';
+
+            if (isset($info['fragment'])) {
+                // 解析锚点
+                $anchor = $info['fragment'];
+
+                if (false !== strpos($anchor, '?')) {
+                    // 解析参数
+                    list($anchor, $info['query']) = explode('?', $anchor, 2);
+                }
+
+                if (false !== strpos($anchor, '@')) {
+                    // 解析域名
+                    list($anchor, $domain) = explode('@', $anchor, 2);
+                }
+            } elseif (strpos($url, '@') && false === strpos($url, '\\')) {
+                // 解析域名
+                list($url, $domain) = explode('@', $url, 2);
+            }
+        }
+
+        // 解析参数
+        if (is_string($vars)) {
+            // aaa=1&bbb=2 转换成数组
+            parse_str($vars, $vars);
+        }
+
+        if ($url) {
+            $checkName   = isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : '');
+            $checkDomain = $domain && is_string($domain) ? $domain : null;
+
+            $rule = $this->app['route']->getName($checkName, $checkDomain);
+
+            if (is_null($rule) && isset($info['query'])) {
+                $rule = $this->app['route']->getName($url);
+                // 解析地址里面参数 合并到vars
+                parse_str($info['query'], $params);
+                $vars = array_merge($params, $vars);
+                unset($info['query']);
+            }
+        }
+
+        if (!empty($rule) && $match = $this->getRuleUrl($rule, $vars, $domain)) {
+            // 匹配路由命名标识
+            $url = $match[0];
+
+            if ($domain) {
+                $domain = $match[1];
+            }
+
+            if (!is_null($match[2])) {
+                $suffix = $match[2];
+            }
+        } elseif (!empty($rule) && isset($name)) {
+            throw new \InvalidArgumentException('route name not exists:' . $name);
+        } else {
+            // 检查别名路由
+            $alias      = $this->app['route']->getAlias();
+            $matchAlias = false;
+
+            if ($alias) {
+                // 别名路由解析
+                foreach ($alias as $key => $item) {
+                    $val = $item->getRoute();
+
+                    if (0 === strpos($url, $val)) {
+                        $url        = $key . substr($url, strlen($val));
+                        $matchAlias = true;
+                        break;
+                    }
+                }
+            }
+
+            if (!$matchAlias) {
+                // 路由标识不存在 直接解析
+                $url = $this->parseUrl($url);
+            }
+
+            // 检测URL绑定
+            if (!$this->bindCheck) {
+                $bind = $this->app['route']->getBind($domain && is_string($domain) ? $domain : null);
+
+                if ($bind && 0 === strpos($url, $bind)) {
+                    $url = substr($url, strlen($bind) + 1);
+                } else {
+                    $binds = $this->app['route']->getBind(true);
+
+                    foreach ($binds as $key => $val) {
+                        if (is_string($val) && 0 === strpos($url, $val) && substr_count($val, '/') > 1) {
+                            $url    = substr($url, strlen($val) + 1);
+                            $domain = $key;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            if (isset($info['query'])) {
+                // 解析地址里面参数 合并到vars
+                parse_str($info['query'], $params);
+                $vars = array_merge($params, $vars);
+            }
+        }
+
+        // 还原URL分隔符
+        $depr = $this->config['pathinfo_depr'];
+        $url  = str_replace('/', $depr, $url);
+
+        // URL后缀
+        if ('/' == substr($url, -1) || '' == $url) {
+            $suffix = '';
+        } else {
+            $suffix = $this->parseSuffix($suffix);
+        }
+
+        // 锚点
+        $anchor = !empty($anchor) ? '#' . $anchor : '';
+
+        // 参数组装
+        if (!empty($vars)) {
+            // 添加参数
+            if ($this->config['url_common_param']) {
+                $vars = http_build_query($vars);
+                $url .= $suffix . '?' . $vars . $anchor;
+            } else {
+                $paramType = $this->config['url_param_type'];
+
+                foreach ($vars as $var => $val) {
+                    if ('' !== trim($val)) {
+                        if ($paramType) {
+                            $url .= $depr . urlencode($val);
+                        } else {
+                            $url .= $depr . $var . $depr . urlencode($val);
+                        }
+                    }
+                }
+
+                $url .= $suffix . $anchor;
+            }
+        } else {
+            $url .= $suffix . $anchor;
+        }
+
+        // 检测域名
+        $domain = $this->parseDomain($url, $domain);
+
+        // URL组装
+        $url = $domain . rtrim($this->root ?: $this->app['request']->root(), '/') . '/' . ltrim($url, '/');
+
+        $this->bindCheck = false;
+
+        return $url;
+    }
+
+    // 直接解析URL地址
+    protected function parseUrl($url)
+    {
+        $request = $this->app['request'];
+
+        if (0 === strpos($url, '/')) {
+            // 直接作为路由地址解析
+            $url = substr($url, 1);
+        } elseif (false !== strpos($url, '\\')) {
+            // 解析到类
+            $url = ltrim(str_replace('\\', '/', $url), '/');
+        } elseif (0 === strpos($url, '@')) {
+            // 解析到控制器
+            $url = substr($url, 1);
+        } else {
+            // 解析到 模块/控制器/操作
+            $module     = $request->module();
+            $module     = $module ? $module . '/' : '';
+            $controller = $request->controller();
+
+            if ('' == $url) {
+                $action = $request->action();
+            } else {
+                $path       = explode('/', $url);
+                $action     = array_pop($path);
+                $controller = empty($path) ? $controller : array_pop($path);
+                $module     = empty($path) ? $module : array_pop($path) . '/';
+            }
+
+            if ($this->config['url_convert']) {
+                $action     = strtolower($action);
+                $controller = Loader::parseName($controller);
+            }
+
+            $url = $module . $controller . '/' . $action;
+        }
+
+        return $url;
+    }
+
+    // 检测域名
+    protected function parseDomain(&$url, $domain)
+    {
+        if (!$domain) {
+            return '';
+        }
+
+        $rootDomain = $this->app['request']->rootDomain();
+        if (true === $domain) {
+            // 自动判断域名
+            $domain = $this->config['app_host'] ?: $this->app['request']->host();
+
+            $domains = $this->app['route']->getDomains();
+
+            if ($domains) {
+                $route_domain = array_keys($domains);
+                foreach ($route_domain as $domain_prefix) {
+                    if (0 === strpos($domain_prefix, '*.') && strpos($domain, ltrim($domain_prefix, '*.')) !== false) {
+                        foreach ($domains as $key => $rule) {
+                            $rule = is_array($rule) ? $rule[0] : $rule;
+                            if (is_string($rule) && false === strpos($key, '*') && 0 === strpos($url, $rule)) {
+                                $url    = ltrim($url, $rule);
+                                $domain = $key;
+
+                                // 生成对应子域名
+                                if (!empty($rootDomain)) {
+                                    $domain .= $rootDomain;
+                                }
+                                break;
+                            } elseif (false !== strpos($key, '*')) {
+                                if (!empty($rootDomain)) {
+                                    $domain .= $rootDomain;
+                                }
+
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        } elseif (0 !== strpos($domain, $rootDomain) && false === strpos($domain, '.')) {
+            $domain .= '.' . $rootDomain;
+        }
+
+        if (false !== strpos($domain, '://')) {
+            $scheme = '';
+        } else {
+            $scheme = $this->app['request']->isSsl() || $this->config['is_https'] ? 'https://' : 'http://';
+
+        }
+
+        return $scheme . $domain;
+    }
+
+    // 解析URL后缀
+    protected function parseSuffix($suffix)
+    {
+        if ($suffix) {
+            $suffix = true === $suffix ? $this->config['url_html_suffix'] : $suffix;
+
+            if ($pos = strpos($suffix, '|')) {
+                $suffix = substr($suffix, 0, $pos);
+            }
+        }
+
+        return (empty($suffix) || 0 === strpos($suffix, '.')) ? $suffix : '.' . $suffix;
+    }
+
+    // 匹配路由地址
+    public function getRuleUrl($rule, &$vars = [], $allowDomain = '')
+    {
+        $port = $this->app['request']->port();
+        foreach ($rule as $item) {
+            list($url, $pattern, $domain, $suffix, $method) = $item;
+
+            if (is_string($allowDomain) && $domain != $allowDomain) {
+                continue;
+            }
+
+            if ($port && !in_array($port, [80, 443])) {
+                $domain .= ':' . $port;
+            }
+
+            if (empty($pattern)) {
+                return [rtrim($url, '?/-'), $domain, $suffix];
+            }
+
+            $type = $this->config['url_common_param'];
+            $keys = [];
+
+            foreach ($pattern as $key => $val) {
+                if (isset($vars[$key])) {
+                    $url    = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key, '<' . $key . '>'], $type ? $vars[$key] : urlencode($vars[$key]), $url);
+                    $keys[] = $key;
+                    $url    = str_replace(['/?', '-?'], ['/', '-'], $url);
+                    $result = [rtrim($url, '?/-'), $domain, $suffix];
+                } elseif (2 == $val) {
+                    $url    = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url);
+                    $url    = str_replace(['/?', '-?'], ['/', '-'], $url);
+                    $result = [rtrim($url, '?/-'), $domain, $suffix];
+                } else {
+                    $result = null;
+                    $keys   = [];
+                    break;
+                }
+            }
+
+            $vars = array_diff_key($vars, array_flip($keys));
+
+            if (isset($result)) {
+                return $result;
+            }
+        }
+
+        return false;
+    }
+
+    // 指定当前生成URL地址的root
+    public function root($root)
+    {
+        $this->root = $root;
+        $this->app['request']->setRoot($root);
+    }
+
+    public function __debugInfo()
+    {
+        $data = get_object_vars($this);
+        unset($data['app']);
+
+        return $data;
+    }
+}

File diff suppressed because it is too large
+ 1554 - 0
thinkphp/library/think/Validate.php


+ 253 - 0
thinkphp/library/think/View.php

@@ -0,0 +1,253 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think;
+
+class View
+{
+    /**
+     * 模板引擎实例
+     * @var object
+     */
+    public $engine;
+
+    /**
+     * 模板变量
+     * @var array
+     */
+    protected $data = [];
+
+    /**
+     * 内容过滤
+     * @var mixed
+     */
+    protected $filter;
+
+    /**
+     * 全局模板变量
+     * @var array
+     */
+    protected static $var = [];
+
+    /**
+     * 初始化
+     * @access public
+     * @param  mixed $engine  模板引擎参数
+     * @return $this
+     */
+    public function init($engine = [])
+    {
+        // 初始化模板引擎
+        $this->engine($engine);
+
+        return $this;
+    }
+
+    public static function __make(Config $config)
+    {
+        return (new static())->init($config->pull('template'));
+    }
+
+    /**
+     * 模板变量静态赋值
+     * @access public
+     * @param  mixed $name  变量名
+     * @param  mixed $value 变量值
+     * @return $this
+     */
+    public function share($name, $value = '')
+    {
+        if (is_array($name)) {
+            self::$var = array_merge(self::$var, $name);
+        } else {
+            self::$var[$name] = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 清理模板变量
+     * @access public
+     * @return void
+     */
+    public function clear()
+    {
+        self::$var  = [];
+        $this->data = [];
+    }
+
+    /**
+     * 模板变量赋值
+     * @access public
+     * @param  mixed $name  变量名
+     * @param  mixed $value 变量值
+     * @return $this
+     */
+    public function assign($name, $value = '')
+    {
+        if (is_array($name)) {
+            $this->data = array_merge($this->data, $name);
+        } else {
+            $this->data[$name] = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 设置当前模板解析的引擎
+     * @access public
+     * @param  array|string $options 引擎参数
+     * @return $this
+     */
+    public function engine($options = [])
+    {
+        if (is_string($options)) {
+            $type    = $options;
+            $options = [];
+        } else {
+            $type = !empty($options['type']) ? $options['type'] : 'Think';
+        }
+
+        if (isset($options['type'])) {
+            unset($options['type']);
+        }
+
+        $this->engine = Loader::factory($type, '\\think\\view\\driver\\', $options);
+
+        return $this;
+    }
+
+    /**
+     * 配置模板引擎
+     * @access public
+     * @param  string|array  $name 参数名
+     * @param  mixed         $value 参数值
+     * @return $this
+     */
+    public function config($name, $value = null)
+    {
+        $this->engine->config($name, $value);
+
+        return $this;
+    }
+
+    /**
+     * 检查模板是否存在
+     * @access public
+     * @param  string|array  $name 参数名
+     * @return bool
+     */
+    public function exists($name)
+    {
+        return $this->engine->exists($name);
+    }
+
+    /**
+     * 视图过滤
+     * @access public
+     * @param Callable  $filter 过滤方法或闭包
+     * @return $this
+     */
+    public function filter($filter)
+    {
+        if ($filter) {
+            $this->filter = $filter;
+        }
+
+        return $this;
+    }
+
+    /**
+     * 解析和获取模板内容 用于输出
+     * @access public
+     * @param  string    $template 模板文件名或者内容
+     * @param  array     $vars     模板输出变量
+     * @param  array     $config     模板参数
+     * @param  bool      $renderContent     是否渲染内容
+     * @return string
+     * @throws \Exception
+     */
+    public function fetch($template = '', $vars = [], $config = [], $renderContent = false)
+    {
+        // 模板变量
+        $vars = array_merge(self::$var, $this->data, $vars);
+
+        // 页面缓存
+        ob_start();
+        ob_implicit_flush(0);
+
+        // 渲染输出
+        try {
+            $method = $renderContent ? 'display' : 'fetch';
+            $this->engine->$method($template, $vars, $config);
+        } catch (\Exception $e) {
+            ob_end_clean();
+            throw $e;
+        }
+
+        // 获取并清空缓存
+        $content = ob_get_clean();
+
+        if ($this->filter) {
+            $content = call_user_func_array($this->filter, [$content]);
+        }
+
+        return $content;
+    }
+
+    /**
+     * 渲染内容输出
+     * @access public
+     * @param  string $content 内容
+     * @param  array  $vars    模板输出变量
+     * @param  array  $config  模板参数
+     * @return mixed
+     */
+    public function display($content, $vars = [], $config = [])
+    {
+        return $this->fetch($content, $vars, $config, true);
+    }
+
+    /**
+     * 模板变量赋值
+     * @access public
+     * @param  string    $name  变量名
+     * @param  mixed     $value 变量值
+     */
+    public function __set($name, $value)
+    {
+        $this->data[$name] = $value;
+    }
+
+    /**
+     * 取得模板显示变量的值
+     * @access protected
+     * @param  string $name 模板变量
+     * @return mixed
+     */
+    public function __get($name)
+    {
+        return $this->data[$name];
+    }
+
+    /**
+     * 检测模板变量是否设置
+     * @access public
+     * @param  string $name 模板变量名
+     * @return bool
+     */
+    public function __isset($name)
+    {
+        return isset($this->data[$name]);
+    }
+}

+ 366 - 0
thinkphp/library/think/cache/Driver.php

@@ -0,0 +1,366 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\cache;
+
+use think\Container;
+
+/**
+ * 缓存基础类
+ */
+abstract class Driver
+{
+    /**
+     * 驱动句柄
+     * @var object
+     */
+    protected $handler = null;
+
+    /**
+     * 缓存读取次数
+     * @var integer
+     */
+    protected $readTimes = 0;
+
+    /**
+     * 缓存写入次数
+     * @var integer
+     */
+    protected $writeTimes = 0;
+
+    /**
+     * 缓存参数
+     * @var array
+     */
+    protected $options = [];
+
+    /**
+     * 缓存标签
+     * @var string
+     */
+    protected $tag;
+
+    /**
+     * 序列化方法
+     * @var array
+     */
+    protected static $serialize = ['serialize', 'unserialize', 'think_serialize:', 16];
+
+    /**
+     * 判断缓存是否存在
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return bool
+     */
+    abstract public function has($name);
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @param  mixed  $default 默认值
+     * @return mixed
+     */
+    abstract public function get($name, $default = false);
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  mixed     $value  存储数据
+     * @param  int       $expire  有效时间 0为永久
+     * @return boolean
+     */
+    abstract public function set($name, $value, $expire = null);
+
+    /**
+     * 自增缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    abstract public function inc($name, $step = 1);
+
+    /**
+     * 自减缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    abstract public function dec($name, $step = 1);
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return boolean
+     */
+    abstract public function rm($name);
+
+    /**
+     * 清除缓存
+     * @access public
+     * @param  string $tag 标签名
+     * @return boolean
+     */
+    abstract public function clear($tag = null);
+
+    /**
+     * 获取有效期
+     * @access protected
+     * @param  integer|\DateTime $expire 有效期
+     * @return integer
+     */
+    protected function getExpireTime($expire)
+    {
+        if ($expire instanceof \DateTime) {
+            $expire = $expire->getTimestamp() - time();
+        }
+
+        return $expire;
+    }
+
+    /**
+     * 获取实际的缓存标识
+     * @access protected
+     * @param  string $name 缓存名
+     * @return string
+     */
+    protected function getCacheKey($name)
+    {
+        return $this->options['prefix'] . $name;
+    }
+
+    /**
+     * 读取缓存并删除
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return mixed
+     */
+    public function pull($name)
+    {
+        $result = $this->get($name, false);
+
+        if ($result) {
+            $this->rm($name);
+            return $result;
+        } else {
+            return;
+        }
+    }
+
+    /**
+     * 如果不存在则写入缓存
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  mixed     $value  存储数据
+     * @param  int       $expire  有效时间 0为永久
+     * @return mixed
+     */
+    public function remember($name, $value, $expire = null)
+    {
+        if (!$this->has($name)) {
+            $time = time();
+            while ($time + 5 > time() && $this->has($name . '_lock')) {
+                // 存在锁定则等待
+                usleep(200000);
+            }
+
+            try {
+                // 锁定
+                $this->set($name . '_lock', true);
+
+                if ($value instanceof \Closure) {
+                    // 获取缓存数据
+                    $value = Container::getInstance()->invokeFunction($value);
+                }
+
+                // 缓存数据
+                $this->set($name, $value, $expire);
+
+                // 解锁
+                $this->rm($name . '_lock');
+            } catch (\Exception $e) {
+                $this->rm($name . '_lock');
+                throw $e;
+            } catch (\throwable $e) {
+                $this->rm($name . '_lock');
+                throw $e;
+            }
+        } else {
+            $value = $this->get($name);
+        }
+
+        return $value;
+    }
+
+    /**
+     * 缓存标签
+     * @access public
+     * @param  string        $name 标签名
+     * @param  string|array  $keys 缓存标识
+     * @param  bool          $overlay 是否覆盖
+     * @return $this
+     */
+    public function tag($name, $keys = null, $overlay = false)
+    {
+        if (is_null($name)) {
+
+        } elseif (is_null($keys)) {
+            $this->tag = $name;
+        } else {
+            $key = $this->getTagkey($name);
+
+            if (is_string($keys)) {
+                $keys = explode(',', $keys);
+            }
+
+            $keys = array_map([$this, 'getCacheKey'], $keys);
+
+            if ($overlay) {
+                $value = $keys;
+            } else {
+                $value = array_unique(array_merge($this->getTagItem($name), $keys));
+            }
+
+            $this->set($key, implode(',', $value), 0);
+        }
+
+        return $this;
+    }
+
+    /**
+     * 更新标签
+     * @access protected
+     * @param  string $name 缓存标识
+     * @return void
+     */
+    protected function setTagItem($name)
+    {
+        if ($this->tag) {
+            $key       = $this->getTagkey($this->tag);
+            $this->tag = null;
+
+            if ($this->has($key)) {
+                $value   = explode(',', $this->get($key));
+                $value[] = $name;
+
+                if (count($value) > 1000) {
+                    array_shift($value);
+                }
+
+                $value = implode(',', array_unique($value));
+            } else {
+                $value = $name;
+            }
+
+            $this->set($key, $value, 0);
+        }
+    }
+
+    /**
+     * 获取标签包含的缓存标识
+     * @access protected
+     * @param  string $tag 缓存标签
+     * @return array
+     */
+    protected function getTagItem($tag)
+    {
+        $key   = $this->getTagkey($tag);
+        $value = $this->get($key);
+
+        if ($value) {
+            return array_filter(explode(',', $value));
+        } else {
+            return [];
+        }
+    }
+
+    protected function getTagKey($tag)
+    {
+        return 'tag_' . md5($tag);
+    }
+
+    /**
+     * 序列化数据
+     * @access protected
+     * @param  mixed $data
+     * @return string
+     */
+    protected function serialize($data)
+    {
+        if (is_scalar($data) || !$this->options['serialize']) {
+            return $data;
+        }
+
+        $serialize = self::$serialize[0];
+
+        return self::$serialize[2] . $serialize($data);
+    }
+
+    /**
+     * 反序列化数据
+     * @access protected
+     * @param  string $data
+     * @return mixed
+     */
+    protected function unserialize($data)
+    {
+        if ($this->options['serialize'] && 0 === strpos($data, self::$serialize[2])) {
+            $unserialize = self::$serialize[1];
+
+            return $unserialize(substr($data, self::$serialize[3]));
+        } else {
+            return $data;
+        }
+    }
+
+    /**
+     * 注册序列化机制
+     * @access public
+     * @param  callable $serialize      序列化方法
+     * @param  callable $unserialize    反序列化方法
+     * @param  string   $prefix         序列化前缀标识
+     * @return $this
+     */
+    public static function registerSerialize($serialize, $unserialize, $prefix = 'think_serialize:')
+    {
+        self::$serialize = [$serialize, $unserialize, $prefix, strlen($prefix)];
+    }
+
+    /**
+     * 返回句柄对象,可执行其它高级方法
+     *
+     * @access public
+     * @return object
+     */
+    public function handler()
+    {
+        return $this->handler;
+    }
+
+    public function getReadTimes()
+    {
+        return $this->readTimes;
+    }
+
+    public function getWriteTimes()
+    {
+        return $this->writeTimes;
+    }
+
+    public function __call($method, $args)
+    {
+        return call_user_func_array([$this->handler, $method], $args);
+    }
+}

+ 307 - 0
thinkphp/library/think/cache/driver/File.php

@@ -0,0 +1,307 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\cache\driver;
+
+use think\cache\Driver;
+use think\Container;
+
+/**
+ * 文件类型缓存类
+ * @author    liu21st <liu21st@gmail.com>
+ */
+class File extends Driver
+{
+    protected $options = [
+        'expire'        => 0,
+        'cache_subdir'  => true,
+        'prefix'        => '',
+        'path'          => '',
+        'hash_type'     => 'md5',
+        'data_compress' => false,
+        'serialize'     => true,
+    ];
+
+    protected $expire;
+
+    /**
+     * 架构函数
+     * @param array $options
+     */
+    public function __construct($options = [])
+    {
+        if (!empty($options)) {
+            $this->options = array_merge($this->options, $options);
+        }
+
+        if (empty($this->options['path'])) {
+            $this->options['path'] = Container::get('app')->getRuntimePath() . 'cache' . DIRECTORY_SEPARATOR;
+        } elseif (substr($this->options['path'], -1) != DIRECTORY_SEPARATOR) {
+            $this->options['path'] .= DIRECTORY_SEPARATOR;
+        }
+
+        $this->init();
+    }
+
+    /**
+     * 初始化检查
+     * @access private
+     * @return boolean
+     */
+    private function init()
+    {
+        // 创建项目缓存目录
+        try {
+            if (!is_dir($this->options['path']) && mkdir($this->options['path'], 0755, true)) {
+                return true;
+            }
+        } catch (\Exception $e) {
+        }
+
+        return false;
+    }
+
+    /**
+     * 取得变量的存储文件名
+     * @access protected
+     * @param  string $name 缓存变量名
+     * @param  bool   $auto 是否自动创建目录
+     * @return string
+     */
+    protected function getCacheKey($name, $auto = false)
+    {
+        $name = hash($this->options['hash_type'], $name);
+
+        if ($this->options['cache_subdir']) {
+            // 使用子目录
+            $name = substr($name, 0, 2) . DIRECTORY_SEPARATOR . substr($name, 2);
+        }
+
+        if ($this->options['prefix']) {
+            $name = $this->options['prefix'] . DIRECTORY_SEPARATOR . $name;
+        }
+
+        $filename = $this->options['path'] . $name . '.php';
+        $dir      = dirname($filename);
+
+        if ($auto && !is_dir($dir)) {
+            try {
+                mkdir($dir, 0755, true);
+            } catch (\Exception $e) {
+            }
+        }
+
+        return $filename;
+    }
+
+    /**
+     * 判断缓存是否存在
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return bool
+     */
+    public function has($name)
+    {
+        return false !== $this->get($name) ? true : false;
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @param  mixed  $default 默认值
+     * @return mixed
+     */
+    public function get($name, $default = false)
+    {
+        $this->readTimes++;
+
+        $filename = $this->getCacheKey($name);
+
+        if (!is_file($filename)) {
+            return $default;
+        }
+
+        $content      = file_get_contents($filename);
+        $this->expire = null;
+
+        if (false !== $content) {
+            $expire = (int) substr($content, 8, 12);
+            if (0 != $expire && time() > filemtime($filename) + $expire) {
+                //缓存过期删除缓存文件
+                $this->unlink($filename);
+                return $default;
+            }
+
+            $this->expire = $expire;
+            $content      = substr($content, 32);
+
+            if ($this->options['data_compress'] && function_exists('gzcompress')) {
+                //启用数据压缩
+                $content = gzuncompress($content);
+            }
+            return $this->unserialize($content);
+        } else {
+            return $default;
+        }
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param  string        $name 缓存变量名
+     * @param  mixed         $value  存储数据
+     * @param  int|\DateTime $expire  有效时间 0为永久
+     * @return boolean
+     */
+    public function set($name, $value, $expire = null)
+    {
+        $this->writeTimes++;
+
+        if (is_null($expire)) {
+            $expire = $this->options['expire'];
+        }
+
+        $expire   = $this->getExpireTime($expire);
+        $filename = $this->getCacheKey($name, true);
+
+        if ($this->tag && !is_file($filename)) {
+            $first = true;
+        }
+
+        $data = $this->serialize($value);
+
+        if ($this->options['data_compress'] && function_exists('gzcompress')) {
+            //数据压缩
+            $data = gzcompress($data, 3);
+        }
+
+        $data   = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
+        $result = file_put_contents($filename, $data);
+
+        if ($result) {
+            isset($first) && $this->setTagItem($filename);
+            clearstatcache();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 自增缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function inc($name, $step = 1)
+    {
+        if ($this->has($name)) {
+            $value  = $this->get($name) + $step;
+            $expire = $this->expire;
+        } else {
+            $value  = $step;
+            $expire = 0;
+        }
+
+        return $this->set($name, $value, $expire) ? $value : false;
+    }
+
+    /**
+     * 自减缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function dec($name, $step = 1)
+    {
+        if ($this->has($name)) {
+            $value  = $this->get($name) - $step;
+            $expire = $this->expire;
+        } else {
+            $value  = -$step;
+            $expire = 0;
+        }
+
+        return $this->set($name, $value, $expire) ? $value : false;
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return boolean
+     */
+    public function rm($name)
+    {
+        $this->writeTimes++;
+
+        try {
+            return $this->unlink($this->getCacheKey($name));
+        } catch (\Exception $e) {
+        }
+    }
+
+    /**
+     * 清除缓存
+     * @access public
+     * @param  string $tag 标签名
+     * @return boolean
+     */
+    public function clear($tag = null)
+    {
+        if ($tag) {
+            // 指定标签清除
+            $keys = $this->getTagItem($tag);
+            foreach ($keys as $key) {
+                $this->unlink($key);
+            }
+            $this->rm($this->getTagKey($tag));
+            return true;
+        }
+
+        $this->writeTimes++;
+
+        $files = (array) glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DIRECTORY_SEPARATOR : '') . '*');
+
+        foreach ($files as $path) {
+            if (is_dir($path)) {
+                $matches = glob($path . DIRECTORY_SEPARATOR . '*.php');
+                if (is_array($matches)) {
+                    array_map(function ($v) {
+                        $this->unlink($v);
+                    }, $matches);
+                }
+                rmdir($path);
+            } else {
+                $this->unlink($path);
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * 判断文件是否存在后,删除
+     * @access private
+     * @param  string $path
+     * @return bool
+     * @author byron sampson <xiaobo.sun@qq.com>
+     * @return boolean
+     */
+    private function unlink($path)
+    {
+        return is_file($path) && unlink($path);
+    }
+
+}

+ 209 - 0
thinkphp/library/think/cache/driver/Lite.php

@@ -0,0 +1,209 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\cache\driver;
+
+use think\cache\Driver;
+
+/**
+ * 文件类型缓存类
+ * @author    liu21st <liu21st@gmail.com>
+ */
+class Lite extends Driver
+{
+    protected $options = [
+        'prefix' => '',
+        'path'   => '',
+        'expire' => 0, // 等于 10*365*24*3600(10年)
+    ];
+
+    /**
+     * 架构函数
+     * @access public
+     *
+     * @param  array $options
+     */
+    public function __construct($options = [])
+    {
+        if (!empty($options)) {
+            $this->options = array_merge($this->options, $options);
+        }
+
+        if (substr($this->options['path'], -1) != DIRECTORY_SEPARATOR) {
+            $this->options['path'] .= DIRECTORY_SEPARATOR;
+        }
+
+    }
+
+    /**
+     * 取得变量的存储文件名
+     * @access protected
+     * @param  string $name 缓存变量名
+     * @return string
+     */
+    protected function getCacheKey($name)
+    {
+        return $this->options['path'] . $this->options['prefix'] . md5($name) . '.php';
+    }
+
+    /**
+     * 判断缓存是否存在
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return mixed
+     */
+    public function has($name)
+    {
+        return $this->get($name) ? true : false;
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @param  mixed  $default 默认值
+     * @return mixed
+     */
+    public function get($name, $default = false)
+    {
+        $this->readTimes++;
+
+        $filename = $this->getCacheKey($name);
+
+        if (is_file($filename)) {
+            // 判断是否过期
+            $mtime = filemtime($filename);
+
+            if ($mtime < time()) {
+                // 清除已经过期的文件
+                unlink($filename);
+                return $default;
+            }
+
+            return include $filename;
+        } else {
+            return $default;
+        }
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param  string        $name  缓存变量名
+     * @param  mixed         $value 存储数据
+     * @param  int|\DateTime $expire 有效时间 0为永久
+     * @return bool
+     */
+    public function set($name, $value, $expire = null)
+    {
+        $this->writeTimes++;
+
+        if (is_null($expire)) {
+            $expire = $this->options['expire'];
+        }
+
+        if ($expire instanceof \DateTime) {
+            $expire = $expire->getTimestamp();
+        } else {
+            $expire = 0 === $expire ? 10 * 365 * 24 * 3600 : $expire;
+            $expire = time() + $expire;
+        }
+
+        $filename = $this->getCacheKey($name);
+
+        if ($this->tag && !is_file($filename)) {
+            $first = true;
+        }
+
+        $ret = file_put_contents($filename, ("<?php return " . var_export($value, true) . ";"));
+
+        // 通过设置修改时间实现有效期
+        if ($ret) {
+            isset($first) && $this->setTagItem($filename);
+            touch($filename, $expire);
+        }
+
+        return $ret;
+    }
+
+    /**
+     * 自增缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function inc($name, $step = 1)
+    {
+        if ($this->has($name)) {
+            $value = $this->get($name) + $step;
+        } else {
+            $value = $step;
+        }
+
+        return $this->set($name, $value, 0) ? $value : false;
+    }
+
+    /**
+     * 自减缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function dec($name, $step = 1)
+    {
+        if ($this->has($name)) {
+            $value = $this->get($name) - $step;
+        } else {
+            $value = -$step;
+        }
+
+        return $this->set($name, $value, 0) ? $value : false;
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return boolean
+     */
+    public function rm($name)
+    {
+        $this->writeTimes++;
+
+        return unlink($this->getCacheKey($name));
+    }
+
+    /**
+     * 清除缓存
+     * @access public
+     * @param  string $tag 标签名
+     * @return bool
+     */
+    public function clear($tag = null)
+    {
+        if ($tag) {
+            // 指定标签清除
+            $keys = $this->getTagItem($tag);
+            foreach ($keys as $key) {
+                unlink($key);
+            }
+
+            $this->rm($this->getTagKey($tag));
+            return true;
+        }
+
+        $this->writeTimes++;
+
+        array_map("unlink", glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DIRECTORY_SEPARATOR : '') . '*.php'));
+    }
+}

+ 206 - 0
thinkphp/library/think/cache/driver/Memcache.php

@@ -0,0 +1,206 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\cache\driver;
+
+use think\cache\Driver;
+
+class Memcache extends Driver
+{
+    protected $options = [
+        'host'       => '127.0.0.1',
+        'port'       => 11211,
+        'expire'     => 0,
+        'timeout'    => 0, // 超时时间(单位:毫秒)
+        'persistent' => true,
+        'prefix'     => '',
+        'serialize'  => true,
+    ];
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  array $options 缓存参数
+     * @throws \BadFunctionCallException
+     */
+    public function __construct($options = [])
+    {
+        if (!extension_loaded('memcache')) {
+            throw new \BadFunctionCallException('not support: memcache');
+        }
+
+        if (!empty($options)) {
+            $this->options = array_merge($this->options, $options);
+        }
+
+        $this->handler = new \Memcache;
+
+        // 支持集群
+        $hosts = explode(',', $this->options['host']);
+        $ports = explode(',', $this->options['port']);
+
+        if (empty($ports[0])) {
+            $ports[0] = 11211;
+        }
+
+        // 建立连接
+        foreach ((array) $hosts as $i => $host) {
+            $port = isset($ports[$i]) ? $ports[$i] : $ports[0];
+            $this->options['timeout'] > 0 ?
+            $this->handler->addServer($host, $port, $this->options['persistent'], 1, $this->options['timeout']) :
+            $this->handler->addServer($host, $port, $this->options['persistent'], 1);
+        }
+    }
+
+    /**
+     * 判断缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return bool
+     */
+    public function has($name)
+    {
+        $key = $this->getCacheKey($name);
+
+        return false !== $this->handler->get($key);
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @param  mixed  $default 默认值
+     * @return mixed
+     */
+    public function get($name, $default = false)
+    {
+        $this->readTimes++;
+
+        $result = $this->handler->get($this->getCacheKey($name));
+
+        return false !== $result ? $this->unserialize($result) : $default;
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param  string        $name 缓存变量名
+     * @param  mixed         $value  存储数据
+     * @param  int|DateTime  $expire  有效时间(秒)
+     * @return bool
+     */
+    public function set($name, $value, $expire = null)
+    {
+        $this->writeTimes++;
+
+        if (is_null($expire)) {
+            $expire = $this->options['expire'];
+        }
+
+        if ($this->tag && !$this->has($name)) {
+            $first = true;
+        }
+
+        $key    = $this->getCacheKey($name);
+        $expire = $this->getExpireTime($expire);
+        $value  = $this->serialize($value);
+
+        if ($this->handler->set($key, $value, 0, $expire)) {
+            isset($first) && $this->setTagItem($key);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 自增缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function inc($name, $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        if ($this->handler->get($key)) {
+            return $this->handler->increment($key, $step);
+        }
+
+        return $this->handler->set($key, $step);
+    }
+
+    /**
+     * 自减缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function dec($name, $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key   = $this->getCacheKey($name);
+        $value = $this->handler->get($key) - $step;
+        $res   = $this->handler->set($key, $value);
+
+        return !$res ? false : $value;
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param  string       $name 缓存变量名
+     * @param  bool|false   $ttl
+     * @return bool
+     */
+    public function rm($name, $ttl = false)
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return false === $ttl ?
+        $this->handler->delete($key) :
+        $this->handler->delete($key, $ttl);
+    }
+
+    /**
+     * 清除缓存
+     * @access public
+     * @param  string $tag 标签名
+     * @return bool
+     */
+    public function clear($tag = null)
+    {
+        if ($tag) {
+            // 指定标签清除
+            $keys = $this->getTagItem($tag);
+
+            foreach ($keys as $key) {
+                $this->handler->delete($key);
+            }
+
+            $tagName = $this->getTagKey($tag);
+            $this->rm($tagName);
+            return true;
+        }
+
+        $this->writeTimes++;
+
+        return $this->handler->flush();
+    }
+
+}

+ 279 - 0
thinkphp/library/think/cache/driver/Memcached.php

@@ -0,0 +1,279 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\cache\driver;
+
+use think\cache\Driver;
+
+class Memcached extends Driver
+{
+    protected $options = [
+        'host'      => '127.0.0.1',
+        'port'      => 11211,
+        'expire'    => 0,
+        'timeout'   => 0, // 超时时间(单位:毫秒)
+        'prefix'    => '',
+        'username'  => '', //账号
+        'password'  => '', //密码
+        'option'    => [],
+        'serialize' => true,
+    ];
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  array $options 缓存参数
+     */
+    public function __construct($options = [])
+    {
+        if (!extension_loaded('memcached')) {
+            throw new \BadFunctionCallException('not support: memcached');
+        }
+
+        if (!empty($options)) {
+            $this->options = array_merge($this->options, $options);
+        }
+
+        $this->handler = new \Memcached;
+
+        if (!empty($this->options['option'])) {
+            $this->handler->setOptions($this->options['option']);
+        }
+
+        // 设置连接超时时间(单位:毫秒)
+        if ($this->options['timeout'] > 0) {
+            $this->handler->setOption(\Memcached::OPT_CONNECT_TIMEOUT, $this->options['timeout']);
+        }
+
+        // 支持集群
+        $hosts = explode(',', $this->options['host']);
+        $ports = explode(',', $this->options['port']);
+        if (empty($ports[0])) {
+            $ports[0] = 11211;
+        }
+
+        // 建立连接
+        $servers = [];
+        foreach ((array) $hosts as $i => $host) {
+            $servers[] = [$host, (isset($ports[$i]) ? $ports[$i] : $ports[0]), 1];
+        }
+
+        $this->handler->addServers($servers);
+        $this->handler->setOption(\Memcached::OPT_COMPRESSION, false);
+        if ('' != $this->options['username']) {
+            $this->handler->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
+            $this->handler->setSaslAuthData($this->options['username'], $this->options['password']);
+        }
+    }
+
+    /**
+     * 判断缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return bool
+     */
+    public function has($name)
+    {
+        $key = $this->getCacheKey($name);
+
+        return $this->handler->get($key) ? true : false;
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @param  mixed  $default 默认值
+     * @return mixed
+     */
+    public function get($name, $default = false)
+    {
+        $this->readTimes++;
+
+        $result = $this->handler->get($this->getCacheKey($name));
+
+        return false !== $result ? $this->unserialize($result) : $default;
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param  string            $name 缓存变量名
+     * @param  mixed             $value  存储数据
+     * @param  integer|\DateTime $expire  有效时间(秒)
+     * @return bool
+     */
+    public function set($name, $value, $expire = null)
+    {
+        $this->writeTimes++;
+
+        if (is_null($expire)) {
+            $expire = $this->options['expire'];
+        }
+
+        if ($this->tag && !$this->has($name)) {
+            $first = true;
+        }
+
+        $key    = $this->getCacheKey($name);
+        $expire = $this->getExpireTime($expire);
+        $value  = $this->serialize($value);
+
+        if ($this->handler->set($key, $value, $expire)) {
+            isset($first) && $this->setTagItem($key);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 自增缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function inc($name, $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        if ($this->handler->get($key)) {
+            return $this->handler->increment($key, $step);
+        }
+
+        return $this->handler->set($key, $step);
+    }
+
+    /**
+     * 自减缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function dec($name, $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key   = $this->getCacheKey($name);
+        $value = $this->handler->get($key) - $step;
+        $res   = $this->handler->set($key, $value);
+
+        return !$res ? false : $value;
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param  string       $name 缓存变量名
+     * @param  bool|false   $ttl
+     * @return bool
+     */
+    public function rm($name, $ttl = false)
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return false === $ttl ?
+        $this->handler->delete($key) :
+        $this->handler->delete($key, $ttl);
+    }
+
+    /**
+     * 清除缓存
+     * @access public
+     * @param  string $tag 标签名
+     * @return bool
+     */
+    public function clear($tag = null)
+    {
+        if ($tag) {
+            // 指定标签清除
+            $keys = $this->getTagItem($tag);
+
+            $this->handler->deleteMulti($keys);
+            $this->rm($this->getTagKey($tag));
+
+            return true;
+        }
+
+        $this->writeTimes++;
+
+        return $this->handler->flush();
+    }
+
+    /**
+     * 缓存标签
+     * @access public
+     * @param  string        $name 标签名
+     * @param  string|array  $keys 缓存标识
+     * @param  bool          $overlay 是否覆盖
+     * @return $this
+     */
+    public function tag($name, $keys = null, $overlay = false)
+    {
+        if (is_null($keys)) {
+            $this->tag = $name;
+        } else {
+            $tagName = $this->getTagKey($name);
+            if ($overlay) {
+                $this->handler->delete($tagName);
+            }
+
+            if (!$this->has($tagName)) {
+                $this->handler->set($tagName, '');
+            }
+
+            foreach ($keys as $key) {
+                $this->handler->append($tagName, ',' . $key);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 更新标签
+     * @access protected
+     * @param  string $name 缓存标识
+     * @return void
+     */
+    protected function setTagItem($name)
+    {
+        if ($this->tag) {
+            $tagName = $this->getTagKey($this->tag);
+
+            if ($this->has($tagName)) {
+                $this->handler->append($tagName, ',' . $name);
+            } else {
+                $this->handler->set($tagName, $name);
+            }
+
+            $this->tag = null;
+        }
+    }
+
+    /**
+     * 获取标签包含的缓存标识
+     * @access public
+     * @param  string $tag 缓存标签
+     * @return array
+     */
+    public function getTagItem($tag)
+    {
+        $tagName = $this->getTagKey($tag);
+        return explode(',', trim($this->handler->get($tagName), ','));
+    }
+}

+ 272 - 0
thinkphp/library/think/cache/driver/Redis.php

@@ -0,0 +1,272 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\cache\driver;
+
+use think\cache\Driver;
+
+/**
+ * Redis缓存驱动,适合单机部署、有前端代理实现高可用的场景,性能最好
+ * 有需要在业务层实现读写分离、或者使用RedisCluster的需求,请使用Redisd驱动
+ *
+ * 要求安装phpredis扩展:https://github.com/nicolasff/phpredis
+ * @author    尘缘 <130775@qq.com>
+ */
+class Redis extends Driver
+{
+    protected $options = [
+        'host'       => '127.0.0.1',
+        'port'       => 6379,
+        'password'   => '',
+        'select'     => 0,
+        'timeout'    => 0,
+        'expire'     => 0,
+        'persistent' => false,
+        'prefix'     => '',
+        'serialize'  => true,
+    ];
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  array $options 缓存参数
+     */
+    public function __construct($options = [])
+    {
+        if (!empty($options)) {
+            $this->options = array_merge($this->options, $options);
+        }
+
+        if (extension_loaded('redis')) {
+            $this->handler = new \Redis;
+
+            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']);
+            }
+
+            if (0 != $this->options['select']) {
+                $this->handler->select($this->options['select']);
+            }
+        } elseif (class_exists('\Predis\Client')) {
+            $params = [];
+            foreach ($this->options as $key => $val) {
+                if (in_array($key, ['aggregate', 'cluster', 'connections', 'exceptions', 'prefix', 'profile', 'replication', 'parameters'])) {
+                    $params[$key] = $val;
+                    unset($this->options[$key]);
+                }
+            }
+
+            if ('' == $this->options['password']) {
+                unset($this->options['password']);
+            }
+
+            $this->handler = new \Predis\Client($this->options, $params);
+
+            $this->options['prefix'] = '';
+        } else {
+            throw new \BadFunctionCallException('not support: redis');
+        }
+    }
+
+    /**
+     * 判断缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return bool
+     */
+    public function has($name)
+    {
+        return $this->handler->exists($this->getCacheKey($name));
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @param  mixed  $default 默认值
+     * @return mixed
+     */
+    public function get($name, $default = false)
+    {
+        $this->readTimes++;
+
+        $value = $this->handler->get($this->getCacheKey($name));
+
+        if (is_null($value) || false === $value) {
+            return $default;
+        }
+
+        return $this->unserialize($value);
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param  string            $name 缓存变量名
+     * @param  mixed             $value  存储数据
+     * @param  integer|\DateTime $expire  有效时间(秒)
+     * @return boolean
+     */
+    public function set($name, $value, $expire = null)
+    {
+        $this->writeTimes++;
+
+        if (is_null($expire)) {
+            $expire = $this->options['expire'];
+        }
+
+        if ($this->tag && !$this->has($name)) {
+            $first = true;
+        }
+
+        $key    = $this->getCacheKey($name);
+        $expire = $this->getExpireTime($expire);
+
+        $value = $this->serialize($value);
+
+        if ($expire) {
+            $result = $this->handler->setex($key, $expire, $value);
+        } else {
+            $result = $this->handler->set($key, $value);
+        }
+
+        isset($first) && $this->setTagItem($key);
+
+        return $result;
+    }
+
+    /**
+     * 自增缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function inc($name, $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return $this->handler->incrby($key, $step);
+    }
+
+    /**
+     * 自减缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function dec($name, $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return $this->handler->decrby($key, $step);
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return boolean
+     */
+    public function rm($name)
+    {
+        $this->writeTimes++;
+
+        return $this->handler->del($this->getCacheKey($name));
+    }
+
+    /**
+     * 清除缓存
+     * @access public
+     * @param  string $tag 标签名
+     * @return boolean
+     */
+    public function clear($tag = null)
+    {
+        if ($tag) {
+            // 指定标签清除
+            $keys = $this->getTagItem($tag);
+
+            $this->handler->del($keys);
+
+            $tagName = $this->getTagKey($tag);
+            $this->handler->del($tagName);
+            return true;
+        }
+
+        $this->writeTimes++;
+
+        return $this->handler->flushDB();
+    }
+
+    /**
+     * 缓存标签
+     * @access public
+     * @param  string        $name 标签名
+     * @param  string|array  $keys 缓存标识
+     * @param  bool          $overlay 是否覆盖
+     * @return $this
+     */
+    public function tag($name, $keys = null, $overlay = false)
+    {
+        if (is_null($keys)) {
+            $this->tag = $name;
+        } else {
+            $tagName = $this->getTagKey($name);
+            if ($overlay) {
+                $this->handler->del($tagName);
+            }
+
+            foreach ($keys as $key) {
+                $this->handler->sAdd($tagName, $key);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * 更新标签
+     * @access protected
+     * @param  string $name 缓存标识
+     * @return void
+     */
+    protected function setTagItem($name)
+    {
+        if ($this->tag) {
+            $tagName = $this->getTagKey($this->tag);
+            $this->handler->sAdd($tagName, $name);
+        }
+    }
+
+    /**
+     * 获取标签包含的缓存标识
+     * @access protected
+     * @param  string $tag 缓存标签
+     * @return array
+     */
+    protected function getTagItem($tag)
+    {
+        $tagName = $this->getTagKey($tag);
+        return $this->handler->sMembers($tagName);
+    }
+}

+ 233 - 0
thinkphp/library/think/cache/driver/Sqlite.php

@@ -0,0 +1,233 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\cache\driver;
+
+use think\cache\Driver;
+
+/**
+ * Sqlite缓存驱动
+ * @author    liu21st <liu21st@gmail.com>
+ */
+class Sqlite extends Driver
+{
+    protected $options = [
+        'db'         => ':memory:',
+        'table'      => 'sharedmemory',
+        'prefix'     => '',
+        'expire'     => 0,
+        'persistent' => false,
+        'serialize'  => true,
+    ];
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  array $options 缓存参数
+     * @throws \BadFunctionCallException
+     */
+    public function __construct($options = [])
+    {
+        if (!extension_loaded('sqlite')) {
+            throw new \BadFunctionCallException('not support: sqlite');
+        }
+
+        if (!empty($options)) {
+            $this->options = array_merge($this->options, $options);
+        }
+
+        $func = $this->options['persistent'] ? 'sqlite_popen' : 'sqlite_open';
+
+        $this->handler = $func($this->options['db']);
+    }
+
+    /**
+     * 获取实际的缓存标识
+     * @access public
+     * @param  string $name 缓存名
+     * @return string
+     */
+    protected function getCacheKey($name)
+    {
+        return $this->options['prefix'] . sqlite_escape_string($name);
+    }
+
+    /**
+     * 判断缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return bool
+     */
+    public function has($name)
+    {
+        $name = $this->getCacheKey($name);
+
+        $sql    = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . time() . ') LIMIT 1';
+        $result = sqlite_query($this->handler, $sql);
+
+        return sqlite_num_rows($result);
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @param  mixed  $default 默认值
+     * @return mixed
+     */
+    public function get($name, $default = false)
+    {
+        $this->readTimes++;
+
+        $name = $this->getCacheKey($name);
+
+        $sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . time() . ') LIMIT 1';
+
+        $result = sqlite_query($this->handler, $sql);
+
+        if (sqlite_num_rows($result)) {
+            $content = sqlite_fetch_single($result);
+            if (function_exists('gzcompress')) {
+                //启用数据压缩
+                $content = gzuncompress($content);
+            }
+
+            return $this->unserialize($content);
+        }
+
+        return $default;
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param  string            $name 缓存变量名
+     * @param  mixed             $value  存储数据
+     * @param  integer|\DateTime $expire  有效时间(秒)
+     * @return boolean
+     */
+    public function set($name, $value, $expire = null)
+    {
+        $this->writeTimes++;
+
+        $name = $this->getCacheKey($name);
+
+        $value = sqlite_escape_string($this->serialize($value));
+
+        if (is_null($expire)) {
+            $expire = $this->options['expire'];
+        }
+
+        if ($expire instanceof \DateTime) {
+            $expire = $expire->getTimestamp();
+        } else {
+            $expire = (0 == $expire) ? 0 : (time() + $expire); //缓存有效期为0表示永久缓存
+        }
+
+        if (function_exists('gzcompress')) {
+            //数据压缩
+            $value = gzcompress($value, 3);
+        }
+
+        if ($this->tag) {
+            $tag       = $this->tag;
+            $this->tag = null;
+        } else {
+            $tag = '';
+        }
+
+        $sql = 'REPLACE INTO ' . $this->options['table'] . ' (var, value, expire, tag) VALUES (\'' . $name . '\', \'' . $value . '\', \'' . $expire . '\', \'' . $tag . '\')';
+
+        if (sqlite_query($this->handler, $sql)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 自增缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function inc($name, $step = 1)
+    {
+        if ($this->has($name)) {
+            $value = $this->get($name) + $step;
+        } else {
+            $value = $step;
+        }
+
+        return $this->set($name, $value, 0) ? $value : false;
+    }
+
+    /**
+     * 自减缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function dec($name, $step = 1)
+    {
+        if ($this->has($name)) {
+            $value = $this->get($name) - $step;
+        } else {
+            $value = -$step;
+        }
+
+        return $this->set($name, $value, 0) ? $value : false;
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return boolean
+     */
+    public function rm($name)
+    {
+        $this->writeTimes++;
+
+        $name = $this->getCacheKey($name);
+
+        $sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\'';
+        sqlite_query($this->handler, $sql);
+
+        return true;
+    }
+
+    /**
+     * 清除缓存
+     * @access public
+     * @param  string $tag 标签名
+     * @return boolean
+     */
+    public function clear($tag = null)
+    {
+        if ($tag) {
+            $name = sqlite_escape_string($this->getTagKey($tag));
+            $sql  = 'DELETE FROM ' . $this->options['table'] . ' WHERE tag=\'' . $name . '\'';
+            sqlite_query($this->handler, $sql);
+            return true;
+        }
+
+        $this->writeTimes++;
+
+        $sql = 'DELETE FROM ' . $this->options['table'];
+
+        sqlite_query($this->handler, $sql);
+
+        return true;
+    }
+}

+ 175 - 0
thinkphp/library/think/cache/driver/Wincache.php

@@ -0,0 +1,175 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\cache\driver;
+
+use think\cache\Driver;
+
+/**
+ * Wincache缓存驱动
+ * @author    liu21st <liu21st@gmail.com>
+ */
+class Wincache extends Driver
+{
+    protected $options = [
+        'prefix'    => '',
+        'expire'    => 0,
+        'serialize' => true,
+    ];
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  array $options 缓存参数
+     * @throws \BadFunctionCallException
+     */
+    public function __construct($options = [])
+    {
+        if (!function_exists('wincache_ucache_info')) {
+            throw new \BadFunctionCallException('not support: WinCache');
+        }
+
+        if (!empty($options)) {
+            $this->options = array_merge($this->options, $options);
+        }
+    }
+
+    /**
+     * 判断缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return bool
+     */
+    public function has($name)
+    {
+        $this->readTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return wincache_ucache_exists($key);
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @param  mixed  $default 默认值
+     * @return mixed
+     */
+    public function get($name, $default = false)
+    {
+        $this->readTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return wincache_ucache_exists($key) ? $this->unserialize(wincache_ucache_get($key)) : $default;
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param  string            $name 缓存变量名
+     * @param  mixed             $value  存储数据
+     * @param  integer|\DateTime $expire  有效时间(秒)
+     * @return boolean
+     */
+    public function set($name, $value, $expire = null)
+    {
+        $this->writeTimes++;
+
+        if (is_null($expire)) {
+            $expire = $this->options['expire'];
+        }
+
+        $key    = $this->getCacheKey($name);
+        $expire = $this->getExpireTime($expire);
+        $value  = $this->serialize($value);
+
+        if ($this->tag && !$this->has($name)) {
+            $first = true;
+        }
+
+        if (wincache_ucache_set($key, $value, $expire)) {
+            isset($first) && $this->setTagItem($key);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 自增缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function inc($name, $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return wincache_ucache_inc($key, $step);
+    }
+
+    /**
+     * 自减缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function dec($name, $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return wincache_ucache_dec($key, $step);
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return boolean
+     */
+    public function rm($name)
+    {
+        $this->writeTimes++;
+
+        return wincache_ucache_delete($this->getCacheKey($name));
+    }
+
+    /**
+     * 清除缓存
+     * @access public
+     * @param  string $tag 标签名
+     * @return boolean
+     */
+    public function clear($tag = null)
+    {
+        if ($tag) {
+            $keys = $this->getTagItem($tag);
+
+            wincache_ucache_delete($keys);
+
+            $tagName = $this->getTagkey($tag);
+            $this->rm($tagName);
+            return true;
+        }
+
+        $this->writeTimes++;
+        return wincache_ucache_clear();
+    }
+
+}

+ 179 - 0
thinkphp/library/think/cache/driver/Xcache.php

@@ -0,0 +1,179 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\cache\driver;
+
+use think\cache\Driver;
+
+/**
+ * Xcache缓存驱动
+ * @author    liu21st <liu21st@gmail.com>
+ */
+class Xcache extends Driver
+{
+    protected $options = [
+        'prefix'    => '',
+        'expire'    => 0,
+        'serialize' => true,
+    ];
+
+    /**
+     * 架构函数
+     * @access public
+     * @param  array $options 缓存参数
+     * @throws \BadFunctionCallException
+     */
+    public function __construct($options = [])
+    {
+        if (!function_exists('xcache_info')) {
+            throw new \BadFunctionCallException('not support: Xcache');
+        }
+
+        if (!empty($options)) {
+            $this->options = array_merge($this->options, $options);
+        }
+    }
+
+    /**
+     * 判断缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return bool
+     */
+    public function has($name)
+    {
+        $key = $this->getCacheKey($name);
+
+        return xcache_isset($key);
+    }
+
+    /**
+     * 读取缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @param  mixed  $default 默认值
+     * @return mixed
+     */
+    public function get($name, $default = false)
+    {
+        $this->readTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return xcache_isset($key) ? $this->unserialize(xcache_get($key)) : $default;
+    }
+
+    /**
+     * 写入缓存
+     * @access public
+     * @param  string            $name 缓存变量名
+     * @param  mixed             $value  存储数据
+     * @param  integer|\DateTime $expire  有效时间(秒)
+     * @return boolean
+     */
+    public function set($name, $value, $expire = null)
+    {
+        $this->writeTimes++;
+
+        if (is_null($expire)) {
+            $expire = $this->options['expire'];
+        }
+
+        if ($this->tag && !$this->has($name)) {
+            $first = true;
+        }
+
+        $key    = $this->getCacheKey($name);
+        $expire = $this->getExpireTime($expire);
+        $value  = $this->serialize($value);
+
+        if (xcache_set($key, $value, $expire)) {
+            isset($first) && $this->setTagItem($key);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * 自增缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function inc($name, $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return xcache_inc($key, $step);
+    }
+
+    /**
+     * 自减缓存(针对数值缓存)
+     * @access public
+     * @param  string    $name 缓存变量名
+     * @param  int       $step 步长
+     * @return false|int
+     */
+    public function dec($name, $step = 1)
+    {
+        $this->writeTimes++;
+
+        $key = $this->getCacheKey($name);
+
+        return xcache_dec($key, $step);
+    }
+
+    /**
+     * 删除缓存
+     * @access public
+     * @param  string $name 缓存变量名
+     * @return boolean
+     */
+    public function rm($name)
+    {
+        $this->writeTimes++;
+
+        return xcache_unset($this->getCacheKey($name));
+    }
+
+    /**
+     * 清除缓存
+     * @access public
+     * @param  string $tag 标签名
+     * @return boolean
+     */
+    public function clear($tag = null)
+    {
+        if ($tag) {
+            // 指定标签清除
+            $keys = $this->getTagItem($tag);
+
+            foreach ($keys as $key) {
+                xcache_unset($key);
+            }
+
+            $this->rm($this->getTagKey($tag));
+            return true;
+        }
+
+        $this->writeTimes++;
+
+        if (function_exists('xcache_unset_by_prefix')) {
+            return xcache_unset_by_prefix($this->options['prefix']);
+        } else {
+            return false;
+        }
+    }
+}

+ 31 - 0
thinkphp/library/think/config/driver/Ini.php

@@ -0,0 +1,31 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\config\driver;
+
+class Ini
+{
+    protected $config;
+
+    public function __construct($config)
+    {
+        $this->config = $config;
+    }
+
+    public function parse()
+    {
+        if (is_file($this->config)) {
+            return parse_ini_file($this->config, true);
+        } else {
+            return parse_ini_string($this->config, true);
+        }
+    }
+}

+ 31 - 0
thinkphp/library/think/config/driver/Json.php

@@ -0,0 +1,31 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\config\driver;
+
+class Json
+{
+    protected $config;
+
+    public function __construct($config)
+    {
+        if (is_file($config)) {
+            $config = file_get_contents($config);
+        }
+
+        $this->config = $config;
+    }
+
+    public function parse()
+    {
+        return json_decode($this->config, true);
+    }
+}

+ 40 - 0
thinkphp/library/think/config/driver/Xml.php

@@ -0,0 +1,40 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+namespace think\config\driver;
+
+class Xml
+{
+    protected $config;
+
+    public function __construct($config)
+    {
+        $this->config = $config;
+    }
+
+    public function parse()
+    {
+        if (is_file($this->config)) {
+            $content = simplexml_load_file($this->config);
+        } else {
+            $content = simplexml_load_string($this->config);
+        }
+
+        $result = (array) $content;
+        foreach ($result as $key => $val) {
+            if (is_object($val)) {
+                $result[$key] = (array) $val;
+            }
+        }
+
+        return $result;
+    }
+}

+ 482 - 0
thinkphp/library/think/console/Command.php

@@ -0,0 +1,482 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console;
+
+use think\Console;
+use think\console\input\Argument;
+use think\console\input\Definition;
+use think\console\input\Option;
+
+class Command
+{
+
+    /** @var  Console */
+    private $console;
+    private $name;
+    private $aliases = [];
+    private $definition;
+    private $help;
+    private $description;
+    private $ignoreValidationErrors          = false;
+    private $consoleDefinitionMerged         = false;
+    private $consoleDefinitionMergedWithArgs = false;
+    private $code;
+    private $synopsis = [];
+    private $usages   = [];
+
+    /** @var  Input */
+    protected $input;
+
+    /** @var  Output */
+    protected $output;
+
+    /**
+     * 构造方法
+     * @param string|null $name 命令名称,如果没有设置则比如在 configure() 里设置
+     * @throws \LogicException
+     * @api
+     */
+    public function __construct($name = null)
+    {
+        $this->definition = new Definition();
+
+        if (null !== $name) {
+            $this->setName($name);
+        }
+
+        $this->configure();
+
+        if (!$this->name) {
+            throw new \LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this)));
+        }
+    }
+
+    /**
+     * 忽略验证错误
+     */
+    public function ignoreValidationErrors()
+    {
+        $this->ignoreValidationErrors = true;
+    }
+
+    /**
+     * 设置控制台
+     * @param Console $console
+     */
+    public function setConsole(Console $console = null)
+    {
+        $this->console = $console;
+    }
+
+    /**
+     * 获取控制台
+     * @return Console
+     * @api
+     */
+    public function getConsole()
+    {
+        return $this->console;
+    }
+
+    /**
+     * 是否有效
+     * @return bool
+     */
+    public function isEnabled()
+    {
+        return true;
+    }
+
+    /**
+     * 配置指令
+     */
+    protected function configure()
+    {
+    }
+
+    /**
+     * 执行指令
+     * @param Input  $input
+     * @param Output $output
+     * @return null|int
+     * @throws \LogicException
+     * @see setCode()
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        throw new \LogicException('You must override the execute() method in the concrete command class.');
+    }
+
+    /**
+     * 用户验证
+     * @param Input  $input
+     * @param Output $output
+     */
+    protected function interact(Input $input, Output $output)
+    {
+    }
+
+    /**
+     * 初始化
+     * @param Input  $input  An InputInterface instance
+     * @param Output $output An OutputInterface instance
+     */
+    protected function initialize(Input $input, Output $output)
+    {
+    }
+
+    /**
+     * 执行
+     * @param Input  $input
+     * @param Output $output
+     * @return int
+     * @throws \Exception
+     * @see setCode()
+     * @see execute()
+     */
+    public function run(Input $input, Output $output)
+    {
+        $this->input  = $input;
+        $this->output = $output;
+
+        $this->getSynopsis(true);
+        $this->getSynopsis(false);
+
+        $this->mergeConsoleDefinition();
+
+        try {
+            $input->bind($this->definition);
+        } catch (\Exception $e) {
+            if (!$this->ignoreValidationErrors) {
+                throw $e;
+            }
+        }
+
+        $this->initialize($input, $output);
+
+        if ($input->isInteractive()) {
+            $this->interact($input, $output);
+        }
+
+        $input->validate();
+
+        if ($this->code) {
+            $statusCode = call_user_func($this->code, $input, $output);
+        } else {
+            $statusCode = $this->execute($input, $output);
+        }
+
+        return is_numeric($statusCode) ? (int) $statusCode : 0;
+    }
+
+    /**
+     * 设置执行代码
+     * @param callable $code callable(InputInterface $input, OutputInterface $output)
+     * @return Command
+     * @throws \InvalidArgumentException
+     * @see execute()
+     */
+    public function setCode(callable $code)
+    {
+        if (!is_callable($code)) {
+            throw new \InvalidArgumentException('Invalid callable provided to Command::setCode.');
+        }
+
+        if (PHP_VERSION_ID >= 50400 && $code instanceof \Closure) {
+            $r = new \ReflectionFunction($code);
+            if (null === $r->getClosureThis()) {
+                $code = \Closure::bind($code, $this);
+            }
+        }
+
+        $this->code = $code;
+
+        return $this;
+    }
+
+    /**
+     * 合并参数定义
+     * @param bool $mergeArgs
+     */
+    public function mergeConsoleDefinition($mergeArgs = true)
+    {
+        if (null === $this->console
+            || (true === $this->consoleDefinitionMerged
+                && ($this->consoleDefinitionMergedWithArgs || !$mergeArgs))
+        ) {
+            return;
+        }
+
+        if ($mergeArgs) {
+            $currentArguments = $this->definition->getArguments();
+            $this->definition->setArguments($this->console->getDefinition()->getArguments());
+            $this->definition->addArguments($currentArguments);
+        }
+
+        $this->definition->addOptions($this->console->getDefinition()->getOptions());
+
+        $this->consoleDefinitionMerged = true;
+        if ($mergeArgs) {
+            $this->consoleDefinitionMergedWithArgs = true;
+        }
+    }
+
+    /**
+     * 设置参数定义
+     * @param array|Definition $definition
+     * @return Command
+     * @api
+     */
+    public function setDefinition($definition)
+    {
+        if ($definition instanceof Definition) {
+            $this->definition = $definition;
+        } else {
+            $this->definition->setDefinition($definition);
+        }
+
+        $this->consoleDefinitionMerged = false;
+
+        return $this;
+    }
+
+    /**
+     * 获取参数定义
+     * @return Definition
+     * @api
+     */
+    public function getDefinition()
+    {
+        return $this->definition;
+    }
+
+    /**
+     * 获取当前指令的参数定义
+     * @return Definition
+     */
+    public function getNativeDefinition()
+    {
+        return $this->getDefinition();
+    }
+
+    /**
+     * 添加参数
+     * @param string $name        名称
+     * @param int    $mode        类型
+     * @param string $description 描述
+     * @param mixed  $default     默认值
+     * @return Command
+     */
+    public function addArgument($name, $mode = null, $description = '', $default = null)
+    {
+        $this->definition->addArgument(new Argument($name, $mode, $description, $default));
+
+        return $this;
+    }
+
+    /**
+     * 添加选项
+     * @param string $name        选项名称
+     * @param string $shortcut    别名
+     * @param int    $mode        类型
+     * @param string $description 描述
+     * @param mixed  $default     默认值
+     * @return Command
+     */
+    public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
+    {
+        $this->definition->addOption(new Option($name, $shortcut, $mode, $description, $default));
+
+        return $this;
+    }
+
+    /**
+     * 设置指令名称
+     * @param string $name
+     * @return Command
+     * @throws \InvalidArgumentException
+     */
+    public function setName($name)
+    {
+        $this->validateName($name);
+
+        $this->name = $name;
+
+        return $this;
+    }
+
+    /**
+     * 获取指令名称
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * 设置描述
+     * @param string $description
+     * @return Command
+     */
+    public function setDescription($description)
+    {
+        $this->description = $description;
+
+        return $this;
+    }
+
+    /**
+     *  获取描述
+     * @return string
+     */
+    public function getDescription()
+    {
+        return $this->description;
+    }
+
+    /**
+     * 设置帮助信息
+     * @param string $help
+     * @return Command
+     */
+    public function setHelp($help)
+    {
+        $this->help = $help;
+
+        return $this;
+    }
+
+    /**
+     * 获取帮助信息
+     * @return string
+     */
+    public function getHelp()
+    {
+        return $this->help;
+    }
+
+    /**
+     * 描述信息
+     * @return string
+     */
+    public function getProcessedHelp()
+    {
+        $name = $this->name;
+
+        $placeholders = [
+            '%command.name%',
+            '%command.full_name%',
+        ];
+        $replacements = [
+            $name,
+            $_SERVER['PHP_SELF'] . ' ' . $name,
+        ];
+
+        return str_replace($placeholders, $replacements, $this->getHelp());
+    }
+
+    /**
+     * 设置别名
+     * @param string[] $aliases
+     * @return Command
+     * @throws \InvalidArgumentException
+     */
+    public function setAliases($aliases)
+    {
+        if (!is_array($aliases) && !$aliases instanceof \Traversable) {
+            throw new \InvalidArgumentException('$aliases must be an array or an instance of \Traversable');
+        }
+
+        foreach ($aliases as $alias) {
+            $this->validateName($alias);
+        }
+
+        $this->aliases = $aliases;
+
+        return $this;
+    }
+
+    /**
+     * 获取别名
+     * @return array
+     */
+    public function getAliases()
+    {
+        return $this->aliases;
+    }
+
+    /**
+     * 获取简介
+     * @param bool $short 是否简单的
+     * @return string
+     */
+    public function getSynopsis($short = false)
+    {
+        $key = $short ? 'short' : 'long';
+
+        if (!isset($this->synopsis[$key])) {
+            $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short)));
+        }
+
+        return $this->synopsis[$key];
+    }
+
+    /**
+     * 添加用法介绍
+     * @param string $usage
+     * @return $this
+     */
+    public function addUsage($usage)
+    {
+        if (0 !== strpos($usage, $this->name)) {
+            $usage = sprintf('%s %s', $this->name, $usage);
+        }
+
+        $this->usages[] = $usage;
+
+        return $this;
+    }
+
+    /**
+     * 获取用法介绍
+     * @return array
+     */
+    public function getUsages()
+    {
+        return $this->usages;
+    }
+
+    /**
+     * 验证指令名称
+     * @param string $name
+     * @throws \InvalidArgumentException
+     */
+    private function validateName($name)
+    {
+        if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
+            throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
+        }
+    }
+
+    /**
+     * 输出表格
+     * @param Table $table
+     * @return string
+     */
+    protected function table(Table $table)
+    {
+        $content = $table->render();
+        $this->output->writeln($content);
+        return $content;
+    }
+}

+ 464 - 0
thinkphp/library/think/console/Input.php

@@ -0,0 +1,464 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console;
+
+use think\console\input\Argument;
+use think\console\input\Definition;
+use think\console\input\Option;
+
+class Input
+{
+
+    /**
+     * @var Definition
+     */
+    protected $definition;
+
+    /**
+     * @var Option[]
+     */
+    protected $options = [];
+
+    /**
+     * @var Argument[]
+     */
+    protected $arguments = [];
+
+    protected $interactive = true;
+
+    private $tokens;
+    private $parsed;
+
+    public function __construct($argv = null)
+    {
+        if (null === $argv) {
+            $argv = $_SERVER['argv'];
+            // 去除命令名
+            array_shift($argv);
+        }
+
+        $this->tokens = $argv;
+
+        $this->definition = new Definition();
+    }
+
+    protected function setTokens(array $tokens)
+    {
+        $this->tokens = $tokens;
+    }
+
+    /**
+     * 绑定实例
+     * @param Definition $definition A InputDefinition instance
+     */
+    public function bind(Definition $definition)
+    {
+        $this->arguments  = [];
+        $this->options    = [];
+        $this->definition = $definition;
+
+        $this->parse();
+    }
+
+    /**
+     * 解析参数
+     */
+    protected function parse()
+    {
+        $parseOptions = true;
+        $this->parsed = $this->tokens;
+        while (null !== $token = array_shift($this->parsed)) {
+            if ($parseOptions && '' == $token) {
+                $this->parseArgument($token);
+            } elseif ($parseOptions && '--' == $token) {
+                $parseOptions = false;
+            } elseif ($parseOptions && 0 === strpos($token, '--')) {
+                $this->parseLongOption($token);
+            } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
+                $this->parseShortOption($token);
+            } else {
+                $this->parseArgument($token);
+            }
+        }
+    }
+
+    /**
+     * 解析短选项
+     * @param string $token 当前的指令.
+     */
+    private function parseShortOption($token)
+    {
+        $name = substr($token, 1);
+
+        if (strlen($name) > 1) {
+            if ($this->definition->hasShortcut($name[0])
+                && $this->definition->getOptionForShortcut($name[0])->acceptValue()
+            ) {
+                $this->addShortOption($name[0], substr($name, 1));
+            } else {
+                $this->parseShortOptionSet($name);
+            }
+        } else {
+            $this->addShortOption($name, null);
+        }
+    }
+
+    /**
+     * 解析短选项
+     * @param string $name 当前指令
+     * @throws \RuntimeException
+     */
+    private function parseShortOptionSet($name)
+    {
+        $len = strlen($name);
+        for ($i = 0; $i < $len; ++$i) {
+            if (!$this->definition->hasShortcut($name[$i])) {
+                throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
+            }
+
+            $option = $this->definition->getOptionForShortcut($name[$i]);
+            if ($option->acceptValue()) {
+                $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
+
+                break;
+            } else {
+                $this->addLongOption($option->getName(), null);
+            }
+        }
+    }
+
+    /**
+     * 解析完整选项
+     * @param string $token 当前指令
+     */
+    private function parseLongOption($token)
+    {
+        $name = substr($token, 2);
+
+        if (false !== $pos = strpos($name, '=')) {
+            $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1));
+        } else {
+            $this->addLongOption($name, null);
+        }
+    }
+
+    /**
+     * 解析参数
+     * @param string $token 当前指令
+     * @throws \RuntimeException
+     */
+    private function parseArgument($token)
+    {
+        $c = count($this->arguments);
+
+        if ($this->definition->hasArgument($c)) {
+            $arg = $this->definition->getArgument($c);
+
+            $this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token;
+
+        } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
+            $arg = $this->definition->getArgument($c - 1);
+
+            $this->arguments[$arg->getName()][] = $token;
+        } else {
+            throw new \RuntimeException('Too many arguments.');
+        }
+    }
+
+    /**
+     * 添加一个短选项的值
+     * @param string $shortcut 短名称
+     * @param mixed  $value    值
+     * @throws \RuntimeException
+     */
+    private function addShortOption($shortcut, $value)
+    {
+        if (!$this->definition->hasShortcut($shortcut)) {
+            throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
+        }
+
+        $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
+    }
+
+    /**
+     * 添加一个完整选项的值
+     * @param string $name  选项名
+     * @param mixed  $value 值
+     * @throws \RuntimeException
+     */
+    private function addLongOption($name, $value)
+    {
+        if (!$this->definition->hasOption($name)) {
+            throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name));
+        }
+
+        $option = $this->definition->getOption($name);
+
+        if (false === $value) {
+            $value = null;
+        }
+
+        if (null !== $value && !$option->acceptValue()) {
+            throw new \RuntimeException(sprintf('The "--%s" option does not accept a value.', $name, $value));
+        }
+
+        if (null === $value && $option->acceptValue() && count($this->parsed)) {
+            $next = array_shift($this->parsed);
+            if (isset($next[0]) && '-' !== $next[0]) {
+                $value = $next;
+            } elseif (empty($next)) {
+                $value = '';
+            } else {
+                array_unshift($this->parsed, $next);
+            }
+        }
+
+        if (null === $value) {
+            if ($option->isValueRequired()) {
+                throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name));
+            }
+
+            if (!$option->isArray()) {
+                $value = $option->isValueOptional() ? $option->getDefault() : true;
+            }
+        }
+
+        if ($option->isArray()) {
+            $this->options[$name][] = $value;
+        } else {
+            $this->options[$name] = $value;
+        }
+    }
+
+    /**
+     * 获取第一个参数
+     * @return string|null
+     */
+    public function getFirstArgument()
+    {
+        foreach ($this->tokens as $token) {
+            if ($token && '-' === $token[0]) {
+                continue;
+            }
+
+            return $token;
+        }
+        return;
+    }
+
+    /**
+     * 检查原始参数是否包含某个值
+     * @param string|array $values 需要检查的值
+     * @return bool
+     */
+    public function hasParameterOption($values)
+    {
+        $values = (array) $values;
+
+        foreach ($this->tokens as $token) {
+            foreach ($values as $value) {
+                if ($token === $value || 0 === strpos($token, $value . '=')) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * 获取原始选项的值
+     * @param string|array $values  需要检查的值
+     * @param mixed        $default 默认值
+     * @return mixed The option value
+     */
+    public function getParameterOption($values, $default = false)
+    {
+        $values = (array) $values;
+        $tokens = $this->tokens;
+
+        while (0 < count($tokens)) {
+            $token = array_shift($tokens);
+
+            foreach ($values as $value) {
+                if ($token === $value || 0 === strpos($token, $value . '=')) {
+                    if (false !== $pos = strpos($token, '=')) {
+                        return substr($token, $pos + 1);
+                    }
+
+                    return array_shift($tokens);
+                }
+            }
+        }
+
+        return $default;
+    }
+
+    /**
+     * 验证输入
+     * @throws \RuntimeException
+     */
+    public function validate()
+    {
+        if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) {
+            throw new \RuntimeException('Not enough arguments.');
+        }
+    }
+
+    /**
+     * 检查输入是否是交互的
+     * @return bool
+     */
+    public function isInteractive()
+    {
+        return $this->interactive;
+    }
+
+    /**
+     * 设置输入的交互
+     * @param bool
+     */
+    public function setInteractive($interactive)
+    {
+        $this->interactive = (bool) $interactive;
+    }
+
+    /**
+     * 获取所有的参数
+     * @return Argument[]
+     */
+    public function getArguments()
+    {
+        return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
+    }
+
+    /**
+     * 根据名称获取参数
+     * @param string $name 参数名
+     * @return mixed
+     * @throws \InvalidArgumentException
+     */
+    public function getArgument($name)
+    {
+        if (!$this->definition->hasArgument($name)) {
+            throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
+        }
+
+        return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)
+            ->getDefault();
+    }
+
+    /**
+     * 设置参数的值
+     * @param string $name  参数名
+     * @param string $value 值
+     * @throws \InvalidArgumentException
+     */
+    public function setArgument($name, $value)
+    {
+        if (!$this->definition->hasArgument($name)) {
+            throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
+        }
+
+        $this->arguments[$name] = $value;
+    }
+
+    /**
+     * 检查是否存在某个参数
+     * @param string|int $name 参数名或位置
+     * @return bool
+     */
+    public function hasArgument($name)
+    {
+        return $this->definition->hasArgument($name);
+    }
+
+    /**
+     * 获取所有的选项
+     * @return Option[]
+     */
+    public function getOptions()
+    {
+        return array_merge($this->definition->getOptionDefaults(), $this->options);
+    }
+
+    /**
+     * 获取选项值
+     * @param string $name 选项名称
+     * @return mixed
+     * @throws \InvalidArgumentException
+     */
+    public function getOption($name)
+    {
+        if (!$this->definition->hasOption($name)) {
+            throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
+        }
+
+        return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
+    }
+
+    /**
+     * 设置选项值
+     * @param string      $name  选项名
+     * @param string|bool $value 值
+     * @throws \InvalidArgumentException
+     */
+    public function setOption($name, $value)
+    {
+        if (!$this->definition->hasOption($name)) {
+            throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
+        }
+
+        $this->options[$name] = $value;
+    }
+
+    /**
+     * 是否有某个选项
+     * @param string $name 选项名
+     * @return bool
+     */
+    public function hasOption($name)
+    {
+        return $this->definition->hasOption($name) && isset($this->options[$name]);
+    }
+
+    /**
+     * 转义指令
+     * @param string $token
+     * @return string
+     */
+    public function escapeToken($token)
+    {
+        return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
+    }
+
+    /**
+     * 返回传递给命令的参数的字符串
+     * @return string
+     */
+    public function __toString()
+    {
+        $tokens = array_map(function ($token) {
+            if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
+                return $match[1] . $this->escapeToken($match[2]);
+            }
+
+            if ($token && '-' !== $token[0]) {
+                return $this->escapeToken($token);
+            }
+
+            return $token;
+        }, $this->tokens);
+
+        return implode(' ', $tokens);
+    }
+}

+ 19 - 0
thinkphp/library/think/console/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2004-2016 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 222 - 0
thinkphp/library/think/console/Output.php

@@ -0,0 +1,222 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console;
+
+use Exception;
+use think\console\output\Ask;
+use think\console\output\Descriptor;
+use think\console\output\driver\Buffer;
+use think\console\output\driver\Console;
+use think\console\output\driver\Nothing;
+use think\console\output\Question;
+use think\console\output\question\Choice;
+use think\console\output\question\Confirmation;
+
+/**
+ * Class Output
+ * @package think\console
+ *
+ * @see     \think\console\output\driver\Console::setDecorated
+ * @method void setDecorated($decorated)
+ *
+ * @see     \think\console\output\driver\Buffer::fetch
+ * @method string fetch()
+ *
+ * @method void info($message)
+ * @method void error($message)
+ * @method void comment($message)
+ * @method void warning($message)
+ * @method void highlight($message)
+ * @method void question($message)
+ */
+class Output
+{
+    const VERBOSITY_QUIET        = 0;
+    const VERBOSITY_NORMAL       = 1;
+    const VERBOSITY_VERBOSE      = 2;
+    const VERBOSITY_VERY_VERBOSE = 3;
+    const VERBOSITY_DEBUG        = 4;
+
+    const OUTPUT_NORMAL = 0;
+    const OUTPUT_RAW    = 1;
+    const OUTPUT_PLAIN  = 2;
+
+    private $verbosity = self::VERBOSITY_NORMAL;
+
+    /** @var Buffer|Console|Nothing */
+    private $handle = null;
+
+    protected $styles = [
+        'info',
+        'error',
+        'comment',
+        'question',
+        'highlight',
+        'warning'
+    ];
+
+    public function __construct($driver = 'console')
+    {
+        $class = '\\think\\console\\output\\driver\\' . ucwords($driver);
+
+        $this->handle = new $class($this);
+    }
+
+    public function ask(Input $input, $question, $default = null, $validator = null)
+    {
+        $question = new Question($question, $default);
+        $question->setValidator($validator);
+
+        return $this->askQuestion($input, $question);
+    }
+
+    public function askHidden(Input $input, $question, $validator = null)
+    {
+        $question = new Question($question);
+
+        $question->setHidden(true);
+        $question->setValidator($validator);
+
+        return $this->askQuestion($input, $question);
+    }
+
+    public function confirm(Input $input, $question, $default = true)
+    {
+        return $this->askQuestion($input, new Confirmation($question, $default));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function choice(Input $input, $question, array $choices, $default = null)
+    {
+        if (null !== $default) {
+            $values  = array_flip($choices);
+            $default = $values[$default];
+        }
+
+        return $this->askQuestion($input, new Choice($question, $choices, $default));
+    }
+
+    protected function askQuestion(Input $input, Question $question)
+    {
+        $ask    = new Ask($input, $this, $question);
+        $answer = $ask->run();
+
+        if ($input->isInteractive()) {
+            $this->newLine();
+        }
+
+        return $answer;
+    }
+
+    protected function block($style, $message)
+    {
+        $this->writeln("<{$style}>{$message}</$style>");
+    }
+
+    /**
+     * 输出空行
+     * @param int $count
+     */
+    public function newLine($count = 1)
+    {
+        $this->write(str_repeat(PHP_EOL, $count));
+    }
+
+    /**
+     * 输出信息并换行
+     * @param string $messages
+     * @param int    $type
+     */
+    public function writeln($messages, $type = self::OUTPUT_NORMAL)
+    {
+        $this->write($messages, true, $type);
+    }
+
+    /**
+     * 输出信息
+     * @param string $messages
+     * @param bool   $newline
+     * @param int    $type
+     */
+    public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
+    {
+        $this->handle->write($messages, $newline, $type);
+    }
+
+    public function renderException(\Exception $e)
+    {
+        $this->handle->renderException($e);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setVerbosity($level)
+    {
+        $this->verbosity = (int) $level;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getVerbosity()
+    {
+        return $this->verbosity;
+    }
+
+    public function isQuiet()
+    {
+        return self::VERBOSITY_QUIET === $this->verbosity;
+    }
+
+    public function isVerbose()
+    {
+        return self::VERBOSITY_VERBOSE <= $this->verbosity;
+    }
+
+    public function isVeryVerbose()
+    {
+        return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
+    }
+
+    public function isDebug()
+    {
+        return self::VERBOSITY_DEBUG <= $this->verbosity;
+    }
+
+    public function describe($object, array $options = [])
+    {
+        $descriptor = new Descriptor();
+        $options    = array_merge([
+            'raw_text' => false,
+        ], $options);
+
+        $descriptor->describe($this, $object, $options);
+    }
+
+    public function __call($method, $args)
+    {
+        if (in_array($method, $this->styles)) {
+            array_unshift($args, $method);
+            return call_user_func_array([$this, 'block'], $args);
+        }
+
+        if ($this->handle && method_exists($this->handle, $method)) {
+            return call_user_func_array([$this->handle, $method], $args);
+        } else {
+            throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
+        }
+    }
+
+}

+ 281 - 0
thinkphp/library/think/console/Table.php

@@ -0,0 +1,281 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console;
+
+class Table
+{
+    const ALIGN_LEFT   = 1;
+    const ALIGN_RIGHT  = 0;
+    const ALIGN_CENTER = 2;
+
+    /**
+     * 头信息数据
+     * @var array
+     */
+    protected $header = [];
+
+    /**
+     * 头部对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
+     * @var int
+     */
+    protected $headerAlign = 1;
+
+    /**
+     * 表格数据(二维数组)
+     * @var array
+     */
+    protected $rows = [];
+
+    /**
+     * 单元格对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
+     * @var int
+     */
+    protected $cellAlign = 1;
+
+    /**
+     * 单元格宽度信息
+     * @var array
+     */
+    protected $colWidth = [];
+
+    /**
+     * 表格输出样式
+     * @var string
+     */
+    protected $style = 'default';
+
+    /**
+     * 表格样式定义
+     * @var array
+     */
+    protected $format = [
+        'compact'    => [],
+        'default'    => [
+            'top'          => ['+', '-', '+', '+'],
+            'cell'         => ['|', ' ', '|', '|'],
+            'middle'       => ['+', '-', '+', '+'],
+            'bottom'       => ['+', '-', '+', '+'],
+            'cross-top'    => ['+', '-', '-', '+'],
+            'cross-bottom' => ['+', '-', '-', '+'],
+        ],
+        'markdown'   => [
+            'top'          => [' ', ' ', ' ', ' '],
+            'cell'         => ['|', ' ', '|', '|'],
+            'middle'       => ['|', '-', '|', '|'],
+            'bottom'       => [' ', ' ', ' ', ' '],
+            'cross-top'    => ['|', ' ', ' ', '|'],
+            'cross-bottom' => ['|', ' ', ' ', '|'],
+        ],
+        'borderless' => [
+            'top'          => ['=', '=', ' ', '='],
+            'cell'         => [' ', ' ', ' ', ' '],
+            'middle'       => ['=', '=', ' ', '='],
+            'bottom'       => ['=', '=', ' ', '='],
+            'cross-top'    => ['=', '=', ' ', '='],
+            'cross-bottom' => ['=', '=', ' ', '='],
+        ],
+        'box'        => [
+            'top'          => ['┌', '─', '┬', '┐'],
+            'cell'         => ['│', ' ', '│', '│'],
+            'middle'       => ['├', '─', '┼', '┤'],
+            'bottom'       => ['└', '─', '┴', '┘'],
+            'cross-top'    => ['├', '─', '┴', '┤'],
+            'cross-bottom' => ['├', '─', '┬', '┤'],
+        ],
+        'box-double' => [
+            'top'          => ['╔', '═', '╤', '╗'],
+            'cell'         => ['║', ' ', '│', '║'],
+            'middle'       => ['╠', '─', '╪', '╣'],
+            'bottom'       => ['╚', '═', '╧', '╝'],
+            'cross-top'    => ['╠', '═', '╧', '╣'],
+            'cross-bottom' => ['╠', '═', '╤', '╣'],
+        ],
+    ];
+
+    /**
+     * 设置表格头信息 以及对齐方式
+     * @access public
+     * @param array $header     要输出的Header信息
+     * @param int   $align      对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
+     * @return void
+     */
+    public function setHeader(array $header, $align = self::ALIGN_LEFT)
+    {
+        $this->header      = $header;
+        $this->headerAlign = $align;
+
+        $this->checkColWidth($header);
+    }
+
+    /**
+     * 设置输出表格数据 及对齐方式
+     * @access public
+     * @param array $rows       要输出的表格数据(二维数组)
+     * @param int   $align      对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
+     * @return void
+     */
+    public function setRows(array $rows, $align = self::ALIGN_LEFT)
+    {
+        $this->rows      = $rows;
+        $this->cellAlign = $align;
+
+        foreach ($rows as $row) {
+            $this->checkColWidth($row);
+        }
+    }
+
+    /**
+     * 检查列数据的显示宽度
+     * @access public
+     * @param  mixed $row       行数据
+     * @return void
+     */
+    protected function checkColWidth($row)
+    {
+        if (is_array($row)) {
+            foreach ($row as $key => $cell) {
+                if (!isset($this->colWidth[$key]) || strlen($cell) > $this->colWidth[$key]) {
+                    $this->colWidth[$key] = strlen($cell);
+                }
+            }
+        }
+    }
+
+    /**
+     * 增加一行表格数据
+     * @access public
+     * @param  mixed $row       行数据
+     * @param  bool  $first     是否在开头插入
+     * @return void
+     */
+    public function addRow($row, $first = false)
+    {
+        if ($first) {
+            array_unshift($this->rows, $row);
+        } else {
+            $this->rows[] = $row;
+        }
+
+        $this->checkColWidth($row);
+    }
+
+    /**
+     * 设置输出表格的样式
+     * @access public
+     * @param  string $style       样式名
+     * @return void
+     */
+    public function setStyle($style)
+    {
+        $this->style = isset($this->format[$style]) ? $style : 'default';
+    }
+
+    /**
+     * 输出分隔行
+     * @access public
+     * @param  string $pos       位置
+     * @return string
+     */
+    protected function renderSeparator($pos)
+    {
+        $style = $this->getStyle($pos);
+        $array = [];
+
+        foreach ($this->colWidth as $width) {
+            $array[] = str_repeat($style[1], $width + 2);
+        }
+
+        return $style[0] . implode($style[2], $array) . $style[3] . PHP_EOL;
+    }
+
+    /**
+     * 输出表格头部
+     * @access public
+     * @return string
+     */
+    protected function renderHeader()
+    {
+        $style   = $this->getStyle('cell');
+        $content = $this->renderSeparator('top');
+
+        foreach ($this->header as $key => $header) {
+            $array[] = ' ' . str_pad($header, $this->colWidth[$key], $style[1], $this->headerAlign);
+        }
+
+        if (!empty($array)) {
+            $content .= $style[0] . implode(' ' . $style[2], $array) . ' ' . $style[3] . PHP_EOL;
+
+            if ($this->rows) {
+                $content .= $this->renderSeparator('middle');
+            }
+        }
+
+        return $content;
+    }
+
+    protected function getStyle($style)
+    {
+        if ($this->format[$this->style]) {
+            $style = $this->format[$this->style][$style];
+        } else {
+            $style = [' ', ' ', ' ', ' '];
+        }
+
+        return $style;
+    }
+
+    /**
+     * 输出表格
+     * @access public
+     * @param  array $dataList       表格数据
+     * @return string
+     */
+    public function render($dataList = [])
+    {
+        if ($dataList) {
+            $this->setRows($dataList);
+        }
+
+        // 输出头部
+        $content = $this->renderHeader();
+        $style   = $this->getStyle('cell');
+
+        if ($this->rows) {
+            foreach ($this->rows as $row) {
+                if (is_string($row) && '-' === $row) {
+                    $content .= $this->renderSeparator('middle');
+                } elseif (is_scalar($row)) {
+                    $content .= $this->renderSeparator('cross-top');
+                    $array = str_pad($row, 3 * (count($this->colWidth) - 1) + array_reduce($this->colWidth, function ($a, $b) {
+                        return $a + $b;
+                    }));
+
+                    $content .= $style[0] . ' ' . $array . ' ' . $style[3] . PHP_EOL;
+                    $content .= $this->renderSeparator('cross-bottom');
+                } else {
+                    $array = [];
+
+                    foreach ($row as $key => $val) {
+                        $array[] = ' ' . str_pad($val, $this->colWidth[$key], ' ', $this->cellAlign);
+                    }
+
+                    $content .= $style[0] . implode(' ' . $style[2], $array) . ' ' . $style[3] . PHP_EOL;
+
+                }
+            }
+        }
+
+        $content .= $this->renderSeparator('bottom');
+
+        return $content;
+    }
+}

+ 1 - 0
thinkphp/library/think/console/bin/README.md

@@ -0,0 +1 @@
+console 工具使用 hiddeninput.exe 在 windows 上隐藏密码输入,该二进制文件由第三方提供,相关源码和其他细节可以在 [Hidden Input](https://github.com/Seldaek/hidden-input) 找到。

BIN
thinkphp/library/think/console/bin/hiddeninput.exe


+ 59 - 0
thinkphp/library/think/console/command/Build.php

@@ -0,0 +1,59 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: yunwuxin <448901948@qq.com>
+// +----------------------------------------------------------------------
+
+namespace think\console\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Option;
+use think\console\Output;
+use think\facade\App;
+use think\facade\Build as AppBuild;
+
+class Build extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        $this->setName('build')
+            ->setDefinition([
+                new Option('config', null, Option::VALUE_OPTIONAL, "build.php path"),
+                new Option('module', null, Option::VALUE_OPTIONAL, "module name"),
+            ])
+            ->setDescription('Build Application Dirs');
+    }
+
+    protected function execute(Input $input, Output $output)
+    {
+        if ($input->hasOption('module')) {
+            AppBuild::module($input->getOption('module'));
+            $output->writeln("Successed");
+            return;
+        }
+
+        if ($input->hasOption('config')) {
+            $build = include $input->getOption('config');
+        } else {
+            $build = include App::getAppPath() . 'build.php';
+        }
+
+        if (empty($build)) {
+            $output->writeln("Build Config Is Empty");
+            return;
+        }
+
+        AppBuild::run($build);
+        $output->writeln("Successed");
+
+    }
+}

+ 0 - 0
thinkphp/library/think/console/command/Clear.php


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