Commit 55887bb8ebeb9dfe4eb1d0bd4d6abe7505d101a3

Authored by teemitop
1 parent fc344f92

文件恢复

Too many changes to show.

To preserve performance only 17 of 17+ files are displayed.

  1 +/vendor
  2 +composer.phar
  3 +composer.lock
  4 +.DS_Store
  5 +Thumbs.db
  6 +/phpunit.xml
  7 +/.idea
  8 +/.vscode
  1 +deny from all
  1 +如何贡献我的源代码
  2 +===
  3 +
  4 +此文档介绍了 ThinkPHP 团队的组成以及运转机制,您提交的代码将给 ThinkPHP 项目带来什么好处,以及如何才能加入我们的行列。
  5 +
  6 +## 通过 Github 贡献代码
  7 +
  8 +ThinkPHP 目前使用 Git 来控制程序版本,如果你想为 ThinkPHP 贡献源代码,请先大致了解 Git 的使用方法。我们目前把项目托管在 GitHub 上,任何 GitHub 用户都可以向我们贡献代码。
  9 +
  10 +参与的方式很简单,`fork`一份 ThinkPHP 的代码到你的仓库中,修改后提交,并向我们发起`pull request`申请,我们会及时对代码进行审查并处理你的申请并。审查通过后,你的代码将被`merge`进我们的仓库中,这样你就会自动出现在贡献者名单里了,非常方便。
  11 +
  12 +我们希望你贡献的代码符合:
  13 +
  14 +* ThinkPHP 的编码规范
  15 +* 适当的注释,能让其他人读懂
  16 +* 遵循 Apache2 开源协议
  17 +
  18 +**如果想要了解更多细节或有任何疑问,请继续阅读下面的内容**
  19 +
  20 +### 注意事项
  21 +
  22 +* 本项目代码格式化标准选用 [**PSR-2**](http://www.kancloud.cn/thinkphp/php-fig-psr/3141);
  23 +* 类名和类文件名遵循 [**PSR-4**](http://www.kancloud.cn/thinkphp/php-fig-psr/3144);
  24 +* 对于 Issues 的处理,请使用诸如 `fix #xxx(Issue ID)` 的 commit title 直接关闭 issue。
  25 +* 系统会自动在 PHP 5.4 5.5 5.6 7.0 和 HHVM 上测试修改,其中 HHVM 下的测试容许报错,请确保你的修改符合 PHP 5.4 ~ 5.6 和 PHP 7.0 的语法规范;
  26 +* 管理员不会合并造成 CI faild 的修改,若出现 CI faild 请检查自己的源代码或修改相应的[单元测试文件](tests);
  27 +
  28 +## GitHub Issue
  29 +
  30 +GitHub 提供了 Issue 功能,该功能可以用于:
  31 +
  32 +* 提出 bug
  33 +* 提出功能改进
  34 +* 反馈使用体验
  35 +
  36 +该功能不应该用于:
  37 +
  38 + * 提出修改意见(涉及代码署名和修订追溯问题)
  39 + * 不友善的言论
  40 +
  41 +## 快速修改
  42 +
  43 +**GitHub 提供了快速编辑文件的功能**
  44 +
  45 +1. 登录 GitHub 帐号;
  46 +2. 浏览项目文件,找到要进行修改的文件;
  47 +3. 点击右上角铅笔图标进行修改;
  48 +4. 填写 `Commit changes` 相关内容(Title 必填);
  49 +5. 提交修改,等待 CI 验证和管理员合并。
  50 +
  51 +**若您需要一次提交大量修改,请继续阅读下面的内容**
  52 +
  53 +## 完整流程
  54 +
  55 +1. `fork`本项目;
  56 +2. 克隆(`clone`)你 `fork` 的项目到本地;
  57 +3. 新建分支(`branch`)并检出(`checkout`)新分支;
  58 +4. 添加本项目到你的本地 git 仓库作为上游(`upstream`);
  59 +5. 进行修改,若你的修改包含方法或函数的增减,请记得修改[单元测试文件](tests);
  60 +6. 变基(衍合 `rebase`)你的分支到上游 master 分支;
  61 +7. `push` 你的本地仓库到 GitHub;
  62 +8. 提交 `pull request`;
  63 +9. 等待 CI 验证(若不通过则重复 5~7,GitHub 会自动更新你的 `pull request`);
  64 +10. 等待管理员处理,并及时 `rebase` 你的分支到上游 master 分支(若上游 master 分支有修改)。
  65 +
  66 +*若有必要,可以 `git push -f` 强行推送 rebase 后的分支到自己的 `fork`*
  67 +
  68 +*绝对不可以使用 `git push -f` 强行推送修改到上游*
  69 +
  70 +### 注意事项
  71 +
  72 +* 若对上述流程有任何不清楚的地方,请查阅 GIT 教程,如 [这个](http://backlogtool.com/git-guide/cn/);
  73 +* 对于代码**不同方面**的修改,请在自己 `fork` 的项目中**创建不同的分支**(原因参见`完整流程`第9条备注部分);
  74 +* 变基及交互式变基操作参见 [Git 交互式变基](http://pakchoi.me/2015/03/17/git-interactive-rebase/)
  75 +
  76 +## 推荐资源
  77 +
  78 +### 开发环境
  79 +
  80 +* XAMPP for Windows 5.5.x
  81 +* WampServer (for Windows)
  82 +* upupw Apache PHP5.4 ( for Windows)
  83 +
  84 +或自行安装
  85 +
  86 +- Apache / Nginx
  87 +- PHP 5.4 ~ 5.6
  88 +- MySQL / MariaDB
  89 +
  90 +*Windows 用户推荐添加 PHP bin 目录到 PATH,方便使用 composer*
  91 +
  92 +*Linux 用户自行配置环境, Mac 用户推荐使用内置 Apache 配合 Homebrew 安装 PHP 和 MariaDB*
  93 +
  94 +### 编辑器
  95 +
  96 +Sublime Text 3 + phpfmt 插件
  97 +
  98 +phpfmt 插件参数
  99 +
  100 +```json
  101 +{
  102 + "autocomplete": true,
  103 + "enable_auto_align": true,
  104 + "format_on_save": true,
  105 + "indent_with_space": true,
  106 + "psr1_naming": false,
  107 + "psr2": true,
  108 + "version": 4
  109 +}
  110 +```
  111 +
  112 +或其他 编辑器 / IDE 配合 PSR2 自动格式化工具
  113 +
  114 +### Git GUI
  115 +
  116 +* SourceTree
  117 +* GitHub Desktop
  118 +
  119 +或其他 Git 图形界面客户端
  1 +
  2 +ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
  3 +版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn)
  4 +All rights reserved。
  5 +ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
  6 +
  7 +Apache Licence是著名的非盈利开源组织Apache采用的协议。
  8 +该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
  9 +允许代码修改,再作为开源或商业软件发布。需要满足
  10 +的条件:
  11 +1. 需要给代码的用户一份Apache Licence ;
  12 +2. 如果你修改了代码,需要在被修改的文件中说明;
  13 +3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
  14 +带有原来代码中的协议,商标,专利声明和其他原来作者规
  15 +定需要包含的说明;
  16 +4. 如果再发布的产品中包含一个Notice文件,则在Notice文
  17 +件中需要带有本协议内容。你可以在Notice中增加自己的
  18 +许可,但不可以表现为对Apache Licence构成更改。
  19 +具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
  20 +
  21 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24 +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25 +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26 +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27 +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  28 +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  29 +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30 +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  31 +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32 +POSSIBILITY OF SUCH DAMAGE.
  1 +![](http://www.thinkphp.cn/Uploads/editor/2016-06-23/576b4732a6e04.png)
  2 +
  3 +ThinkPHP 5.1 —— 12载初心,你值得信赖的PHP框架
  4 +===============
  5 +
  6 +[![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)
  7 +[![Build Status](https://travis-ci.org/top-think/framework.svg?branch=master)](https://travis-ci.org/top-think/framework)
  8 +[![Total Downloads](https://poser.pugx.org/topthink/framework/downloads)](https://packagist.org/packages/topthink/framework)
  9 +[![Latest Stable Version](https://poser.pugx.org/topthink/framework/v/stable)](https://packagist.org/packages/topthink/framework)
  10 +[![PHP Version](https://img.shields.io/badge/php-%3E%3D5.6-8892BF.svg)](http://www.php.net/)
  11 +[![License](https://poser.pugx.org/topthink/framework/license)](https://packagist.org/packages/topthink/framework)
  12 +
  13 +ThinkPHP5.1对底层架构做了进一步的改进,减少依赖,其主要特性包括:
  14 +
  15 + + 采用容器统一管理对象
  16 + + 支持Facade
  17 + + 更易用的路由
  18 + + 注解路由支持
  19 + + 路由跨域请求支持
  20 + + 验证类增强
  21 + + 配置和路由目录独立
  22 + + 取消系统常量
  23 + + 类库别名机制
  24 + + 模型和数据库增强
  25 + + 依赖注入完善
  26 + + 支持PSR-3日志规范
  27 + + 中间件支持(`V5.1.6+`)
  28 + + 支持`Swoole`/`Workerman`运行(`V5.1.18+`)
  29 +
  30 +
  31 +### 废除的功能:
  32 +
  33 + + 聚合模型
  34 + + 内置控制器扩展类
  35 + + 模型自动验证
  36 +
  37 +> ThinkPHP5.1的运行环境要求PHP5.6+。
  38 +
  39 +## 安装
  40 +
  41 +使用composer安装
  42 +
  43 +~~~
  44 +composer create-project topthink/think tp
  45 +~~~
  46 +
  47 +启动服务
  48 +
  49 +~~~
  50 +cd tp
  51 +php think run
  52 +~~~
  53 +
  54 +然后就可以在浏览器中访问
  55 +
  56 +~~~
  57 +http://localhost:8000
  58 +~~~
  59 +
  60 +更新框架
  61 +~~~
  62 +composer update topthink/framework
  63 +~~~
  64 +
  65 +
  66 +## 在线手册
  67 +
  68 ++ [完全开发手册](https://www.kancloud.cn/manual/thinkphp5_1/content)
  69 ++ [升级指导](https://www.kancloud.cn/manual/thinkphp5_1/354155)
  70 +
  71 +## 命名规范
  72 +
  73 +`ThinkPHP5.1`遵循PSR-2命名规范和PSR-4自动加载规范。
  74 +
  75 +## 参与开发
  76 +
  77 +请参阅 [ThinkPHP5 核心框架包](https://github.com/top-think/framework)。
  78 +
  79 +## 版权信息
  80 +
  81 +ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
  82 +
  83 +本项目包含的第三方源码和二进制文件之版权信息另行标注。
  84 +
  85 +版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn)
  86 +
  87 +All rights reserved。
  88 +
  89 +ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
  90 +
  91 +更多细节参阅 [LICENSE.txt](LICENSE.txt)
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace think;
  12 +
  13 +// 载入Loader类
  14 +require __DIR__ . '/library/think/Loader.php';
  15 +
  16 +// 注册自动加载
  17 +Loader::register();
  18 +
  19 +// 注册错误和异常处理机制
  20 +Error::register();
  21 +
  22 +// 实现日志接口
  23 +if (interface_exists('Psr\Log\LoggerInterface')) {
  24 + interface LoggerInterface extends \Psr\Log\LoggerInterface
  25 + {}
  26 +} else {
  27 + interface LoggerInterface
  28 + {}
  29 +}
  30 +
  31 +// 注册类库别名
  32 +Loader::addClassAlias([
  33 + 'App' => facade\App::class,
  34 + 'Build' => facade\Build::class,
  35 + 'Cache' => facade\Cache::class,
  36 + 'Config' => facade\Config::class,
  37 + 'Cookie' => facade\Cookie::class,
  38 + 'Db' => Db::class,
  39 + 'Debug' => facade\Debug::class,
  40 + 'Env' => facade\Env::class,
  41 + 'Facade' => Facade::class,
  42 + 'Hook' => facade\Hook::class,
  43 + 'Lang' => facade\Lang::class,
  44 + 'Log' => facade\Log::class,
  45 + 'Request' => facade\Request::class,
  46 + 'Response' => facade\Response::class,
  47 + 'Route' => facade\Route::class,
  48 + 'Session' => facade\Session::class,
  49 + 'Url' => facade\Url::class,
  50 + 'Validate' => facade\Validate::class,
  51 + 'View' => facade\View::class,
  52 +]);
  1 +{
  2 + "name": "topthink/framework",
  3 + "description": "the new thinkphp framework",
  4 + "type": "think-framework",
  5 + "keywords": [
  6 + "framework",
  7 + "thinkphp",
  8 + "ORM"
  9 + ],
  10 + "homepage": "http://thinkphp.cn/",
  11 + "license": "Apache-2.0",
  12 + "authors": [
  13 + {
  14 + "name": "liu21st",
  15 + "email": "liu21st@gmail.com"
  16 + },
  17 + {
  18 + "name": "yunwuxin",
  19 + "email": "448901948@qq.com"
  20 + }
  21 + ],
  22 + "require": {
  23 + "php": ">=5.6.0",
  24 + "topthink/think-installer": "2.*"
  25 + },
  26 + "require-dev": {
  27 + "phpunit/phpunit": "^5.0|^6.0",
  28 + "johnkary/phpunit-speedtrap": "^1.0",
  29 + "mikey179/vfsStream": "~1.6",
  30 + "phploc/phploc": "2.*",
  31 + "sebastian/phpcpd": "2.*",
  32 + "squizlabs/php_codesniffer": "2.*",
  33 + "phpdocumentor/reflection-docblock": "^2.0"
  34 + }
  35 +}
  1 +<?php
  2 +
  3 +return [
  4 + // +----------------------------------------------------------------------
  5 + // | 应用设置
  6 + // +----------------------------------------------------------------------
  7 + 'app' => [
  8 + // 应用名称
  9 + 'app_name' => '',
  10 + // 应用地址
  11 + 'app_host' => '',
  12 + // 应用调试模式
  13 + 'app_debug' => false,
  14 + // 应用Trace
  15 + 'app_trace' => false,
  16 + // 应用模式状态
  17 + 'app_status' => '',
  18 + // 是否HTTPS
  19 + 'is_https' => false,
  20 + // 入口自动绑定模块
  21 + 'auto_bind_module' => false,
  22 + // 注册的根命名空间
  23 + 'root_namespace' => [],
  24 + // 默认输出类型
  25 + 'default_return_type' => 'html',
  26 + // 默认AJAX 数据返回格式,可选json xml ...
  27 + 'default_ajax_return' => 'json',
  28 + // 默认JSONP格式返回的处理方法
  29 + 'default_jsonp_handler' => 'jsonpReturn',
  30 + // 默认JSONP处理方法
  31 + 'var_jsonp_handler' => 'callback',
  32 + // 默认时区
  33 + 'default_timezone' => 'Asia/Shanghai',
  34 + // 是否开启多语言
  35 + 'lang_switch_on' => false,
  36 + // 默认验证器
  37 + 'default_validate' => '',
  38 + // 默认语言
  39 + 'default_lang' => 'zh-cn',
  40 +
  41 + // +----------------------------------------------------------------------
  42 + // | 模块设置
  43 + // +----------------------------------------------------------------------
  44 +
  45 + // 自动搜索控制器
  46 + 'controller_auto_search' => false,
  47 + // 操作方法前缀
  48 + 'use_action_prefix' => false,
  49 + // 操作方法后缀
  50 + 'action_suffix' => '',
  51 + // 默认的空控制器名
  52 + 'empty_controller' => 'Error',
  53 + // 默认的空模块名
  54 + 'empty_module' => '',
  55 + // 默认模块名
  56 + 'default_module' => 'index',
  57 + // 是否支持多模块
  58 + 'app_multi_module' => true,
  59 + // 禁止访问模块
  60 + 'deny_module_list' => ['common'],
  61 + // 默认控制器名
  62 + 'default_controller' => 'Index',
  63 + // 默认操作名
  64 + 'default_action' => 'index',
  65 + // 是否自动转换URL中的控制器和操作名
  66 + 'url_convert' => true,
  67 + // 默认的访问控制器层
  68 + 'url_controller_layer' => 'controller',
  69 + // 应用类库后缀
  70 + 'class_suffix' => false,
  71 + // 控制器类后缀
  72 + 'controller_suffix' => false,
  73 +
  74 + // +----------------------------------------------------------------------
  75 + // | URL请求设置
  76 + // +----------------------------------------------------------------------
  77 +
  78 + // 默认全局过滤方法 用逗号分隔多个
  79 + 'default_filter' => '',
  80 + // PATHINFO变量名 用于兼容模式
  81 + 'var_pathinfo' => 's',
  82 + // 兼容PATH_INFO获取
  83 + 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
  84 + // HTTPS代理标识
  85 + 'https_agent_name' => '',
  86 + // IP代理获取标识
  87 + 'http_agent_ip' => 'HTTP_X_REAL_IP',
  88 + // URL伪静态后缀
  89 + 'url_html_suffix' => 'html',
  90 + // 域名根,如thinkphp.cn
  91 + 'url_domain_root' => '',
  92 + // 表单请求类型伪装变量
  93 + 'var_method' => '_method',
  94 + // 表单ajax伪装变量
  95 + 'var_ajax' => '_ajax',
  96 + // 表单pjax伪装变量
  97 + 'var_pjax' => '_pjax',
  98 + // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
  99 + 'request_cache' => false,
  100 + // 请求缓存有效期
  101 + 'request_cache_expire' => null,
  102 + // 全局请求缓存排除规则
  103 + 'request_cache_except' => [],
  104 +
  105 + // +----------------------------------------------------------------------
  106 + // | 路由设置
  107 + // +----------------------------------------------------------------------
  108 +
  109 + // pathinfo分隔符
  110 + 'pathinfo_depr' => '/',
  111 + // URL普通方式参数 用于自动生成
  112 + 'url_common_param' => false,
  113 + // URL参数方式 0 按名称成对解析 1 按顺序解析
  114 + 'url_param_type' => 0,
  115 + // 是否开启路由延迟解析
  116 + 'url_lazy_route' => false,
  117 + // 是否强制使用路由
  118 + 'url_route_must' => false,
  119 + // 合并路由规则
  120 + 'route_rule_merge' => false,
  121 + // 路由是否完全匹配
  122 + 'route_complete_match' => false,
  123 + // 使用注解路由
  124 + 'route_annotation' => false,
  125 + // 默认的路由变量规则
  126 + 'default_route_pattern' => '\w+',
  127 + // 是否开启路由缓存
  128 + 'route_check_cache' => false,
  129 + // 路由缓存的Key自定义设置(闭包),默认为当前URL和请求类型的md5
  130 + 'route_check_cache_key' => '',
  131 +
  132 + // +----------------------------------------------------------------------
  133 + // | 异常及错误设置
  134 + // +----------------------------------------------------------------------
  135 +
  136 + // 默认跳转页面对应的模板文件
  137 + 'dispatch_success_tmpl' => __DIR__ . '/tpl/dispatch_jump.tpl',
  138 + 'dispatch_error_tmpl' => __DIR__ . '/tpl/dispatch_jump.tpl',
  139 + // 异常页面的模板文件
  140 + 'exception_tmpl' => __DIR__ . '/tpl/think_exception.tpl',
  141 + // 错误显示信息,非调试模式有效
  142 + 'error_message' => '页面错误!请稍后再试~',
  143 + // 显示错误信息
  144 + 'show_error_msg' => false,
  145 + // 异常处理handle类 留空使用 \think\exception\Handle
  146 + 'exception_handle' => '',
  147 + ],
  148 +
  149 + // +----------------------------------------------------------------------
  150 + // | 模板设置
  151 + // +----------------------------------------------------------------------
  152 +
  153 + 'template' => [
  154 + // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写
  155 + 'auto_rule' => 1,
  156 + // 模板引擎类型 支持 php think 支持扩展
  157 + 'type' => 'Think',
  158 + // 视图基础目录,配置目录为所有模块的视图起始目录
  159 + 'view_base' => '',
  160 + // 当前模板的视图目录 留空为自动获取
  161 + 'view_path' => '',
  162 + // 模板后缀
  163 + 'view_suffix' => 'html',
  164 + // 模板文件名分隔符
  165 + 'view_depr' => DIRECTORY_SEPARATOR,
  166 + // 模板引擎普通标签开始标记
  167 + 'tpl_begin' => '{',
  168 + // 模板引擎普通标签结束标记
  169 + 'tpl_end' => '}',
  170 + // 标签库标签开始标记
  171 + 'taglib_begin' => '{',
  172 + // 标签库标签结束标记
  173 + 'taglib_end' => '}',
  174 + ],
  175 +
  176 + // +----------------------------------------------------------------------
  177 + // | 日志设置
  178 + // +----------------------------------------------------------------------
  179 +
  180 + 'log' => [
  181 + // 日志记录方式,内置 file socket 支持扩展
  182 + 'type' => 'File',
  183 + // 日志保存目录
  184 + //'path' => LOG_PATH,
  185 + // 日志记录级别
  186 + 'level' => [],
  187 + // 是否记录trace信息到日志
  188 + 'record_trace' => false,
  189 + // 是否JSON格式记录
  190 + 'json' => false,
  191 + ],
  192 +
  193 + // +----------------------------------------------------------------------
  194 + // | Trace设置 开启 app_trace 后 有效
  195 + // +----------------------------------------------------------------------
  196 +
  197 + 'trace' => [
  198 + // 内置Html Console 支持扩展
  199 + 'type' => 'Html',
  200 + 'file' => __DIR__ . '/tpl/page_trace.tpl',
  201 + ],
  202 +
  203 + // +----------------------------------------------------------------------
  204 + // | 缓存设置
  205 + // +----------------------------------------------------------------------
  206 +
  207 + 'cache' => [
  208 + // 驱动方式
  209 + 'type' => 'File',
  210 + // 缓存保存目录
  211 + //'path' => CACHE_PATH,
  212 + // 缓存前缀
  213 + 'prefix' => '',
  214 + // 缓存有效期 0表示永久缓存
  215 + 'expire' => 0,
  216 + ],
  217 +
  218 + // +----------------------------------------------------------------------
  219 + // | 会话设置
  220 + // +----------------------------------------------------------------------
  221 +
  222 + 'session' => [
  223 + 'id' => '',
  224 + // SESSION_ID的提交变量,解决flash上传跨域
  225 + 'var_session_id' => '',
  226 + // SESSION 前缀
  227 + 'prefix' => 'think',
  228 + // 驱动方式 支持redis memcache memcached
  229 + 'type' => '',
  230 + // 是否自动开启 SESSION
  231 + 'auto_start' => true,
  232 + 'httponly' => true,
  233 + 'secure' => false,
  234 + ],
  235 +
  236 + // +----------------------------------------------------------------------
  237 + // | Cookie设置
  238 + // +----------------------------------------------------------------------
  239 +
  240 + 'cookie' => [
  241 + // cookie 名称前缀
  242 + 'prefix' => '',
  243 + // cookie 保存时间
  244 + 'expire' => 0,
  245 + // cookie 保存路径
  246 + 'path' => '/',
  247 + // cookie 有效域名
  248 + 'domain' => '',
  249 + // cookie 启用安全传输
  250 + 'secure' => false,
  251 + // httponly设置
  252 + 'httponly' => '',
  253 + // 是否使用 setcookie
  254 + 'setcookie' => true,
  255 + ],
  256 +
  257 + // +----------------------------------------------------------------------
  258 + // | 数据库设置
  259 + // +----------------------------------------------------------------------
  260 +
  261 + 'database' => [
  262 + // 数据库类型
  263 + 'type' => 'mysql',
  264 + // 数据库连接DSN配置
  265 + 'dsn' => '',
  266 + // 服务器地址
  267 + 'hostname' => '127.0.0.1',
  268 + // 数据库名
  269 + 'database' => '',
  270 + // 数据库用户名
  271 + 'username' => 'root',
  272 + // 数据库密码
  273 + 'password' => '',
  274 + // 数据库连接端口
  275 + 'hostport' => '',
  276 + // 数据库连接参数
  277 + 'params' => [],
  278 + // 数据库编码默认采用utf8
  279 + 'charset' => 'utf8',
  280 + // 数据库表前缀
  281 + 'prefix' => '',
  282 + // 数据库调试模式
  283 + 'debug' => false,
  284 + // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
  285 + 'deploy' => 0,
  286 + // 数据库读写是否分离 主从式有效
  287 + 'rw_separate' => false,
  288 + // 读写分离后 主服务器数量
  289 + 'master_num' => 1,
  290 + // 指定从服务器序号
  291 + 'slave_no' => '',
  292 + // 是否严格检查字段是否存在
  293 + 'fields_strict' => true,
  294 + // 数据集返回类型
  295 + 'resultset_type' => 'array',
  296 + // 自动写入时间戳字段
  297 + 'auto_timestamp' => false,
  298 + // 时间字段取出后的默认时间格式
  299 + 'datetime_format' => 'Y-m-d H:i:s',
  300 + // 是否需要进行SQL性能分析
  301 + 'sql_explain' => false,
  302 + // 查询对象
  303 + 'query' => '\\think\\db\\Query',
  304 + ],
  305 +
  306 + //分页配置
  307 + 'paginate' => [
  308 + 'type' => 'bootstrap',
  309 + 'var_page' => 'page',
  310 + 'list_rows' => 15,
  311 + ],
  312 +
  313 + //控制台配置
  314 + 'console' => [
  315 + 'name' => 'Think Console',
  316 + 'version' => '0.1',
  317 + 'user' => null,
  318 + ],
  319 +
  320 + // 中间件配置
  321 + 'middleware' => [
  322 + 'default_namespace' => 'app\\http\\middleware\\',
  323 + ],
  324 +];
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +//------------------------
  13 +// ThinkPHP 助手函数
  14 +//-------------------------
  15 +
  16 +use think\Container;
  17 +use think\Db;
  18 +use think\exception\HttpException;
  19 +use think\exception\HttpResponseException;
  20 +use think\facade\Cache;
  21 +use think\facade\Config;
  22 +use think\facade\Cookie;
  23 +use think\facade\Debug;
  24 +use think\facade\Env;
  25 +use think\facade\Hook;
  26 +use think\facade\Lang;
  27 +use think\facade\Log;
  28 +use think\facade\Request;
  29 +use think\facade\Route;
  30 +use think\facade\Session;
  31 +use think\facade\Url;
  32 +use think\Response;
  33 +use think\route\RuleItem;
  34 +
  35 +if (!function_exists('abort')) {
  36 + /**
  37 + * 抛出HTTP异常
  38 + * @param integer|Response $code 状态码 或者 Response对象实例
  39 + * @param string $message 错误信息
  40 + * @param array $header 参数
  41 + */
  42 + function abort($code, $message = null, $header = [])
  43 + {
  44 + if ($code instanceof Response) {
  45 + throw new HttpResponseException($code);
  46 + } else {
  47 + throw new HttpException($code, $message, null, $header);
  48 + }
  49 + }
  50 +}
  51 +
  52 +if (!function_exists('action')) {
  53 + /**
  54 + * 调用模块的操作方法 参数格式 [模块/控制器/]操作
  55 + * @param string $url 调用地址
  56 + * @param string|array $vars 调用参数 支持字符串和数组
  57 + * @param string $layer 要调用的控制层名称
  58 + * @param bool $appendSuffix 是否添加类名后缀
  59 + * @return mixed
  60 + */
  61 + function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
  62 + {
  63 + return app()->action($url, $vars, $layer, $appendSuffix);
  64 + }
  65 +}
  66 +
  67 +if (!function_exists('app')) {
  68 + /**
  69 + * 快速获取容器中的实例 支持依赖注入
  70 + * @param string $name 类名或标识 默认获取当前应用实例
  71 + * @param array $args 参数
  72 + * @param bool $newInstance 是否每次创建新的实例
  73 + * @return mixed|\think\App
  74 + */
  75 + function app($name = 'think\App', $args = [], $newInstance = false)
  76 + {
  77 + return Container::get($name, $args, $newInstance);
  78 + }
  79 +}
  80 +
  81 +if (!function_exists('behavior')) {
  82 + /**
  83 + * 执行某个行为(run方法) 支持依赖注入
  84 + * @param mixed $behavior 行为类名或者别名
  85 + * @param mixed $args 参数
  86 + * @return mixed
  87 + */
  88 + function behavior($behavior, $args = null)
  89 + {
  90 + return Hook::exec($behavior, $args);
  91 + }
  92 +}
  93 +
  94 +if (!function_exists('bind')) {
  95 + /**
  96 + * 绑定一个类到容器
  97 + * @access public
  98 + * @param string $abstract 类标识、接口
  99 + * @param mixed $concrete 要绑定的类、闭包或者实例
  100 + * @return Container
  101 + */
  102 + function bind($abstract, $concrete = null)
  103 + {
  104 + return Container::getInstance()->bindTo($abstract, $concrete);
  105 + }
  106 +}
  107 +
  108 +if (!function_exists('cache')) {
  109 + /**
  110 + * 缓存管理
  111 + * @param mixed $name 缓存名称,如果为数组表示进行缓存设置
  112 + * @param mixed $value 缓存值
  113 + * @param mixed $options 缓存参数
  114 + * @param string $tag 缓存标签
  115 + * @return mixed
  116 + */
  117 + function cache($name, $value = '', $options = null, $tag = null)
  118 + {
  119 + if (is_array($options)) {
  120 + // 缓存操作的同时初始化
  121 + Cache::connect($options);
  122 + } elseif (is_array($name)) {
  123 + // 缓存初始化
  124 + return Cache::connect($name);
  125 + }
  126 +
  127 + if ('' === $value) {
  128 + // 获取缓存
  129 + return 0 === strpos($name, '?') ? Cache::has(substr($name, 1)) : Cache::get($name);
  130 + } elseif (is_null($value)) {
  131 + // 删除缓存
  132 + return Cache::rm($name);
  133 + } else {
  134 + // 缓存数据
  135 + if (is_array($options)) {
  136 + $expire = isset($options['expire']) ? $options['expire'] : null; //修复查询缓存无法设置过期时间
  137 + } else {
  138 + $expire = is_numeric($options) ? $options : null; //默认快捷缓存设置过期时间
  139 + }
  140 +
  141 + if (is_null($tag)) {
  142 + return Cache::set($name, $value, $expire);
  143 + } else {
  144 + return Cache::tag($tag)->set($name, $value, $expire);
  145 + }
  146 + }
  147 + }
  148 +}
  149 +
  150 +if (!function_exists('call')) {
  151 + /**
  152 + * 调用反射执行callable 支持依赖注入
  153 + * @param mixed $callable 支持闭包等callable写法
  154 + * @param array $args 参数
  155 + * @return mixed
  156 + */
  157 + function call($callable, $args = [])
  158 + {
  159 + return Container::getInstance()->invoke($callable, $args);
  160 + }
  161 +}
  162 +
  163 +if (!function_exists('class_basename')) {
  164 + /**
  165 + * 获取类名(不包含命名空间)
  166 + *
  167 + * @param string|object $class
  168 + * @return string
  169 + */
  170 + function class_basename($class)
  171 + {
  172 + $class = is_object($class) ? get_class($class) : $class;
  173 + return basename(str_replace('\\', '/', $class));
  174 + }
  175 +}
  176 +
  177 +if (!function_exists('class_uses_recursive')) {
  178 + /**
  179 + *获取一个类里所有用到的trait,包括父类的
  180 + *
  181 + * @param $class
  182 + * @return array
  183 + */
  184 + function class_uses_recursive($class)
  185 + {
  186 + if (is_object($class)) {
  187 + $class = get_class($class);
  188 + }
  189 +
  190 + $results = [];
  191 + $classes = array_merge([$class => $class], class_parents($class));
  192 + foreach ($classes as $class) {
  193 + $results += trait_uses_recursive($class);
  194 + }
  195 +
  196 + return array_unique($results);
  197 + }
  198 +}
  199 +
  200 +if (!function_exists('config')) {
  201 + /**
  202 + * 获取和设置配置参数
  203 + * @param string|array $name 参数名
  204 + * @param mixed $value 参数值
  205 + * @return mixed
  206 + */
  207 + function config($name = '', $value = null)
  208 + {
  209 + if (is_null($value) && is_string($name)) {
  210 + if ('.' == substr($name, -1)) {
  211 + return Config::pull(substr($name, 0, -1));
  212 + }
  213 +
  214 + return 0 === strpos($name, '?') ? Config::has(substr($name, 1)) : Config::get($name);
  215 + } else {
  216 + return Config::set($name, $value);
  217 + }
  218 + }
  219 +}
  220 +
  221 +if (!function_exists('container')) {
  222 + /**
  223 + * 获取容器对象实例
  224 + * @return Container
  225 + */
  226 + function container()
  227 + {
  228 + return Container::getInstance();
  229 + }
  230 +}
  231 +
  232 +if (!function_exists('controller')) {
  233 + /**
  234 + * 实例化控制器 格式:[模块/]控制器
  235 + * @param string $name 资源地址
  236 + * @param string $layer 控制层名称
  237 + * @param bool $appendSuffix 是否添加类名后缀
  238 + * @return \think\Controller
  239 + */
  240 + function controller($name, $layer = 'controller', $appendSuffix = false)
  241 + {
  242 + return app()->controller($name, $layer, $appendSuffix);
  243 + }
  244 +}
  245 +
  246 +if (!function_exists('cookie')) {
  247 + /**
  248 + * Cookie管理
  249 + * @param string|array $name cookie名称,如果为数组表示进行cookie设置
  250 + * @param mixed $value cookie值
  251 + * @param mixed $option 参数
  252 + * @return mixed
  253 + */
  254 + function cookie($name, $value = '', $option = null)
  255 + {
  256 + if (is_array($name)) {
  257 + // 初始化
  258 + Cookie::init($name);
  259 + } elseif (is_null($name)) {
  260 + // 清除
  261 + Cookie::clear($value);
  262 + } elseif ('' === $value) {
  263 + // 获取
  264 + return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1), $option) : Cookie::get($name);
  265 + } elseif (is_null($value)) {
  266 + // 删除
  267 + return Cookie::delete($name);
  268 + } else {
  269 + // 设置
  270 + return Cookie::set($name, $value, $option);
  271 + }
  272 + }
  273 +}
  274 +
  275 +if (!function_exists('db')) {
  276 + /**
  277 + * 实例化数据库类
  278 + * @param string $name 操作的数据表名称(不含前缀)
  279 + * @param array|string $config 数据库配置参数
  280 + * @param bool $force 是否强制重新连接
  281 + * @return \think\db\Query
  282 + */
  283 + function db($name = '', $config = [], $force = true)
  284 + {
  285 + return Db::connect($config, $force)->name($name);
  286 + }
  287 +}
  288 +
  289 +if (!function_exists('debug')) {
  290 + /**
  291 + * 记录时间(微秒)和内存使用情况
  292 + * @param string $start 开始标签
  293 + * @param string $end 结束标签
  294 + * @param integer|string $dec 小数位 如果是m 表示统计内存占用
  295 + * @return mixed
  296 + */
  297 + function debug($start, $end = '', $dec = 6)
  298 + {
  299 + if ('' == $end) {
  300 + Debug::remark($start);
  301 + } else {
  302 + return 'm' == $dec ? Debug::getRangeMem($start, $end) : Debug::getRangeTime($start, $end, $dec);
  303 + }
  304 + }
  305 +}
  306 +
  307 +if (!function_exists('download')) {
  308 + /**
  309 + * 获取\think\response\Download对象实例
  310 + * @param string $filename 要下载的文件
  311 + * @param string $name 显示文件名
  312 + * @param bool $content 是否为内容
  313 + * @param integer $expire 有效期(秒)
  314 + * @return \think\response\Download
  315 + */
  316 + function download($filename, $name = '', $content = false, $expire = 180)
  317 + {
  318 + return Response::create($filename, 'download')->name($name)->isContent($content)->expire($expire);
  319 + }
  320 +}
  321 +
  322 +if (!function_exists('dump')) {
  323 + /**
  324 + * 浏览器友好的变量输出
  325 + * @param mixed $var 变量
  326 + * @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串
  327 + * @param string $label 标签 默认为空
  328 + * @return void|string
  329 + */
  330 + function dump($var, $echo = true, $label = null)
  331 + {
  332 + return Debug::dump($var, $echo, $label);
  333 + }
  334 +}
  335 +
  336 +if (!function_exists('env')) {
  337 + /**
  338 + * 获取环境变量值
  339 + * @access public
  340 + * @param string $name 环境变量名(支持二级 .号分割)
  341 + * @param string $default 默认值
  342 + * @return mixed
  343 + */
  344 + function env($name = null, $default = null)
  345 + {
  346 + return Env::get($name, $default);
  347 + }
  348 +}
  349 +
  350 +if (!function_exists('exception')) {
  351 + /**
  352 + * 抛出异常处理
  353 + *
  354 + * @param string $msg 异常消息
  355 + * @param integer $code 异常代码 默认为0
  356 + * @param string $exception 异常类
  357 + *
  358 + * @throws Exception
  359 + */
  360 + function exception($msg, $code = 0, $exception = '')
  361 + {
  362 + $e = $exception ?: '\think\Exception';
  363 + throw new $e($msg, $code);
  364 + }
  365 +}
  366 +
  367 +if (!function_exists('halt')) {
  368 + /**
  369 + * 调试变量并且中断输出
  370 + * @param mixed $var 调试变量或者信息
  371 + */
  372 + function halt($var)
  373 + {
  374 + dump($var);
  375 +
  376 + throw new HttpResponseException(new Response);
  377 + }
  378 +}
  379 +
  380 +if (!function_exists('input')) {
  381 + /**
  382 + * 获取输入数据 支持默认值和过滤
  383 + * @param string $key 获取的变量名
  384 + * @param mixed $default 默认值
  385 + * @param string $filter 过滤方法
  386 + * @return mixed
  387 + */
  388 + function input($key = '', $default = null, $filter = '')
  389 + {
  390 + if (0 === strpos($key, '?')) {
  391 + $key = substr($key, 1);
  392 + $has = true;
  393 + }
  394 +
  395 + if ($pos = strpos($key, '.')) {
  396 + // 指定参数来源
  397 + $method = substr($key, 0, $pos);
  398 + if (in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'route', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) {
  399 + $key = substr($key, $pos + 1);
  400 + } else {
  401 + $method = 'param';
  402 + }
  403 + } else {
  404 + // 默认为自动判断
  405 + $method = 'param';
  406 + }
  407 +
  408 + if (isset($has)) {
  409 + return request()->has($key, $method, $default);
  410 + } else {
  411 + return request()->$method($key, $default, $filter);
  412 + }
  413 + }
  414 +}
  415 +
  416 +if (!function_exists('json')) {
  417 + /**
  418 + * 获取\think\response\Json对象实例
  419 + * @param mixed $data 返回的数据
  420 + * @param integer $code 状态码
  421 + * @param array $header 头部
  422 + * @param array $options 参数
  423 + * @return \think\response\Json
  424 + */
  425 + function json($data = [], $code = 200, $header = [], $options = [])
  426 + {
  427 + return Response::create($data, 'json', $code, $header, $options);
  428 + }
  429 +}
  430 +
  431 +if (!function_exists('jsonp')) {
  432 + /**
  433 + * 获取\think\response\Jsonp对象实例
  434 + * @param mixed $data 返回的数据
  435 + * @param integer $code 状态码
  436 + * @param array $header 头部
  437 + * @param array $options 参数
  438 + * @return \think\response\Jsonp
  439 + */
  440 + function jsonp($data = [], $code = 200, $header = [], $options = [])
  441 + {
  442 + return Response::create($data, 'jsonp', $code, $header, $options);
  443 + }
  444 +}
  445 +
  446 +if (!function_exists('lang')) {
  447 + /**
  448 + * 获取语言变量值
  449 + * @param string $name 语言变量名
  450 + * @param array $vars 动态变量值
  451 + * @param string $lang 语言
  452 + * @return mixed
  453 + */
  454 + function lang($name, $vars = [], $lang = '')
  455 + {
  456 + return Lang::get($name, $vars, $lang);
  457 + }
  458 +}
  459 +
  460 +if (!function_exists('model')) {
  461 + /**
  462 + * 实例化Model
  463 + * @param string $name Model名称
  464 + * @param string $layer 业务层名称
  465 + * @param bool $appendSuffix 是否添加类名后缀
  466 + * @return \think\Model
  467 + */
  468 + function model($name = '', $layer = 'model', $appendSuffix = false)
  469 + {
  470 + return app()->model($name, $layer, $appendSuffix);
  471 + }
  472 +}
  473 +
  474 +if (!function_exists('parse_name')) {
  475 + /**
  476 + * 字符串命名风格转换
  477 + * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
  478 + * @param string $name 字符串
  479 + * @param integer $type 转换类型
  480 + * @param bool $ucfirst 首字母是否大写(驼峰规则)
  481 + * @return string
  482 + */
  483 + function parse_name($name, $type = 0, $ucfirst = true)
  484 + {
  485 + if ($type) {
  486 + $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
  487 + return strtoupper($match[1]);
  488 + }, $name);
  489 +
  490 + return $ucfirst ? ucfirst($name) : lcfirst($name);
  491 + } else {
  492 + return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
  493 + }
  494 + }
  495 +}
  496 +
  497 +if (!function_exists('redirect')) {
  498 + /**
  499 + * 获取\think\response\Redirect对象实例
  500 + * @param mixed $url 重定向地址 支持Url::build方法的地址
  501 + * @param array|integer $params 额外参数
  502 + * @param integer $code 状态码
  503 + * @return \think\response\Redirect
  504 + */
  505 + function redirect($url = [], $params = [], $code = 302)
  506 + {
  507 + if (is_integer($params)) {
  508 + $code = $params;
  509 + $params = [];
  510 + }
  511 +
  512 + return Response::create($url, 'redirect', $code)->params($params);
  513 + }
  514 +}
  515 +
  516 +if (!function_exists('request')) {
  517 + /**
  518 + * 获取当前Request对象实例
  519 + * @return Request
  520 + */
  521 + function request()
  522 + {
  523 + return app('request');
  524 + }
  525 +}
  526 +
  527 +if (!function_exists('response')) {
  528 + /**
  529 + * 创建普通 Response 对象实例
  530 + * @param mixed $data 输出数据
  531 + * @param int|string $code 状态码
  532 + * @param array $header 头信息
  533 + * @param string $type
  534 + * @return Response
  535 + */
  536 + function response($data = [], $code = 200, $header = [], $type = 'html')
  537 + {
  538 + return Response::create($data, $type, $code, $header);
  539 + }
  540 +}
  541 +
  542 +if (!function_exists('route')) {
  543 + /**
  544 + * 路由注册
  545 + * @param string $rule 路由规则
  546 + * @param mixed $route 路由地址
  547 + * @param array $option 路由参数
  548 + * @param array $pattern 变量规则
  549 + * @return RuleItem
  550 + */
  551 + function route($rule, $route, $option = [], $pattern = [])
  552 + {
  553 + return Route::rule($rule, $route, '*', $option, $pattern);
  554 + }
  555 +}
  556 +
  557 +if (!function_exists('session')) {
  558 + /**
  559 + * Session管理
  560 + * @param string|array $name session名称,如果为数组表示进行session设置
  561 + * @param mixed $value session值
  562 + * @param string $prefix 前缀
  563 + * @return mixed
  564 + */
  565 + function session($name, $value = '', $prefix = null)
  566 + {
  567 + if (is_array($name)) {
  568 + // 初始化
  569 + Session::init($name);
  570 + } elseif (is_null($name)) {
  571 + // 清除
  572 + Session::clear($value);
  573 + } elseif ('' === $value) {
  574 + // 判断或获取
  575 + return 0 === strpos($name, '?') ? Session::has(substr($name, 1), $prefix) : Session::get($name, $prefix);
  576 + } elseif (is_null($value)) {
  577 + // 删除
  578 + return Session::delete($name, $prefix);
  579 + } else {
  580 + // 设置
  581 + return Session::set($name, $value, $prefix);
  582 + }
  583 + }
  584 +}
  585 +
  586 +if (!function_exists('token')) {
  587 + /**
  588 + * 生成表单令牌
  589 + * @param string $name 令牌名称
  590 + * @param mixed $type 令牌生成方法
  591 + * @return string
  592 + */
  593 + function token($name = '__token__', $type = 'md5')
  594 + {
  595 + $token = Request::token($name, $type);
  596 +
  597 + return '<input type="hidden" name="' . $name . '" value="' . $token . '" />';
  598 + }
  599 +}
  600 +
  601 +if (!function_exists('trace')) {
  602 + /**
  603 + * 记录日志信息
  604 + * @param mixed $log log信息 支持字符串和数组
  605 + * @param string $level 日志级别
  606 + * @return array|void
  607 + */
  608 + function trace($log = '[think]', $level = 'log')
  609 + {
  610 + if ('[think]' === $log) {
  611 + return Log::getLog();
  612 + } else {
  613 + Log::record($log, $level);
  614 + }
  615 + }
  616 +}
  617 +
  618 +if (!function_exists('trait_uses_recursive')) {
  619 + /**
  620 + * 获取一个trait里所有引用到的trait
  621 + *
  622 + * @param string $trait
  623 + * @return array
  624 + */
  625 + function trait_uses_recursive($trait)
  626 + {
  627 + $traits = class_uses($trait);
  628 + foreach ($traits as $trait) {
  629 + $traits += trait_uses_recursive($trait);
  630 + }
  631 +
  632 + return $traits;
  633 + }
  634 +}
  635 +
  636 +if (!function_exists('url')) {
  637 + /**
  638 + * Url生成
  639 + * @param string $url 路由地址
  640 + * @param string|array $vars 变量
  641 + * @param bool|string $suffix 生成的URL后缀
  642 + * @param bool|string $domain 域名
  643 + * @return string
  644 + */
  645 + function url($url = '', $vars = '', $suffix = true, $domain = false)
  646 + {
  647 + return Url::build($url, $vars, $suffix, $domain);
  648 + }
  649 +}
  650 +
  651 +if (!function_exists('validate')) {
  652 + /**
  653 + * 实例化验证器
  654 + * @param string $name 验证器名称
  655 + * @param string $layer 业务层名称
  656 + * @param bool $appendSuffix 是否添加类名后缀
  657 + * @return \think\Validate
  658 + */
  659 + function validate($name = '', $layer = 'validate', $appendSuffix = false)
  660 + {
  661 + return app()->validate($name, $layer, $appendSuffix);
  662 + }
  663 +}
  664 +
  665 +if (!function_exists('view')) {
  666 + /**
  667 + * 渲染模板输出
  668 + * @param string $template 模板文件
  669 + * @param array $vars 模板变量
  670 + * @param integer $code 状态码
  671 + * @param callable $filter 内容过滤
  672 + * @return \think\response\View
  673 + */
  674 + function view($template = '', $vars = [], $code = 200, $filter = null)
  675 + {
  676 + return Response::create($template, 'view', $code)->assign($vars)->filter($filter);
  677 + }
  678 +}
  679 +
  680 +if (!function_exists('widget')) {
  681 + /**
  682 + * 渲染输出Widget
  683 + * @param string $name Widget名称
  684 + * @param array $data 传入的参数
  685 + * @return mixed
  686 + */
  687 + function widget($name, $data = [])
  688 + {
  689 + return app()->action($name, $data, 'widget');
  690 + }
  691 +}
  692 +
  693 +if (!function_exists('xml')) {
  694 + /**
  695 + * 获取\think\response\Xml对象实例
  696 + * @param mixed $data 返回的数据
  697 + * @param integer $code 状态码
  698 + * @param array $header 头部
  699 + * @param array $options 参数
  700 + * @return \think\response\Xml
  701 + */
  702 + function xml($data = [], $code = 200, $header = [], $options = [])
  703 + {
  704 + return Response::create($data, 'xml', $code, $header, $options);
  705 + }
  706 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +// 核心中文语言包
  13 +return [
  14 + // 系统错误提示
  15 + 'Undefined variable' => '未定义变量',
  16 + 'Undefined index' => '未定义数组索引',
  17 + 'Undefined offset' => '未定义数组下标',
  18 + 'Parse error' => '语法解析错误',
  19 + 'Type error' => '类型错误',
  20 + 'Fatal error' => '致命错误',
  21 + 'syntax error' => '语法错误',
  22 +
  23 + // 框架核心错误提示
  24 + 'dispatch type not support' => '不支持的调度类型',
  25 + 'method param miss' => '方法参数错误',
  26 + 'method not exists' => '方法不存在',
  27 + 'function not exists' => '函数不存在',
  28 + 'file not exists' => '文件不存在',
  29 + 'module not exists' => '模块不存在',
  30 + 'controller not exists' => '控制器不存在',
  31 + 'class not exists' => '类不存在',
  32 + 'property not exists' => '类的属性不存在',
  33 + 'template not exists' => '模板文件不存在',
  34 + 'illegal controller name' => '非法的控制器名称',
  35 + 'illegal action name' => '非法的操作名称',
  36 + 'url suffix deny' => '禁止的URL后缀访问',
  37 + 'Route Not Found' => '当前访问路由未定义或不匹配',
  38 + 'Undefined db type' => '未定义数据库类型',
  39 + 'variable type error' => '变量类型错误',
  40 + 'PSR-4 error' => 'PSR-4 规范错误',
  41 + 'not support total' => '简洁模式下不能获取数据总数',
  42 + 'not support last' => '简洁模式下不能获取最后一页',
  43 + 'error session handler' => '错误的SESSION处理器类',
  44 + 'not allow php tag' => '模板不允许使用PHP语法',
  45 + 'not support' => '不支持',
  46 + 'redisd master' => 'Redisd 主服务器错误',
  47 + 'redisd slave' => 'Redisd 从服务器错误',
  48 + 'must run at sae' => '必须在SAE运行',
  49 + 'memcache init error' => '未开通Memcache服务,请在SAE管理平台初始化Memcache服务',
  50 + 'KVDB init error' => '没有初始化KVDB,请在SAE管理平台初始化KVDB服务',
  51 + 'fields not exists' => '数据表字段不存在',
  52 + 'where express error' => '查询表达式错误',
  53 + 'no data to update' => '没有任何数据需要更新',
  54 + 'miss data to insert' => '缺少需要写入的数据',
  55 + 'not support data' => '不支持的数据表达式',
  56 + 'miss complex primary data' => '缺少复合主键数据',
  57 + 'miss update condition' => '缺少更新条件',
  58 + 'model data Not Found' => '模型数据不存在',
  59 + 'table data not Found' => '表数据不存在',
  60 + 'delete without condition' => '没有条件不会执行删除操作',
  61 + 'miss relation data' => '缺少关联表数据',
  62 + 'tag attr must' => '模板标签属性必须',
  63 + 'tag error' => '模板标签错误',
  64 + 'cache write error' => '缓存写入失败',
  65 + 'sae mc write error' => 'SAE mc 写入错误',
  66 + 'route name not exists' => '路由标识不存在(或参数不够)',
  67 + 'invalid request' => '非法请求',
  68 + 'bind attr has exists' => '模型的属性已经存在',
  69 + 'relation data not exists' => '关联数据不存在',
  70 + 'relation not support' => '关联不支持',
  71 + 'chunk not support order' => 'Chunk不支持调用order方法',
  72 + 'route pattern error' => '路由变量规则定义错误',
  73 + 'route behavior will not support' => '路由行为废弃(使用中间件替代)',
  74 + 'closure not support cache(true)' => '使用闭包查询不支持cache(true),请指定缓存Key',
  75 +
  76 + // 上传错误信息
  77 + 'unknown upload error' => '未知上传错误!',
  78 + 'file write error' => '文件写入失败!',
  79 + 'upload temp dir not found' => '找不到临时文件夹!',
  80 + 'no file to uploaded' => '没有文件被上传!',
  81 + 'only the portion of file is uploaded' => '文件只有部分被上传!',
  82 + 'upload File size exceeds the maximum value' => '上传文件大小超过了最大值!',
  83 + 'upload write error' => '文件上传保存错误!',
  84 + 'has the same filename: {:filename}' => '存在同名文件:{:filename}',
  85 + 'upload illegal files' => '非法上传文件',
  86 + 'illegal image files' => '非法图片文件',
  87 + 'extensions to upload is not allowed' => '上传文件后缀不允许',
  88 + 'mimetype to upload is not allowed' => '上传文件MIME类型不允许!',
  89 + 'filesize not match' => '上传文件大小不符!',
  90 + 'directory {:path} creation failed' => '目录 {:path} 创建失败!',
  91 +
  92 + 'The middleware must return Response instance' => '中间件方法必须返回Response对象实例',
  93 + 'The queue was exhausted, with no response returned' => '中间件队列为空',
  94 + // Validate Error Message
  95 + ':attribute require' => ':attribute不能为空',
  96 + ':attribute must' => ':attribute必须',
  97 + ':attribute must be numeric' => ':attribute必须是数字',
  98 + ':attribute must be integer' => ':attribute必须是整数',
  99 + ':attribute must be float' => ':attribute必须是浮点数',
  100 + ':attribute must be bool' => ':attribute必须是布尔值',
  101 + ':attribute not a valid email address' => ':attribute格式不符',
  102 + ':attribute not a valid mobile' => ':attribute格式不符',
  103 + ':attribute must be a array' => ':attribute必须是数组',
  104 + ':attribute must be yes,on or 1' => ':attribute必须是yes、on或者1',
  105 + ':attribute not a valid datetime' => ':attribute不是一个有效的日期或时间格式',
  106 + ':attribute not a valid file' => ':attribute不是有效的上传文件',
  107 + ':attribute not a valid image' => ':attribute不是有效的图像文件',
  108 + ':attribute must be alpha' => ':attribute只能是字母',
  109 + ':attribute must be alpha-numeric' => ':attribute只能是字母和数字',
  110 + ':attribute must be alpha-numeric, dash, underscore' => ':attribute只能是字母、数字和下划线_及破折号-',
  111 + ':attribute not a valid domain or ip' => ':attribute不是有效的域名或者IP',
  112 + ':attribute must be chinese' => ':attribute只能是汉字',
  113 + ':attribute must be chinese or alpha' => ':attribute只能是汉字、字母',
  114 + ':attribute must be chinese,alpha-numeric' => ':attribute只能是汉字、字母和数字',
  115 + ':attribute must be chinese,alpha-numeric,underscore, dash' => ':attribute只能是汉字、字母、数字和下划线_及破折号-',
  116 + ':attribute not a valid url' => ':attribute不是有效的URL地址',
  117 + ':attribute not a valid ip' => ':attribute不是有效的IP地址',
  118 + ':attribute must be dateFormat of :rule' => ':attribute必须使用日期格式 :rule',
  119 + ':attribute must be in :rule' => ':attribute必须在 :rule 范围内',
  120 + ':attribute be notin :rule' => ':attribute不能在 :rule 范围内',
  121 + ':attribute must between :1 - :2' => ':attribute只能在 :1 - :2 之间',
  122 + ':attribute not between :1 - :2' => ':attribute不能在 :1 - :2 之间',
  123 + 'size of :attribute must be :rule' => ':attribute长度不符合要求 :rule',
  124 + 'max size of :attribute must be :rule' => ':attribute长度不能超过 :rule',
  125 + 'min size of :attribute must be :rule' => ':attribute长度不能小于 :rule',
  126 + ':attribute cannot be less than :rule' => ':attribute日期不能小于 :rule',
  127 + ':attribute cannot exceed :rule' => ':attribute日期不能超过 :rule',
  128 + ':attribute not within :rule' => '不在有效期内 :rule',
  129 + 'access IP is not allowed' => '不允许的IP访问',
  130 + 'access IP denied' => '禁止的IP访问',
  131 + ':attribute out of accord with :2' => ':attribute和确认字段:2不一致',
  132 + ':attribute cannot be same with :2' => ':attribute和比较字段:2不能相同',
  133 + ':attribute must greater than or equal :rule' => ':attribute必须大于等于 :rule',
  134 + ':attribute must greater than :rule' => ':attribute必须大于 :rule',
  135 + ':attribute must less than or equal :rule' => ':attribute必须小于等于 :rule',
  136 + ':attribute must less than :rule' => ':attribute必须小于 :rule',
  137 + ':attribute must equal :rule' => ':attribute必须等于 :rule',
  138 + ':attribute has exists' => ':attribute已存在',
  139 + ':attribute not conform to the rules' => ':attribute不符合指定规则',
  140 + 'invalid Request method' => '无效的请求类型',
  141 + 'invalid token' => '令牌数据无效',
  142 + 'not conform to the rules' => '规则错误',
  143 +];
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think;
  13 +
  14 +use think\exception\ClassNotFoundException;
  15 +use think\exception\HttpResponseException;
  16 +use think\route\Dispatch;
  17 +
  18 +/**
  19 + * App 应用管理
  20 + */
  21 +class App extends Container
  22 +{
  23 + const VERSION = '5.1.22';
  24 +
  25 + /**
  26 + * 当前模块路径
  27 + * @var string
  28 + */
  29 + protected $modulePath;
  30 +
  31 + /**
  32 + * 应用调试模式
  33 + * @var bool
  34 + */
  35 + protected $appDebug = true;
  36 +
  37 + /**
  38 + * 应用开始时间
  39 + * @var float
  40 + */
  41 + protected $beginTime;
  42 +
  43 + /**
  44 + * 应用内存初始占用
  45 + * @var integer
  46 + */
  47 + protected $beginMem;
  48 +
  49 + /**
  50 + * 应用类库命名空间
  51 + * @var string
  52 + */
  53 + protected $namespace = 'app';
  54 +
  55 + /**
  56 + * 应用类库后缀
  57 + * @var bool
  58 + */
  59 + protected $suffix = false;
  60 +
  61 + /**
  62 + * 严格路由检测
  63 + * @var bool
  64 + */
  65 + protected $routeMust;
  66 +
  67 + /**
  68 + * 应用类库目录
  69 + * @var string
  70 + */
  71 + protected $appPath;
  72 +
  73 + /**
  74 + * 框架目录
  75 + * @var string
  76 + */
  77 + protected $thinkPath;
  78 +
  79 + /**
  80 + * 应用根目录
  81 + * @var string
  82 + */
  83 + protected $rootPath;
  84 +
  85 + /**
  86 + * 运行时目录
  87 + * @var string
  88 + */
  89 + protected $runtimePath;
  90 +
  91 + /**
  92 + * 配置目录
  93 + * @var string
  94 + */
  95 + protected $configPath;
  96 +
  97 + /**
  98 + * 路由目录
  99 + * @var string
  100 + */
  101 + protected $routePath;
  102 +
  103 + /**
  104 + * 配置后缀
  105 + * @var string
  106 + */
  107 + protected $configExt;
  108 +
  109 + /**
  110 + * 应用调度实例
  111 + * @var Dispatch
  112 + */
  113 + protected $dispatch;
  114 +
  115 + /**
  116 + * 绑定模块(控制器)
  117 + * @var string
  118 + */
  119 + protected $bindModule;
  120 +
  121 + /**
  122 + * 初始化
  123 + * @var bool
  124 + */
  125 + protected $initialized = false;
  126 +
  127 + public function __construct($appPath = '')
  128 + {
  129 + $this->appPath = $appPath ? realpath($appPath) . DIRECTORY_SEPARATOR : $this->getAppPath();
  130 +
  131 + $this->thinkPath = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
  132 + $this->rootPath = dirname($this->appPath) . DIRECTORY_SEPARATOR;
  133 + $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
  134 + $this->routePath = $this->rootPath . 'route' . DIRECTORY_SEPARATOR;
  135 + $this->configPath = $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
  136 + }
  137 +
  138 + /**
  139 + * 绑定模块或者控制器
  140 + * @access public
  141 + * @param string $bind
  142 + * @return $this
  143 + */
  144 + public function bind($bind)
  145 + {
  146 + $this->bindModule = $bind;
  147 + return $this;
  148 + }
  149 +
  150 + /**
  151 + * 设置应用类库目录
  152 + * @access public
  153 + * @param string $path 路径
  154 + * @return $this
  155 + */
  156 + public function path($path)
  157 + {
  158 + $this->appPath = $path;
  159 + return $this;
  160 + }
  161 +
  162 + /**
  163 + * 初始化应用
  164 + * @access public
  165 + * @return void
  166 + */
  167 + public function initialize()
  168 + {
  169 + if ($this->initialized) {
  170 + return;
  171 + }
  172 +
  173 + $this->initialized = true;
  174 + $this->beginTime = microtime(true);
  175 + $this->beginMem = memory_get_usage();
  176 +
  177 + static::setInstance($this);
  178 +
  179 + $this->instance('app', $this);
  180 +
  181 + $this->configExt = $this->env->get('config_ext', '.php');
  182 +
  183 + // 加载惯例配置文件
  184 + $this->config->set(include $this->thinkPath . 'convention.php');
  185 +
  186 + // 设置路径环境变量
  187 + $this->env->set([
  188 + 'think_path' => $this->thinkPath,
  189 + 'root_path' => $this->rootPath,
  190 + 'app_path' => $this->appPath,
  191 + 'config_path' => $this->configPath,
  192 + 'route_path' => $this->routePath,
  193 + 'runtime_path' => $this->runtimePath,
  194 + 'extend_path' => $this->rootPath . 'extend' . DIRECTORY_SEPARATOR,
  195 + 'vendor_path' => $this->rootPath . 'vendor' . DIRECTORY_SEPARATOR,
  196 + ]);
  197 +
  198 + // 加载环境变量配置文件
  199 + if (is_file($this->rootPath . '.env')) {
  200 + $this->env->load($this->rootPath . '.env');
  201 + }
  202 +
  203 + $this->namespace = $this->env->get('app_namespace', $this->namespace);
  204 + $this->env->set('app_namespace', $this->namespace);
  205 +
  206 + // 注册应用命名空间
  207 + Loader::addNamespace($this->namespace, $this->appPath);
  208 +
  209 + // 初始化应用
  210 + $this->init();
  211 +
  212 + // 开启类名后缀
  213 + $this->suffix = $this->config('app.class_suffix');
  214 +
  215 + // 应用调试模式
  216 + $this->appDebug = $this->env->get('app_debug', $this->config('app.app_debug'));
  217 + $this->env->set('app_debug', $this->appDebug);
  218 +
  219 + if (!$this->appDebug) {
  220 + ini_set('display_errors', 'Off');
  221 + } elseif (PHP_SAPI != 'cli') {
  222 + //重新申请一块比较大的buffer
  223 + if (ob_get_level() > 0) {
  224 + $output = ob_get_clean();
  225 + }
  226 + ob_start();
  227 + if (!empty($output)) {
  228 + echo $output;
  229 + }
  230 + }
  231 +
  232 + // 注册异常处理类
  233 + if ($this->config('app.exception_handle')) {
  234 + Error::setExceptionHandler($this->config('app.exception_handle'));
  235 + }
  236 +
  237 + // 注册根命名空间
  238 + if (!empty($this->config('app.root_namespace'))) {
  239 + Loader::addNamespace($this->config('app.root_namespace'));
  240 + }
  241 +
  242 + // 加载composer autofile文件
  243 + Loader::loadComposerAutoloadFiles();
  244 +
  245 + // 注册类库别名
  246 + Loader::addClassAlias($this->config->pull('alias'));
  247 +
  248 + // 数据库配置初始化
  249 + Db::init($this->config->pull('database'));
  250 +
  251 + // 设置系统时区
  252 + date_default_timezone_set($this->config('app.default_timezone'));
  253 +
  254 + // 读取语言包
  255 + $this->loadLangPack();
  256 +
  257 + // 路由初始化
  258 + $this->routeInit();
  259 + }
  260 +
  261 + /**
  262 + * 初始化应用或模块
  263 + * @access public
  264 + * @param string $module 模块名
  265 + * @return void
  266 + */
  267 + public function init($module = '')
  268 + {
  269 + // 定位模块目录
  270 + $module = $module ? $module . DIRECTORY_SEPARATOR : '';
  271 + $path = $this->appPath . $module;
  272 +
  273 + // 加载初始化文件
  274 + if (is_file($path . 'init.php')) {
  275 + include $path . 'init.php';
  276 + } elseif (is_file($this->runtimePath . $module . 'init.php')) {
  277 + include $this->runtimePath . $module . 'init.php';
  278 + } else {
  279 + // 加载行为扩展文件
  280 + if (is_file($path . 'tags.php')) {
  281 + $tags = include $path . 'tags.php';
  282 + if (is_array($tags)) {
  283 + $this->hook->import($tags);
  284 + }
  285 + }
  286 +
  287 + // 加载公共文件
  288 + if (is_file($path . 'common.php')) {
  289 + include_once $path . 'common.php';
  290 + }
  291 +
  292 + if ('' == $module) {
  293 + // 加载系统助手函数
  294 + include $this->thinkPath . 'helper.php';
  295 + }
  296 +
  297 + // 加载中间件
  298 + if (is_file($path . 'middleware.php')) {
  299 + $middleware = include $path . 'middleware.php';
  300 + if (is_array($middleware)) {
  301 + $this->middleware->import($middleware);
  302 + }
  303 + }
  304 +
  305 + // 注册服务的容器对象实例
  306 + if (is_file($path . 'provider.php')) {
  307 + $provider = include $path . 'provider.php';
  308 + if (is_array($provider)) {
  309 + $this->bindTo($provider);
  310 + }
  311 + }
  312 +
  313 + // 自动读取配置文件
  314 + if (is_dir($path . 'config')) {
  315 + $dir = $path . 'config'.DIRECTORY_SEPARATOR;
  316 + } elseif (is_dir($this->configPath . $module)) {
  317 + $dir = $this->configPath . $module;
  318 + }
  319 +
  320 + $files = isset($dir) ? scandir($dir) : [];
  321 +
  322 + foreach ($files as $file) {
  323 + if ('.' . pathinfo($file, PATHINFO_EXTENSION) === $this->configExt) {
  324 + $this->config->load($dir . $file, pathinfo($file, PATHINFO_FILENAME));
  325 + }
  326 + }
  327 + }
  328 +
  329 + $this->setModulePath($path);
  330 +
  331 + if ($module) {
  332 + // 对容器中的对象实例进行配置更新
  333 + $this->containerConfigUpdate($module);
  334 + }
  335 + }
  336 +
  337 + protected function containerConfigUpdate($module)
  338 + {
  339 + $config = $this->config->get();
  340 +
  341 + // 注册异常处理类
  342 + if ($config['app']['exception_handle']) {
  343 + Error::setExceptionHandler($config['app']['exception_handle']);
  344 + }
  345 +
  346 + Db::init($config['database']);
  347 + $this->middleware->setConfig($config['middleware']);
  348 + $this->route->setConfig($config['app']);
  349 + $this->request->init($config['app']);
  350 + $this->cookie->init($config['cookie']);
  351 + $this->view->init($config['template']);
  352 + $this->log->init($config['log']);
  353 + $this->session->setConfig($config['session']);
  354 + $this->debug->setConfig($config['trace']);
  355 + $this->cache->init($config['cache'], true);
  356 +
  357 + // 加载当前模块语言包
  358 + $this->lang->load($this->appPath . $module . DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php');
  359 +
  360 + // 模块请求缓存检查
  361 + $this->checkRequestCache(
  362 + $config['app']['request_cache'],
  363 + $config['app']['request_cache_expire'],
  364 + $config['app']['request_cache_except']
  365 + );
  366 + }
  367 +
  368 + /**
  369 + * 执行应用程序
  370 + * @access public
  371 + * @return Response
  372 + * @throws Exception
  373 + */
  374 + public function run()
  375 + {
  376 + try {
  377 + // 初始化应用
  378 + $this->initialize();
  379 +
  380 + // 监听app_init
  381 + $this->hook->listen('app_init');
  382 +
  383 + if ($this->bindModule) {
  384 + // 模块/控制器绑定
  385 + $this->route->bind($this->bindModule);
  386 + } elseif ($this->config('app.auto_bind_module')) {
  387 + // 入口自动绑定
  388 + $name = pathinfo($this->request->baseFile(), PATHINFO_FILENAME);
  389 + if ($name && 'index' != $name && is_dir($this->appPath . $name)) {
  390 + $this->route->bind($name);
  391 + }
  392 + }
  393 +
  394 + // 监听app_dispatch
  395 + $this->hook->listen('app_dispatch');
  396 +
  397 + $dispatch = $this->dispatch;
  398 +
  399 + if (empty($dispatch)) {
  400 + // 路由检测
  401 + $dispatch = $this->routeCheck()->init();
  402 + }
  403 +
  404 + // 记录当前调度信息
  405 + $this->request->dispatch($dispatch);
  406 +
  407 + // 记录路由和请求信息
  408 + if ($this->appDebug) {
  409 + $this->log('[ ROUTE ] ' . var_export($this->request->routeInfo(), true));
  410 + $this->log('[ HEADER ] ' . var_export($this->request->header(), true));
  411 + $this->log('[ PARAM ] ' . var_export($this->request->param(), true));
  412 + }
  413 +
  414 + // 监听app_begin
  415 + $this->hook->listen('app_begin');
  416 +
  417 + // 请求缓存检查
  418 + $this->checkRequestCache(
  419 + $this->config('request_cache'),
  420 + $this->config('request_cache_expire'),
  421 + $this->config('request_cache_except')
  422 + );
  423 +
  424 + $data = null;
  425 + } catch (HttpResponseException $exception) {
  426 + $dispatch = null;
  427 + $data = $exception->getResponse();
  428 + }
  429 +
  430 + $this->middleware->add(function (Request $request, $next) use ($dispatch, $data) {
  431 + return is_null($data) ? $dispatch->run() : $data;
  432 + });
  433 +
  434 + $response = $this->middleware->dispatch($this->request);
  435 +
  436 + // 监听app_end
  437 + $this->hook->listen('app_end', $response);
  438 +
  439 + return $response;
  440 + }
  441 +
  442 + protected function getRouteCacheKey()
  443 + {
  444 + if ($this->config->get('route_check_cache_key')) {
  445 + $closure = $this->config->get('route_check_cache_key');
  446 + $routeKey = $closure($this->request);
  447 + } else {
  448 + $routeKey = md5($this->request->baseUrl(true) . ':' . $this->request->method());
  449 + }
  450 +
  451 + return $routeKey;
  452 + }
  453 +
  454 + protected function loadLangPack()
  455 + {
  456 + // 读取默认语言
  457 + $this->lang->range($this->config('app.default_lang'));
  458 +
  459 + if ($this->config('app.lang_switch_on')) {
  460 + // 开启多语言机制 检测当前语言
  461 + $this->lang->detect();
  462 + }
  463 +
  464 + $this->request->setLangset($this->lang->range());
  465 +
  466 + // 加载系统语言包
  467 + $this->lang->load([
  468 + $this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php',
  469 + $this->appPath . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php',
  470 + ]);
  471 + }
  472 +
  473 + /**
  474 + * 设置当前地址的请求缓存
  475 + * @access public
  476 + * @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id
  477 + * @param mixed $expire 缓存有效期
  478 + * @param array $except 缓存排除
  479 + * @param string $tag 缓存标签
  480 + * @return void
  481 + */
  482 + public function checkRequestCache($key, $expire = null, $except = [], $tag = null)
  483 + {
  484 + $cache = $this->request->cache($key, $expire, $except, $tag);
  485 +
  486 + if ($cache) {
  487 + list($key, $expire, $tag) = $cache;
  488 + if (strtotime($this->request->server('HTTP_IF_MODIFIED_SINCE')) + $expire > $this->request->server('REQUEST_TIME')) {
  489 + // 读取缓存
  490 + $response = Response::create()->code(304);
  491 + throw new HttpResponseException($response);
  492 + } elseif ($this->cache->has($key)) {
  493 + list($content, $header) = $this->cache->get($key);
  494 +
  495 + $response = Response::create($content)->header($header);
  496 + throw new HttpResponseException($response);
  497 + }
  498 + }
  499 + }
  500 +
  501 + /**
  502 + * 设置当前请求的调度信息
  503 + * @access public
  504 + * @param Dispatch $dispatch 调度信息
  505 + * @return $this
  506 + */
  507 + public function dispatch(Dispatch $dispatch)
  508 + {
  509 + $this->dispatch = $dispatch;
  510 + return $this;
  511 + }
  512 +
  513 + /**
  514 + * 记录调试信息
  515 + * @access public
  516 + * @param mixed $msg 调试信息
  517 + * @param string $type 信息类型
  518 + * @return void
  519 + */
  520 + public function log($msg, $type = 'info')
  521 + {
  522 + $this->appDebug && $this->log->record($msg, $type);
  523 + }
  524 +
  525 + /**
  526 + * 获取配置参数 为空则获取所有配置
  527 + * @access public
  528 + * @param string $name 配置参数名(支持二级配置 .号分割)
  529 + * @return mixed
  530 + */
  531 + public function config($name = '')
  532 + {
  533 + return $this->config->get($name);
  534 + }
  535 +
  536 + /**
  537 + * 路由初始化 导入路由定义规则
  538 + * @access public
  539 + * @return void
  540 + */
  541 + public function routeInit()
  542 + {
  543 + // 路由检测
  544 + $files = scandir($this->routePath);
  545 + foreach ($files as $file) {
  546 + if (strpos($file, '.php')) {
  547 + $filename = $this->routePath . $file;
  548 + // 导入路由配置
  549 + $rules = include $filename;
  550 + if (is_array($rules)) {
  551 + $this->route->import($rules);
  552 + }
  553 + }
  554 + }
  555 +
  556 + if ($this->route->config('route_annotation')) {
  557 + // 自动生成路由定义
  558 + if ($this->appDebug) {
  559 + $suffix = $this->route->config('controller_suffix') || $this->route->config('class_suffix');
  560 + $this->build->buildRoute($suffix);
  561 + }
  562 +
  563 + $filename = $this->runtimePath . 'build_route.php';
  564 +
  565 + if (is_file($filename)) {
  566 + include $filename;
  567 + }
  568 + }
  569 + }
  570 +
  571 + /**
  572 + * URL路由检测(根据PATH_INFO)
  573 + * @access public
  574 + * @return Dispatch
  575 + */
  576 + public function routeCheck()
  577 + {
  578 + // 检测路由缓存
  579 + if (!$this->appDebug && $this->config->get('route_check_cache')) {
  580 + $routeKey = $this->getRouteCacheKey();
  581 + $option = $this->config->get('route_cache_option') ?: $this->cache->getConfig();
  582 +
  583 + if ($this->cache->connect($option)->has($routeKey)) {
  584 + return $this->cache->connect($option)->get($routeKey);
  585 + }
  586 + }
  587 +
  588 + // 获取应用调度信息
  589 + $path = $this->request->path();
  590 +
  591 + // 是否强制路由模式
  592 + $must = !is_null($this->routeMust) ? $this->routeMust : $this->route->config('url_route_must');
  593 +
  594 + // 路由检测 返回一个Dispatch对象
  595 + $dispatch = $this->route->check($path, $must);
  596 +
  597 + if (!empty($routeKey)) {
  598 + try {
  599 + $this->cache
  600 + ->connect($option)
  601 + ->tag('route_cache')
  602 + ->set($routeKey, $dispatch);
  603 + } catch (\Exception $e) {
  604 + // 存在闭包的时候缓存无效
  605 + }
  606 + }
  607 +
  608 + return $dispatch;
  609 + }
  610 +
  611 + /**
  612 + * 设置应用的路由检测机制
  613 + * @access public
  614 + * @param bool $must 是否强制检测路由
  615 + * @return $this
  616 + */
  617 + public function routeMust($must = false)
  618 + {
  619 + $this->routeMust = $must;
  620 + return $this;
  621 + }
  622 +
  623 + /**
  624 + * 解析模块和类名
  625 + * @access protected
  626 + * @param string $name 资源地址
  627 + * @param string $layer 验证层名称
  628 + * @param bool $appendSuffix 是否添加类名后缀
  629 + * @return array
  630 + */
  631 + protected function parseModuleAndClass($name, $layer, $appendSuffix)
  632 + {
  633 + if (false !== strpos($name, '\\')) {
  634 + $class = $name;
  635 + $module = $this->request->module();
  636 + } else {
  637 + if (strpos($name, '/')) {
  638 + list($module, $name) = explode('/', $name, 2);
  639 + } else {
  640 + $module = $this->request->module();
  641 + }
  642 +
  643 + $class = $this->parseClass($module, $layer, $name, $appendSuffix);
  644 + }
  645 +
  646 + return [$module, $class];
  647 + }
  648 +
  649 + /**
  650 + * 实例化应用类库
  651 + * @access public
  652 + * @param string $name 类名称
  653 + * @param string $layer 业务层名称
  654 + * @param bool $appendSuffix 是否添加类名后缀
  655 + * @param string $common 公共模块名
  656 + * @return object
  657 + * @throws ClassNotFoundException
  658 + */
  659 + public function create($name, $layer, $appendSuffix = false, $common = 'common')
  660 + {
  661 + $guid = $name . $layer;
  662 +
  663 + if ($this->__isset($guid)) {
  664 + return $this->__get($guid);
  665 + }
  666 +
  667 + list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);
  668 +
  669 + if (class_exists($class)) {
  670 + $object = $this->__get($class);
  671 + } else {
  672 + $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
  673 + if (class_exists($class)) {
  674 + $object = $this->__get($class);
  675 + } else {
  676 + throw new ClassNotFoundException('class not exists:' . $class, $class);
  677 + }
  678 + }
  679 +
  680 + $this->__set($guid, $class);
  681 +
  682 + return $object;
  683 + }
  684 +
  685 + /**
  686 + * 实例化(分层)模型
  687 + * @access public
  688 + * @param string $name Model名称
  689 + * @param string $layer 业务层名称
  690 + * @param bool $appendSuffix 是否添加类名后缀
  691 + * @param string $common 公共模块名
  692 + * @return Model
  693 + * @throws ClassNotFoundException
  694 + */
  695 + public function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common')
  696 + {
  697 + return $this->create($name, $layer, $appendSuffix, $common);
  698 + }
  699 +
  700 + /**
  701 + * 实例化(分层)控制器 格式:[模块名/]控制器名
  702 + * @access public
  703 + * @param string $name 资源地址
  704 + * @param string $layer 控制层名称
  705 + * @param bool $appendSuffix 是否添加类名后缀
  706 + * @param string $empty 空控制器名称
  707 + * @return object
  708 + * @throws ClassNotFoundException
  709 + */
  710 + public function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
  711 + {
  712 + list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);
  713 +
  714 + if (class_exists($class)) {
  715 + return $this->__get($class);
  716 + } elseif ($empty && class_exists($emptyClass = $this->parseClass($module, $layer, $empty, $appendSuffix))) {
  717 + return $this->__get($emptyClass);
  718 + }
  719 +
  720 + throw new ClassNotFoundException('class not exists:' . $class, $class);
  721 + }
  722 +
  723 + /**
  724 + * 实例化验证类 格式:[模块名/]验证器名
  725 + * @access public
  726 + * @param string $name 资源地址
  727 + * @param string $layer 验证层名称
  728 + * @param bool $appendSuffix 是否添加类名后缀
  729 + * @param string $common 公共模块名
  730 + * @return Validate
  731 + * @throws ClassNotFoundException
  732 + */
  733 + public function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common')
  734 + {
  735 + $name = $name ?: $this->config('default_validate');
  736 +
  737 + if (empty($name)) {
  738 + return new Validate;
  739 + }
  740 +
  741 + return $this->create($name, $layer, $appendSuffix, $common);
  742 + }
  743 +
  744 + /**
  745 + * 数据库初始化
  746 + * @access public
  747 + * @param mixed $config 数据库配置
  748 + * @param bool|string $name 连接标识 true 强制重新连接
  749 + * @return \think\db\Query
  750 + */
  751 + public function db($config = [], $name = false)
  752 + {
  753 + return Db::connect($config, $name);
  754 + }
  755 +
  756 + /**
  757 + * 远程调用模块的操作方法 参数格式 [模块/控制器/]操作
  758 + * @access public
  759 + * @param string $url 调用地址
  760 + * @param string|array $vars 调用参数 支持字符串和数组
  761 + * @param string $layer 要调用的控制层名称
  762 + * @param bool $appendSuffix 是否添加类名后缀
  763 + * @return mixed
  764 + * @throws ClassNotFoundException
  765 + */
  766 + public function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
  767 + {
  768 + $info = pathinfo($url);
  769 + $action = $info['basename'];
  770 + $module = '.' != $info['dirname'] ? $info['dirname'] : $this->request->controller();
  771 + $class = $this->controller($module, $layer, $appendSuffix);
  772 +
  773 + if (is_scalar($vars)) {
  774 + if (strpos($vars, '=')) {
  775 + parse_str($vars, $vars);
  776 + } else {
  777 + $vars = [$vars];
  778 + }
  779 + }
  780 +
  781 + return $this->invokeMethod([$class, $action . $this->config('action_suffix')], $vars);
  782 + }
  783 +
  784 + /**
  785 + * 解析应用类的类名
  786 + * @access public
  787 + * @param string $module 模块名
  788 + * @param string $layer 层名 controller model ...
  789 + * @param string $name 类名
  790 + * @param bool $appendSuffix
  791 + * @return string
  792 + */
  793 + public function parseClass($module, $layer, $name, $appendSuffix = false)
  794 + {
  795 + $name = str_replace(['/', '.'], '\\', $name);
  796 + $array = explode('\\', $name);
  797 + $class = Loader::parseName(array_pop($array), 1) . ($this->suffix || $appendSuffix ? ucfirst($layer) : '');
  798 + $path = $array ? implode('\\', $array) . '\\' : '';
  799 +
  800 + return $this->namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $path . $class;
  801 + }
  802 +
  803 + /**
  804 + * 获取框架版本
  805 + * @access public
  806 + * @return string
  807 + */
  808 + public function version()
  809 + {
  810 + return static::VERSION;
  811 + }
  812 +
  813 + /**
  814 + * 是否为调试模式
  815 + * @access public
  816 + * @return bool
  817 + */
  818 + public function isDebug()
  819 + {
  820 + return $this->appDebug;
  821 + }
  822 +
  823 + /**
  824 + * 获取模块路径
  825 + * @access public
  826 + * @return string
  827 + */
  828 + public function getModulePath()
  829 + {
  830 + return $this->modulePath;
  831 + }
  832 +
  833 + /**
  834 + * 设置模块路径
  835 + * @access public
  836 + * @param string $path 路径
  837 + * @return void
  838 + */
  839 + public function setModulePath($path)
  840 + {
  841 + $this->modulePath = $path;
  842 + $this->env->set('module_path', $path);
  843 + }
  844 +
  845 + /**
  846 + * 获取应用根目录
  847 + * @access public
  848 + * @return string
  849 + */
  850 + public function getRootPath()
  851 + {
  852 + return $this->rootPath;
  853 + }
  854 +
  855 + /**
  856 + * 获取应用类库目录
  857 + * @access public
  858 + * @return string
  859 + */
  860 + public function getAppPath()
  861 + {
  862 + if (is_null($this->appPath)) {
  863 + $this->appPath = Loader::getRootPath() . 'application' . DIRECTORY_SEPARATOR;
  864 + }
  865 +
  866 + return $this->appPath;
  867 + }
  868 +
  869 + /**
  870 + * 获取应用运行时目录
  871 + * @access public
  872 + * @return string
  873 + */
  874 + public function getRuntimePath()
  875 + {
  876 + return $this->runtimePath;
  877 + }
  878 +
  879 + /**
  880 + * 获取核心框架目录
  881 + * @access public
  882 + * @return string
  883 + */
  884 + public function getThinkPath()
  885 + {
  886 + return $this->thinkPath;
  887 + }
  888 +
  889 + /**
  890 + * 获取路由目录
  891 + * @access public
  892 + * @return string
  893 + */
  894 + public function getRoutePath()
  895 + {
  896 + return $this->routePath;
  897 + }
  898 +
  899 + /**
  900 + * 获取应用配置目录
  901 + * @access public
  902 + * @return string
  903 + */
  904 + public function getConfigPath()
  905 + {
  906 + return $this->configPath;
  907 + }
  908 +
  909 + /**
  910 + * 获取配置后缀
  911 + * @access public
  912 + * @return string
  913 + */
  914 + public function getConfigExt()
  915 + {
  916 + return $this->configExt;
  917 + }
  918 +
  919 + /**
  920 + * 获取应用类库命名空间
  921 + * @access public
  922 + * @return string
  923 + */
  924 + public function getNamespace()
  925 + {
  926 + return $this->namespace;
  927 + }
  928 +
  929 + /**
  930 + * 设置应用类库命名空间
  931 + * @access public
  932 + * @param string $namespace 命名空间名称
  933 + * @return $this
  934 + */
  935 + public function setNamespace($namespace)
  936 + {
  937 + $this->namespace = $namespace;
  938 + return $this;
  939 + }
  940 +
  941 + /**
  942 + * 是否启用类库后缀
  943 + * @access public
  944 + * @return bool
  945 + */
  946 + public function getSuffix()
  947 + {
  948 + return $this->suffix;
  949 + }
  950 +
  951 + /**
  952 + * 获取应用开启时间
  953 + * @access public
  954 + * @return float
  955 + */
  956 + public function getBeginTime()
  957 + {
  958 + return $this->beginTime;
  959 + }
  960 +
  961 + /**
  962 + * 获取应用初始内存占用
  963 + * @access public
  964 + * @return integer
  965 + */
  966 + public function getBeginMem()
  967 + {
  968 + return $this->beginMem;
  969 + }
  970 +
  971 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think;
  13 +
  14 +class Build
  15 +{
  16 + /**
  17 + * 应用对象
  18 + * @var App
  19 + */
  20 + protected $app;
  21 +
  22 + /**
  23 + * 应用目录
  24 + * @var string
  25 + */
  26 + protected $basePath;
  27 +
  28 + public function __construct(App $app)
  29 + {
  30 + $this->app = $app;
  31 + $this->basePath = $this->app->getAppPath();
  32 + }
  33 +
  34 + /**
  35 + * 根据传入的build资料创建目录和文件
  36 + * @access public
  37 + * @param array $build build列表
  38 + * @param string $namespace 应用类库命名空间
  39 + * @param bool $suffix 类库后缀
  40 + * @return void
  41 + */
  42 + public function run(array $build = [], $namespace = 'app', $suffix = false)
  43 + {
  44 + // 锁定
  45 + $lockfile = $this->basePath . 'build.lock';
  46 +
  47 + if (is_writable($lockfile)) {
  48 + return;
  49 + } elseif (!touch($lockfile)) {
  50 + throw new Exception('应用目录[' . $this->basePath . ']不可写,目录无法自动生成!<BR>请手动生成项目目录~', 10006);
  51 + }
  52 +
  53 + foreach ($build as $module => $list) {
  54 + if ('__dir__' == $module) {
  55 + // 创建目录列表
  56 + $this->buildDir($list);
  57 + } elseif ('__file__' == $module) {
  58 + // 创建文件列表
  59 + $this->buildFile($list);
  60 + } else {
  61 + // 创建模块
  62 + $this->module($module, $list, $namespace, $suffix);
  63 + }
  64 + }
  65 +
  66 + // 解除锁定
  67 + unlink($lockfile);
  68 + }
  69 +
  70 + /**
  71 + * 创建目录
  72 + * @access protected
  73 + * @param array $list 目录列表
  74 + * @return void
  75 + */
  76 + protected function buildDir($list)
  77 + {
  78 + foreach ($list as $dir) {
  79 + $this->checkDirBuild($this->basePath . $dir);
  80 + }
  81 + }
  82 +
  83 + /**
  84 + * 创建文件
  85 + * @access protected
  86 + * @param array $list 文件列表
  87 + * @return void
  88 + */
  89 + protected function buildFile($list)
  90 + {
  91 + foreach ($list as $file) {
  92 + if (!is_dir($this->basePath . dirname($file))) {
  93 + // 创建目录
  94 + mkdir($this->basePath . dirname($file), 0755, true);
  95 + }
  96 +
  97 + if (!is_file($this->basePath . $file)) {
  98 + file_put_contents($this->basePath . $file, 'php' == pathinfo($file, PATHINFO_EXTENSION) ? "<?php\n" : '');
  99 + }
  100 + }
  101 + }
  102 +
  103 + /**
  104 + * 创建模块
  105 + * @access public
  106 + * @param string $module 模块名
  107 + * @param array $list build列表
  108 + * @param string $namespace 应用类库命名空间
  109 + * @param bool $suffix 类库后缀
  110 + * @return void
  111 + */
  112 + public function module($module = '', $list = [], $namespace = 'app', $suffix = false)
  113 + {
  114 + $module = $module ? $module : '';
  115 +
  116 + if (!is_dir($this->basePath . $module)) {
  117 + // 创建模块目录
  118 + mkdir($this->basePath . $module);
  119 + }
  120 +
  121 + if (basename($this->app->getRuntimePath()) != $module) {
  122 + // 创建配置文件和公共文件
  123 + $this->buildCommon($module);
  124 + // 创建模块的默认页面
  125 + $this->buildHello($module, $namespace, $suffix);
  126 + }
  127 +
  128 + if (empty($list)) {
  129 + // 创建默认的模块目录和文件
  130 + $list = [
  131 + '__file__' => ['common.php'],
  132 + '__dir__' => ['controller', 'model', 'view', 'config'],
  133 + ];
  134 + }
  135 +
  136 + // 创建子目录和文件
  137 + foreach ($list as $path => $file) {
  138 + $modulePath = $this->basePath . $module . DIRECTORY_SEPARATOR;
  139 + if ('__dir__' == $path) {
  140 + // 生成子目录
  141 + foreach ($file as $dir) {
  142 + $this->checkDirBuild($modulePath . $dir);
  143 + }
  144 + } elseif ('__file__' == $path) {
  145 + // 生成(空白)文件
  146 + foreach ($file as $name) {
  147 + if (!is_file($modulePath . $name)) {
  148 + file_put_contents($modulePath . $name, 'php' == pathinfo($name, PATHINFO_EXTENSION) ? "<?php\n" : '');
  149 + }
  150 + }
  151 + } else {
  152 + // 生成相关MVC文件
  153 + foreach ($file as $val) {
  154 + $val = trim($val);
  155 + $filename = $modulePath . $path . DIRECTORY_SEPARATOR . $val . ($suffix ? ucfirst($path) : '') . '.php';
  156 + $space = $namespace . '\\' . ($module ? $module . '\\' : '') . $path;
  157 + $class = $val . ($suffix ? ucfirst($path) : '');
  158 + switch ($path) {
  159 + case 'controller': // 控制器
  160 + $content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
  161 + break;
  162 + case 'model': // 模型
  163 + $content = "<?php\nnamespace {$space};\n\nuse think\Model;\n\nclass {$class} extends Model\n{\n\n}";
  164 + break;
  165 + case 'view': // 视图
  166 + $filename = $modulePath . $path . DIRECTORY_SEPARATOR . $val . '.html';
  167 + $this->checkDirBuild(dirname($filename));
  168 + $content = '';
  169 + break;
  170 + default:
  171 + // 其他文件
  172 + $content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
  173 + }
  174 +
  175 + if (!is_file($filename)) {
  176 + file_put_contents($filename, $content);
  177 + }
  178 + }
  179 + }
  180 + }
  181 + }
  182 +
  183 + /**
  184 + * 根据注释自动生成路由规则
  185 + * @access public
  186 + * @param bool $suffix 类库后缀
  187 + * @param string $layer 控制器层目录名
  188 + * @return string
  189 + */
  190 + public function buildRoute($suffix = false, $layer = '')
  191 + {
  192 + $namespace = $this->app->getNameSpace();
  193 + $modules = glob($this->basePath . '*', GLOB_ONLYDIR);
  194 + $content = '<?php ' . PHP_EOL . '//根据 Annotation 自动生成的路由规则';
  195 +
  196 + if (!$layer) {
  197 + $layer = $this->app->config('app.url_controller_layer');
  198 + }
  199 +
  200 + foreach ($modules as $module) {
  201 + $module = basename($module);
  202 +
  203 + if (in_array($module, $this->app->config('app.deny_module_list'))) {
  204 + continue;
  205 + }
  206 +
  207 + $path = $this->basePath . $module . DIRECTORY_SEPARATOR . $layer . DIRECTORY_SEPARATOR;
  208 + $content .= $this->buildDirRoute($path, $namespace, $module, $suffix, $layer);
  209 + }
  210 +
  211 + $filename = $this->app->getRuntimePath() . 'build_route.php';
  212 + file_put_contents($filename, $content);
  213 +
  214 + return $filename;
  215 + }
  216 +
  217 + /**
  218 + * 生成子目录控制器类的路由规则
  219 + * @access protected
  220 + * @param string $path 控制器目录
  221 + * @param string $namespace 应用命名空间
  222 + * @param string $module 模块
  223 + * @param bool $suffix 类库后缀
  224 + * @param string $layer 控制器层目录名
  225 + * @return string
  226 + */
  227 + protected function buildDirRoute($path, $namespace, $module, $suffix, $layer)
  228 + {
  229 + $content = '';
  230 + $controllers = glob($path . '*.php');
  231 +
  232 + foreach ($controllers as $controller) {
  233 + $controller = basename($controller, '.php');
  234 +
  235 + $class = new \ReflectionClass($namespace . '\\' . $module . '\\' . $layer . '\\' . $controller);
  236 +
  237 + if ($suffix) {
  238 + // 控制器后缀
  239 + $controller = substr($controller, 0, -10);
  240 + }
  241 +
  242 + if (strpos($layer, '\\')) {
  243 + // 多级控制器
  244 + $level = str_replace(DIRECTORY_SEPARATOR, '.', substr($layer, 11));
  245 + $controller = $level . '.' . $controller;
  246 + }
  247 +
  248 + $content .= $this->getControllerRoute($class, $module, $controller);
  249 + }
  250 +
  251 + $subDir = glob($path . '*', GLOB_ONLYDIR);
  252 +
  253 + foreach ($subDir as $dir) {
  254 + $content .= $this->buildDirRoute($dir . DIRECTORY_SEPARATOR, $namespace, $module, $suffix, $layer . '\\' . basename($dir));
  255 + }
  256 +
  257 + return $content;
  258 + }
  259 +
  260 + /**
  261 + * 生成控制器类的路由规则
  262 + * @access protected
  263 + * @param string $class 控制器完整类名
  264 + * @param string $module 模块名
  265 + * @param string $controller 控制器名
  266 + * @return string
  267 + */
  268 + protected function getControllerRoute($class, $module, $controller)
  269 + {
  270 + $content = '';
  271 + $comment = $class->getDocComment();
  272 +
  273 + if (false !== strpos($comment, '@route(')) {
  274 + $comment = $this->parseRouteComment($comment);
  275 + $route = $module . '/' . $controller;
  276 + $comment = preg_replace('/route\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\)/is', 'Route::resource(\1,\'' . $route . '\')', $comment);
  277 + $content .= PHP_EOL . $comment;
  278 + } elseif (false !== strpos($comment, '@alias(')) {
  279 + $comment = $this->parseRouteComment($comment, '@alias(');
  280 + $route = $module . '/' . $controller;
  281 + $comment = preg_replace('/alias\(\s?([\'\"][\-\_\/\w]+[\'\"])\s?\)/is', 'Route::alias(\1,\'' . $route . '\')', $comment);
  282 + $content .= PHP_EOL . $comment;
  283 + }
  284 +
  285 + $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC);
  286 +
  287 + foreach ($methods as $method) {
  288 + $comment = $this->getMethodRouteComment($module, $controller, $method);
  289 + if ($comment) {
  290 + $content .= PHP_EOL . $comment;
  291 + }
  292 + }
  293 +
  294 + return $content;
  295 + }
  296 +
  297 + /**
  298 + * 解析路由注释
  299 + * @access protected
  300 + * @param string $comment
  301 + * @param string $tag
  302 + * @return string
  303 + */
  304 + protected function parseRouteComment($comment, $tag = '@route(')
  305 + {
  306 + $comment = substr($comment, 3, -2);
  307 + $comment = explode(PHP_EOL, substr(strstr(trim($comment), $tag), 1));
  308 + $comment = array_map(function ($item) {return trim(trim($item), ' \t*');}, $comment);
  309 +
  310 + if (count($comment) > 1) {
  311 + $key = array_search('', $comment);
  312 + $comment = array_slice($comment, 0, false === $key ? 1 : $key);
  313 + }
  314 +
  315 + $comment = implode(PHP_EOL . "\t", $comment) . ';';
  316 +
  317 + if (strpos($comment, '{')) {
  318 + $comment = preg_replace_callback('/\{\s?.*?\s?\}/s', function ($matches) {
  319 + return false !== strpos($matches[0], '"') ? '[' . substr(var_export(json_decode($matches[0], true), true), 7, -1) . ']' : $matches[0];
  320 + }, $comment);
  321 + }
  322 + return $comment;
  323 + }
  324 +
  325 + /**
  326 + * 获取方法的路由注释
  327 + * @access protected
  328 + * @param string $module 模块
  329 + * @param string $controller 控制器名
  330 + * @param \ReflectMethod $reflectMethod
  331 + * @return string|void
  332 + */
  333 + protected function getMethodRouteComment($module, $controller, $reflectMethod)
  334 + {
  335 + $comment = $reflectMethod->getDocComment();
  336 +
  337 + if (false !== strpos($comment, '@route(')) {
  338 + $comment = $this->parseRouteComment($comment);
  339 + $action = $reflectMethod->getName();
  340 +
  341 + if ($suffix = $this->app->config('app.action_suffix')) {
  342 + $action = substr($action, 0, -strlen($suffix));
  343 + }
  344 +
  345 + $route = $module . '/' . $controller . '/' . $action;
  346 + $comment = preg_replace('/route\s?\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\,?\s?[\'\"]?(\w+?)[\'\"]?\s?\)/is', 'Route::\2(\1,\'' . $route . '\')', $comment);
  347 + $comment = preg_replace('/route\s?\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\)/is', 'Route::rule(\1,\'' . $route . '\')', $comment);
  348 +
  349 + return $comment;
  350 + }
  351 + }
  352 +
  353 + /**
  354 + * 创建模块的欢迎页面
  355 + * @access protected
  356 + * @param string $module 模块名
  357 + * @param string $namespace 应用类库命名空间
  358 + * @param bool $suffix 类库后缀
  359 + * @return void
  360 + */
  361 + protected function buildHello($module, $namespace, $suffix = false)
  362 + {
  363 + $filename = $this->basePath . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'controller' . DIRECTORY_SEPARATOR . 'Index' . ($suffix ? 'Controller' : '') . '.php';
  364 + if (!is_file($filename)) {
  365 + $content = file_get_contents($this->app->getThinkPath() . 'tpl' . DIRECTORY_SEPARATOR . 'default_index.tpl');
  366 + $content = str_replace(['{$app}', '{$module}', '{layer}', '{$suffix}'], [$namespace, $module ? $module . '\\' : '', 'controller', $suffix ? 'Controller' : ''], $content);
  367 + $this->checkDirBuild(dirname($filename));
  368 +
  369 + file_put_contents($filename, $content);
  370 + }
  371 + }
  372 +
  373 + /**
  374 + * 创建模块的公共文件
  375 + * @access protected
  376 + * @param string $module 模块名
  377 + * @return void
  378 + */
  379 + protected function buildCommon($module)
  380 + {
  381 + $filename = $this->app->getConfigPath() . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'app.php';
  382 + $this->checkDirBuild(dirname($filename));
  383 +
  384 + if (!is_file($filename)) {
  385 + file_put_contents($filename, "<?php\n//配置文件\nreturn [\n\n];");
  386 + }
  387 +
  388 + $filename = $this->basePath . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'common.php';
  389 +
  390 + if (!is_file($filename)) {
  391 + file_put_contents($filename, "<?php\n");
  392 + }
  393 + }
  394 +
  395 + /**
  396 + * 创建目录
  397 + * @access protected
  398 + * @param string $dirname 目录名称
  399 + * @return void
  400 + */
  401 + protected function checkDirBuild($dirname)
  402 + {
  403 + if (!is_dir($dirname)) {
  404 + mkdir($dirname, 0755, true);
  405 + }
  406 + }
  407 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think;
  13 +
  14 +use think\cache\Driver;
  15 +
  16 +/**
  17 + * Class Cache
  18 + *
  19 + * @package think
  20 + *
  21 + * @mixin Driver
  22 + * @mixin \think\cache\driver\File
  23 + */
  24 +class Cache
  25 +{
  26 + /**
  27 + * 缓存实例
  28 + * @var array
  29 + */
  30 + protected $instance = [];
  31 +
  32 + /**
  33 + * 缓存配置
  34 + * @var array
  35 + */
  36 + protected $config = [];
  37 +
  38 + /**
  39 + * 操作句柄
  40 + * @var object
  41 + */
  42 + protected $handler;
  43 +
  44 + public function __construct(array $config = [])
  45 + {
  46 + $this->config = $config;
  47 + $this->init($config);
  48 + }
  49 +
  50 + /**
  51 + * 连接缓存
  52 + * @access public
  53 + * @param array $options 配置数组
  54 + * @param bool|string $name 缓存连接标识 true 强制重新连接
  55 + * @return Driver
  56 + */
  57 + public function connect(array $options = [], $name = false)
  58 + {
  59 + if (false === $name) {
  60 + $name = md5(serialize($options));
  61 + }
  62 +
  63 + if (true === $name || !isset($this->instance[$name])) {
  64 + $type = !empty($options['type']) ? $options['type'] : 'File';
  65 +
  66 + if (true === $name) {
  67 + $name = md5(serialize($options));
  68 + }
  69 +
  70 + $this->instance[$name] = Loader::factory($type, '\\think\\cache\\driver\\', $options);
  71 + }
  72 +
  73 + return $this->instance[$name];
  74 + }
  75 +
  76 + /**
  77 + * 自动初始化缓存
  78 + * @access public
  79 + * @param array $options 配置数组
  80 + * @param bool $force 强制更新
  81 + * @return Driver
  82 + */
  83 + public function init(array $options = [], $force = false)
  84 + {
  85 + if (is_null($this->handler) || $force) {
  86 +
  87 + if ('complex' == $options['type']) {
  88 + $default = $options['default'];
  89 + $options = isset($options[$default['type']]) ? $options[$default['type']] : $default;
  90 + }
  91 +
  92 + $this->handler = $this->connect($options);
  93 + }
  94 +
  95 + return $this->handler;
  96 + }
  97 +
  98 + public static function __make(Config $config)
  99 + {
  100 + return new static($config->pull('cache'));
  101 + }
  102 +
  103 + public function getConfig()
  104 + {
  105 + return $this->config;
  106 + }
  107 +
  108 + public function setConfig(array $config)
  109 + {
  110 + $this->config = array_merge($this->config, $config);
  111 + }
  112 +
  113 + /**
  114 + * 切换缓存类型 需要配置 cache.type 为 complex
  115 + * @access public
  116 + * @param string $name 缓存标识
  117 + * @return Driver
  118 + */
  119 + public function store($name = '')
  120 + {
  121 + if ('' !== $name && 'complex' == $this->config['type']) {
  122 + return $this->connect($this->config[$name], strtolower($name));
  123 + }
  124 +
  125 + return $this->init();
  126 + }
  127 +
  128 + public function __call($method, $args)
  129 + {
  130 + return call_user_func_array([$this->init(), $method], $args);
  131 + }
  132 +
  133 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: zhangyajun <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think;
  13 +
  14 +use ArrayAccess;
  15 +use ArrayIterator;
  16 +use Countable;
  17 +use IteratorAggregate;
  18 +use JsonSerializable;
  19 +
  20 +class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
  21 +{
  22 + /**
  23 + * 数据集数据
  24 + * @var array
  25 + */
  26 + protected $items = [];
  27 +
  28 + public function __construct($items = [])
  29 + {
  30 + $this->items = $this->convertToArray($items);
  31 + }
  32 +
  33 + public static function make($items = [])
  34 + {
  35 + return new static($items);
  36 + }
  37 +
  38 + /**
  39 + * 是否为空
  40 + * @access public
  41 + * @return bool
  42 + */
  43 + public function isEmpty()
  44 + {
  45 + return empty($this->items);
  46 + }
  47 +
  48 + public function toArray()
  49 + {
  50 + return array_map(function ($value) {
  51 + return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value;
  52 + }, $this->items);
  53 + }
  54 +
  55 + public function all()
  56 + {
  57 + return $this->items;
  58 + }
  59 +
  60 + /**
  61 + * 合并数组
  62 + *
  63 + * @access public
  64 + * @param mixed $items
  65 + * @return static
  66 + */
  67 + public function merge($items)
  68 + {
  69 + return new static(array_merge($this->items, $this->convertToArray($items)));
  70 + }
  71 +
  72 + /**
  73 + * 比较数组,返回差集
  74 + *
  75 + * @access public
  76 + * @param mixed $items
  77 + * @return static
  78 + */
  79 + public function diff($items)
  80 + {
  81 + return new static(array_diff($this->items, $this->convertToArray($items)));
  82 + }
  83 +
  84 + /**
  85 + * 交换数组中的键和值
  86 + *
  87 + * @access public
  88 + * @return static
  89 + */
  90 + public function flip()
  91 + {
  92 + return new static(array_flip($this->items));
  93 + }
  94 +
  95 + /**
  96 + * 比较数组,返回交集
  97 + *
  98 + * @access public
  99 + * @param mixed $items
  100 + * @return static
  101 + */
  102 + public function intersect($items)
  103 + {
  104 + return new static(array_intersect($this->items, $this->convertToArray($items)));
  105 + }
  106 +
  107 + /**
  108 + * 返回数组中所有的键名
  109 + *
  110 + * @access public
  111 + * @return static
  112 + */
  113 + public function keys()
  114 + {
  115 + return new static(array_keys($this->items));
  116 + }
  117 +
  118 + /**
  119 + * 删除数组的最后一个元素(出栈)
  120 + *
  121 + * @access public
  122 + * @return mixed
  123 + */
  124 + public function pop()
  125 + {
  126 + return array_pop($this->items);
  127 + }
  128 +
  129 + /**
  130 + * 通过使用用户自定义函数,以字符串返回数组
  131 + *
  132 + * @access public
  133 + * @param callable $callback
  134 + * @param mixed $initial
  135 + * @return mixed
  136 + */
  137 + public function reduce(callable $callback, $initial = null)
  138 + {
  139 + return array_reduce($this->items, $callback, $initial);
  140 + }
  141 +
  142 + /**
  143 + * 以相反的顺序返回数组。
  144 + *
  145 + * @access public
  146 + * @return static
  147 + */
  148 + public function reverse()
  149 + {
  150 + return new static(array_reverse($this->items));
  151 + }
  152 +
  153 + /**
  154 + * 删除数组中首个元素,并返回被删除元素的值
  155 + *
  156 + * @access public
  157 + * @return mixed
  158 + */
  159 + public function shift()
  160 + {
  161 + return array_shift($this->items);
  162 + }
  163 +
  164 + /**
  165 + * 在数组结尾插入一个元素
  166 + * @access public
  167 + * @param mixed $value
  168 + * @param mixed $key
  169 + * @return void
  170 + */
  171 + public function push($value, $key = null)
  172 + {
  173 + if (is_null($key)) {
  174 + $this->items[] = $value;
  175 + } else {
  176 + $this->items[$key] = $value;
  177 + }
  178 + }
  179 +
  180 + /**
  181 + * 把一个数组分割为新的数组块.
  182 + *
  183 + * @access public
  184 + * @param int $size
  185 + * @param bool $preserveKeys
  186 + * @return static
  187 + */
  188 + public function chunk($size, $preserveKeys = false)
  189 + {
  190 + $chunks = [];
  191 +
  192 + foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) {
  193 + $chunks[] = new static($chunk);
  194 + }
  195 +
  196 + return new static($chunks);
  197 + }
  198 +
  199 + /**
  200 + * 在数组开头插入一个元素
  201 + * @access public
  202 + * @param mixed $value
  203 + * @param mixed $key
  204 + * @return void
  205 + */
  206 + public function unshift($value, $key = null)
  207 + {
  208 + if (is_null($key)) {
  209 + array_unshift($this->items, $value);
  210 + } else {
  211 + $this->items = [$key => $value] + $this->items;
  212 + }
  213 + }
  214 +
  215 + /**
  216 + * 给每个元素执行个回调
  217 + *
  218 + * @access public
  219 + * @param callable $callback
  220 + * @return $this
  221 + */
  222 + public function each(callable $callback)
  223 + {
  224 + foreach ($this->items as $key => $item) {
  225 + $result = $callback($item, $key);
  226 +
  227 + if (false === $result) {
  228 + break;
  229 + } elseif (!is_object($item)) {
  230 + $this->items[$key] = $result;
  231 + }
  232 + }
  233 +
  234 + return $this;
  235 + }
  236 +
  237 + /**
  238 + * 用回调函数过滤数组中的元素
  239 + * @access public
  240 + * @param callable|null $callback
  241 + * @return static
  242 + */
  243 + public function filter(callable $callback = null)
  244 + {
  245 + if ($callback) {
  246 + return new static(array_filter($this->items, $callback));
  247 + }
  248 +
  249 + return new static(array_filter($this->items));
  250 + }
  251 +
  252 + /**
  253 + * 根据字段条件过滤数组中的元素
  254 + * @access public
  255 + * @param string $field 字段名
  256 + * @param mixed $operator 操作符
  257 + * @param mixed $value 数据
  258 + * @return static
  259 + */
  260 + public function where($field, $operator, $value = null)
  261 + {
  262 + if (is_null($value)) {
  263 + $value = $operator;
  264 + $operator = '=';
  265 + }
  266 +
  267 + return $this->filter(function ($data) use ($field, $operator, $value) {
  268 + if (strpos($field, '.')) {
  269 + list($field, $relation) = explode('.', $field);
  270 +
  271 + $result = isset($data[$field][$relation]) ? $data[$field][$relation] : null;
  272 + } else {
  273 + $result = isset($data[$field]) ? $data[$field] : null;
  274 + }
  275 +
  276 + switch ($operator) {
  277 + case '===':
  278 + return $result === $value;
  279 + case '!==':
  280 + return $result !== $value;
  281 + case '!=':
  282 + case '<>':
  283 + return $result != $value;
  284 + case '>':
  285 + return $result > $value;
  286 + case '>=':
  287 + return $result >= $value;
  288 + case '<':
  289 + return $result < $value;
  290 + case '<=':
  291 + return $result <= $value;
  292 + case 'like':
  293 + return is_string($result) && false !== strpos($result, $value);
  294 + case 'not like':
  295 + return is_string($result) && false === strpos($result, $value);
  296 + case 'in':
  297 + return is_scalar($result) && in_array($result, $value, true);
  298 + case 'not in':
  299 + return is_scalar($result) && !in_array($result, $value, true);
  300 + case 'between':
  301 + list($min, $max) = is_string($value) ? explode(',', $value) : $value;
  302 + return is_scalar($result) && $result >= $min && $result <= $max;
  303 + case 'not between':
  304 + list($min, $max) = is_string($value) ? explode(',', $value) : $value;
  305 + return is_scalar($result) && $result > $max || $result < $min;
  306 + case '==':
  307 + case '=':
  308 + default:
  309 + return $result == $value;
  310 + }
  311 + });
  312 + }
  313 +
  314 + /**
  315 + * 返回数据中指定的一列
  316 + * @access public
  317 + * @param mixed $columnKey 键名
  318 + * @param mixed $indexKey 作为索引值的列
  319 + * @return array
  320 + */
  321 + public function column($columnKey, $indexKey = null)
  322 + {
  323 + return array_column($this->items, $columnKey, $indexKey);
  324 + }
  325 +
  326 + /**
  327 + * 对数组排序
  328 + *
  329 + * @access public
  330 + * @param callable|null $callback
  331 + * @return static
  332 + */
  333 + public function sort(callable $callback = null)
  334 + {
  335 + $items = $this->items;
  336 +
  337 + $callback = $callback ?: function ($a, $b) {
  338 + return $a == $b ? 0 : (($a < $b) ? -1 : 1);
  339 +
  340 + };
  341 +
  342 + uasort($items, $callback);
  343 +
  344 + return new static($items);
  345 + }
  346 +
  347 + /**
  348 + * 将数组打乱
  349 + *
  350 + * @access public
  351 + * @return static
  352 + */
  353 + public function shuffle()
  354 + {
  355 + $items = $this->items;
  356 +
  357 + shuffle($items);
  358 +
  359 + return new static($items);
  360 + }
  361 +
  362 + /**
  363 + * 截取数组
  364 + *
  365 + * @access public
  366 + * @param int $offset
  367 + * @param int $length
  368 + * @param bool $preserveKeys
  369 + * @return static
  370 + */
  371 + public function slice($offset, $length = null, $preserveKeys = false)
  372 + {
  373 + return new static(array_slice($this->items, $offset, $length, $preserveKeys));
  374 + }
  375 +
  376 + // ArrayAccess
  377 + public function offsetExists($offset)
  378 + {
  379 + return array_key_exists($offset, $this->items);
  380 + }
  381 +
  382 + public function offsetGet($offset)
  383 + {
  384 + return $this->items[$offset];
  385 + }
  386 +
  387 + public function offsetSet($offset, $value)
  388 + {
  389 + if (is_null($offset)) {
  390 + $this->items[] = $value;
  391 + } else {
  392 + $this->items[$offset] = $value;
  393 + }
  394 + }
  395 +
  396 + public function offsetUnset($offset)
  397 + {
  398 + unset($this->items[$offset]);
  399 + }
  400 +
  401 + //Countable
  402 + public function count()
  403 + {
  404 + return count($this->items);
  405 + }
  406 +
  407 + //IteratorAggregate
  408 + public function getIterator()
  409 + {
  410 + return new ArrayIterator($this->items);
  411 + }
  412 +
  413 + //JsonSerializable
  414 + public function jsonSerialize()
  415 + {
  416 + return $this->toArray();
  417 + }
  418 +
  419 + /**
  420 + * 转换当前数据集为JSON字符串
  421 + * @access public
  422 + * @param integer $options json参数
  423 + * @return string
  424 + */
  425 + public function toJson($options = JSON_UNESCAPED_UNICODE)
  426 + {
  427 + return json_encode($this->toArray(), $options);
  428 + }
  429 +
  430 + public function __toString()
  431 + {
  432 + return $this->toJson();
  433 + }
  434 +
  435 + /**
  436 + * 转换成数组
  437 + *
  438 + * @access public
  439 + * @param mixed $items
  440 + * @return array
  441 + */
  442 + protected function convertToArray($items)
  443 + {
  444 + if ($items instanceof self) {
  445 + return $items->all();
  446 + }
  447 +
  448 + return (array) $items;
  449 + }
  450 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think;
  13 +/**
  14 + 图片压缩操作类
  15 + v1.0
  16 +*/
  17 +class Compression
  18 +{
  19 + private $src;
  20 + public $percent = 0.1;
  21 + public function __construct($src){
  22 +
  23 + $this->src = $src;
  24 +
  25 + }
  26 + /**
  27 + * 图片压缩处理
  28 + * @param string $sFile 图片路径
  29 + * @param int $iWidth 自定义图片宽度
  30 + * @param int $iHeight 自定义图片高度
  31 + */
  32 + function getThumb(){
  33 + $iWidth = 375;
  34 + $iHeight = 240;
  35 + $sFile = $this->src;
  36 + //判断该图片是否存在
  37 + if(!file_exists($sFile)) return $sFile;
  38 + //判断图片格式
  39 + $attach_fileext = $this->get_filetype($sFile);
  40 + if (!in_array($attach_fileext, array('jpg','png','jpeg'))){
  41 + return $sFile;
  42 + }
  43 + //压缩图片
  44 + $sFileNameS = str_replace(".".$attach_fileext, "_".$iWidth.'_'.$iHeight.'.'.$attach_fileext, $sFile);
  45 + //判断是否已压缩图片,若是则返回压缩图片路径
  46 + if(file_exists($sFileNameS)){
  47 + return $sFileNameS;
  48 + }
  49 + //解决手机端上传图片被旋转问题
  50 + /*if (in_array($attach_fileext, array('jpeg')) ){
  51 + $this->adjustPicOrientation($sFile);
  52 + }*/
  53 + //生成压缩图片,并存储到原图同路径下
  54 + $this->resizeImage($sFile, $sFileNameS, $iWidth, $iHeight);
  55 + if(!file_exists($sFileNameS)){
  56 + return $sFile;
  57 + }
  58 + return $sFileNameS;
  59 + }
  60 + /**
  61 + *获取文件后缀名
  62 + */
  63 + function get_filetype($filename) {
  64 + $extend = explode("." , $filename);
  65 + return strtolower($extend[count($extend) - 1]);
  66 + }
  67 + /**
  68 + * 生成图片
  69 + * @param string $im 源图片路径
  70 + * @param string $dest 目标图片路径
  71 + * @param int $maxwidth 生成图片宽
  72 + * @param int $maxheight 生成图片高
  73 + */
  74 + function resizeImage($im, $dest, $maxwidth, $maxheight) {
  75 + $img = getimagesize($im);
  76 + switch ($img[2]) {
  77 + case 1:
  78 + $im = @imagecreatefromgif($im);
  79 + break;
  80 + case 2:
  81 + $im = @imagecreatefromjpeg($im);
  82 + break;
  83 + case 3:
  84 + $im = @imagecreatefrompng($im);
  85 + break;
  86 + }
  87 +
  88 + $pic_width = imagesx($im);
  89 + $pic_height = imagesy($im);
  90 + $resizewidth_tag = false;
  91 + $resizeheight_tag = false;
  92 + if (($maxwidth && $pic_width > $maxwidth) || ($maxheight && $pic_height > $maxheight)) {
  93 + if ($maxwidth && $pic_width > $maxwidth) {
  94 + $widthratio = $maxwidth / $pic_width;
  95 + $resizewidth_tag = true;
  96 + }
  97 +
  98 + if ($maxheight && $pic_height > $maxheight) {
  99 + $heightratio = $maxheight / $pic_height;
  100 + $resizeheight_tag = true;
  101 + }
  102 +
  103 + if ($resizewidth_tag && $resizeheight_tag) {
  104 + if ($widthratio < $heightratio)
  105 + $ratio = $widthratio;
  106 + else
  107 + $ratio = $heightratio;
  108 + }
  109 +
  110 +
  111 + if ($resizewidth_tag && !$resizeheight_tag)
  112 + $ratio = $widthratio;
  113 + if ($resizeheight_tag && !$resizewidth_tag)
  114 + $ratio = $heightratio;
  115 + $newwidth = $pic_width * $ratio;
  116 + $newheight = $pic_height * $ratio;
  117 +
  118 + if (function_exists("imagecopyresampled")) {
  119 + $newim = imagecreatetruecolor($newwidth, $newheight);
  120 + imagecopyresampled($newim, $im, 0, 0, 0, 0, $newwidth, $newheight, $pic_width, $pic_height);
  121 + } else {
  122 + $newim = imagecreate($newwidth, $newheight);
  123 + imagecopyresized($newim, $im, 0, 0, 0, 0, $newwidth, $newheight, $pic_width, $pic_height);
  124 + }
  125 +
  126 + imagejpeg($newim, $dest);
  127 + imagedestroy($newim);
  128 + } else {
  129 + imagejpeg($im, $dest);
  130 + }
  131 + }
  132 + /**
  133 + * 解决手机上传图片被旋转问题
  134 + * @param string $full_filename 文件路径
  135 + */
  136 + function adjustPicOrientation($full_filename){
  137 + $exif = exif_read_data($full_filename);
  138 + if($exif && isset($exif['Orientation'])) {
  139 + $orientation = $exif['Orientation'];
  140 + if($orientation != 1){
  141 + $img = imagecreatefromjpeg($full_filename);
  142 +
  143 + $mirror = false;
  144 + $deg = 0;
  145 +
  146 + switch ($orientation) {
  147 + case 2:
  148 + $mirror = true;
  149 + break;
  150 + case 3:
  151 + $deg = 180;
  152 + break;
  153 + case 4:
  154 + $deg = 180;
  155 + $mirror = true;
  156 + break;
  157 + case 5:
  158 + $deg = 270;
  159 + $mirror = true;
  160 + break;
  161 + case 6:
  162 + $deg = 270;
  163 + break;
  164 + case 7:
  165 + $deg = 90;
  166 + $mirror = true;
  167 + break;
  168 + case 8:
  169 + $deg = 90;
  170 + break;
  171 + }
  172 + if ($deg) $img = imagerotate($img, $deg, 0);
  173 + if ($mirror) $img = _mirrorImage($img);
  174 + //$full_filename = str_replace('.jpg', "-O$orientation.jpg", $full_filename);新文件名
  175 + imagejpeg($img, $full_filename, 95);
  176 + }
  177 + }
  178 + return $full_filename;
  179 + }
  180 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: liu21st <liu21st@gmail.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think;
  13 +
  14 +use Yaconf;
  15 +
  16 +class Config implements \ArrayAccess
  17 +{
  18 + /**
  19 + * 配置参数
  20 + * @var array
  21 + */
  22 + protected $config = [];
  23 +
  24 + /**
  25 + * 配置前缀
  26 + * @var string
  27 + */
  28 + protected $prefix = 'app';
  29 +
  30 + /**
  31 + * 配置文件目录
  32 + * @var string
  33 + */
  34 + protected $path;
  35 +
  36 + /**
  37 + * 配置文件后缀
  38 + * @var string
  39 + */
  40 + protected $ext;
  41 +
  42 + /**
  43 + * 是否支持Yaconf
  44 + * @var bool
  45 + */
  46 + protected $yaconf;
  47 +
  48 + /**
  49 + * 构造方法
  50 + * @access public
  51 + */
  52 + public function __construct($path = '', $ext = '.php')
  53 + {
  54 + $this->path = $path;
  55 + $this->ext = $ext;
  56 + $this->yaconf = class_exists('Yaconf');
  57 + }
  58 +
  59 + public static function __make(App $app)
  60 + {
  61 + $path = $app->getConfigPath();
  62 + $ext = $app->getConfigExt();
  63 + return new static($path, $ext);
  64 + }
  65 +
  66 + /**
  67 + * 设置开启Yaconf
  68 + * @access public
  69 + * @param bool $yaconf 是否使用Yaconf
  70 + * @return void
  71 + */
  72 + public function useYaconf($yaconf)
  73 + {
  74 + $this->yaconf = $yaconf;
  75 + }
  76 +
  77 + /**
  78 + * 设置配置参数默认前缀
  79 + * @access public
  80 + * @param string $prefix 前缀
  81 + * @return void
  82 + */
  83 + public function setDefaultPrefix($prefix)
  84 + {
  85 + $this->prefix = $prefix;
  86 + }
  87 +
  88 + /**
  89 + * 解析配置文件或内容
  90 + * @access public
  91 + * @param string $config 配置文件路径或内容
  92 + * @param string $type 配置解析类型
  93 + * @param string $name 配置名(如设置即表示二级配置)
  94 + * @return mixed
  95 + */
  96 + public function parse($config, $type = '', $name = '')
  97 + {
  98 + if (empty($type)) {
  99 + $type = pathinfo($config, PATHINFO_EXTENSION);
  100 + }
  101 +
  102 + $object = Loader::factory($type, '\\think\\config\\driver\\', $config);
  103 +
  104 + return $this->set($object->parse(), $name);
  105 + }
  106 +
  107 + /**
  108 + * 加载配置文件(多种格式)
  109 + * @access public
  110 + * @param string $file 配置文件名
  111 + * @param string $name 一级配置名
  112 + * @return mixed
  113 + */
  114 + public function load($file, $name = '')
  115 + {
  116 + if (is_file($file)) {
  117 + $filename = $file;
  118 + } elseif (is_file($this->path . $file . $this->ext)) {
  119 + $filename = $this->path . $file . $this->ext;
  120 + }
  121 +
  122 + if (isset($filename)) {
  123 + return $this->loadFile($filename, $name);
  124 + } elseif ($this->yaconf && Yaconf::has($file)) {
  125 + return $this->set(Yaconf::get($file), $name);
  126 + }
  127 +
  128 + return $this->config;
  129 + }
  130 +
  131 + protected function loadFile($file, $name)
  132 + {
  133 + $name = strtolower($name);
  134 + $type = pathinfo($file, PATHINFO_EXTENSION);
  135 +
  136 + if ('php' == $type) {
  137 + return $this->set(include $file, $name);
  138 + } elseif ('yaml' == $type && function_exists('yaml_parse_file')) {
  139 + return $this->set(yaml_parse_file($file), $name);
  140 + }
  141 +
  142 + return $this->parse($file, $type, $name);
  143 + }
  144 +
  145 + /**
  146 + * 检测配置是否存在
  147 + * @access public
  148 + * @param string $name 配置参数名(支持多级配置 .号分割)
  149 + * @return bool
  150 + */
  151 + public function has($name)
  152 + {
  153 + if (!strpos($name, '.')) {
  154 + $name = $this->prefix . '.' . $name;
  155 + }
  156 +
  157 + return !is_null($this->get($name));
  158 + }
  159 +
  160 + /**
  161 + * 获取一级配置
  162 + * @access public
  163 + * @param string $name 一级配置名
  164 + * @return array
  165 + */
  166 + public function pull($name)
  167 + {
  168 + $name = strtolower($name);
  169 +
  170 + if ($this->yaconf && Yaconf::has($name)) {
  171 + $config = Yaconf::get($name);
  172 + return isset($this->config[$name]) ? array_merge($this->config[$name], $config) : $config;
  173 + }
  174 +
  175 + return isset($this->config[$name]) ? $this->config[$name] : [];
  176 + }
  177 +
  178 + /**
  179 + * 获取配置参数 为空则获取所有配置
  180 + * @access public
  181 + * @param string $name 配置参数名(支持多级配置 .号分割)
  182 + * @param mixed $default 默认值
  183 + * @return mixed
  184 + */
  185 + public function get($name = null, $default = null)
  186 + {
  187 + if ($name && !strpos($name, '.')) {
  188 + $name = $this->prefix . '.' . $name;
  189 + }
  190 +
  191 + // 无参数时获取所有
  192 + if (empty($name)) {
  193 + return $this->config;
  194 + }
  195 +
  196 + if ('.' == substr($name, -1)) {
  197 + return $this->pull(substr($name, 0, -1));
  198 + }
  199 +
  200 + if ($this->yaconf && Yaconf::has($name)) {
  201 + return Yaconf::get($name);
  202 + }
  203 +
  204 + $name = explode('.', $name);
  205 + $name[0] = strtolower($name[0]);
  206 + $config = $this->config;
  207 +
  208 + // 按.拆分成多维数组进行判断
  209 + foreach ($name as $val) {
  210 + if (isset($config[$val])) {
  211 + $config = $config[$val];
  212 + } else {
  213 + return $default;
  214 + }
  215 + }
  216 +
  217 + return $config;
  218 + }
  219 +
  220 + /**
  221 + * 设置配置参数 name为数组则为批量设置
  222 + * @access public
  223 + * @param string|array $name 配置参数名(支持三级配置 .号分割)
  224 + * @param mixed $value 配置值
  225 + * @return mixed
  226 + */
  227 + public function set($name, $value = null)
  228 + {
  229 + if (is_string($name)) {
  230 + if (!strpos($name, '.')) {
  231 + $name = $this->prefix . '.' . $name;
  232 + }
  233 +
  234 + $name = explode('.', $name, 3);
  235 +
  236 + if (count($name) == 2) {
  237 + $this->config[strtolower($name[0])][$name[1]] = $value;
  238 + } else {
  239 + $this->config[strtolower($name[0])][$name[1]][$name[2]] = $value;
  240 + }
  241 +
  242 + return $value;
  243 + } elseif (is_array($name)) {
  244 + // 批量设置
  245 + if (!empty($value)) {
  246 + if (isset($this->config[$value])) {
  247 + $result = array_merge($this->config[$value], $name);
  248 + } else {
  249 + $result = $name;
  250 + }
  251 +
  252 + $this->config[$value] = $result;
  253 + } else {
  254 + $result = $this->config = array_merge($this->config, $name);
  255 + }
  256 + } else {
  257 + // 为空直接返回 已有配置
  258 + $result = $this->config;
  259 + }
  260 +
  261 + return $result;
  262 + }
  263 +
  264 + /**
  265 + * 移除配置
  266 + * @access public
  267 + * @param string $name 配置参数名(支持三级配置 .号分割)
  268 + * @return void
  269 + */
  270 + public function remove($name)
  271 + {
  272 + if (!strpos($name, '.')) {
  273 + $name = $this->prefix . '.' . $name;
  274 + }
  275 +
  276 + $name = explode('.', $name, 3);
  277 +
  278 + if (count($name) == 2) {
  279 + unset($this->config[strtolower($name[0])][$name[1]]);
  280 + } else {
  281 + unset($this->config[strtolower($name[0])][$name[1]][$name[2]]);
  282 + }
  283 + }
  284 +
  285 + /**
  286 + * 重置配置参数
  287 + * @access public
  288 + * @param string $prefix 配置前缀名
  289 + * @return void
  290 + */
  291 + public function reset($prefix = '')
  292 + {
  293 + if ('' === $prefix) {
  294 + $this->config = [];
  295 + } else {
  296 + $this->config[$prefix] = [];
  297 + }
  298 + }
  299 +
  300 + /**
  301 + * 设置配置
  302 + * @access public
  303 + * @param string $name 参数名
  304 + * @param mixed $value 值
  305 + */
  306 + public function __set($name, $value)
  307 + {
  308 + return $this->set($name, $value);
  309 + }
  310 +
  311 + /**
  312 + * 获取配置参数
  313 + * @access public
  314 + * @param string $name 参数名
  315 + * @return mixed
  316 + */
  317 + public function __get($name)
  318 + {
  319 + return $this->get($name);
  320 + }
  321 +
  322 + /**
  323 + * 检测是否存在参数
  324 + * @access public
  325 + * @param string $name 参数名
  326 + * @return bool
  327 + */
  328 + public function __isset($name)
  329 + {
  330 + return $this->has($name);
  331 + }
  332 +
  333 + // ArrayAccess
  334 + public function offsetSet($name, $value)
  335 + {
  336 + $this->set($name, $value);
  337 + }
  338 +
  339 + public function offsetExists($name)
  340 + {
  341 + return $this->has($name);
  342 + }
  343 +
  344 + public function offsetUnset($name)
  345 + {
  346 + $this->remove($name);
  347 + }
  348 +
  349 + public function offsetGet($name)
  350 + {
  351 + return $this->get($name);
  352 + }
  353 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | TopThink [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2015 http://www.topthink.com All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Author: zhangyajun <448901948@qq.com>
  8 +// +----------------------------------------------------------------------
  9 +
  10 +namespace think;
  11 +
  12 +use think\console\Command;
  13 +use think\console\command\Help as HelpCommand;
  14 +use think\console\Input;
  15 +use think\console\input\Argument as InputArgument;
  16 +use think\console\input\Definition as InputDefinition;
  17 +use think\console\input\Option as InputOption;
  18 +use think\console\Output;
  19 +use think\console\output\driver\Buffer;
  20 +
  21 +class Console
  22 +{
  23 +
  24 + private $name;
  25 + private $version;
  26 +
  27 + /** @var Command[] */
  28 + private $commands = [];
  29 +
  30 + private $wantHelps = false;
  31 +
  32 + private $catchExceptions = true;
  33 + private $autoExit = true;
  34 + private $definition;
  35 + private $defaultCommand;
  36 +
  37 + private static $defaultCommands = [
  38 + "think\\console\\command\\Help",
  39 + "think\\console\\command\\Lists",
  40 + "think\\console\\command\\Build",
  41 + "think\\console\\command\\Clear",
  42 + "think\\console\\command\\make\\Controller",
  43 + "think\\console\\command\\make\\Model",
  44 + "think\\console\\command\\make\\Middleware",
  45 + "think\\console\\command\\make\\Validate",
  46 + "think\\console\\command\\optimize\\Autoload",
  47 + "think\\console\\command\\optimize\\Config",
  48 + "think\\console\\command\\optimize\\Schema",
  49 + "think\\console\\command\\optimize\\Route",
  50 + "think\\console\\command\\RunServer",
  51 + "think\\console\\command\\Version",
  52 + ];
  53 +
  54 + /**
  55 + * Console constructor.
  56 + * @access public
  57 + * @param string $name 名称
  58 + * @param string $version 版本
  59 + * @param null|string $user 执行用户
  60 + */
  61 + public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN', $user = null)
  62 + {
  63 + $this->name = $name;
  64 + $this->version = $version;
  65 +
  66 + if ($user) {
  67 + $this->setUser($user);
  68 + }
  69 +
  70 + $this->defaultCommand = 'list';
  71 + $this->definition = $this->getDefaultInputDefinition();
  72 +
  73 + foreach ($this->getDefaultCommands() as $command) {
  74 + $this->add($command);
  75 + }
  76 + }
  77 +
  78 + /**
  79 + * 设置执行用户
  80 + * @param $user
  81 + */
  82 + public function setUser($user)
  83 + {
  84 + $user = posix_getpwnam($user);
  85 + if ($user) {
  86 + posix_setuid($user['uid']);
  87 + posix_setgid($user['gid']);
  88 + }
  89 + }
  90 +
  91 + /**
  92 + * 初始化 Console
  93 + * @access public
  94 + * @param bool $run 是否运行 Console
  95 + * @return int|Console
  96 + */
  97 + public static function init($run = true)
  98 + {
  99 + static $console;
  100 +
  101 + if (!$console) {
  102 + $config = Container::get('config')->pull('console');
  103 + // 实例化 console
  104 + $console = new self($config['name'], $config['version'], $config['user']);
  105 +
  106 + // 读取指令集
  107 + $file = Container::get('env')->get('app_path') . 'command.php';
  108 +
  109 + if (is_file($file)) {
  110 + $commands = include $file;
  111 +
  112 + if (is_array($commands)) {
  113 + foreach ($commands as $command) {
  114 + if (class_exists($command) && is_subclass_of($command, "\\think\\console\\Command")) {
  115 + // 注册指令
  116 + $console->add(new $command());
  117 + }
  118 + }
  119 + }
  120 + }
  121 + }
  122 +
  123 + if ($run) {
  124 + // 运行
  125 + return $console->run();
  126 + } else {
  127 + return $console;
  128 + }
  129 + }
  130 +
  131 + /**
  132 + * @access public
  133 + * @param string $command
  134 + * @param array $parameters
  135 + * @param string $driver
  136 + * @return Output|Buffer
  137 + */
  138 + public static function call($command, array $parameters = [], $driver = 'buffer')
  139 + {
  140 + $console = self::init(false);
  141 +
  142 + array_unshift($parameters, $command);
  143 +
  144 + $input = new Input($parameters);
  145 + $output = new Output($driver);
  146 +
  147 + $console->setCatchExceptions(false);
  148 + $console->find($command)->run($input, $output);
  149 +
  150 + return $output;
  151 + }
  152 +
  153 + /**
  154 + * 执行当前的指令
  155 + * @access public
  156 + * @return int
  157 + * @throws \Exception
  158 + * @api
  159 + */
  160 + public function run()
  161 + {
  162 + $input = new Input();
  163 + $output = new Output();
  164 +
  165 + $this->configureIO($input, $output);
  166 +
  167 + try {
  168 + $exitCode = $this->doRun($input, $output);
  169 + } catch (\Exception $e) {
  170 + if (!$this->catchExceptions) {
  171 + throw $e;
  172 + }
  173 +
  174 + $output->renderException($e);
  175 +
  176 + $exitCode = $e->getCode();
  177 + if (is_numeric($exitCode)) {
  178 + $exitCode = (int) $exitCode;
  179 + if (0 === $exitCode) {
  180 + $exitCode = 1;
  181 + }
  182 + } else {
  183 + $exitCode = 1;
  184 + }
  185 + }
  186 +
  187 + if ($this->autoExit) {
  188 + if ($exitCode > 255) {
  189 + $exitCode = 255;
  190 + }
  191 +
  192 + exit($exitCode);
  193 + }
  194 +
  195 + return $exitCode;
  196 + }
  197 +
  198 + /**
  199 + * 执行指令
  200 + * @access public
  201 + * @param Input $input
  202 + * @param Output $output
  203 + * @return int
  204 + */
  205 + public function doRun(Input $input, Output $output)
  206 + {
  207 + if (true === $input->hasParameterOption(['--version', '-V'])) {
  208 + $output->writeln($this->getLongVersion());
  209 +
  210 + return 0;
  211 + }
  212 +
  213 + $name = $this->getCommandName($input);
  214 +
  215 + if (true === $input->hasParameterOption(['--help', '-h'])) {
  216 + if (!$name) {
  217 + $name = 'help';
  218 + $input = new Input(['help']);
  219 + } else {
  220 + $this->wantHelps = true;
  221 + }
  222 + }
  223 +
  224 + if (!$name) {
  225 + $name = $this->defaultCommand;
  226 + $input = new Input([$this->defaultCommand]);
  227 + }
  228 +
  229 + $command = $this->find($name);
  230 +
  231 + $exitCode = $this->doRunCommand($command, $input, $output);
  232 +
  233 + return $exitCode;
  234 + }
  235 +
  236 + /**
  237 + * 设置输入参数定义
  238 + * @access public
  239 + * @param InputDefinition $definition
  240 + */
  241 + public function setDefinition(InputDefinition $definition)
  242 + {
  243 + $this->definition = $definition;
  244 + }
  245 +
  246 + /**
  247 + * 获取输入参数定义
  248 + * @access public
  249 + * @return InputDefinition The InputDefinition instance
  250 + */
  251 + public function getDefinition()
  252 + {
  253 + return $this->definition;
  254 + }
  255 +
  256 + /**
  257 + * Gets the help message.
  258 + * @access public
  259 + * @return string A help message.
  260 + */
  261 + public function getHelp()
  262 + {
  263 + return $this->getLongVersion();
  264 + }
  265 +
  266 + /**
  267 + * 是否捕获异常
  268 + * @access public
  269 + * @param bool $boolean
  270 + * @api
  271 + */
  272 + public function setCatchExceptions($boolean)
  273 + {
  274 + $this->catchExceptions = (bool) $boolean;
  275 + }
  276 +
  277 + /**
  278 + * 是否自动退出
  279 + * @access public
  280 + * @param bool $boolean
  281 + * @api
  282 + */
  283 + public function setAutoExit($boolean)
  284 + {
  285 + $this->autoExit = (bool) $boolean;
  286 + }
  287 +
  288 + /**
  289 + * 获取名称
  290 + * @access public
  291 + * @return string
  292 + */
  293 + public function getName()
  294 + {
  295 + return $this->name;
  296 + }
  297 +
  298 + /**
  299 + * 设置名称
  300 + * @access public
  301 + * @param string $name
  302 + */
  303 + public function setName($name)
  304 + {
  305 + $this->name = $name;
  306 + }
  307 +
  308 + /**
  309 + * 获取版本
  310 + * @access public
  311 + * @return string
  312 + * @api
  313 + */
  314 + public function getVersion()
  315 + {
  316 + return $this->version;
  317 + }
  318 +
  319 + /**
  320 + * 设置版本
  321 + * @access public
  322 + * @param string $version
  323 + */
  324 + public function setVersion($version)
  325 + {
  326 + $this->version = $version;
  327 + }
  328 +
  329 + /**
  330 + * 获取完整的版本号
  331 + * @access public
  332 + * @return string
  333 + */
  334 + public function getLongVersion()
  335 + {
  336 + if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
  337 + return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
  338 + }
  339 +
  340 + return '<info>Console Tool</info>';
  341 + }
  342 +
  343 + /**
  344 + * 注册一个指令
  345 + * @access public
  346 + * @param string $name
  347 + * @return Command
  348 + */
  349 + public function register($name)
  350 + {
  351 + return $this->add(new Command($name));
  352 + }
  353 +
  354 + /**
  355 + * 添加指令
  356 + * @access public
  357 + * @param Command[] $commands
  358 + */
  359 + public function addCommands(array $commands)
  360 + {
  361 + foreach ($commands as $command) {
  362 + $this->add($command);
  363 + }
  364 + }
  365 +
  366 + /**
  367 + * 添加一个指令
  368 + * @access public
  369 + * @param Command $command
  370 + * @return Command
  371 + */
  372 + public function add(Command $command)
  373 + {
  374 + $command->setConsole($this);
  375 +
  376 + if (!$command->isEnabled()) {
  377 + $command->setConsole(null);
  378 + return;
  379 + }
  380 +
  381 + if (null === $command->getDefinition()) {
  382 + throw new \LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command)));
  383 + }
  384 +
  385 + $this->commands[$command->getName()] = $command;
  386 +
  387 + foreach ($command->getAliases() as $alias) {
  388 + $this->commands[$alias] = $command;
  389 + }
  390 +
  391 + return $command;
  392 + }
  393 +
  394 + /**
  395 + * 获取指令
  396 + * @access public
  397 + * @param string $name 指令名称
  398 + * @return Command
  399 + * @throws \InvalidArgumentException
  400 + */
  401 + public function get($name)
  402 + {
  403 + if (!isset($this->commands[$name])) {
  404 + throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name));
  405 + }
  406 +
  407 + $command = $this->commands[$name];
  408 +
  409 + if ($this->wantHelps) {
  410 + $this->wantHelps = false;
  411 +
  412 + /** @var HelpCommand $helpCommand */
  413 + $helpCommand = $this->get('help');
  414 + $helpCommand->setCommand($command);
  415 +
  416 + return $helpCommand;
  417 + }
  418 +
  419 + return $command;
  420 + }
  421 +
  422 + /**
  423 + * 某个指令是否存在
  424 + * @access public
  425 + * @param string $name 指令名称
  426 + * @return bool
  427 + */
  428 + public function has($name)
  429 + {
  430 + return isset($this->commands[$name]);
  431 + }
  432 +
  433 + /**
  434 + * 获取所有的命名空间
  435 + * @access public
  436 + * @return array
  437 + */
  438 + public function getNamespaces()
  439 + {
  440 + $namespaces = [];
  441 + foreach ($this->commands as $command) {
  442 + $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
  443 +
  444 + foreach ($command->getAliases() as $alias) {
  445 + $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
  446 + }
  447 + }
  448 +
  449 + return array_values(array_unique(array_filter($namespaces)));
  450 + }
  451 +
  452 + /**
  453 + * 查找注册命名空间中的名称或缩写。
  454 + * @access public
  455 + * @param string $namespace
  456 + * @return string
  457 + * @throws \InvalidArgumentException
  458 + */
  459 + public function findNamespace($namespace)
  460 + {
  461 + $allNamespaces = $this->getNamespaces();
  462 + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
  463 + return preg_quote($matches[1]) . '[^:]*';
  464 + }, $namespace);
  465 + $namespaces = preg_grep('{^' . $expr . '}', $allNamespaces);
  466 +
  467 + if (empty($namespaces)) {
  468 + $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
  469 +
  470 + if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
  471 + if (1 == count($alternatives)) {
  472 + $message .= "\n\nDid you mean this?\n ";
  473 + } else {
  474 + $message .= "\n\nDid you mean one of these?\n ";
  475 + }
  476 +
  477 + $message .= implode("\n ", $alternatives);
  478 + }
  479 +
  480 + throw new \InvalidArgumentException($message);
  481 + }
  482 +
  483 + $exact = in_array($namespace, $namespaces, true);
  484 + if (count($namespaces) > 1 && !$exact) {
  485 + throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))));
  486 + }
  487 +
  488 + return $exact ? $namespace : reset($namespaces);
  489 + }
  490 +
  491 + /**
  492 + * 查找指令
  493 + * @access public
  494 + * @param string $name 名称或者别名
  495 + * @return Command
  496 + * @throws \InvalidArgumentException
  497 + */
  498 + public function find($name)
  499 + {
  500 + $allCommands = array_keys($this->commands);
  501 +
  502 + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
  503 + return preg_quote($matches[1]) . '[^:]*';
  504 + }, $name);
  505 +
  506 + $commands = preg_grep('{^' . $expr . '}', $allCommands);
  507 +
  508 + if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) {
  509 + if (false !== $pos = strrpos($name, ':')) {
  510 + $this->findNamespace(substr($name, 0, $pos));
  511 + }
  512 +
  513 + $message = sprintf('Command "%s" is not defined.', $name);
  514 +
  515 + if ($alternatives = $this->findAlternatives($name, $allCommands)) {
  516 + if (1 == count($alternatives)) {
  517 + $message .= "\n\nDid you mean this?\n ";
  518 + } else {
  519 + $message .= "\n\nDid you mean one of these?\n ";
  520 + }
  521 + $message .= implode("\n ", $alternatives);
  522 + }
  523 +
  524 + throw new \InvalidArgumentException($message);
  525 + }
  526 +
  527 + if (count($commands) > 1) {
  528 + $commandList = $this->commands;
  529 +
  530 + $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) {
  531 + $commandName = $commandList[$nameOrAlias]->getName();
  532 +
  533 + return $commandName === $nameOrAlias || !in_array($commandName, $commands);
  534 + });
  535 + }
  536 +
  537 + $exact = in_array($name, $commands, true);
  538 + if (count($commands) > 1 && !$exact) {
  539 + $suggestions = $this->getAbbreviationSuggestions(array_values($commands));
  540 +
  541 + throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions));
  542 + }
  543 +
  544 + return $this->get($exact ? $name : reset($commands));
  545 + }
  546 +
  547 + /**
  548 + * 获取所有的指令
  549 + * @access public
  550 + * @param string $namespace 命名空间
  551 + * @return Command[]
  552 + * @api
  553 + */
  554 + public function all($namespace = null)
  555 + {
  556 + if (null === $namespace) {
  557 + return $this->commands;
  558 + }
  559 +
  560 + $commands = [];
  561 + foreach ($this->commands as $name => $command) {
  562 + if ($this->extractNamespace($name, substr_count($namespace, ':') + 1) === $namespace) {
  563 + $commands[$name] = $command;
  564 + }
  565 + }
  566 +
  567 + return $commands;
  568 + }
  569 +
  570 + /**
  571 + * 获取可能的指令名
  572 + * @access public
  573 + * @param array $names
  574 + * @return array
  575 + */
  576 + public static function getAbbreviations($names)
  577 + {
  578 + $abbrevs = [];
  579 + foreach ($names as $name) {
  580 + for ($len = strlen($name); $len > 0; --$len) {
  581 + $abbrev = substr($name, 0, $len);
  582 + $abbrevs[$abbrev][] = $name;
  583 + }
  584 + }
  585 +
  586 + return $abbrevs;
  587 + }
  588 +
  589 + /**
  590 + * 配置基于用户的参数和选项的输入和输出实例。
  591 + * @access protected
  592 + * @param Input $input 输入实例
  593 + * @param Output $output 输出实例
  594 + */
  595 + protected function configureIO(Input $input, Output $output)
  596 + {
  597 + if (true === $input->hasParameterOption(['--ansi'])) {
  598 + $output->setDecorated(true);
  599 + } elseif (true === $input->hasParameterOption(['--no-ansi'])) {
  600 + $output->setDecorated(false);
  601 + }
  602 +
  603 + if (true === $input->hasParameterOption(['--no-interaction', '-n'])) {
  604 + $input->setInteractive(false);
  605 + }
  606 +
  607 + if (true === $input->hasParameterOption(['--quiet', '-q'])) {
  608 + $output->setVerbosity(Output::VERBOSITY_QUIET);
  609 + } else {
  610 + if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) {
  611 + $output->setVerbosity(Output::VERBOSITY_DEBUG);
  612 + } elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) {
  613 + $output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE);
  614 + } elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) {
  615 + $output->setVerbosity(Output::VERBOSITY_VERBOSE);
  616 + }
  617 + }
  618 + }
  619 +
  620 + /**
  621 + * 执行指令
  622 + * @access protected
  623 + * @param Command $command 指令实例
  624 + * @param Input $input 输入实例
  625 + * @param Output $output 输出实例
  626 + * @return int
  627 + * @throws \Exception
  628 + */
  629 + protected function doRunCommand(Command $command, Input $input, Output $output)
  630 + {
  631 + return $command->run($input, $output);
  632 + }
  633 +
  634 + /**
  635 + * 获取指令的基础名称
  636 + * @access protected
  637 + * @param Input $input
  638 + * @return string
  639 + */
  640 + protected function getCommandName(Input $input)
  641 + {
  642 + return $input->getFirstArgument();
  643 + }
  644 +
  645 + /**
  646 + * 获取默认输入定义
  647 + * @access protected
  648 + * @return InputDefinition
  649 + */
  650 + protected function getDefaultInputDefinition()
  651 + {
  652 + return new InputDefinition([
  653 + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
  654 + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'),
  655 + new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this console version'),
  656 + new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
  657 + 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'),
  658 + new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
  659 + new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
  660 + new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
  661 + ]);
  662 + }
  663 +
  664 + /**
  665 + * 设置默认命令
  666 + * @access protected
  667 + * @return Command[] An array of default Command instances
  668 + */
  669 + protected function getDefaultCommands()
  670 + {
  671 + $defaultCommands = [];
  672 +
  673 + foreach (self::$defaultCommands as $classname) {
  674 + if (class_exists($classname) && is_subclass_of($classname, "think\\console\\Command")) {
  675 + $defaultCommands[] = new $classname();
  676 + }
  677 + }
  678 +
  679 + return $defaultCommands;
  680 + }
  681 +
  682 + public static function addDefaultCommands(array $classnames)
  683 + {
  684 + self::$defaultCommands = array_merge(self::$defaultCommands, $classnames);
  685 + }
  686 +
  687 + /**
  688 + * 获取可能的建议
  689 + * @access private
  690 + * @param array $abbrevs
  691 + * @return string
  692 + */
  693 + private function getAbbreviationSuggestions($abbrevs)
  694 + {
  695 + return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '');
  696 + }
  697 +
  698 + /**
  699 + * 返回命名空间部分
  700 + * @access public
  701 + * @param string $name 指令
  702 + * @param string $limit 部分的命名空间的最大数量
  703 + * @return string
  704 + */
  705 + public function extractNamespace($name, $limit = null)
  706 + {
  707 + $parts = explode(':', $name);
  708 + array_pop($parts);
  709 +
  710 + return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit));
  711 + }
  712 +
  713 + /**
  714 + * 查找可替代的建议
  715 + * @access private
  716 + * @param string $name
  717 + * @param array|\Traversable $collection
  718 + * @return array
  719 + */
  720 + private function findAlternatives($name, $collection)
  721 + {
  722 + $threshold = 1e3;
  723 + $alternatives = [];
  724 +
  725 + $collectionParts = [];
  726 + foreach ($collection as $item) {
  727 + $collectionParts[$item] = explode(':', $item);
  728 + }
  729 +
  730 + foreach (explode(':', $name) as $i => $subname) {
  731 + foreach ($collectionParts as $collectionName => $parts) {
  732 + $exists = isset($alternatives[$collectionName]);
  733 + if (!isset($parts[$i]) && $exists) {
  734 + $alternatives[$collectionName] += $threshold;
  735 + continue;
  736 + } elseif (!isset($parts[$i])) {
  737 + continue;
  738 + }
  739 +
  740 + $lev = levenshtein($subname, $parts[$i]);
  741 + if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
  742 + $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
  743 + } elseif ($exists) {
  744 + $alternatives[$collectionName] += $threshold;
  745 + }
  746 + }
  747 + }
  748 +
  749 + foreach ($collection as $item) {
  750 + $lev = levenshtein($name, $item);
  751 + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
  752 + $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
  753 + }
  754 + }
  755 +
  756 + $alternatives = array_filter($alternatives, function ($lev) use ($threshold) {
  757 + return $lev < 2 * $threshold;
  758 + });
  759 + asort($alternatives);
  760 +
  761 + return array_keys($alternatives);
  762 + }
  763 +
  764 + /**
  765 + * 设置默认的指令
  766 + * @access public
  767 + * @param string $commandName The Command name
  768 + */
  769 + public function setDefaultCommand($commandName)
  770 + {
  771 + $this->defaultCommand = $commandName;
  772 + }
  773 +
  774 + /**
  775 + * 返回所有的命名空间
  776 + * @access private
  777 + * @param string $name
  778 + * @return array
  779 + */
  780 + private function extractAllNamespaces($name)
  781 + {
  782 + $parts = explode(':', $name, -1);
  783 + $namespaces = [];
  784 +
  785 + foreach ($parts as $part) {
  786 + if (count($namespaces)) {
  787 + $namespaces[] = end($namespaces) . ':' . $part;
  788 + } else {
  789 + $namespaces[] = $part;
  790 + }
  791 + }
  792 +
  793 + return $namespaces;
  794 + }
  795 +
  796 + public function __debugInfo()
  797 + {
  798 + $data = get_object_vars($this);
  799 + unset($data['commands'], $data['definition']);
  800 +
  801 + return $data;
  802 + }
  803 +}