作者 郭文星

123

要显示太多修改。

为保证性能只显示 23 of 23+ 个文件。

1 /nbproject/ 1 /nbproject/
2 -/thinkphp/  
3 -/vendor/  
4 /runtime/* 2 /runtime/*
5 /addons/* 3 /addons/*
6 /public/assets/libs/ 4 /public/assets/libs/
  1 +/composer.lock
  2 +/vendor
  3 +.idea
  4 +.DS_Store
  1 +deny from all
  1 +sudo: false
  2 +
  3 +language: php
  4 +
  5 +services:
  6 + - memcached
  7 + - mongodb
  8 + - mysql
  9 + - postgresql
  10 + - redis-server
  11 +
  12 +matrix:
  13 + fast_finish: true
  14 + include:
  15 + - php: 5.4
  16 + - php: 5.5
  17 + - php: 5.6
  18 + - php: 7.0
  19 + - php: hhvm
  20 + allow_failures:
  21 + - php: hhvm
  22 +
  23 +cache:
  24 + directories:
  25 + - $HOME/.composer/cache
  26 +
  27 +before_install:
  28 + - composer self-update
  29 + - mysql -e "create database IF NOT EXISTS test;" -uroot
  30 + - psql -c 'DROP DATABASE IF EXISTS test;' -U postgres
  31 + - psql -c 'create database test;' -U postgres
  32 +
  33 +install:
  34 + - ./tests/script/install.sh
  35 +
  36 +script:
  37 + ## LINT
  38 + - find . -path ./vendor -prune -o -type f -name \*.php -exec php -l {} \;
  39 + ## PHP Copy/Paste Detector
  40 + - vendor/bin/phpcpd --verbose --exclude vendor ./ || true
  41 + ## PHPLOC
  42 + - vendor/bin/phploc --exclude vendor ./
  43 + ## PHPUNIT
  44 + - vendor/bin/phpunit --coverage-clover=coverage.xml --configuration=phpunit.xml
  45 +
  46 +after_success:
  47 + - bash <(curl -s https://codecov.io/bash)
  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,不需要重新提交 `pull request`,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-2017 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 +# ThinkPHP 5.0(FastAdmin 团队长期维护)
  2 +
  3 +## 维护原因
  4 +
  5 +FastAdmin 致力于服务开发者,为开发者节省时间,为了 FastAdmin 开源项目持续发展下去。
  6 +
  7 +## 参与开源
  8 +
  9 +[贡献代码](https://doc.fastadmin.net/doc/contributing.html)
  10 +
  11 +
  12 +## 使用方法
  13 +
  14 +- 本框架已经应用在 FastAdmin 后台框架中,在 FastAdmin 项目中使用 `composer update topthink/framework -vvv` 命令即可更新。
  15 +
  16 +- 非 FastAdmin 的 ThinkPHP5.0 项目使用此仓库,请修改 `composer.json` 添加以下配置,在执行`composer update topthink/framework -vvv` 命令,具体可以参考 FastAdmin 项目目录下的 `composer.json` 文件内容。
  17 + ```
  18 + "repositories": [
  19 + {
  20 + "type": "git",
  21 + "url": "https://gitee.com/fastadminnet/framework.git"
  22 + }
  23 + ]
  24 + ```
  25 +
  26 +## 环境要求
  27 +
  28 +php 7.1+
  29 +
  30 +
  31 +
  32 +## ThinkPHP 介绍
  33 +
  34 +ThinkPHP5 在保持快速开发和大道至简的核心理念不变的同时,优化核心,减少依赖,基于全新的架构思想和命名空间实现,是 ThinkPHP 突破原有框架思路的颠覆之作,其主要特性包括:
  35 +
  36 + + 基于命名空间和众多PHP新特性
  37 + + 核心功能组件化
  38 + + 强化路由功能
  39 + + 更灵活的控制器
  40 + + 重构的模型和数据库类
  41 + + 配置文件可分离
  42 + + 重写的自动验证和完成
  43 + + 简化扩展机制
  44 + + API支持完善
  45 + + 改进的Log类
  46 + + 命令行访问支持
  47 + + REST支持
  48 + + 引导文件支持
  49 + + 方便的自动生成定义
  50 + + 真正惰性加载
  51 + + 分布式环境支持
  52 + + 支持Composer
  53 + + 支持MongoDb
  54 +
  55 +详细开发文档参考 [ThinkPHP5完全开发手册](http://www.kancloud.cn/manual/thinkphp5) 以及[ThinkPHP5入门系列教程](http://www.kancloud.cn/special/thinkphp5_quickstart)
  56 +
  57 +## 版权信息
  58 +
  59 +ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
  60 +
  61 +本项目包含的第三方源码和二进制文件之版权信息另行标注。
  62 +
  63 +版权所有Copyright © 2006-2022 by ThinkPHP (http://thinkphp.cn)
  64 +
  65 +All rights reserved。
  66 +
  67 +ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
  68 +
  69 +更多细节参阅 [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 +
  12 +define('THINK_VERSION', '5.0.25');
  13 +define('THINK_START_TIME', microtime(true));
  14 +define('THINK_START_MEM', memory_get_usage());
  15 +define('EXT', '.php');
  16 +define('DS', DIRECTORY_SEPARATOR);
  17 +defined('THINK_PATH') or define('THINK_PATH', __DIR__ . DS);
  18 +define('LIB_PATH', THINK_PATH . 'library' . DS);
  19 +define('CORE_PATH', LIB_PATH . 'think' . DS);
  20 +define('TRAIT_PATH', LIB_PATH . 'traits' . DS);
  21 +defined('APP_PATH') or define('APP_PATH', dirname($_SERVER['SCRIPT_FILENAME']) . DS);
  22 +defined('ROOT_PATH') or define('ROOT_PATH', dirname(realpath(APP_PATH)) . DS);
  23 +defined('EXTEND_PATH') or define('EXTEND_PATH', ROOT_PATH . 'extend' . DS);
  24 +defined('VENDOR_PATH') or define('VENDOR_PATH', ROOT_PATH . 'vendor' . DS);
  25 +defined('RUNTIME_PATH') or define('RUNTIME_PATH', ROOT_PATH . 'runtime' . DS);
  26 +defined('LOG_PATH') or define('LOG_PATH', RUNTIME_PATH . 'log' . DS);
  27 +defined('CACHE_PATH') or define('CACHE_PATH', RUNTIME_PATH . 'cache' . DS);
  28 +defined('TEMP_PATH') or define('TEMP_PATH', RUNTIME_PATH . 'temp' . DS);
  29 +defined('CONF_PATH') or define('CONF_PATH', APP_PATH); // 配置文件目录
  30 +defined('CONF_EXT') or define('CONF_EXT', EXT); // 配置文件后缀
  31 +defined('ENV_PREFIX') or define('ENV_PREFIX', 'PHP_'); // 环境变量的配置前缀
  32 +
  33 +// 环境常量
  34 +define('IS_CLI', PHP_SAPI == 'cli' ? true : false);
  35 +define('IS_WIN', strpos(PHP_OS, 'WIN') !== false);
  36 +
  37 +// 载入Loader类
  38 +require CORE_PATH . 'Loader.php';
  39 +
  40 +// 加载环境变量配置文件
  41 +if (is_file(ROOT_PATH . '.env')) {
  42 + $env = parse_ini_file(ROOT_PATH . '.env', true);
  43 +
  44 + foreach ($env as $key => $val) {
  45 + $name = ENV_PREFIX . strtoupper($key);
  46 +
  47 + if (is_array($val)) {
  48 + foreach ($val as $k => $v) {
  49 + $item = $name . '_' . strtoupper($k);
  50 + putenv("$item=$v");
  51 + }
  52 + } else {
  53 + putenv("$name=$val");
  54 + }
  55 + }
  56 +}
  57 +
  58 +// 注册自动加载
  59 +\think\Loader::register();
  60 +
  61 +// 注册错误和异常处理机制
  62 +\think\Error::register();
  63 +
  64 +// 加载惯例配置文件
  65 +\think\Config::set(include THINK_PATH . 'convention' . EXT);
  1 +comment:
  2 + layout: header, changes, diff
  3 +coverage:
  4 + ignore:
  5 + - base.php
  6 + - helper.php
  7 + - convention.php
  8 + - lang/zh-cn.php
  9 + - start.php
  10 + - console.php
  11 + status:
  12 + patch: false
  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 + "require": {
  19 + "php": ">=7.1.0",
  20 + "topthink/think-installer": "~1.0"
  21 + },
  22 + "require-dev": {
  23 + "phpunit/phpunit": "4.8.*",
  24 + "johnkary/phpunit-speedtrap": "^1.0",
  25 + "mikey179/vfsstream": "~1.6",
  26 + "phploc/phploc": "2.*",
  27 + "sebastian/phpcpd": "2.*",
  28 + "phpdocumentor/reflection-docblock": "^2.0"
  29 + },
  30 + "autoload": {
  31 + "psr-4": {
  32 + "think\\": "library/think"
  33 + }
  34 + }
  35 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2017 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: yunwuxin <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think;
  13 +
  14 +// ThinkPHP 引导文件
  15 +// 加载基础文件
  16 +require __DIR__ . '/base.php';
  17 +
  18 +// 执行应用
  19 +App::initCommon();
  20 +Console::init();
  1 +<?php
  2 +
  3 +return [
  4 + // +----------------------------------------------------------------------
  5 + // | 应用设置
  6 + // +----------------------------------------------------------------------
  7 + // 默认Host地址
  8 + 'app_host' => '',
  9 + // 应用调试模式
  10 + 'app_debug' => false,
  11 + // 应用Trace
  12 + 'app_trace' => false,
  13 + // 应用模式状态
  14 + 'app_status' => '',
  15 + // 是否支持多模块
  16 + 'app_multi_module' => true,
  17 + // 入口自动绑定模块
  18 + 'auto_bind_module' => false,
  19 + // 注册的根命名空间
  20 + 'root_namespace' => [],
  21 + // 扩展函数文件
  22 + 'extra_file_list' => [THINK_PATH . 'helper' . EXT],
  23 + // 默认输出类型
  24 + 'default_return_type' => 'html',
  25 + // 默认AJAX 数据返回格式,可选json xml ...
  26 + 'default_ajax_return' => 'json',
  27 + // 默认JSONP格式返回的处理方法
  28 + 'default_jsonp_handler' => 'jsonpReturn',
  29 + // 默认JSONP处理方法
  30 + 'var_jsonp_handler' => 'callback',
  31 + // 默认时区
  32 + 'default_timezone' => 'PRC',
  33 + // 是否开启多语言
  34 + 'lang_switch_on' => false,
  35 + // 默认全局过滤方法 用逗号分隔多个
  36 + 'default_filter' => '',
  37 + // 默认语言
  38 + 'default_lang' => 'zh-cn',
  39 + // 应用类库后缀
  40 + 'class_suffix' => false,
  41 + // 控制器类后缀
  42 + 'controller_suffix' => false,
  43 +
  44 + // +----------------------------------------------------------------------
  45 + // | 模块设置
  46 + // +----------------------------------------------------------------------
  47 +
  48 + // 默认模块名
  49 + 'default_module' => 'index',
  50 + // 禁止访问模块
  51 + 'deny_module_list' => ['common'],
  52 + // 默认控制器名
  53 + 'default_controller' => 'Index',
  54 + // 默认操作名
  55 + 'default_action' => 'index',
  56 + // 默认验证器
  57 + 'default_validate' => '',
  58 + // 默认的空控制器名
  59 + 'empty_controller' => 'Error',
  60 + // 操作方法前缀
  61 + 'use_action_prefix' => false,
  62 + // 操作方法后缀
  63 + 'action_suffix' => '',
  64 + // 自动搜索控制器
  65 + 'controller_auto_search' => false,
  66 +
  67 + // +----------------------------------------------------------------------
  68 + // | URL设置
  69 + // +----------------------------------------------------------------------
  70 +
  71 + // PATHINFO变量名 用于兼容模式
  72 + 'var_pathinfo' => 's',
  73 + // 兼容PATH_INFO获取
  74 + 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
  75 + // pathinfo分隔符
  76 + 'pathinfo_depr' => '/',
  77 + // HTTPS代理标识
  78 + 'https_agent_name' => '',
  79 + // URL伪静态后缀
  80 + 'url_html_suffix' => 'html',
  81 + // URL普通方式参数 用于自动生成
  82 + 'url_common_param' => false,
  83 + // URL参数方式 0 按名称成对解析 1 按顺序解析
  84 + 'url_param_type' => 0,
  85 + // 是否开启路由
  86 + 'url_route_on' => true,
  87 + // 路由配置文件(支持配置多个)
  88 + 'route_config_file' => ['route'],
  89 + // 路由使用完整匹配
  90 + 'route_complete_match' => false,
  91 + // 是否强制使用路由
  92 + 'url_route_must' => false,
  93 + // 域名部署
  94 + 'url_domain_deploy' => false,
  95 + // 域名根,如thinkphp.cn
  96 + 'url_domain_root' => '',
  97 + // 是否自动转换URL中的控制器和操作名
  98 + 'url_convert' => true,
  99 + // 默认的访问控制器层
  100 + 'url_controller_layer' => 'controller',
  101 + // 表单请求类型伪装变量
  102 + 'var_method' => '_method',
  103 + // 表单ajax伪装变量
  104 + 'var_ajax' => '_ajax',
  105 + // 表单pjax伪装变量
  106 + 'var_pjax' => '_pjax',
  107 + // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
  108 + 'request_cache' => false,
  109 + // 请求缓存有效期
  110 + 'request_cache_expire' => null,
  111 + // 全局请求缓存排除规则
  112 + 'request_cache_except' => [],
  113 +
  114 + // +----------------------------------------------------------------------
  115 + // | 模板设置
  116 + // +----------------------------------------------------------------------
  117 +
  118 + 'template' => [
  119 + // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写
  120 + 'auto_rule' => 1,
  121 + // 模板引擎类型 支持 php think 支持扩展
  122 + 'type' => 'Think',
  123 + // 视图基础目录,配置目录为所有模块的视图起始目录
  124 + 'view_base' => '',
  125 + // 当前模板的视图目录 留空为自动获取
  126 + 'view_path' => '',
  127 + // 模板后缀
  128 + 'view_suffix' => 'html',
  129 + // 模板文件名分隔符
  130 + 'view_depr' => DS,
  131 + // 模板引擎普通标签开始标记
  132 + 'tpl_begin' => '{',
  133 + // 模板引擎普通标签结束标记
  134 + 'tpl_end' => '}',
  135 + // 标签库标签开始标记
  136 + 'taglib_begin' => '{',
  137 + // 标签库标签结束标记
  138 + 'taglib_end' => '}',
  139 + ],
  140 +
  141 + // 视图输出字符串内容替换
  142 + 'view_replace_str' => [],
  143 + // 默认跳转页面对应的模板文件
  144 + 'dispatch_success_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl',
  145 + 'dispatch_error_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl',
  146 +
  147 + // +----------------------------------------------------------------------
  148 + // | 异常及错误设置
  149 + // +----------------------------------------------------------------------
  150 +
  151 + // 异常页面的模板文件
  152 + 'exception_tmpl' => THINK_PATH . 'tpl' . DS . 'think_exception.tpl',
  153 +
  154 + // 错误显示信息,非调试模式有效
  155 + 'error_message' => '页面错误!请稍后再试~',
  156 + // 显示错误信息
  157 + 'show_error_msg' => false,
  158 + // 异常处理handle类 留空使用 \think\exception\Handle
  159 + 'exception_handle' => '',
  160 + // 是否记录trace信息到日志
  161 + 'record_trace' => false,
  162 +
  163 + // +----------------------------------------------------------------------
  164 + // | 日志设置
  165 + // +----------------------------------------------------------------------
  166 +
  167 + 'log' => [
  168 + // 日志记录方式,内置 file socket 支持扩展
  169 + 'type' => 'File',
  170 + // 日志保存目录
  171 + 'path' => LOG_PATH,
  172 + // 日志记录级别
  173 + 'level' => [],
  174 + ],
  175 +
  176 + // +----------------------------------------------------------------------
  177 + // | Trace设置 开启 app_trace 后 有效
  178 + // +----------------------------------------------------------------------
  179 + 'trace' => [
  180 + // 内置Html Console 支持扩展
  181 + 'type' => 'Html',
  182 + ],
  183 +
  184 + // +----------------------------------------------------------------------
  185 + // | 缓存设置
  186 + // +----------------------------------------------------------------------
  187 +
  188 + 'cache' => [
  189 + // 驱动方式
  190 + 'type' => 'File',
  191 + // 缓存保存目录
  192 + 'path' => CACHE_PATH,
  193 + // 缓存前缀
  194 + 'prefix' => '',
  195 + // 缓存有效期 0表示永久缓存
  196 + 'expire' => 0,
  197 + ],
  198 +
  199 + // +----------------------------------------------------------------------
  200 + // | 会话设置
  201 + // +----------------------------------------------------------------------
  202 +
  203 + 'session' => [
  204 + 'id' => '',
  205 + // SESSION_ID的提交变量,解决flash上传跨域
  206 + 'var_session_id' => '',
  207 + // SESSION 前缀
  208 + 'prefix' => 'think',
  209 + // 驱动方式 支持redis memcache memcached
  210 + 'type' => '',
  211 + // 是否自动开启 SESSION
  212 + 'auto_start' => true,
  213 + 'httponly' => true,
  214 + 'secure' => false,
  215 + ],
  216 +
  217 + // +----------------------------------------------------------------------
  218 + // | Cookie设置
  219 + // +----------------------------------------------------------------------
  220 + 'cookie' => [
  221 + // cookie 名称前缀
  222 + 'prefix' => '',
  223 + // cookie 保存时间
  224 + 'expire' => 0,
  225 + // cookie 保存路径
  226 + 'path' => '/',
  227 + // cookie 有效域名
  228 + 'domain' => '',
  229 + // cookie 启用安全传输
  230 + 'secure' => false,
  231 + // httponly设置
  232 + 'httponly' => '',
  233 + // 是否使用 setcookie
  234 + 'setcookie' => true,
  235 + ],
  236 +
  237 + // +----------------------------------------------------------------------
  238 + // | 数据库设置
  239 + // +----------------------------------------------------------------------
  240 +
  241 + 'database' => [
  242 + // 数据库类型
  243 + 'type' => 'mysql',
  244 + // 数据库连接DSN配置
  245 + 'dsn' => '',
  246 + // 服务器地址
  247 + 'hostname' => '127.0.0.1',
  248 + // 数据库名
  249 + 'database' => '',
  250 + // 数据库用户名
  251 + 'username' => 'root',
  252 + // 数据库密码
  253 + 'password' => '',
  254 + // 数据库连接端口
  255 + 'hostport' => '',
  256 + // 数据库连接参数
  257 + 'params' => [],
  258 + // 数据库编码默认采用utf8
  259 + 'charset' => 'utf8',
  260 + // 数据库表前缀
  261 + 'prefix' => '',
  262 + // 数据库调试模式
  263 + 'debug' => false,
  264 + // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
  265 + 'deploy' => 0,
  266 + // 数据库读写是否分离 主从式有效
  267 + 'rw_separate' => false,
  268 + // 读写分离后 主服务器数量
  269 + 'master_num' => 1,
  270 + // 指定从服务器序号
  271 + 'slave_no' => '',
  272 + // 是否严格检查字段是否存在
  273 + 'fields_strict' => true,
  274 + // 数据集返回类型
  275 + 'resultset_type' => 'array',
  276 + // 自动写入时间戳字段
  277 + 'auto_timestamp' => false,
  278 + // 时间字段取出后的默认时间格式
  279 + 'datetime_format' => 'Y-m-d H:i:s',
  280 + // 是否需要进行SQL性能分析
  281 + 'sql_explain' => false,
  282 + ],
  283 +
  284 + //分页配置
  285 + 'paginate' => [
  286 + 'type' => 'bootstrap',
  287 + 'var_page' => 'page',
  288 + 'list_rows' => 15,
  289 + ],
  290 +
  291 + //控制台配置
  292 + 'console' => [
  293 + 'name' => 'Think Console',
  294 + 'version' => '0.1',
  295 + 'user' => null,
  296 + ],
  297 +
  298 +];
  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\Cache;
  17 +use think\Config;
  18 +use think\Cookie;
  19 +use think\Db;
  20 +use think\Debug;
  21 +use think\exception\HttpException;
  22 +use think\exception\HttpResponseException;
  23 +use think\Lang;
  24 +use think\Loader;
  25 +use think\Log;
  26 +use think\Model;
  27 +use think\Request;
  28 +use think\Response;
  29 +use think\Session;
  30 +use think\Url;
  31 +use think\View;
  32 +
  33 +if (!function_exists('load_trait')) {
  34 + /**
  35 + * 快速导入Traits PHP5.5以上无需调用
  36 + * @param string $class trait库
  37 + * @param string $ext 类库后缀
  38 + * @return boolean
  39 + */
  40 + function load_trait($class, $ext = EXT)
  41 + {
  42 + return Loader::import($class, TRAIT_PATH, $ext);
  43 + }
  44 +}
  45 +
  46 +if (!function_exists('exception')) {
  47 + /**
  48 + * 抛出异常处理
  49 + *
  50 + * @param string $msg 异常消息
  51 + * @param integer $code 异常代码 默认为0
  52 + * @param string $exception 异常类
  53 + *
  54 + * @throws Exception
  55 + */
  56 + function exception($msg, $code = 0, $exception = '')
  57 + {
  58 + $e = $exception ?: '\think\Exception';
  59 + throw new $e($msg, $code);
  60 + }
  61 +}
  62 +
  63 +if (!function_exists('debug')) {
  64 + /**
  65 + * 记录时间(微秒)和内存使用情况
  66 + * @param string $start 开始标签
  67 + * @param string $end 结束标签
  68 + * @param integer|string $dec 小数位 如果是m 表示统计内存占用
  69 + * @return mixed
  70 + */
  71 + function debug($start, $end = '', $dec = 6)
  72 + {
  73 + if ('' == $end) {
  74 + Debug::remark($start);
  75 + } else {
  76 + return 'm' == $dec ? Debug::getRangeMem($start, $end) : Debug::getRangeTime($start, $end, $dec);
  77 + }
  78 + }
  79 +}
  80 +
  81 +if (!function_exists('lang')) {
  82 + /**
  83 + * 获取语言变量值
  84 + * @param string $name 语言变量名
  85 + * @param array $vars 动态变量值
  86 + * @param string $lang 语言
  87 + * @return mixed
  88 + */
  89 + function lang($name, $vars = [], $lang = '')
  90 + {
  91 + return Lang::get($name, $vars, $lang);
  92 + }
  93 +}
  94 +
  95 +if (!function_exists('config')) {
  96 + /**
  97 + * 获取和设置配置参数
  98 + * @param string|array $name 参数名
  99 + * @param mixed $value 参数值
  100 + * @param string $range 作用域
  101 + * @return mixed
  102 + */
  103 + function config($name = '', $value = null, $range = '')
  104 + {
  105 + if (is_null($value) && is_string($name)) {
  106 + return 0 === strpos($name, '?') ? Config::has(substr($name, 1), $range) : Config::get($name, $range);
  107 + } else {
  108 + return Config::set($name, $value, $range);
  109 + }
  110 + }
  111 +}
  112 +
  113 +if (!function_exists('input')) {
  114 + /**
  115 + * 获取输入数据 支持默认值和过滤
  116 + * @param string $key 获取的变量名
  117 + * @param mixed $default 默认值
  118 + * @param string $filter 过滤方法
  119 + * @return mixed
  120 + */
  121 + function input($key = '', $default = null, $filter = '')
  122 + {
  123 + if (0 === strpos($key, '?')) {
  124 + $key = substr($key, 1);
  125 + $has = true;
  126 + }
  127 + if ($pos = strpos($key, '.')) {
  128 + // 指定参数来源
  129 + list($method, $key) = explode('.', $key, 2);
  130 + if (!in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'route', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) {
  131 + $key = $method . '.' . $key;
  132 + $method = 'param';
  133 + }
  134 + } else {
  135 + // 默认为自动判断
  136 + $method = 'param';
  137 + }
  138 + if (isset($has)) {
  139 + return request()->has($key, $method, $default);
  140 + } else {
  141 + return request()->$method($key, $default, $filter);
  142 + }
  143 + }
  144 +}
  145 +
  146 +if (!function_exists('widget')) {
  147 + /**
  148 + * 渲染输出Widget
  149 + * @param string $name Widget名称
  150 + * @param array $data 传入的参数
  151 + * @return mixed
  152 + */
  153 + function widget($name, $data = [])
  154 + {
  155 + return Loader::action($name, $data, 'widget');
  156 + }
  157 +}
  158 +
  159 +if (!function_exists('model')) {
  160 + /**
  161 + * 实例化Model
  162 + * @param string $name Model名称
  163 + * @param string $layer 业务层名称
  164 + * @param bool $appendSuffix 是否添加类名后缀
  165 + * @return \think\Model
  166 + */
  167 + function model($name = '', $layer = 'model', $appendSuffix = false)
  168 + {
  169 + return Loader::model($name, $layer, $appendSuffix);
  170 + }
  171 +}
  172 +
  173 +if (!function_exists('validate')) {
  174 + /**
  175 + * 实例化验证器
  176 + * @param string $name 验证器名称
  177 + * @param string $layer 业务层名称
  178 + * @param bool $appendSuffix 是否添加类名后缀
  179 + * @return \think\Validate
  180 + */
  181 + function validate($name = '', $layer = 'validate', $appendSuffix = false)
  182 + {
  183 + return Loader::validate($name, $layer, $appendSuffix);
  184 + }
  185 +}
  186 +
  187 +if (!function_exists('db')) {
  188 + /**
  189 + * 实例化数据库类
  190 + * @param string $name 操作的数据表名称(不含前缀)
  191 + * @param array|string $config 数据库配置参数
  192 + * @param bool $force 是否强制重新连接
  193 + * @return \think\db\Query
  194 + */
  195 + function db($name = '', $config = [], $force = false)
  196 + {
  197 + return Db::connect($config, $force)->name($name);
  198 + }
  199 +}
  200 +
  201 +if (!function_exists('controller')) {
  202 + /**
  203 + * 实例化控制器 格式:[模块/]控制器
  204 + * @param string $name 资源地址
  205 + * @param string $layer 控制层名称
  206 + * @param bool $appendSuffix 是否添加类名后缀
  207 + * @return \think\Controller
  208 + */
  209 + function controller($name, $layer = 'controller', $appendSuffix = false)
  210 + {
  211 + return Loader::controller($name, $layer, $appendSuffix);
  212 + }
  213 +}
  214 +
  215 +if (!function_exists('action')) {
  216 + /**
  217 + * 调用模块的操作方法 参数格式 [模块/控制器/]操作
  218 + * @param string $url 调用地址
  219 + * @param string|array $vars 调用参数 支持字符串和数组
  220 + * @param string $layer 要调用的控制层名称
  221 + * @param bool $appendSuffix 是否添加类名后缀
  222 + * @return mixed
  223 + */
  224 + function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
  225 + {
  226 + return Loader::action($url, $vars, $layer, $appendSuffix);
  227 + }
  228 +}
  229 +
  230 +if (!function_exists('import')) {
  231 + /**
  232 + * 导入所需的类库 同java的Import 本函数有缓存功能
  233 + * @param string $class 类库命名空间字符串
  234 + * @param string $baseUrl 起始路径
  235 + * @param string $ext 导入的文件扩展名
  236 + * @return boolean
  237 + */
  238 + function import($class, $baseUrl = '', $ext = EXT)
  239 + {
  240 + return Loader::import($class, $baseUrl, $ext);
  241 + }
  242 +}
  243 +
  244 +if (!function_exists('vendor')) {
  245 + /**
  246 + * 快速导入第三方框架类库 所有第三方框架的类库文件统一放到 系统的Vendor目录下面
  247 + * @param string $class 类库
  248 + * @param string $ext 类库后缀
  249 + * @return boolean
  250 + */
  251 + function vendor($class, $ext = EXT)
  252 + {
  253 + return Loader::import($class, VENDOR_PATH, $ext);
  254 + }
  255 +}
  256 +
  257 +if (!function_exists('dump')) {
  258 + /**
  259 + * 浏览器友好的变量输出
  260 + * @param mixed $var 变量
  261 + * @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串
  262 + * @param string $label 标签 默认为空
  263 + * @return void|string
  264 + */
  265 + function dump($var, $echo = true, $label = null)
  266 + {
  267 + return Debug::dump($var, $echo, $label);
  268 + }
  269 +}
  270 +
  271 +if (!function_exists('url')) {
  272 + /**
  273 + * Url生成
  274 + * @param string $url 路由地址
  275 + * @param string|array $vars 变量
  276 + * @param bool|string $suffix 生成的URL后缀
  277 + * @param bool|string $domain 域名
  278 + * @return string
  279 + */
  280 + function url($url = '', $vars = '', $suffix = true, $domain = false)
  281 + {
  282 + return Url::build($url, $vars, $suffix, $domain);
  283 + }
  284 +}
  285 +
  286 +if (!function_exists('session')) {
  287 + /**
  288 + * Session管理
  289 + * @param string|array $name session名称,如果为数组表示进行session设置
  290 + * @param mixed $value session值
  291 + * @param string $prefix 前缀
  292 + * @return mixed
  293 + */
  294 + function session($name, $value = '', $prefix = null)
  295 + {
  296 + if (is_array($name)) {
  297 + // 初始化
  298 + Session::init($name);
  299 + } elseif (is_null($name)) {
  300 + // 清除
  301 + Session::clear('' === $value ? null : $value);
  302 + } elseif ('' === $value) {
  303 + // 判断或获取
  304 + return 0 === strpos($name, '?') ? Session::has(substr($name, 1), $prefix) : Session::get($name, $prefix);
  305 + } elseif (is_null($value)) {
  306 + // 删除
  307 + return Session::delete($name, $prefix);
  308 + } else {
  309 + // 设置
  310 + return Session::set($name, $value, $prefix);
  311 + }
  312 + }
  313 +}
  314 +
  315 +if (!function_exists('cookie')) {
  316 + /**
  317 + * Cookie管理
  318 + * @param string|array $name cookie名称,如果为数组表示进行cookie设置
  319 + * @param mixed $value cookie值
  320 + * @param mixed $option 参数
  321 + * @return mixed
  322 + */
  323 + function cookie($name, $value = '', $option = null)
  324 + {
  325 + if (is_array($name)) {
  326 + // 初始化
  327 + Cookie::init($name);
  328 + } elseif (is_null($name)) {
  329 + // 清除
  330 + Cookie::clear($value);
  331 + } elseif ('' === $value) {
  332 + // 获取
  333 + return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1), $option) : Cookie::get($name, $option);
  334 + } elseif (is_null($value)) {
  335 + // 删除
  336 + return Cookie::delete($name);
  337 + } else {
  338 + // 设置
  339 + return Cookie::set($name, $value, $option);
  340 + }
  341 + }
  342 +}
  343 +
  344 +if (!function_exists('cache')) {
  345 + /**
  346 + * 缓存管理
  347 + * @param mixed $name 缓存名称,如果为数组表示进行缓存设置
  348 + * @param mixed $value 缓存值
  349 + * @param mixed $options 缓存参数
  350 + * @param string $tag 缓存标签
  351 + * @return mixed
  352 + */
  353 + function cache($name, $value = '', $options = null, $tag = null)
  354 + {
  355 + if (is_array($options)) {
  356 + // 缓存操作的同时初始化
  357 + $cache = Cache::connect($options);
  358 + } elseif (is_array($name)) {
  359 + // 缓存初始化
  360 + return Cache::connect($name);
  361 + } else {
  362 + $cache = Cache::init();
  363 + }
  364 +
  365 + if (is_null($name)) {
  366 + return $cache->clear($value);
  367 + } elseif ('' === $value) {
  368 + // 获取缓存
  369 + return 0 === strpos($name, '?') ? $cache->has(substr($name, 1)) : $cache->get($name);
  370 + } elseif (is_null($value)) {
  371 + // 删除缓存
  372 + return $cache->rm($name);
  373 + } elseif (0 === strpos($name, '?') && '' !== $value) {
  374 + $expire = is_numeric($options) ? $options : null;
  375 + return $cache->remember(substr($name, 1), $value, $expire);
  376 + } else {
  377 + // 缓存数据
  378 + if (is_array($options)) {
  379 + $expire = isset($options['expire']) ? $options['expire'] : null; //修复查询缓存无法设置过期时间
  380 + } else {
  381 + $expire = is_numeric($options) ? $options : null; //默认快捷缓存设置过期时间
  382 + }
  383 + if (is_null($tag)) {
  384 + return $cache->set($name, $value, $expire);
  385 + } else {
  386 + return $cache->tag($tag)->set($name, $value, $expire);
  387 + }
  388 + }
  389 + }
  390 +}
  391 +
  392 +if (!function_exists('trace')) {
  393 + /**
  394 + * 记录日志信息
  395 + * @param mixed $log log信息 支持字符串和数组
  396 + * @param string $level 日志级别
  397 + * @return void|array
  398 + */
  399 + function trace($log = '[think]', $level = 'log')
  400 + {
  401 + if ('[think]' === $log) {
  402 + return Log::getLog();
  403 + } else {
  404 + Log::record($log, $level);
  405 + }
  406 + }
  407 +}
  408 +
  409 +if (!function_exists('request')) {
  410 + /**
  411 + * 获取当前Request对象实例
  412 + * @return Request
  413 + */
  414 + function request()
  415 + {
  416 + return Request::instance();
  417 + }
  418 +}
  419 +
  420 +if (!function_exists('response')) {
  421 + /**
  422 + * 创建普通 Response 对象实例
  423 + * @param mixed $data 输出数据
  424 + * @param int|string $code 状态码
  425 + * @param array $header 头信息
  426 + * @param string $type
  427 + * @return Response
  428 + */
  429 + function response($data = [], $code = 200, $header = [], $type = 'html')
  430 + {
  431 + return Response::create($data, $type, $code, $header);
  432 + }
  433 +}
  434 +
  435 +if (!function_exists('view')) {
  436 + /**
  437 + * 渲染模板输出
  438 + * @param string $template 模板文件
  439 + * @param array $vars 模板变量
  440 + * @param array $replace 模板替换
  441 + * @param integer $code 状态码
  442 + * @return \think\response\View
  443 + */
  444 + function view($template = '', $vars = [], $replace = [], $code = 200)
  445 + {
  446 + return Response::create($template, 'view', $code)->replace($replace)->assign($vars);
  447 + }
  448 +}
  449 +
  450 +if (!function_exists('json')) {
  451 + /**
  452 + * 获取\think\response\Json对象实例
  453 + * @param mixed $data 返回的数据
  454 + * @param integer $code 状态码
  455 + * @param array $header 头部
  456 + * @param array $options 参数
  457 + * @return \think\response\Json
  458 + */
  459 + function json($data = [], $code = 200, $header = [], $options = [])
  460 + {
  461 + return Response::create($data, 'json', $code, $header, $options);
  462 + }
  463 +}
  464 +
  465 +if (!function_exists('jsonp')) {
  466 + /**
  467 + * 获取\think\response\Jsonp对象实例
  468 + * @param mixed $data 返回的数据
  469 + * @param integer $code 状态码
  470 + * @param array $header 头部
  471 + * @param array $options 参数
  472 + * @return \think\response\Jsonp
  473 + */
  474 + function jsonp($data = [], $code = 200, $header = [], $options = [])
  475 + {
  476 + return Response::create($data, 'jsonp', $code, $header, $options);
  477 + }
  478 +}
  479 +
  480 +if (!function_exists('xml')) {
  481 + /**
  482 + * 获取\think\response\Xml对象实例
  483 + * @param mixed $data 返回的数据
  484 + * @param integer $code 状态码
  485 + * @param array $header 头部
  486 + * @param array $options 参数
  487 + * @return \think\response\Xml
  488 + */
  489 + function xml($data = [], $code = 200, $header = [], $options = [])
  490 + {
  491 + return Response::create($data, 'xml', $code, $header, $options);
  492 + }
  493 +}
  494 +
  495 +if (!function_exists('redirect')) {
  496 + /**
  497 + * 获取\think\response\Redirect对象实例
  498 + * @param mixed $url 重定向地址 支持Url::build方法的地址
  499 + * @param array|integer $params 额外参数
  500 + * @param integer $code 状态码
  501 + * @param array $with 隐式传参
  502 + * @return \think\response\Redirect
  503 + */
  504 + function redirect($url = [], $params = [], $code = 302, $with = [])
  505 + {
  506 + if (is_integer($params)) {
  507 + $code = $params;
  508 + $params = [];
  509 + }
  510 + return Response::create($url, 'redirect', $code)->params($params)->with($with);
  511 + }
  512 +}
  513 +
  514 +if (!function_exists('abort')) {
  515 + /**
  516 + * 抛出HTTP异常
  517 + * @param integer|Response $code 状态码 或者 Response对象实例
  518 + * @param string $message 错误信息
  519 + * @param array $header 参数
  520 + */
  521 + function abort($code, $message = null, $header = [])
  522 + {
  523 + if ($code instanceof Response) {
  524 + throw new HttpResponseException($code);
  525 + } else {
  526 + throw new HttpException($code, $message, null, $header);
  527 + }
  528 + }
  529 +}
  530 +
  531 +if (!function_exists('halt')) {
  532 + /**
  533 + * 调试变量并且中断输出
  534 + * @param mixed $var 调试变量或者信息
  535 + */
  536 + function halt($var)
  537 + {
  538 + dump($var);
  539 + throw new HttpResponseException(new Response);
  540 + }
  541 +}
  542 +
  543 +if (!function_exists('token')) {
  544 + /**
  545 + * 生成表单令牌
  546 + * @param string $name 令牌名称
  547 + * @param mixed $type 令牌生成方法
  548 + * @return string
  549 + */
  550 + function token($name = '__token__', $type = 'md5')
  551 + {
  552 + $token = Request::instance()->token($name, $type);
  553 + return '<input type="hidden" name="' . $name . '" value="' . $token . '" />';
  554 + }
  555 +}
  556 +
  557 +if (!function_exists('load_relation')) {
  558 + /**
  559 + * 延迟预载入关联查询
  560 + * @param mixed $resultSet 数据集
  561 + * @param mixed $relation 关联
  562 + * @return array
  563 + */
  564 + function load_relation($resultSet, $relation)
  565 + {
  566 + $item = current($resultSet);
  567 + if ($item instanceof Model) {
  568 + $item->eagerlyResultSet($resultSet, $relation);
  569 + }
  570 + return $resultSet;
  571 + }
  572 +}
  573 +
  574 +if (!function_exists('collection')) {
  575 + /**
  576 + * 数组转换为数据集对象
  577 + * @param array $resultSet 数据集数组
  578 + * @return \think\model\Collection|\think\Collection
  579 + */
  580 + function collection($resultSet)
  581 + {
  582 + $item = current($resultSet);
  583 + if ($item instanceof Model) {
  584 + return \think\model\Collection::make($resultSet);
  585 + } else {
  586 + return \think\Collection::make($resultSet);
  587 + }
  588 + }
  589 +}
  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 + 'module not exists' => '模块不存在',
  28 + 'controller not exists' => '控制器不存在',
  29 + 'class not exists' => '类不存在',
  30 + 'property not exists' => '类的属性不存在',
  31 + 'template not exists' => '模板文件不存在',
  32 + 'illegal controller name' => '非法的控制器名称',
  33 + 'illegal action name' => '非法的操作名称',
  34 + 'url suffix deny' => '禁止的URL后缀访问',
  35 + 'Route Not Found' => '当前访问路由未定义',
  36 + 'Undefined db type' => '未定义数据库类型',
  37 + 'variable type error' => '变量类型错误',
  38 + 'PSR-4 error' => 'PSR-4 规范错误',
  39 + 'not support total' => '简洁模式下不能获取数据总数',
  40 + 'not support last' => '简洁模式下不能获取最后一页',
  41 + 'error session handler' => '错误的SESSION处理器类',
  42 + 'not allow php tag' => '模板不允许使用PHP语法',
  43 + 'not support' => '不支持',
  44 + 'redisd master' => 'Redisd 主服务器错误',
  45 + 'redisd slave' => 'Redisd 从服务器错误',
  46 + 'must run at sae' => '必须在SAE运行',
  47 + 'memcache init error' => '未开通Memcache服务,请在SAE管理平台初始化Memcache服务',
  48 + 'KVDB init error' => '没有初始化KVDB,请在SAE管理平台初始化KVDB服务',
  49 + 'fields not exists' => '数据表字段不存在',
  50 + 'where express error' => '查询表达式错误',
  51 + 'not support data' => '不支持的数据表达式',
  52 + 'no data to update' => '没有任何数据需要更新',
  53 + 'miss data to insert' => '缺少需要写入的数据',
  54 + 'miss complex primary data' => '缺少复合主键数据',
  55 + 'miss update condition' => '缺少更新条件',
  56 + 'model data Not Found' => '模型数据不存在',
  57 + 'table data not Found' => '表数据不存在',
  58 + 'delete without condition' => '没有条件不会执行删除操作',
  59 + 'miss relation data' => '缺少关联表数据',
  60 + 'tag attr must' => '模板标签属性必须',
  61 + 'tag error' => '模板标签错误',
  62 + 'cache write error' => '缓存写入失败',
  63 + 'sae mc write error' => 'SAE mc 写入错误',
  64 + 'route name not exists' => '路由标识不存在(或参数不够)',
  65 + 'invalid request' => '非法请求',
  66 + 'bind attr has exists' => '模型的属性已经存在',
  67 + 'relation data not exists' => '关联数据不存在',
  68 + 'relation not support' => '关联不支持',
  69 + 'chunk not support order' => 'Chunk不支持调用order方法',
  70 + 'closure not support cache(true)' => '使用闭包查询不支持cache(true),请指定缓存Key',
  71 +
  72 + // 上传错误信息
  73 + 'unknown upload error' => '未知上传错误!',
  74 + 'file write error' => '文件写入失败!',
  75 + 'upload temp dir not found' => '找不到临时文件夹!',
  76 + 'no file to uploaded' => '没有文件被上传!',
  77 + 'only the portion of file is uploaded' => '文件只有部分被上传!',
  78 + 'upload File size exceeds the maximum value' => '上传文件大小超过了最大值!',
  79 + 'upload write error' => '文件上传保存错误!',
  80 + 'has the same filename: {:filename}' => '存在同名文件:{:filename}',
  81 + 'upload illegal files' => '非法上传文件',
  82 + 'illegal image files' => '非法图片文件',
  83 + 'extensions to upload is not allowed' => '上传文件后缀不允许',
  84 + 'mimetype to upload is not allowed' => '上传文件MIME类型不允许!',
  85 + 'filesize not match' => '上传文件大小不符!',
  86 + 'directory {:path} creation failed' => '目录 {:path} 创建失败!',
  87 +
  88 + // Validate Error Message
  89 + ':attribute require' => ':attribute不能为空',
  90 + ':attribute must be numeric' => ':attribute必须是数字',
  91 + ':attribute must be integer' => ':attribute必须是整数',
  92 + ':attribute must be float' => ':attribute必须是浮点数',
  93 + ':attribute must be bool' => ':attribute必须是布尔值',
  94 + ':attribute not a valid email address' => ':attribute格式不符',
  95 + ':attribute not a valid mobile' => ':attribute格式不符',
  96 + ':attribute must be a array' => ':attribute必须是数组',
  97 + ':attribute must be yes,on or 1' => ':attribute必须是yes、on或者1',
  98 + ':attribute not a valid datetime' => ':attribute不是一个有效的日期或时间格式',
  99 + ':attribute not a valid file' => ':attribute不是有效的上传文件',
  100 + ':attribute not a valid image' => ':attribute不是有效的图像文件',
  101 + ':attribute must be alpha' => ':attribute只能是字母',
  102 + ':attribute must be alpha-numeric' => ':attribute只能是字母和数字',
  103 + ':attribute must be alpha-numeric, dash, underscore' => ':attribute只能是字母、数字和下划线_及破折号-',
  104 + ':attribute not a valid domain or ip' => ':attribute不是有效的域名或者IP',
  105 + ':attribute must be chinese' => ':attribute只能是汉字',
  106 + ':attribute must be chinese or alpha' => ':attribute只能是汉字、字母',
  107 + ':attribute must be chinese,alpha-numeric' => ':attribute只能是汉字、字母和数字',
  108 + ':attribute must be chinese,alpha-numeric,underscore, dash' => ':attribute只能是汉字、字母、数字和下划线_及破折号-',
  109 + ':attribute not a valid url' => ':attribute不是有效的URL地址',
  110 + ':attribute not a valid ip' => ':attribute不是有效的IP地址',
  111 + ':attribute must be dateFormat of :rule' => ':attribute必须使用日期格式 :rule',
  112 + ':attribute must be in :rule' => ':attribute必须在 :rule 范围内',
  113 + ':attribute be notin :rule' => ':attribute不能在 :rule 范围内',
  114 + ':attribute must between :1 - :2' => ':attribute只能在 :1 - :2 之间',
  115 + ':attribute not between :1 - :2' => ':attribute不能在 :1 - :2 之间',
  116 + 'size of :attribute must be :rule' => ':attribute长度不符合要求 :rule',
  117 + 'max size of :attribute must be :rule' => ':attribute长度不能超过 :rule',
  118 + 'min size of :attribute must be :rule' => ':attribute长度不能小于 :rule',
  119 + ':attribute cannot be less than :rule' => ':attribute日期不能小于 :rule',
  120 + ':attribute cannot exceed :rule' => ':attribute日期不能超过 :rule',
  121 + ':attribute not within :rule' => '不在有效期内 :rule',
  122 + 'access IP is not allowed' => '不允许的IP访问',
  123 + 'access IP denied' => '禁止的IP访问',
  124 + ':attribute out of accord with :2' => ':attribute和确认字段:2不一致',
  125 + ':attribute cannot be same with :2' => ':attribute和比较字段:2不能相同',
  126 + ':attribute must greater than or equal :rule' => ':attribute必须大于等于 :rule',
  127 + ':attribute must greater than :rule' => ':attribute必须大于 :rule',
  128 + ':attribute must less than or equal :rule' => ':attribute必须小于等于 :rule',
  129 + ':attribute must less than :rule' => ':attribute必须小于 :rule',
  130 + ':attribute must equal :rule' => ':attribute必须等于 :rule',
  131 + ':attribute has exists' => ':attribute已存在',
  132 + ':attribute not conform to the rules' => ':attribute不符合指定规则',
  133 + 'invalid Request method' => '无效的请求类型',
  134 + 'invalid token' => '令牌数据无效',
  135 + 'not conform to the rules' => '规则错误',
  136 +];
  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\HttpException;
  16 +use think\exception\HttpResponseException;
  17 +use think\exception\RouteNotFoundException;
  18 +
  19 +/**
  20 + * App 应用管理
  21 + * @author liu21st <liu21st@gmail.com>
  22 + */
  23 +class App
  24 +{
  25 + /**
  26 + * @var bool 是否初始化过
  27 + */
  28 + protected static $init = false;
  29 +
  30 + /**
  31 + * @var string 当前模块路径
  32 + */
  33 + public static $modulePath;
  34 +
  35 + /**
  36 + * @var bool 应用调试模式
  37 + */
  38 + public static $debug = true;
  39 +
  40 + /**
  41 + * @var string 应用类库命名空间
  42 + */
  43 + public static $namespace = 'app';
  44 +
  45 + /**
  46 + * @var bool 应用类库后缀
  47 + */
  48 + public static $suffix = false;
  49 +
  50 + /**
  51 + * @var bool 应用路由检测
  52 + */
  53 + protected static $routeCheck;
  54 +
  55 + /**
  56 + * @var bool 严格路由检测
  57 + */
  58 + protected static $routeMust;
  59 +
  60 + /**
  61 + * @var array 请求调度分发
  62 + */
  63 + protected static $dispatch;
  64 +
  65 + /**
  66 + * @var array 额外加载文件
  67 + */
  68 + protected static $file = [];
  69 +
  70 + /**
  71 + * 执行应用程序
  72 + * @access public
  73 + * @param Request $request 请求对象
  74 + * @return Response
  75 + * @throws Exception
  76 + */
  77 + public static function run(Request $request = null)
  78 + {
  79 + $request = is_null($request) ? Request::instance() : $request;
  80 +
  81 + try {
  82 + $config = self::initCommon();
  83 +
  84 + // 模块/控制器绑定
  85 + if (defined('BIND_MODULE')) {
  86 + BIND_MODULE && Route::bind(BIND_MODULE);
  87 + } elseif ($config['auto_bind_module']) {
  88 + // 入口自动绑定
  89 + $name = pathinfo($request->baseFile(), PATHINFO_FILENAME);
  90 + if ($name && 'index' != $name && is_dir(APP_PATH . $name)) {
  91 + Route::bind($name);
  92 + }
  93 + }
  94 +
  95 + $request->filter($config['default_filter']);
  96 +
  97 + // 默认语言
  98 + Lang::range($config['default_lang']);
  99 + // 开启多语言机制 检测当前语言
  100 + $config['lang_switch_on'] && Lang::detect();
  101 + $request->langset(Lang::range());
  102 +
  103 + // 加载系统语言包
  104 + Lang::load([
  105 + THINK_PATH . 'lang' . DS . $request->langset() . EXT,
  106 + APP_PATH . 'lang' . DS . $request->langset() . EXT,
  107 + ]);
  108 +
  109 + // 监听 app_dispatch
  110 + Hook::listen('app_dispatch', self::$dispatch);
  111 + // 获取应用调度信息
  112 + $dispatch = self::$dispatch;
  113 +
  114 + // 未设置调度信息则进行 URL 路由检测
  115 + if (empty($dispatch)) {
  116 + $dispatch = self::routeCheck($request, $config);
  117 + }
  118 +
  119 + // 记录当前调度信息
  120 + $request->dispatch($dispatch);
  121 +
  122 + // 记录路由和请求信息
  123 + if (self::$debug) {
  124 + Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info');
  125 + Log::record('[ HEADER ] ' . var_export($request->header(), true), 'info');
  126 + Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info');
  127 + }
  128 +
  129 + // 监听 app_begin
  130 + Hook::listen('app_begin', $dispatch);
  131 +
  132 + // 请求缓存检查
  133 + $request->cache(
  134 + $config['request_cache'],
  135 + $config['request_cache_expire'],
  136 + $config['request_cache_except']
  137 + );
  138 +
  139 + $data = self::exec($dispatch, $config);
  140 + } catch (HttpResponseException $exception) {
  141 + $data = $exception->getResponse();
  142 + }
  143 +
  144 + // 清空类的实例化
  145 + Loader::clearInstance();
  146 +
  147 + // 输出数据到客户端
  148 + if ($data instanceof Response) {
  149 + $response = $data;
  150 + } elseif (!is_null($data)) {
  151 + // 默认自动识别响应输出类型
  152 + $type = $request->isAjax() ?
  153 + Config::get('default_ajax_return') :
  154 + Config::get('default_return_type');
  155 +
  156 + $response = Response::create($data, $type);
  157 + } else {
  158 + $response = Response::create();
  159 + }
  160 +
  161 + // 监听 app_end
  162 + Hook::listen('app_end', $response);
  163 +
  164 + return $response;
  165 + }
  166 +
  167 + /**
  168 + * 初始化应用,并返回配置信息
  169 + * @access public
  170 + * @return array
  171 + */
  172 + public static function initCommon()
  173 + {
  174 + if (empty(self::$init)) {
  175 + if (defined('APP_NAMESPACE')) {
  176 + self::$namespace = APP_NAMESPACE;
  177 + }
  178 +
  179 + Loader::addNamespace(self::$namespace, APP_PATH);
  180 +
  181 + // 初始化应用
  182 + $config = self::init();
  183 + self::$suffix = $config['class_suffix'];
  184 +
  185 + // 应用调试模式
  186 + self::$debug = Env::get('app_debug', Config::get('app_debug'));
  187 +
  188 + if (!self::$debug) {
  189 + ini_set('display_errors', 'Off');
  190 + } elseif (!IS_CLI) {
  191 + // 重新申请一块比较大的 buffer
  192 + if (ob_get_level() > 0) {
  193 + $output = ob_get_clean();
  194 + }
  195 +
  196 + ob_start();
  197 +
  198 + if (!empty($output)) {
  199 + echo $output;
  200 + }
  201 +
  202 + }
  203 +
  204 + if (!empty($config['root_namespace'])) {
  205 + Loader::addNamespace($config['root_namespace']);
  206 + }
  207 +
  208 + // 加载额外文件
  209 + if (!empty($config['extra_file_list'])) {
  210 + foreach ($config['extra_file_list'] as $file) {
  211 + $file = strpos($file, '.') ? $file : APP_PATH . $file . EXT;
  212 + if (is_file($file) && !isset(self::$file[$file])) {
  213 + include $file;
  214 + self::$file[$file] = true;
  215 + }
  216 + }
  217 + }
  218 +
  219 + // 设置系统时区
  220 + date_default_timezone_set($config['default_timezone']);
  221 +
  222 + // 监听 app_init
  223 + Hook::listen('app_init');
  224 +
  225 + self::$init = true;
  226 + }
  227 +
  228 + return Config::get();
  229 + }
  230 +
  231 + /**
  232 + * 初始化应用或模块
  233 + * @access public
  234 + * @param string $module 模块名
  235 + * @return array
  236 + */
  237 + private static function init($module = '')
  238 + {
  239 + // 定位模块目录
  240 + $module = $module ? $module . DS : '';
  241 +
  242 + // 加载初始化文件
  243 + if (is_file(APP_PATH . $module . 'init' . EXT)) {
  244 + include APP_PATH . $module . 'init' . EXT;
  245 + } elseif (is_file(RUNTIME_PATH . $module . 'init' . EXT)) {
  246 + include RUNTIME_PATH . $module . 'init' . EXT;
  247 + } else {
  248 + // 加载模块配置
  249 + $config = Config::load(CONF_PATH . $module . 'config' . CONF_EXT);
  250 +
  251 + // 读取数据库配置文件
  252 + $filename = CONF_PATH . $module . 'database' . CONF_EXT;
  253 + Config::load($filename, 'database');
  254 +
  255 + // 读取扩展配置文件
  256 + if (is_dir(CONF_PATH . $module . 'extra')) {
  257 + $dir = CONF_PATH . $module . 'extra';
  258 + $files = scandir($dir);
  259 + foreach ($files as $file) {
  260 + if ('.' . pathinfo($file, PATHINFO_EXTENSION) === CONF_EXT) {
  261 + $filename = $dir . DS . $file;
  262 + Config::load($filename, pathinfo($file, PATHINFO_FILENAME));
  263 + }
  264 + }
  265 + }
  266 +
  267 + // 加载应用状态配置
  268 + if ($config['app_status']) {
  269 + Config::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT);
  270 + }
  271 +
  272 + // 加载行为扩展文件
  273 + if (is_file(CONF_PATH . $module . 'tags' . EXT)) {
  274 + Hook::import(include CONF_PATH . $module . 'tags' . EXT);
  275 + }
  276 +
  277 + // 加载公共文件
  278 + $path = APP_PATH . $module;
  279 + if (is_file($path . 'common' . EXT)) {
  280 + include $path . 'common' . EXT;
  281 + }
  282 +
  283 + // 加载当前模块语言包
  284 + if ($module) {
  285 + Lang::load($path . 'lang' . DS . Request::instance()->langset() . EXT);
  286 + }
  287 + }
  288 +
  289 + return Config::get();
  290 + }
  291 +
  292 + /**
  293 + * 设置当前请求的调度信息
  294 + * @access public
  295 + * @param array|string $dispatch 调度信息
  296 + * @param string $type 调度类型
  297 + * @return void
  298 + */
  299 + public static function dispatch($dispatch, $type = 'module')
  300 + {
  301 + self::$dispatch = ['type' => $type, $type => $dispatch];
  302 + }
  303 +
  304 + /**
  305 + * 执行函数或者闭包方法 支持参数调用
  306 + * @access public
  307 + * @param string|array|\Closure $function 函数或者闭包
  308 + * @param array $vars 变量
  309 + * @return mixed
  310 + */
  311 + public static function invokeFunction($function, $vars = [])
  312 + {
  313 + $reflect = new \ReflectionFunction($function);
  314 + $args = self::bindParams($reflect, $vars);
  315 +
  316 + // 记录执行信息
  317 + self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info');
  318 +
  319 + return $reflect->invokeArgs($args);
  320 + }
  321 +
  322 + /**
  323 + * 调用反射执行类的方法 支持参数绑定
  324 + * @access public
  325 + * @param string|array $method 方法
  326 + * @param array $vars 变量
  327 + * @return mixed
  328 + */
  329 + public static function invokeMethod($method, $vars = [])
  330 + {
  331 + if (is_array($method)) {
  332 + $class = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]);
  333 + $reflect = new \ReflectionMethod($class, $method[1]);
  334 + } else {
  335 + // 静态方法
  336 + $reflect = new \ReflectionMethod($method);
  337 + }
  338 +
  339 + $args = self::bindParams($reflect, $vars);
  340 +
  341 + self::$debug && Log::record('[ RUN ] ' . $reflect->class . '->' . $reflect->name . '[ ' . $reflect->getFileName() . ' ]', 'info');
  342 +
  343 + return $reflect->invokeArgs(isset($class) ? $class : null, $args);
  344 + }
  345 +
  346 + /**
  347 + * 调用反射执行类的实例化 支持依赖注入
  348 + * @access public
  349 + * @param string $class 类名
  350 + * @param array $vars 变量
  351 + * @return mixed
  352 + */
  353 + public static function invokeClass($class, $vars = [])
  354 + {
  355 + $reflect = new \ReflectionClass($class);
  356 + $constructor = $reflect->getConstructor();
  357 + $args = $constructor ? self::bindParams($constructor, $vars) : [];
  358 +
  359 + return $reflect->newInstanceArgs($args);
  360 + }
  361 +
  362 + /**
  363 + * 绑定参数
  364 + * @access private
  365 + * @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
  366 + * @param array $vars 变量
  367 + * @return array
  368 + */
  369 + private static function bindParams($reflect, $vars = [])
  370 + {
  371 + // 自动获取请求变量
  372 + if (empty($vars)) {
  373 + $vars = Config::get('url_param_type') ?
  374 + Request::instance()->route() :
  375 + Request::instance()->param();
  376 + }
  377 +
  378 + $args = [];
  379 + if ($reflect->getNumberOfParameters() > 0) {
  380 + // 判断数组类型 数字数组时按顺序绑定参数
  381 + reset($vars);
  382 + $type = key($vars) === 0 ? 1 : 0;
  383 +
  384 + foreach ($reflect->getParameters() as $param) {
  385 + $args[] = self::getParamValue($param, $vars, $type);
  386 + }
  387 + }
  388 +
  389 + return $args;
  390 + }
  391 +
  392 + /**
  393 + * 获取参数值
  394 + * @access private
  395 + * @param \ReflectionParameter $param 参数
  396 + * @param array $vars 变量
  397 + * @param string $type 类别
  398 + * @return array
  399 + */
  400 + private static function getParamValue($param, &$vars, $type)
  401 + {
  402 + $name = $param->getName();
  403 + $reflectionType = $param->getType();
  404 +
  405 + if ($reflectionType && $reflectionType->isBuiltin() === false) {
  406 + $className = $reflectionType->getName();
  407 + $bind = Request::instance()->$name;
  408 +
  409 + if ($bind instanceof $className) {
  410 + $result = $bind;
  411 + } else {
  412 + if (method_exists($className, 'invoke')) {
  413 + $method = new \ReflectionMethod($className, 'invoke');
  414 +
  415 + if ($method->isPublic() && $method->isStatic()) {
  416 + return $className::invoke(Request::instance());
  417 + }
  418 + }
  419 +
  420 + $result = method_exists($className, 'instance') ?
  421 + $className::instance() :
  422 + new $className;
  423 + }
  424 + } elseif (1 == $type && !empty($vars)) {
  425 + $result = array_shift($vars);
  426 + } elseif (0 == $type && isset($vars[$name])) {
  427 + $result = $vars[$name];
  428 + } elseif ($param->isDefaultValueAvailable()) {
  429 + $result = $param->getDefaultValue();
  430 + } else {
  431 + throw new \InvalidArgumentException('method param miss:' . $name);
  432 + }
  433 +
  434 + return $result;
  435 + }
  436 +
  437 + /**
  438 + * 执行调用分发
  439 + * @access protected
  440 + * @param array $dispatch 调用信息
  441 + * @param array $config 配置信息
  442 + * @return Response|mixed
  443 + * @throws \InvalidArgumentException
  444 + */
  445 + protected static function exec($dispatch, $config)
  446 + {
  447 + switch ($dispatch['type']) {
  448 + case 'redirect': // 重定向跳转
  449 + $data = Response::create($dispatch['url'], 'redirect')
  450 + ->code($dispatch['status']);
  451 + break;
  452 + case 'module': // 模块/控制器/操作
  453 + $data = self::module(
  454 + $dispatch['module'],
  455 + $config,
  456 + isset($dispatch['convert']) ? $dispatch['convert'] : null
  457 + );
  458 + break;
  459 + case 'controller': // 执行控制器操作
  460 + $vars = array_merge(Request::instance()->param(), $dispatch['var']);
  461 + $data = Loader::action(
  462 + $dispatch['controller'],
  463 + $vars,
  464 + $config['url_controller_layer'],
  465 + $config['controller_suffix']
  466 + );
  467 + break;
  468 + case 'method': // 回调方法
  469 + $vars = array_merge(Request::instance()->param(), $dispatch['var']);
  470 + $data = self::invokeMethod($dispatch['method'], $vars);
  471 + break;
  472 + case 'function': // 闭包
  473 + $data = self::invokeFunction($dispatch['function']);
  474 + break;
  475 + case 'response': // Response 实例
  476 + $data = $dispatch['response'];
  477 + break;
  478 + default:
  479 + throw new \InvalidArgumentException('dispatch type not support');
  480 + }
  481 +
  482 + return $data;
  483 + }
  484 +
  485 + /**
  486 + * 执行模块
  487 + * @access public
  488 + * @param array $result 模块/控制器/操作
  489 + * @param array $config 配置参数
  490 + * @param bool $convert 是否自动转换控制器和操作名
  491 + * @return mixed
  492 + * @throws HttpException
  493 + */
  494 + public static function module($result, $config, $convert = null)
  495 + {
  496 + if (is_string($result)) {
  497 + $result = explode('/', $result);
  498 + }
  499 +
  500 + $request = Request::instance();
  501 +
  502 + if ($config['app_multi_module']) {
  503 + // 多模块部署
  504 + $module = strip_tags(strtolower($result[0] ?: $config['default_module']));
  505 + $bind = Route::getBind('module');
  506 + $available = false;
  507 +
  508 + if ($bind) {
  509 + // 绑定模块
  510 + list($bindModule) = explode('/', $bind);
  511 +
  512 + if (empty($result[0])) {
  513 + $module = $bindModule;
  514 + $available = true;
  515 + } elseif ($module == $bindModule) {
  516 + $available = true;
  517 + }
  518 + } elseif (!in_array($module, $config['deny_module_list']) && is_dir(APP_PATH . $module)) {
  519 + $available = true;
  520 + }
  521 +
  522 + // 模块初始化
  523 + if ($module && $available) {
  524 + // 初始化模块
  525 + $request->module($module);
  526 + $config = self::init($module);
  527 +
  528 + // 模块请求缓存检查
  529 + $request->cache(
  530 + $config['request_cache'],
  531 + $config['request_cache_expire'],
  532 + $config['request_cache_except']
  533 + );
  534 + } else {
  535 + throw new HttpException(404, 'module not exists:' . $module);
  536 + }
  537 + } else {
  538 + // 单一模块部署
  539 + $module = '';
  540 + $request->module($module);
  541 + }
  542 +
  543 + // 设置默认过滤机制
  544 + $request->filter($config['default_filter']);
  545 +
  546 + // 当前模块路径
  547 + App::$modulePath = APP_PATH . ($module ? $module . DS : '');
  548 +
  549 + // 是否自动转换控制器和操作名
  550 + $convert = is_bool($convert) ? $convert : $config['url_convert'];
  551 +
  552 + // 获取控制器名
  553 + $controller = strip_tags($result[1] ?: $config['default_controller']);
  554 +
  555 + if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) {
  556 + throw new HttpException(404, 'controller not exists:' . $controller);
  557 + }
  558 +
  559 + $controller = $convert ? strtolower($controller) : $controller;
  560 +
  561 + // 获取操作名
  562 + $actionName = strip_tags($result[2] ?: $config['default_action']);
  563 + if (!empty($config['action_convert'])) {
  564 + $actionName = Loader::parseName($actionName, 1);
  565 + } else {
  566 + $actionName = $convert ? strtolower($actionName) : $actionName;
  567 + }
  568 +
  569 + // 设置当前请求的控制器、操作
  570 + $request->controller(Loader::parseName($controller, 1))->action($actionName);
  571 +
  572 + // 监听module_init
  573 + Hook::listen('module_init', $request);
  574 +
  575 + try {
  576 + $instance = Loader::controller(
  577 + $controller,
  578 + $config['url_controller_layer'],
  579 + $config['controller_suffix'],
  580 + $config['empty_controller']
  581 + );
  582 + } catch (ClassNotFoundException $e) {
  583 + throw new HttpException(404, 'controller not exists:' . $e->getClass());
  584 + }
  585 +
  586 + // 获取当前操作名
  587 + $action = $actionName . $config['action_suffix'];
  588 +
  589 + $vars = [];
  590 + if (is_callable([$instance, $action])) {
  591 + // 执行操作方法
  592 + $call = [$instance, $action];
  593 + // 严格获取当前操作方法名
  594 + $reflect = new \ReflectionMethod($instance, $action);
  595 + $methodName = $reflect->getName();
  596 + $suffix = $config['action_suffix'];
  597 + $actionName = $suffix ? substr($methodName, 0, -strlen($suffix)) : $methodName;
  598 + $request->action($actionName);
  599 +
  600 + } elseif (is_callable([$instance, '_empty'])) {
  601 + // 空操作
  602 + $call = [$instance, '_empty'];
  603 + $vars = [$actionName];
  604 + } else {
  605 + // 操作不存在
  606 + throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
  607 + }
  608 +
  609 + Hook::listen('action_begin', $call);
  610 +
  611 + return self::invokeMethod($call, $vars);
  612 + }
  613 +
  614 + /**
  615 + * URL路由检测(根据PATH_INFO)
  616 + * @access public
  617 + * @param \think\Request $request 请求实例
  618 + * @param array $config 配置信息
  619 + * @return array
  620 + * @throws \think\Exception
  621 + */
  622 + public static function routeCheck($request, array $config)
  623 + {
  624 + $path = $request->path();
  625 + $depr = $config['pathinfo_depr'];
  626 + $result = false;
  627 +
  628 + // 路由检测
  629 + $check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on'];
  630 + if ($check) {
  631 + // 开启路由
  632 + if (is_file(RUNTIME_PATH . 'route.php')) {
  633 + // 读取路由缓存
  634 + $rules = include RUNTIME_PATH . 'route.php';
  635 + is_array($rules) && Route::rules($rules);
  636 + } else {
  637 + $files = $config['route_config_file'];
  638 + foreach ($files as $file) {
  639 + if (is_file(CONF_PATH . $file . CONF_EXT)) {
  640 + // 导入路由配置
  641 + $rules = include CONF_PATH . $file . CONF_EXT;
  642 + is_array($rules) && Route::import($rules);
  643 + }
  644 + }
  645 + }
  646 +
  647 + // 路由检测(根据路由定义返回不同的URL调度)
  648 + $result = Route::check($request, $path, $depr, $config['url_domain_deploy']);
  649 + $must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must'];
  650 +
  651 + if ($must && false === $result) {
  652 + // 路由无效
  653 + throw new RouteNotFoundException();
  654 + }
  655 + }
  656 +
  657 + // 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索
  658 + if (false === $result) {
  659 + $result = Route::parseUrl($path, $depr, $config['controller_auto_search']);
  660 + }
  661 +
  662 + return $result;
  663 + }
  664 +
  665 + /**
  666 + * 设置应用的路由检测机制
  667 + * @access public
  668 + * @param bool $route 是否需要检测路由
  669 + * @param bool $must 是否强制检测路由
  670 + * @return void
  671 + */
  672 + public static function route($route, $must = false)
  673 + {
  674 + self::$routeCheck = $route;
  675 + self::$routeMust = $must;
  676 + }
  677 +}
  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 + * 根据传入的 build 资料创建目录和文件
  18 + * @access public
  19 + * @param array $build build 列表
  20 + * @param string $namespace 应用类库命名空间
  21 + * @param bool $suffix 类库后缀
  22 + * @return void
  23 + * @throws Exception
  24 + */
  25 + public static function run(array $build = [], $namespace = 'app', $suffix = false)
  26 + {
  27 + // 锁定
  28 + $lock = APP_PATH . 'build.lock';
  29 +
  30 + // 如果锁定文件不可写(不存在)则进行处理,否则表示已经有程序在处理了
  31 + if (!is_writable($lock)) {
  32 + if (!touch($lock)) {
  33 + throw new Exception(
  34 + '应用目录[' . APP_PATH . ']不可写,目录无法自动生成!<BR>请手动生成项目目录~',
  35 + 10006
  36 + );
  37 + }
  38 +
  39 + foreach ($build as $module => $list) {
  40 + if ('__dir__' == $module) {
  41 + // 创建目录列表
  42 + self::buildDir($list);
  43 + } elseif ('__file__' == $module) {
  44 + // 创建文件列表
  45 + self::buildFile($list);
  46 + } else {
  47 + // 创建模块
  48 + self::module($module, $list, $namespace, $suffix);
  49 + }
  50 + }
  51 +
  52 + // 解除锁定
  53 + unlink($lock);
  54 + }
  55 + }
  56 +
  57 + /**
  58 + * 创建目录
  59 + * @access protected
  60 + * @param array $list 目录列表
  61 + * @return void
  62 + */
  63 + protected static function buildDir($list)
  64 + {
  65 + foreach ($list as $dir) {
  66 + // 目录不存在则创建目录
  67 + !is_dir(APP_PATH . $dir) && mkdir(APP_PATH . $dir, 0755, true);
  68 + }
  69 + }
  70 +
  71 + /**
  72 + * 创建文件
  73 + * @access protected
  74 + * @param array $list 文件列表
  75 + * @return void
  76 + */
  77 + protected static function buildFile($list)
  78 + {
  79 + foreach ($list as $file) {
  80 + // 先创建目录
  81 + if (!is_dir(APP_PATH . dirname($file))) {
  82 + mkdir(APP_PATH . dirname($file), 0755, true);
  83 + }
  84 +
  85 + // 再创建文件
  86 + if (!is_file(APP_PATH . $file)) {
  87 + file_put_contents(
  88 + APP_PATH . $file,
  89 + 'php' == pathinfo($file, PATHINFO_EXTENSION) ? "<?php\n" : ''
  90 + );
  91 + }
  92 + }
  93 + }
  94 +
  95 + /**
  96 + * 创建模块
  97 + * @access public
  98 + * @param string $module 模块名
  99 + * @param array $list build 列表
  100 + * @param string $namespace 应用类库命名空间
  101 + * @param bool $suffix 类库后缀
  102 + * @return void
  103 + */
  104 + public static function module($module = '', $list = [], $namespace = 'app', $suffix = false)
  105 + {
  106 + $module = $module ?: '';
  107 +
  108 + // 创建模块目录
  109 + !is_dir(APP_PATH . $module) && mkdir(APP_PATH . $module);
  110 +
  111 + // 如果不是 runtime 目录则需要创建配置文件和公共文件、创建模块的默认页面
  112 + if (basename(RUNTIME_PATH) != $module) {
  113 + self::buildCommon($module);
  114 + self::buildHello($module, $namespace, $suffix);
  115 + }
  116 +
  117 + // 未指定文件和目录,则创建默认的模块目录和文件
  118 + if (empty($list)) {
  119 + $list = [
  120 + '__file__' => ['config.php', 'common.php'],
  121 + '__dir__' => ['controller', 'model', 'view'],
  122 + ];
  123 + }
  124 +
  125 + // 创建子目录和文件
  126 + foreach ($list as $path => $file) {
  127 + $modulePath = APP_PATH . $module . DS;
  128 +
  129 + if ('__dir__' == $path) {
  130 + // 生成子目录
  131 + foreach ($file as $dir) {
  132 + self::checkDirBuild($modulePath . $dir);
  133 + }
  134 + } elseif ('__file__' == $path) {
  135 + // 生成(空白)文件
  136 + foreach ($file as $name) {
  137 + if (!is_file($modulePath . $name)) {
  138 + file_put_contents(
  139 + $modulePath . $name,
  140 + 'php' == pathinfo($name, PATHINFO_EXTENSION) ? "<?php\n" : ''
  141 + );
  142 + }
  143 + }
  144 + } else {
  145 + // 生成相关 MVC 文件
  146 + foreach ($file as $val) {
  147 + $val = trim($val);
  148 + $filename = $modulePath . $path . DS . $val . ($suffix ? ucfirst($path) : '') . EXT;
  149 + $space = $namespace . '\\' . ($module ? $module . '\\' : '') . $path;
  150 + $class = $val . ($suffix ? ucfirst($path) : '');
  151 +
  152 + switch ($path) {
  153 + case 'controller': // 控制器
  154 + $content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
  155 + break;
  156 + case 'model': // 模型
  157 + $content = "<?php\nnamespace {$space};\n\nuse think\Model;\n\nclass {$class} extends Model\n{\n\n}";
  158 + break;
  159 + case 'view': // 视图
  160 + $filename = $modulePath . $path . DS . $val . '.html';
  161 + self::checkDirBuild(dirname($filename));
  162 + $content = '';
  163 + break;
  164 + default:
  165 + // 其他文件
  166 + $content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
  167 + }
  168 +
  169 + if (!is_file($filename)) {
  170 + file_put_contents($filename, $content);
  171 + }
  172 + }
  173 + }
  174 + }
  175 + }
  176 +
  177 + /**
  178 + * 创建模块的欢迎页面
  179 + * @access protected
  180 + * @param string $module 模块名
  181 + * @param string $namespace 应用类库命名空间
  182 + * @param bool $suffix 类库后缀
  183 + * @return void
  184 + */
  185 + protected static function buildHello($module, $namespace, $suffix = false)
  186 + {
  187 + $filename = APP_PATH . ($module ? $module . DS : '') .
  188 + 'controller' . DS . 'Index' .
  189 + ($suffix ? 'Controller' : '') . EXT;
  190 +
  191 + if (!is_file($filename)) {
  192 + $module = $module ? $module . '\\' : '';
  193 + $suffix = $suffix ? 'Controller' : '';
  194 + $content = str_replace(
  195 + ['{$app}', '{$module}', '{layer}', '{$suffix}'],
  196 + [$namespace, $module, 'controller', $suffix],
  197 + file_get_contents(THINK_PATH . 'tpl' . DS . 'default_index.tpl')
  198 + );
  199 +
  200 + self::checkDirBuild(dirname($filename));
  201 + file_put_contents($filename, $content);
  202 + }
  203 + }
  204 +
  205 + /**
  206 + * 创建模块的公共文件
  207 + * @access protected
  208 + * @param string $module 模块名
  209 + * @return void
  210 + */
  211 + protected static function buildCommon($module)
  212 + {
  213 + $config = CONF_PATH . ($module ? $module . DS : '') . 'config.php';
  214 +
  215 + self::checkDirBuild(dirname($config));
  216 +
  217 + if (!is_file($config)) {
  218 + file_put_contents($config, "<?php\n//配置文件\nreturn [\n\n];");
  219 + }
  220 +
  221 + $common = APP_PATH . ($module ? $module . DS : '') . 'common.php';
  222 + if (!is_file($common)) file_put_contents($common, "<?php\n");
  223 + }
  224 +
  225 + /**
  226 + * 创建目录
  227 + * @access protected
  228 + * @param string $dirname 目录名称
  229 + * @return void
  230 + */
  231 + protected static function checkDirBuild($dirname)
  232 + {
  233 + !is_dir($dirname) && mkdir($dirname, 0755, true);
  234 + }
  235 +}
  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 +class Cache
  17 +{
  18 + /**
  19 + * @var array 缓存的实例
  20 + */
  21 + public static $instance = [];
  22 +
  23 + /**
  24 + * @var int 缓存读取次数
  25 + */
  26 + public static $readTimes = 0;
  27 +
  28 + /**
  29 + * @var int 缓存写入次数
  30 + */
  31 + public static $writeTimes = 0;
  32 +
  33 + /**
  34 + * @var object 操作句柄
  35 + */
  36 + public static $handler;
  37 +
  38 + /**
  39 + * 连接缓存驱动
  40 + * @access public
  41 + * @param array $options 配置数组
  42 + * @param bool|string $name 缓存连接标识 true 强制重新连接
  43 + * @return Driver
  44 + */
  45 + public static function connect(array $options = [], $name = false)
  46 + {
  47 + $type = !empty($options['type']) ? $options['type'] : 'File';
  48 +
  49 + if (false === $name) {
  50 + $name = md5(serialize($options));
  51 + }
  52 +
  53 + if (true === $name || !isset(self::$instance[$name])) {
  54 + $class = false === strpos($type, '\\') ?
  55 + '\\think\\cache\\driver\\' . ucwords($type) :
  56 + $type;
  57 +
  58 + // 记录初始化信息
  59 + App::$debug && Log::record('[ CACHE ] INIT ' . $type, 'info');
  60 +
  61 + if (true === $name) {
  62 + return new $class($options);
  63 + }
  64 +
  65 + self::$instance[$name] = new $class($options);
  66 + }
  67 +
  68 + return self::$instance[$name];
  69 + }
  70 +
  71 + /**
  72 + * 自动初始化缓存
  73 + * @access public
  74 + * @param array $options 配置数组
  75 + * @return Driver
  76 + */
  77 + public static function init(array $options = [])
  78 + {
  79 + if (is_null(self::$handler)) {
  80 + if (empty($options) && 'complex' == Config::get('cache.type')) {
  81 + $default = Config::get('cache.default');
  82 + // 获取默认缓存配置,并连接
  83 + $options = Config::get('cache.' . $default['type']) ?: $default;
  84 + } elseif (empty($options)) {
  85 + $options = Config::get('cache');
  86 + }
  87 +
  88 + self::$handler = self::connect($options);
  89 + }
  90 +
  91 + return self::$handler;
  92 + }
  93 +
  94 + /**
  95 + * 切换缓存类型 需要配置 cache.type 为 complex
  96 + * @access public
  97 + * @param string $name 缓存标识
  98 + * @return Driver
  99 + */
  100 + public static function store($name = '')
  101 + {
  102 + if ('' !== $name && 'complex' == Config::get('cache.type')) {
  103 + return self::connect(Config::get('cache.' . $name), strtolower($name));
  104 + }
  105 +
  106 + return self::init();
  107 + }
  108 +
  109 + /**
  110 + * 判断缓存是否存在
  111 + * @access public
  112 + * @param string $name 缓存变量名
  113 + * @return bool
  114 + */
  115 + public static function has($name)
  116 + {
  117 + self::$readTimes++;
  118 +
  119 + return self::init()->has($name);
  120 + }
  121 +
  122 + /**
  123 + * 读取缓存
  124 + * @access public
  125 + * @param string $name 缓存标识
  126 + * @param mixed $default 默认值
  127 + * @return mixed
  128 + */
  129 + public static function get($name, $default = false)
  130 + {
  131 + self::$readTimes++;
  132 +
  133 + return self::init()->get($name, $default);
  134 + }
  135 +
  136 + /**
  137 + * 写入缓存
  138 + * @access public
  139 + * @param string $name 缓存标识
  140 + * @param mixed $value 存储数据
  141 + * @param int|null $expire 有效时间 0为永久
  142 + * @return boolean
  143 + */
  144 + public static function set($name, $value, $expire = null)
  145 + {
  146 + self::$writeTimes++;
  147 +
  148 + return self::init()->set($name, $value, $expire);
  149 + }
  150 +
  151 + /**
  152 + * 自增缓存(针对数值缓存)
  153 + * @access public
  154 + * @param string $name 缓存变量名
  155 + * @param int $step 步长
  156 + * @return false|int
  157 + */
  158 + public static function inc($name, $step = 1)
  159 + {
  160 + self::$writeTimes++;
  161 +
  162 + return self::init()->inc($name, $step);
  163 + }
  164 +
  165 + /**
  166 + * 自减缓存(针对数值缓存)
  167 + * @access public
  168 + * @param string $name 缓存变量名
  169 + * @param int $step 步长
  170 + * @return false|int
  171 + */
  172 + public static function dec($name, $step = 1)
  173 + {
  174 + self::$writeTimes++;
  175 +
  176 + return self::init()->dec($name, $step);
  177 + }
  178 +
  179 + /**
  180 + * 删除缓存
  181 + * @access public
  182 + * @param string $name 缓存标识
  183 + * @return boolean
  184 + */
  185 + public static function rm($name)
  186 + {
  187 + self::$writeTimes++;
  188 +
  189 + return self::init()->rm($name);
  190 + }
  191 +
  192 + /**
  193 + * 清除缓存
  194 + * @access public
  195 + * @param string $tag 标签名
  196 + * @return boolean
  197 + */
  198 + public static function clear($tag = null)
  199 + {
  200 + self::$writeTimes++;
  201 +
  202 + return self::init()->clear($tag);
  203 + }
  204 +
  205 + /**
  206 + * 读取缓存并删除
  207 + * @access public
  208 + * @param string $name 缓存变量名
  209 + * @return mixed
  210 + */
  211 + public static function pull($name)
  212 + {
  213 + self::$readTimes++;
  214 + self::$writeTimes++;
  215 +
  216 + return self::init()->pull($name);
  217 + }
  218 +
  219 + /**
  220 + * 如果不存在则写入缓存
  221 + * @access public
  222 + * @param string $name 缓存变量名
  223 + * @param mixed $value 存储数据
  224 + * @param int $expire 有效时间 0为永久
  225 + * @return mixed
  226 + */
  227 + public static function remember($name, $value, $expire = null)
  228 + {
  229 + self::$readTimes++;
  230 +
  231 + return self::init()->remember($name, $value, $expire);
  232 + }
  233 +
  234 + /**
  235 + * 缓存标签
  236 + * @access public
  237 + * @param string $name 标签名
  238 + * @param string|array $keys 缓存标识
  239 + * @param bool $overlay 是否覆盖
  240 + * @return Driver
  241 + */
  242 + public static function tag($name, $keys = null, $overlay = false)
  243 + {
  244 + return self::init()->tag($name, $keys, $overlay);
  245 + }
  246 +
  247 +}
  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 + * @var array 数据
  24 + */
  25 + protected $items = [];
  26 +
  27 + /**
  28 + * Collection constructor.
  29 + * @access public
  30 + * @param array $items 数据
  31 + */
  32 + public function __construct($items = [])
  33 + {
  34 + $this->items = $this->convertToArray($items);
  35 + }
  36 +
  37 + /**
  38 + * 创建 Collection 实例
  39 + * @access public
  40 + * @param array $items 数据
  41 + * @return static
  42 + */
  43 + public static function make($items = [])
  44 + {
  45 + return new static($items);
  46 + }
  47 +
  48 + /**
  49 + * 判断数据是否为空
  50 + * @access public
  51 + * @return bool
  52 + */
  53 + public function isEmpty()
  54 + {
  55 + return empty($this->items);
  56 + }
  57 +
  58 + /**
  59 + * 将数据转成数组
  60 + * @access public
  61 + * @return array
  62 + */
  63 + public function toArray()
  64 + {
  65 + return array_map(function ($value) {
  66 + return ($value instanceof Model || $value instanceof self) ?
  67 + $value->toArray() :
  68 + $value;
  69 + }, $this->items);
  70 + }
  71 +
  72 + /**
  73 + * 获取全部的数据
  74 + * @access public
  75 + * @return array
  76 + */
  77 + public function all()
  78 + {
  79 + return $this->items;
  80 + }
  81 +
  82 + /**
  83 + * 交换数组中的键和值
  84 + * @access public
  85 + * @return static
  86 + */
  87 + public function flip()
  88 + {
  89 + return new static(array_flip($this->items));
  90 + }
  91 +
  92 + /**
  93 + * 返回数组中所有的键名组成的新 Collection 实例
  94 + * @access public
  95 + * @return static
  96 + */
  97 + public function keys()
  98 + {
  99 + return new static(array_keys($this->items));
  100 + }
  101 +
  102 + /**
  103 + * 返回数组中所有的值组成的新 Collection 实例
  104 + * @access public
  105 + * @return static
  106 + */
  107 + public function values()
  108 + {
  109 + return new static(array_values($this->items));
  110 + }
  111 +
  112 + /**
  113 + * 合并数组并返回一个新的 Collection 实例
  114 + * @access public
  115 + * @param mixed $items 新的数据
  116 + * @return static
  117 + */
  118 + public function merge($items)
  119 + {
  120 + return new static(array_merge($this->items, $this->convertToArray($items)));
  121 + }
  122 +
  123 + /**
  124 + * 比较数组,返回差集生成的新 Collection 实例
  125 + * @access public
  126 + * @param mixed $items 做比较的数据
  127 + * @return static
  128 + */
  129 + public function diff($items)
  130 + {
  131 + return new static(array_diff($this->items, $this->convertToArray($items)));
  132 + }
  133 +
  134 + /**
  135 + * 比较数组,返回交集组成的 Collection 新实例
  136 + * @access public
  137 + * @param mixed $items 比较数据
  138 + * @return static
  139 + */
  140 + public function intersect($items)
  141 + {
  142 + return new static(array_intersect($this->items, $this->convertToArray($items)));
  143 + }
  144 +
  145 + /**
  146 + * 返回并删除数据中的的最后一个元素(出栈)
  147 + * @access public
  148 + * @return mixed
  149 + */
  150 + public function pop()
  151 + {
  152 + return array_pop($this->items);
  153 + }
  154 +
  155 + /**
  156 + * 返回并删除数据中首个元素
  157 + * @access public
  158 + * @return mixed
  159 + */
  160 + public function shift()
  161 + {
  162 + return array_shift($this->items);
  163 + }
  164 +
  165 + /**
  166 + * 在数组开头插入一个元素
  167 + * @access public
  168 + * @param mixed $value 值
  169 + * @param mixed $key 键名
  170 + * @return void
  171 + */
  172 + public function unshift($value, $key = null)
  173 + {
  174 + if (is_null($key)) {
  175 + array_unshift($this->items, $value);
  176 + } else {
  177 + $this->items = [$key => $value] + $this->items;
  178 + }
  179 + }
  180 +
  181 + /**
  182 + * 在数组结尾插入一个元素
  183 + * @access public
  184 + * @param mixed $value 值
  185 + * @param mixed $key 键名
  186 + * @return void
  187 + */
  188 + public function push($value, $key = null)
  189 + {
  190 + if (is_null($key)) {
  191 + $this->items[] = $value;
  192 + } else {
  193 + $this->items[$key] = $value;
  194 + }
  195 + }
  196 +
  197 + /**
  198 + * 通过使用用户自定义函数,以字符串返回数组
  199 + * @access public
  200 + * @param callable $callback 回调函数
  201 + * @param mixed $initial 初始值
  202 + * @return mixed
  203 + */
  204 + public function reduce(callable $callback, $initial = null)
  205 + {
  206 + return array_reduce($this->items, $callback, $initial);
  207 + }
  208 +
  209 + /**
  210 + * 以相反的顺序创建一个新的 Collection 实例
  211 + * @access public
  212 + * @return static
  213 + */
  214 + public function reverse()
  215 + {
  216 + return new static(array_reverse($this->items));
  217 + }
  218 +
  219 + /**
  220 + * 把数据分割为新的数组块
  221 + * @access public
  222 + * @param int $size 分隔长度
  223 + * @param bool $preserveKeys 是否保持原数据索引
  224 + * @return static
  225 + */
  226 + public function chunk($size, $preserveKeys = false)
  227 + {
  228 + $chunks = [];
  229 +
  230 + foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) {
  231 + $chunks[] = new static($chunk);
  232 + }
  233 +
  234 + return new static($chunks);
  235 + }
  236 +
  237 + /**
  238 + * 给数据中的每个元素执行回调
  239 + * @access public
  240 + * @param callable $callback 回调函数
  241 + * @return $this
  242 + */
  243 + public function each(callable $callback)
  244 + {
  245 + foreach ($this->items as $key => $item) {
  246 + $result = $callback($item, $key);
  247 +
  248 + if (false === $result) {
  249 + break;
  250 + }
  251 +
  252 + if (!is_object($item)) {
  253 + $this->items[$key] = $result;
  254 + }
  255 + }
  256 +
  257 + return $this;
  258 + }
  259 +
  260 + /**
  261 + * 用回调函数过滤数据中的元素
  262 + * @access public
  263 + * @param callable|null $callback 回调函数
  264 + * @return static
  265 + */
  266 + public function filter(callable $callback = null)
  267 + {
  268 + return new static(array_filter($this->items, $callback ?: null));
  269 + }
  270 +
  271 + /**
  272 + * 返回数据中指定的一列
  273 + * @access public
  274 + * @param mixed $columnKey 键名
  275 + * @param null $indexKey 作为索引值的列
  276 + * @return array
  277 + */
  278 + public function column($columnKey, $indexKey = null)
  279 + {
  280 + if (function_exists('array_column')) {
  281 + return array_column($this->items, $columnKey, $indexKey);
  282 + }
  283 +
  284 + $result = [];
  285 + foreach ($this->items as $row) {
  286 + $key = $value = null;
  287 + $keySet = $valueSet = false;
  288 +
  289 + if (null !== $indexKey && array_key_exists($indexKey, $row)) {
  290 + $key = (string) $row[$indexKey];
  291 + $keySet = true;
  292 + }
  293 +
  294 + if (null === $columnKey) {
  295 + $valueSet = true;
  296 + $value = $row;
  297 + } elseif (is_array($row) && array_key_exists($columnKey, $row)) {
  298 + $valueSet = true;
  299 + $value = $row[$columnKey];
  300 + }
  301 +
  302 + if ($valueSet) {
  303 + if ($keySet) {
  304 + $result[$key] = $value;
  305 + } else {
  306 + $result[] = $value;
  307 + }
  308 + }
  309 + }
  310 +
  311 + return $result;
  312 + }
  313 +
  314 + /**
  315 + * 对数据排序,并返回排序后的数据组成的新 Collection 实例
  316 + * @access public
  317 + * @param callable|null $callback 回调函数
  318 + * @return static
  319 + */
  320 + public function sort(callable $callback = null)
  321 + {
  322 + $items = $this->items;
  323 + $callback = $callback ?: function ($a, $b) {
  324 + return $a == $b ? 0 : (($a < $b) ? -1 : 1);
  325 + };
  326 +
  327 + uasort($items, $callback);
  328 + return new static($items);
  329 + }
  330 +
  331 + /**
  332 + * 将数据打乱后组成新的 Collection 实例
  333 + * @access public
  334 + * @return static
  335 + */
  336 + public function shuffle()
  337 + {
  338 + $items = $this->items;
  339 +
  340 + shuffle($items);
  341 + return new static($items);
  342 + }
  343 +
  344 + /**
  345 + * 截取数据并返回新的 Collection 实例
  346 + * @access public
  347 + * @param int $offset 起始位置
  348 + * @param int $length 截取长度
  349 + * @param bool $preserveKeys 是否保持原先的键名
  350 + * @return static
  351 + */
  352 + public function slice($offset, $length = null, $preserveKeys = false)
  353 + {
  354 + return new static(array_slice($this->items, $offset, $length, $preserveKeys));
  355 + }
  356 +
  357 + /**
  358 + * 指定的键是否存在
  359 + * @access public
  360 + * @param mixed $offset 键名
  361 + * @return bool
  362 + */
  363 + public function offsetExists($offset)
  364 + {
  365 + return array_key_exists($offset, $this->items);
  366 + }
  367 +
  368 + /**
  369 + * 获取指定键对应的值
  370 + * @access public
  371 + * @param mixed $offset 键名
  372 + * @return mixed
  373 + */
  374 + public function offsetGet($offset)
  375 + {
  376 + return $this->items[$offset];
  377 + }
  378 +
  379 + /**
  380 + * 设置键值
  381 + * @access public
  382 + * @param mixed $offset 键名
  383 + * @param mixed $value 值
  384 + * @return void
  385 + */
  386 + public function offsetSet($offset, $value)
  387 + {
  388 + if (is_null($offset)) {
  389 + $this->items[] = $value;
  390 + } else {
  391 + $this->items[$offset] = $value;
  392 + }
  393 + }
  394 +
  395 + /**
  396 + * 删除指定键值
  397 + * @access public
  398 + * @param mixed $offset 键名
  399 + * @return void
  400 + */
  401 + public function offsetUnset($offset)
  402 + {
  403 + unset($this->items[$offset]);
  404 + }
  405 +
  406 + /**
  407 + * 统计数据的个数
  408 + * @access public
  409 + * @return int
  410 + */
  411 + public function count()
  412 + {
  413 + return count($this->items);
  414 + }
  415 +
  416 + /**
  417 + * 获取数据的迭代器
  418 + * @access public
  419 + * @return ArrayIterator
  420 + */
  421 + public function getIterator()
  422 + {
  423 + return new ArrayIterator($this->items);
  424 + }
  425 +
  426 + /**
  427 + * 将数据反序列化成数组
  428 + * @access public
  429 + * @return array
  430 + */
  431 + public function jsonSerialize()
  432 + {
  433 + return $this->toArray();
  434 + }
  435 +
  436 + /**
  437 + * 转换当前数据集为 JSON 字符串
  438 + * @access public
  439 + * @param integer $options json 参数
  440 + * @return string
  441 + */
  442 + public function toJson($options = JSON_UNESCAPED_UNICODE)
  443 + {
  444 + return json_encode($this->toArray(), $options);
  445 + }
  446 +
  447 + /**
  448 + * 将数据转换成字符串
  449 + * @access public
  450 + * @return string
  451 + */
  452 + public function __toString()
  453 + {
  454 + return $this->toJson();
  455 + }
  456 +
  457 + /**
  458 + * 将数据转换成数组
  459 + * @access protected
  460 + * @param mixed $items 数据
  461 + * @return array
  462 + */
  463 + protected function convertToArray($items)
  464 + {
  465 + return $items instanceof self ? $items->all() : (array) $items;
  466 + }
  467 +}
  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 Config
  15 +{
  16 + /**
  17 + * @var array 配置参数
  18 + */
  19 + private static $config = [];
  20 +
  21 + /**
  22 + * @var string 参数作用域
  23 + */
  24 + private static $range = '_sys_';
  25 +
  26 + /**
  27 + * 设定配置参数的作用域
  28 + * @access public
  29 + * @param string $range 作用域
  30 + * @return void
  31 + */
  32 + public static function range($range)
  33 + {
  34 + self::$range = $range;
  35 +
  36 + if (!isset(self::$config[$range])) self::$config[$range] = [];
  37 + }
  38 +
  39 + /**
  40 + * 解析配置文件或内容
  41 + * @access public
  42 + * @param string $config 配置文件路径或内容
  43 + * @param string $type 配置解析类型
  44 + * @param string $name 配置名(如设置即表示二级配置)
  45 + * @param string $range 作用域
  46 + * @return mixed
  47 + */
  48 + public static function parse($config, $type = '', $name = '', $range = '')
  49 + {
  50 + $range = $range ?: self::$range;
  51 +
  52 + if (empty($type)) $type = pathinfo($config, PATHINFO_EXTENSION);
  53 +
  54 + $class = false !== strpos($type, '\\') ?
  55 + $type :
  56 + '\\think\\config\\driver\\' . ucwords($type);
  57 +
  58 + return self::set((new $class())->parse($config), $name, $range);
  59 + }
  60 +
  61 + /**
  62 + * 加载配置文件(PHP格式)
  63 + * @access public
  64 + * @param string $file 配置文件名
  65 + * @param string $name 配置名(如设置即表示二级配置)
  66 + * @param string $range 作用域
  67 + * @return mixed
  68 + */
  69 + public static function load($file, $name = '', $range = '')
  70 + {
  71 + $range = $range ?: self::$range;
  72 +
  73 + if (!isset(self::$config[$range])) self::$config[$range] = [];
  74 +
  75 + if (is_file($file)) {
  76 + $name = strtolower($name);
  77 + $type = pathinfo($file, PATHINFO_EXTENSION);
  78 +
  79 + if ('php' == $type) {
  80 + return self::set(include $file, $name, $range);
  81 + }
  82 +
  83 + if ('yaml' == $type && function_exists('yaml_parse_file')) {
  84 + return self::set(yaml_parse_file($file), $name, $range);
  85 + }
  86 +
  87 + return self::parse($file, $type, $name, $range);
  88 + }
  89 +
  90 + return self::$config[$range];
  91 + }
  92 +
  93 + /**
  94 + * 检测配置是否存在
  95 + * @access public
  96 + * @param string $name 配置参数名(支持二级配置 . 号分割)
  97 + * @param string $range 作用域
  98 + * @return bool
  99 + */
  100 + public static function has($name, $range = '')
  101 + {
  102 + $range = $range ?: self::$range;
  103 +
  104 + if (!strpos($name, '.')) {
  105 + return isset(self::$config[$range][strtolower($name)]);
  106 + }
  107 +
  108 + // 二维数组设置和获取支持
  109 + $name = explode('.', $name, 2);
  110 + return isset(self::$config[$range][strtolower($name[0])][$name[1]]);
  111 + }
  112 +
  113 + /**
  114 + * 获取配置参数 为空则获取所有配置
  115 + * @access public
  116 + * @param string $name 配置参数名(支持二级配置 . 号分割)
  117 + * @param string $range 作用域
  118 + * @return mixed
  119 + */
  120 + public static function get($name = null, $range = '')
  121 + {
  122 + $range = $range ?: self::$range;
  123 +
  124 + // 无参数时获取所有
  125 + if (empty($name) && isset(self::$config[$range])) {
  126 + return self::$config[$range];
  127 + }
  128 +
  129 + // 非二级配置时直接返回
  130 + if (!strpos($name, '.')) {
  131 + $name = strtolower($name);
  132 + return isset(self::$config[$range][$name]) ? self::$config[$range][$name] : null;
  133 + }
  134 +
  135 + // 二维数组设置和获取支持
  136 + $name = explode('.', $name, 2);
  137 + $name[0] = strtolower($name[0]);
  138 +
  139 + if (!isset(self::$config[$range][$name[0]])) {
  140 + // 动态载入额外配置
  141 + $module = Request::instance()->module();
  142 + $file = CONF_PATH . ($module ? $module . DS : '') . 'extra' . DS . $name[0] . CONF_EXT;
  143 +
  144 + is_file($file) && self::load($file, $name[0]);
  145 + }
  146 +
  147 + return isset(self::$config[$range][$name[0]][$name[1]]) ?
  148 + self::$config[$range][$name[0]][$name[1]] :
  149 + null;
  150 + }
  151 +
  152 + /**
  153 + * 设置配置参数 name 为数组则为批量设置
  154 + * @access public
  155 + * @param string|array $name 配置参数名(支持二级配置 . 号分割)
  156 + * @param mixed $value 配置值
  157 + * @param string $range 作用域
  158 + * @return mixed
  159 + */
  160 + public static function set($name, $value = null, $range = '')
  161 + {
  162 + $range = $range ?: self::$range;
  163 +
  164 + if (!isset(self::$config[$range])) self::$config[$range] = [];
  165 +
  166 + // 字符串则表示单个配置设置
  167 + if (is_string($name)) {
  168 + if (!strpos($name, '.')) {
  169 + self::$config[$range][strtolower($name)] = $value;
  170 + } else {
  171 + // 二维数组
  172 + $name = explode('.', $name, 2);
  173 + self::$config[$range][strtolower($name[0])][$name[1]] = $value;
  174 + }
  175 +
  176 + return $value;
  177 + }
  178 +
  179 + // 数组则表示批量设置
  180 + if (is_array($name)) {
  181 + if (!empty($value)) {
  182 + self::$config[$range][$value] = isset(self::$config[$range][$value]) ?
  183 + array_merge(self::$config[$range][$value], $name) :
  184 + $name;
  185 +
  186 + return self::$config[$range][$value];
  187 + }
  188 +
  189 + return self::$config[$range] = array_merge(
  190 + self::$config[$range], array_change_key_case($name)
  191 + );
  192 + }
  193 +
  194 + // 为空直接返回已有配置
  195 + return self::$config[$range];
  196 + }
  197 +
  198 + /**
  199 + * 重置配置参数
  200 + * @access public
  201 + * @param string $range 作用域
  202 + * @return void
  203 + */
  204 + public static function reset($range = '')
  205 + {
  206 + $range = $range ?: self::$range;
  207 +
  208 + if (true === $range) {
  209 + self::$config = [];
  210 + } else {
  211 + self::$config[$range] = [];
  212 + }
  213 + }
  214 +}
  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 + * @var string 命令名称
  25 + */
  26 + private $name;
  27 +
  28 + /**
  29 + * @var string 命令版本
  30 + */
  31 + private $version;
  32 +
  33 + /**
  34 + * @var Command[] 命令
  35 + */
  36 + private $commands = [];
  37 +
  38 + /**
  39 + * @var bool 是否需要帮助信息
  40 + */
  41 + private $wantHelps = false;
  42 +
  43 + /**
  44 + * @var bool 是否捕获异常
  45 + */
  46 + private $catchExceptions = true;
  47 +
  48 + /**
  49 + * @var bool 是否自动退出执行
  50 + */
  51 + private $autoExit = true;
  52 +
  53 + /**
  54 + * @var InputDefinition 输入定义
  55 + */
  56 + private $definition;
  57 +
  58 + /**
  59 + * @var string 默认执行的命令
  60 + */
  61 + private $defaultCommand;
  62 +
  63 + /**
  64 + * @var array 默认提供的命令
  65 + */
  66 + private static $defaultCommands = [
  67 + "think\\console\\command\\Help",
  68 + "think\\console\\command\\Lists",
  69 + "think\\console\\command\\Build",
  70 + "think\\console\\command\\Clear",
  71 + "think\\console\\command\\make\\Controller",
  72 + "think\\console\\command\\make\\Model",
  73 + "think\\console\\command\\optimize\\Autoload",
  74 + "think\\console\\command\\optimize\\Config",
  75 + "think\\console\\command\\optimize\\Route",
  76 + "think\\console\\command\\optimize\\Schema",
  77 + ];
  78 +
  79 + /**
  80 + * Console constructor.
  81 + * @access public
  82 + * @param string $name 名称
  83 + * @param string $version 版本
  84 + * @param null|string $user 执行用户
  85 + */
  86 + public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN', $user = null)
  87 + {
  88 + $this->name = $name;
  89 + $this->version = $version;
  90 +
  91 + if ($user) {
  92 + $this->setUser($user);
  93 + }
  94 +
  95 + $this->defaultCommand = 'list';
  96 + $this->definition = $this->getDefaultInputDefinition();
  97 +
  98 + foreach ($this->getDefaultCommands() as $command) {
  99 + $this->add($command);
  100 + }
  101 + }
  102 +
  103 + /**
  104 + * 设置执行用户
  105 + * @param $user
  106 + */
  107 + public function setUser($user)
  108 + {
  109 + $user = posix_getpwnam($user);
  110 + if ($user) {
  111 + posix_setuid($user['uid']);
  112 + posix_setgid($user['gid']);
  113 + }
  114 + }
  115 +
  116 + /**
  117 + * 初始化 Console
  118 + * @access public
  119 + * @param bool $run 是否运行 Console
  120 + * @return int|Console
  121 + */
  122 + public static function init($run = true)
  123 + {
  124 + static $console;
  125 +
  126 + if (!$console) {
  127 + $config = Config::get('console');
  128 + // 实例化 console
  129 + $console = new self($config['name'], $config['version'], $config['user']);
  130 +
  131 + // 读取指令集
  132 + if (is_file(CONF_PATH . 'command' . EXT)) {
  133 + $commands = include CONF_PATH . 'command' . EXT;
  134 +
  135 + if (is_array($commands)) {
  136 + foreach ($commands as $command) {
  137 + class_exists($command) &&
  138 + is_subclass_of($command, "\\think\\console\\Command") &&
  139 + $console->add(new $command()); // 注册指令
  140 + }
  141 + }
  142 + }
  143 + }
  144 +
  145 + return $run ? $console->run() : $console;
  146 + }
  147 +
  148 + /**
  149 + * 调用命令
  150 + * @access public
  151 + * @param string $command
  152 + * @param array $parameters
  153 + * @param string $driver
  154 + * @return Output
  155 + */
  156 + public static function call($command, array $parameters = [], $driver = 'buffer')
  157 + {
  158 + $console = self::init(false);
  159 +
  160 + array_unshift($parameters, $command);
  161 +
  162 + $input = new Input($parameters);
  163 + $output = new Output($driver);
  164 +
  165 + $console->setCatchExceptions(false);
  166 + $console->find($command)->run($input, $output);
  167 +
  168 + return $output;
  169 + }
  170 +
  171 + /**
  172 + * 执行当前的指令
  173 + * @access public
  174 + * @return int
  175 + * @throws \Exception
  176 + */
  177 + public function run()
  178 + {
  179 + $input = new Input();
  180 + $output = new Output();
  181 +
  182 + $this->configureIO($input, $output);
  183 +
  184 + try {
  185 + $exitCode = $this->doRun($input, $output);
  186 + } catch (\Exception $e) {
  187 + if (!$this->catchExceptions) throw $e;
  188 +
  189 + $output->renderException($e);
  190 +
  191 + $exitCode = $e->getCode();
  192 +
  193 + if (is_numeric($exitCode)) {
  194 + $exitCode = ((int) $exitCode) ?: 1;
  195 + } else {
  196 + $exitCode = 1;
  197 + }
  198 + }
  199 +
  200 + if ($this->autoExit) {
  201 + if ($exitCode > 255) $exitCode = 255;
  202 +
  203 + exit($exitCode);
  204 + }
  205 +
  206 + return $exitCode;
  207 + }
  208 +
  209 + /**
  210 + * 执行指令
  211 + * @access public
  212 + * @param Input $input 输入
  213 + * @param Output $output 输出
  214 + * @return int
  215 + */
  216 + public function doRun(Input $input, Output $output)
  217 + {
  218 + // 获取版本信息
  219 + if (true === $input->hasParameterOption(['--version', '-V'])) {
  220 + $output->writeln($this->getLongVersion());
  221 +
  222 + return 0;
  223 + }
  224 +
  225 + $name = $this->getCommandName($input);
  226 +
  227 + // 获取帮助信息
  228 + if (true === $input->hasParameterOption(['--help', '-h'])) {
  229 + if (!$name) {
  230 + $name = 'help';
  231 + $input = new Input(['help']);
  232 + } else {
  233 + $this->wantHelps = true;
  234 + }
  235 + }
  236 +
  237 + if (!$name) {
  238 + $name = $this->defaultCommand;
  239 + $input = new Input([$this->defaultCommand]);
  240 + }
  241 +
  242 + return $this->doRunCommand($this->find($name), $input, $output);
  243 + }
  244 +
  245 + /**
  246 + * 设置输入参数定义
  247 + * @access public
  248 + * @param InputDefinition $definition 输入定义
  249 + * @return $this;
  250 + */
  251 + public function setDefinition(InputDefinition $definition)
  252 + {
  253 + $this->definition = $definition;
  254 +
  255 + return $this;
  256 + }
  257 +
  258 + /**
  259 + * 获取输入参数定义
  260 + * @access public
  261 + * @return InputDefinition
  262 + */
  263 + public function getDefinition()
  264 + {
  265 + return $this->definition;
  266 + }
  267 +
  268 + /**
  269 + * 获取帮助信息
  270 + * @access public
  271 + * @return string
  272 + */
  273 + public function getHelp()
  274 + {
  275 + return $this->getLongVersion();
  276 + }
  277 +
  278 + /**
  279 + * 设置是否捕获异常
  280 + * @access public
  281 + * @param bool $boolean 是否捕获
  282 + * @return $this
  283 + */
  284 + public function setCatchExceptions($boolean)
  285 + {
  286 + $this->catchExceptions = (bool) $boolean;
  287 +
  288 + return $this;
  289 + }
  290 +
  291 + /**
  292 + * 设置是否自动退出
  293 + * @access public
  294 + * @param bool $boolean 是否自动退出
  295 + * @return $this
  296 + */
  297 + public function setAutoExit($boolean)
  298 + {
  299 + $this->autoExit = (bool) $boolean;
  300 +
  301 + return $this;
  302 + }
  303 +
  304 + /**
  305 + * 获取名称
  306 + * @access public
  307 + * @return string
  308 + */
  309 + public function getName()
  310 + {
  311 + return $this->name;
  312 + }
  313 +
  314 + /**
  315 + * 设置名称
  316 + * @access public
  317 + * @param string $name 名称
  318 + * @return $this
  319 + */
  320 + public function setName($name)
  321 + {
  322 + $this->name = $name;
  323 +
  324 + return $this;
  325 + }
  326 +
  327 + /**
  328 + * 获取版本
  329 + * @access public
  330 + * @return string
  331 + */
  332 + public function getVersion()
  333 + {
  334 + return $this->version;
  335 + }
  336 +
  337 + /**
  338 + * 设置版本
  339 + * @access public
  340 + * @param string $version 版本信息
  341 + * @return $this
  342 + */
  343 + public function setVersion($version)
  344 + {
  345 + $this->version = $version;
  346 +
  347 + return $this;
  348 + }
  349 +
  350 + /**
  351 + * 获取完整的版本号
  352 + * @access public
  353 + * @return string
  354 + */
  355 + public function getLongVersion()
  356 + {
  357 + if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
  358 + return sprintf(
  359 + '<info>%s</info> version <comment>%s</comment>',
  360 + $this->getName(),
  361 + $this->getVersion()
  362 + );
  363 + }
  364 +
  365 + return '<info>Console Tool</info>';
  366 + }
  367 +
  368 + /**
  369 + * 注册一个指令
  370 + * @access public
  371 + * @param string $name 指令名称
  372 + * @return Command
  373 + */
  374 + public function register($name)
  375 + {
  376 + return $this->add(new Command($name));
  377 + }
  378 +
  379 + /**
  380 + * 批量添加指令
  381 + * @access public
  382 + * @param Command[] $commands 指令实例
  383 + * @return $this
  384 + */
  385 + public function addCommands(array $commands)
  386 + {
  387 + foreach ($commands as $command) $this->add($command);
  388 +
  389 + return $this;
  390 + }
  391 +
  392 + /**
  393 + * 添加一个指令
  394 + * @access public
  395 + * @param Command $command 命令实例
  396 + * @return Command|bool
  397 + */
  398 + public function add(Command $command)
  399 + {
  400 + if (!$command->isEnabled()) {
  401 + $command->setConsole(null);
  402 + return false;
  403 + }
  404 +
  405 + $command->setConsole($this);
  406 +
  407 + if (null === $command->getDefinition()) {
  408 + throw new \LogicException(
  409 + sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command))
  410 + );
  411 + }
  412 +
  413 + $this->commands[$command->getName()] = $command;
  414 +
  415 + foreach ($command->getAliases() as $alias) {
  416 + $this->commands[$alias] = $command;
  417 + }
  418 +
  419 + return $command;
  420 + }
  421 +
  422 + /**
  423 + * 获取指令
  424 + * @access public
  425 + * @param string $name 指令名称
  426 + * @return Command
  427 + * @throws \InvalidArgumentException
  428 + */
  429 + public function get($name)
  430 + {
  431 + if (!isset($this->commands[$name])) {
  432 + throw new \InvalidArgumentException(
  433 + sprintf('The command "%s" does not exist.', $name)
  434 + );
  435 + }
  436 +
  437 + $command = $this->commands[$name];
  438 +
  439 + if ($this->wantHelps) {
  440 + $this->wantHelps = false;
  441 +
  442 + /** @var HelpCommand $helpCommand */
  443 + $helpCommand = $this->get('help');
  444 + $helpCommand->setCommand($command);
  445 +
  446 + return $helpCommand;
  447 + }
  448 +
  449 + return $command;
  450 + }
  451 +
  452 + /**
  453 + * 某个指令是否存在
  454 + * @access public
  455 + * @param string $name 指令名称
  456 + * @return bool
  457 + */
  458 + public function has($name)
  459 + {
  460 + return isset($this->commands[$name]);
  461 + }
  462 +
  463 + /**
  464 + * 获取所有的命名空间
  465 + * @access public
  466 + * @return array
  467 + */
  468 + public function getNamespaces()
  469 + {
  470 + $namespaces = [];
  471 +
  472 + foreach ($this->commands as $command) {
  473 + $namespaces = array_merge(
  474 + $namespaces, $this->extractAllNamespaces($command->getName())
  475 + );
  476 +
  477 + foreach ($command->getAliases() as $alias) {
  478 + $namespaces = array_merge(
  479 + $namespaces, $this->extractAllNamespaces($alias)
  480 + );
  481 + }
  482 + }
  483 +
  484 + return array_values(array_unique(array_filter($namespaces)));
  485 + }
  486 +
  487 + /**
  488 + * 查找注册命名空间中的名称或缩写
  489 + * @access public
  490 + * @param string $namespace
  491 + * @return string
  492 + * @throws \InvalidArgumentException
  493 + */
  494 + public function findNamespace($namespace)
  495 + {
  496 + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
  497 + return preg_quote($matches[1]) . '[^:]*';
  498 + }, $namespace);
  499 +
  500 + $allNamespaces = $this->getNamespaces();
  501 + $namespaces = preg_grep('{^' . $expr . '}', $allNamespaces);
  502 +
  503 + if (empty($namespaces)) {
  504 + $message = sprintf(
  505 + 'There are no commands defined in the "%s" namespace.', $namespace
  506 + );
  507 +
  508 + if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
  509 + if (1 == count($alternatives)) {
  510 + $message .= "\n\nDid you mean this?\n ";
  511 + } else {
  512 + $message .= "\n\nDid you mean one of these?\n ";
  513 + }
  514 +
  515 + $message .= implode("\n ", $alternatives);
  516 + }
  517 +
  518 + throw new \InvalidArgumentException($message);
  519 + }
  520 +
  521 + $exact = in_array($namespace, $namespaces, true);
  522 +
  523 + if (count($namespaces) > 1 && !$exact) {
  524 + throw new \InvalidArgumentException(
  525 + sprintf(
  526 + 'The namespace "%s" is ambiguous (%s).',
  527 + $namespace,
  528 + $this->getAbbreviationSuggestions(array_values($namespaces)))
  529 + );
  530 + }
  531 +
  532 + return $exact ? $namespace : reset($namespaces);
  533 + }
  534 +
  535 + /**
  536 + * 查找指令
  537 + * @access public
  538 + * @param string $name 名称或者别名
  539 + * @return Command
  540 + * @throws \InvalidArgumentException
  541 + */
  542 + public function find($name)
  543 + {
  544 + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
  545 + return preg_quote($matches[1]) . '[^:]*';
  546 + }, $name);
  547 +
  548 + $allCommands = array_keys($this->commands);
  549 + $commands = preg_grep('{^' . $expr . '}', $allCommands);
  550 +
  551 + if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) {
  552 + if (false !== ($pos = strrpos($name, ':'))) {
  553 + $this->findNamespace(substr($name, 0, $pos));
  554 + }
  555 +
  556 + $message = sprintf('Command "%s" is not defined.', $name);
  557 +
  558 + if ($alternatives = $this->findAlternatives($name, $allCommands)) {
  559 + if (1 == count($alternatives)) {
  560 + $message .= "\n\nDid you mean this?\n ";
  561 + } else {
  562 + $message .= "\n\nDid you mean one of these?\n ";
  563 + }
  564 + $message .= implode("\n ", $alternatives);
  565 + }
  566 +
  567 + throw new \InvalidArgumentException($message);
  568 + }
  569 +
  570 + if (count($commands) > 1) {
  571 + $commandList = $this->commands;
  572 + $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) {
  573 + $commandName = $commandList[$nameOrAlias]->getName();
  574 +
  575 + return $commandName === $nameOrAlias || !in_array($commandName, $commands);
  576 + });
  577 + }
  578 +
  579 + $exact = in_array($name, $commands, true);
  580 + if (count($commands) > 1 && !$exact) {
  581 + $suggestions = $this->getAbbreviationSuggestions(array_values($commands));
  582 +
  583 + throw new \InvalidArgumentException(
  584 + sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions)
  585 + );
  586 + }
  587 +
  588 + return $this->get($exact ? $name : reset($commands));
  589 + }
  590 +
  591 + /**
  592 + * 获取所有的指令
  593 + * @access public
  594 + * @param string $namespace 命名空间
  595 + * @return Command[]
  596 + */
  597 + public function all($namespace = null)
  598 + {
  599 + if (null === $namespace) return $this->commands;
  600 +
  601 + $commands = [];
  602 +
  603 + foreach ($this->commands as $name => $command) {
  604 + $ext = $this->extractNamespace($name, substr_count($namespace, ':') + 1);
  605 +
  606 + if ($ext === $namespace) $commands[$name] = $command;
  607 + }
  608 +
  609 + return $commands;
  610 + }
  611 +
  612 + /**
  613 + * 获取可能的指令名
  614 + * @access public
  615 + * @param array $names 指令名
  616 + * @return array
  617 + */
  618 + public static function getAbbreviations($names)
  619 + {
  620 + $abbrevs = [];
  621 + foreach ($names as $name) {
  622 + for ($len = strlen($name); $len > 0; --$len) {
  623 + $abbrev = substr($name, 0, $len);
  624 + $abbrevs[$abbrev][] = $name;
  625 + }
  626 + }
  627 +
  628 + return $abbrevs;
  629 + }
  630 +
  631 + /**
  632 + * 配置基于用户的参数和选项的输入和输出实例
  633 + * @access protected
  634 + * @param Input $input 输入实例
  635 + * @param Output $output 输出实例
  636 + * @return void
  637 + */
  638 + protected function configureIO(Input $input, Output $output)
  639 + {
  640 + if (true === $input->hasParameterOption(['--ansi'])) {
  641 + $output->setDecorated(true);
  642 + } elseif (true === $input->hasParameterOption(['--no-ansi'])) {
  643 + $output->setDecorated(false);
  644 + }
  645 +
  646 + if (true === $input->hasParameterOption(['--no-interaction', '-n'])) {
  647 + $input->setInteractive(false);
  648 + }
  649 +
  650 + if (true === $input->hasParameterOption(['--quiet', '-q'])) {
  651 + $output->setVerbosity(Output::VERBOSITY_QUIET);
  652 + } else {
  653 + if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) {
  654 + $output->setVerbosity(Output::VERBOSITY_DEBUG);
  655 + } elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) {
  656 + $output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE);
  657 + } elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) {
  658 + $output->setVerbosity(Output::VERBOSITY_VERBOSE);
  659 + }
  660 + }
  661 + }
  662 +
  663 + /**
  664 + * 执行指令
  665 + * @access protected
  666 + * @param Command $command 指令实例
  667 + * @param Input $input 输入实例
  668 + * @param Output $output 输出实例
  669 + * @return int
  670 + * @throws \Exception
  671 + */
  672 + protected function doRunCommand(Command $command, Input $input, Output $output)
  673 + {
  674 + return $command->run($input, $output);
  675 + }
  676 +
  677 + /**
  678 + * 获取指令的名称
  679 + * @access protected
  680 + * @param Input $input 输入实例
  681 + * @return string
  682 + */
  683 + protected function getCommandName(Input $input)
  684 + {
  685 + return $input->getFirstArgument();
  686 + }
  687 +
  688 + /**
  689 + * 获取默认输入定义
  690 + * @access protected
  691 + * @return InputDefinition
  692 + */
  693 + protected function getDefaultInputDefinition()
  694 + {
  695 + return new InputDefinition([
  696 + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
  697 + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'),
  698 + new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this console version'),
  699 + new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
  700 + 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'),
  701 + new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
  702 + new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
  703 + new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
  704 + ]);
  705 + }
  706 +
  707 + /**
  708 + * 获取默认命令
  709 + * @access protected
  710 + * @return Command[]
  711 + */
  712 + protected function getDefaultCommands()
  713 + {
  714 + $defaultCommands = [];
  715 +
  716 + foreach (self::$defaultCommands as $class) {
  717 + if (class_exists($class) && is_subclass_of($class, "think\\console\\Command")) {
  718 + $defaultCommands[] = new $class();
  719 + }
  720 + }
  721 +
  722 + return $defaultCommands;
  723 + }
  724 +
  725 + /**
  726 + * 添加默认指令
  727 + * @access public
  728 + * @param array $classes 指令
  729 + * @return void
  730 + */
  731 + public static function addDefaultCommands(array $classes)
  732 + {
  733 + self::$defaultCommands = array_merge(self::$defaultCommands, $classes);
  734 + }
  735 +
  736 + /**
  737 + * 获取可能的建议
  738 + * @access private
  739 + * @param array $abbrevs
  740 + * @return string
  741 + */
  742 + private function getAbbreviationSuggestions($abbrevs)
  743 + {
  744 + return sprintf(
  745 + '%s, %s%s',
  746 + $abbrevs[0],
  747 + $abbrevs[1],
  748 + count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''
  749 + );
  750 + }
  751 +
  752 + /**
  753 + * 返回指令的命名空间部分
  754 + * @access public
  755 + * @param string $name 指令名称
  756 + * @param string $limit 部分的命名空间的最大数量
  757 + * @return string
  758 + */
  759 + public function extractNamespace($name, $limit = null)
  760 + {
  761 + $parts = explode(':', $name);
  762 + array_pop($parts);
  763 +
  764 + return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit));
  765 + }
  766 +
  767 + /**
  768 + * 查找可替代的建议
  769 + * @access private
  770 + * @param string $name 指令名称
  771 + * @param array|\Traversable $collection 建议集合
  772 + * @return array
  773 + */
  774 + private function findAlternatives($name, $collection)
  775 + {
  776 + $threshold = 1e3;
  777 + $alternatives = [];
  778 + $collectionParts = [];
  779 +
  780 + foreach ($collection as $item) {
  781 + $collectionParts[$item] = explode(':', $item);
  782 + }
  783 +
  784 + foreach (explode(':', $name) as $i => $subname) {
  785 + foreach ($collectionParts as $collectionName => $parts) {
  786 + $exists = isset($alternatives[$collectionName]);
  787 +
  788 + if (!isset($parts[$i]) && $exists) {
  789 + $alternatives[$collectionName] += $threshold;
  790 + continue;
  791 + } elseif (!isset($parts[$i])) {
  792 + continue;
  793 + }
  794 +
  795 + $lev = levenshtein($subname, $parts[$i]);
  796 +
  797 + if ($lev <= strlen($subname) / 3 ||
  798 + '' !== $subname &&
  799 + false !== strpos($parts[$i], $subname)
  800 + ) {
  801 + $alternatives[$collectionName] = $exists ?
  802 + $alternatives[$collectionName] + $lev :
  803 + $lev;
  804 + } elseif ($exists) {
  805 + $alternatives[$collectionName] += $threshold;
  806 + }
  807 + }
  808 + }
  809 +
  810 + foreach ($collection as $item) {
  811 + $lev = levenshtein($name, $item);
  812 +
  813 + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
  814 + $alternatives[$item] = isset($alternatives[$item]) ?
  815 + $alternatives[$item] - $lev :
  816 + $lev;
  817 + }
  818 + }
  819 +
  820 + $alternatives = array_filter($alternatives, function ($lev) use ($threshold) {
  821 + return $lev < 2 * $threshold;
  822 + });
  823 +
  824 + asort($alternatives);
  825 +
  826 + return array_keys($alternatives);
  827 + }
  828 +
  829 + /**
  830 + * 设置默认的指令
  831 + * @access public
  832 + * @param string $commandName 指令名称
  833 + * @return $this
  834 + */
  835 + public function setDefaultCommand($commandName)
  836 + {
  837 + $this->defaultCommand = $commandName;
  838 +
  839 + return $this;
  840 + }
  841 +
  842 + /**
  843 + * 返回所有的命名空间
  844 + * @access private
  845 + * @param string $name 指令名称
  846 + * @return array
  847 + */
  848 + private function extractAllNamespaces($name)
  849 + {
  850 + $namespaces = [];
  851 +
  852 + foreach (explode(':', $name, -1) as $part) {
  853 + if (count($namespaces)) {
  854 + $namespaces[] = end($namespaces) . ':' . $part;
  855 + } else {
  856 + $namespaces[] = $part;
  857 + }
  858 + }
  859 +
  860 + return $namespaces;
  861 + }
  862 +
  863 +}
  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\ValidateException;
  15 +use traits\controller\Jump;
  16 +
  17 +Loader::import('controller/Jump', TRAIT_PATH, EXT);
  18 +
  19 +class Controller
  20 +{
  21 + use Jump;
  22 +
  23 + /**
  24 + * @var \think\View 视图类实例
  25 + */
  26 + protected $view;
  27 +
  28 + /**
  29 + * @var \think\Request Request 实例
  30 + */
  31 + protected $request;
  32 +
  33 + /**
  34 + * @var bool 验证失败是否抛出异常
  35 + */
  36 + protected $failException = false;
  37 +
  38 + /**
  39 + * @var bool 是否批量验证
  40 + */
  41 + protected $batchValidate = false;
  42 +
  43 + /**
  44 + * @var array 前置操作方法列表
  45 + */
  46 + protected $beforeActionList = [];
  47 +
  48 + /**
  49 + * 构造方法
  50 + * @access public
  51 + * @param Request $request Request 对象
  52 + */
  53 + public function __construct(Request $request = null)
  54 + {
  55 + $this->view = View::instance(Config::get('template'), Config::get('view_replace_str'));
  56 + $this->request = is_null($request) ? Request::instance() : $request;
  57 +
  58 + // 控制器初始化
  59 + $this->_initialize();
  60 +
  61 + // 前置操作方法
  62 + if ($this->beforeActionList) {
  63 + foreach ($this->beforeActionList as $method => $options) {
  64 + is_numeric($method) ?
  65 + $this->beforeAction($options) :
  66 + $this->beforeAction($method, $options);
  67 + }
  68 + }
  69 + }
  70 +
  71 + /**
  72 + * 初始化操作
  73 + * @access protected
  74 + */
  75 + protected function _initialize()
  76 + {
  77 + }
  78 +
  79 + /**
  80 + * 前置操作
  81 + * @access protected
  82 + * @param string $method 前置操作方法名
  83 + * @param array $options 调用参数 ['only'=>[...]] 或者 ['except'=>[...]]
  84 + * @return void
  85 + */
  86 + protected function beforeAction($method, $options = [])
  87 + {
  88 + if (isset($options['only'])) {
  89 + if (is_string($options['only'])) {
  90 + $options['only'] = explode(',', $options['only']);
  91 + }
  92 +
  93 + if (!in_array($this->request->action(), $options['only'])) {
  94 + return;
  95 + }
  96 + } elseif (isset($options['except'])) {
  97 + if (is_string($options['except'])) {
  98 + $options['except'] = explode(',', $options['except']);
  99 + }
  100 +
  101 + if (in_array($this->request->action(), $options['except'])) {
  102 + return;
  103 + }
  104 + }
  105 +
  106 + call_user_func([$this, $method]);
  107 + }
  108 +
  109 + /**
  110 + * 加载模板输出
  111 + * @access protected
  112 + * @param string $template 模板文件名
  113 + * @param array $vars 模板输出变量
  114 + * @param array $replace 模板替换
  115 + * @param array $config 模板参数
  116 + * @return mixed
  117 + */
  118 + protected function fetch($template = '', $vars = [], $replace = [], $config = [])
  119 + {
  120 + return $this->view->fetch($template, $vars, $replace, $config);
  121 + }
  122 +
  123 + /**
  124 + * 渲染内容输出
  125 + * @access protected
  126 + * @param string $content 模板内容
  127 + * @param array $vars 模板输出变量
  128 + * @param array $replace 替换内容
  129 + * @param array $config 模板参数
  130 + * @return mixed
  131 + */
  132 + protected function display($content = '', $vars = [], $replace = [], $config = [])
  133 + {
  134 + return $this->view->display($content, $vars, $replace, $config);
  135 + }
  136 +
  137 + /**
  138 + * 模板变量赋值
  139 + * @access protected
  140 + * @param mixed $name 要显示的模板变量
  141 + * @param mixed $value 变量的值
  142 + * @return $this
  143 + */
  144 + protected function assign($name, $value = '')
  145 + {
  146 + $this->view->assign($name, $value);
  147 +
  148 + return $this;
  149 + }
  150 +
  151 + /**
  152 + * 初始化模板引擎
  153 + * @access protected
  154 + * @param array|string $engine 引擎参数
  155 + * @return $this
  156 + */
  157 + protected function engine($engine)
  158 + {
  159 + $this->view->engine($engine);
  160 +
  161 + return $this;
  162 + }
  163 +
  164 + /**
  165 + * 设置验证失败后是否抛出异常
  166 + * @access protected
  167 + * @param bool $fail 是否抛出异常
  168 + * @return $this
  169 + */
  170 + protected function validateFailException($fail = true)
  171 + {
  172 + $this->failException = $fail;
  173 +
  174 + return $this;
  175 + }
  176 +
  177 + /**
  178 + * 验证数据
  179 + * @access protected
  180 + * @param array $data 数据
  181 + * @param string|array $validate 验证器名或者验证规则数组
  182 + * @param array $message 提示信息
  183 + * @param bool $batch 是否批量验证
  184 + * @param mixed $callback 回调方法(闭包)
  185 + * @return array|string|true
  186 + * @throws ValidateException
  187 + */
  188 + protected function validate($data, $validate, $message = [], $batch = false, $callback = null)
  189 + {
  190 + if (is_array($validate)) {
  191 + $v = Loader::validate();
  192 + $v->rule($validate);
  193 + } else {
  194 + // 支持场景
  195 + if (strpos($validate, '.')) {
  196 + list($validate, $scene) = explode('.', $validate);
  197 + }
  198 +
  199 + $v = Loader::validate($validate);
  200 +
  201 + !empty($scene) && $v->scene($scene);
  202 + }
  203 +
  204 + // 批量验证
  205 + if ($batch || $this->batchValidate) {
  206 + $v->batch(true);
  207 + }
  208 +
  209 + // 设置错误信息
  210 + if (is_array($message)) {
  211 + $v->message($message);
  212 + }
  213 +
  214 + // 使用回调验证
  215 + if ($callback && is_callable($callback)) {
  216 + call_user_func_array($callback, [$v, &$data]);
  217 + }
  218 +
  219 + if (!$v->check($data)) {
  220 + if ($this->failException) {
  221 + throw new ValidateException($v->getError());
  222 + }
  223 +
  224 + return $v->getError();
  225 + }
  226 +
  227 + return true;
  228 + }
  229 +}
  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 Cookie
  15 +{
  16 + /**
  17 + * @var array cookie 设置参数
  18 + */
  19 + protected static $config = [
  20 + 'prefix' => '', // cookie 名称前缀
  21 + 'expire' => 0, // cookie 保存时间
  22 + 'path' => '/', // cookie 保存路径
  23 + 'domain' => '', // cookie 有效域名
  24 + 'secure' => false, // cookie 启用安全传输
  25 + 'httponly' => false, // httponly 设置
  26 + 'setcookie' => true, // 是否使用 setcookie
  27 + ];
  28 +
  29 + /**
  30 + * @var bool 是否完成初始化了
  31 + */
  32 + protected static $init;
  33 +
  34 + /**
  35 + * Cookie初始化
  36 + * @access public
  37 + * @param array $config 配置参数
  38 + * @return void
  39 + */
  40 + public static function init(array $config = [])
  41 + {
  42 + if (empty($config)) {
  43 + $config = Config::get('cookie');
  44 + }
  45 +
  46 + self::$config = array_merge(self::$config, array_change_key_case($config));
  47 +
  48 + if (!empty(self::$config['httponly'])) {
  49 + ini_set('session.cookie_httponly', 1);
  50 + }
  51 +
  52 + self::$init = true;
  53 + }
  54 +
  55 + /**
  56 + * 设置或者获取 cookie 作用域(前缀)
  57 + * @access public
  58 + * @param string $prefix 前缀
  59 + * @return string|
  60 + */
  61 + public static function prefix($prefix = '')
  62 + {
  63 + if (empty($prefix)) {
  64 + return self::$config['prefix'];
  65 + }
  66 +
  67 + return self::$config['prefix'] = $prefix;
  68 + }
  69 +
  70 + /**
  71 + * Cookie 设置、获取、删除
  72 + * @access public
  73 + * @param string $name cookie 名称
  74 + * @param mixed $value cookie 值
  75 + * @param mixed $option 可选参数 可能会是 null|integer|string
  76 + * @return void
  77 + */
  78 + public static function set($name, $value = '', $option = null)
  79 + {
  80 + !isset(self::$init) && self::init();
  81 +
  82 + // 参数设置(会覆盖黙认设置)
  83 + if (!is_null($option)) {
  84 + if (is_numeric($option)) {
  85 + $option = ['expire' => $option];
  86 + } elseif (is_string($option)) {
  87 + parse_str($option, $option);
  88 + }
  89 +
  90 + $config = array_merge(self::$config, array_change_key_case($option));
  91 + } else {
  92 + $config = self::$config;
  93 + }
  94 +
  95 + $name = $config['prefix'] . $name;
  96 +
  97 + // 设置 cookie
  98 + if (is_array($value)) {
  99 + array_walk_recursive($value, 'self::jsonFormatProtect', 'encode');
  100 + $value = 'think:' . json_encode($value);
  101 + }
  102 +
  103 + $expire = !empty($config['expire']) ?
  104 + $_SERVER['REQUEST_TIME'] + intval($config['expire']) :
  105 + 0;
  106 +
  107 + if ($config['setcookie']) {
  108 + setcookie(
  109 + $name, $value, $expire, $config['path'], $config['domain'],
  110 + $config['secure'], $config['httponly']
  111 + );
  112 + }
  113 +
  114 + $_COOKIE[$name] = $value;
  115 + }
  116 +
  117 + /**
  118 + * 永久保存 Cookie 数据
  119 + * @access public
  120 + * @param string $name cookie 名称
  121 + * @param mixed $value cookie 值
  122 + * @param mixed $option 可选参数 可能会是 null|integer|string
  123 + * @return void
  124 + */
  125 + public static function forever($name, $value = '', $option = null)
  126 + {
  127 + if (is_null($option) || is_numeric($option)) {
  128 + $option = [];
  129 + }
  130 +
  131 + $option['expire'] = 315360000;
  132 +
  133 + self::set($name, $value, $option);
  134 + }
  135 +
  136 + /**
  137 + * 判断是否有 Cookie 数据
  138 + * @access public
  139 + * @param string $name cookie 名称
  140 + * @param string|null $prefix cookie 前缀
  141 + * @return bool
  142 + */
  143 + public static function has($name, $prefix = null)
  144 + {
  145 + !isset(self::$init) && self::init();
  146 +
  147 + $prefix = !is_null($prefix) ? $prefix : self::$config['prefix'];
  148 +
  149 + return isset($_COOKIE[$prefix . $name]);
  150 + }
  151 +
  152 + /**
  153 + * 获取 Cookie 的值
  154 + * @access public
  155 + * @param string $name cookie 名称
  156 + * @param string|null $prefix cookie 前缀
  157 + * @return mixed
  158 + */
  159 + public static function get($name = '', $prefix = null)
  160 + {
  161 + !isset(self::$init) && self::init();
  162 +
  163 + $prefix = !is_null($prefix) ? $prefix : self::$config['prefix'];
  164 + $key = $prefix . $name;
  165 +
  166 + if ('' == $name) {
  167 + // 获取全部
  168 + if ($prefix) {
  169 + $value = [];
  170 +
  171 + foreach ($_COOKIE as $k => $val) {
  172 + if (0 === strpos($k, $prefix)) {
  173 + $value[$k] = $val;
  174 + }
  175 +
  176 + }
  177 + } else {
  178 + $value = $_COOKIE;
  179 + }
  180 + } elseif (isset($_COOKIE[$key])) {
  181 + $value = $_COOKIE[$key];
  182 +
  183 + if (0 === strpos($value, 'think:')) {
  184 + $value = json_decode(substr($value, 6), true);
  185 + array_walk_recursive($value, 'self::jsonFormatProtect', 'decode');
  186 + }
  187 + } else {
  188 + $value = null;
  189 + }
  190 +
  191 + return $value;
  192 + }
  193 +
  194 + /**
  195 + * 删除 Cookie
  196 + * @access public
  197 + * @param string $name cookie 名称
  198 + * @param string|null $prefix cookie 前缀
  199 + * @return void
  200 + */
  201 + public static function delete($name, $prefix = null)
  202 + {
  203 + !isset(self::$init) && self::init();
  204 +
  205 + $config = self::$config;
  206 + $prefix = !is_null($prefix) ? $prefix : $config['prefix'];
  207 + $name = $prefix . $name;
  208 +
  209 + if ($config['setcookie']) {
  210 + setcookie(
  211 + $name, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'],
  212 + $config['domain'], $config['secure'], $config['httponly']
  213 + );
  214 + }
  215 +
  216 + // 删除指定 cookie
  217 + unset($_COOKIE[$name]);
  218 + }
  219 +
  220 + /**
  221 + * 清除指定前缀的所有 cookie
  222 + * @access public
  223 + * @param string|null $prefix cookie 前缀
  224 + * @return void
  225 + */
  226 + public static function clear($prefix = null)
  227 + {
  228 + if (empty($_COOKIE)) {
  229 + return;
  230 + }
  231 +
  232 + !isset(self::$init) && self::init();
  233 +
  234 + // 要删除的 cookie 前缀,不指定则删除 config 设置的指定前缀
  235 + $config = self::$config;
  236 + $prefix = !is_null($prefix) ? $prefix : $config['prefix'];
  237 +
  238 + if ($prefix) {
  239 + foreach ($_COOKIE as $key => $val) {
  240 + if (0 === strpos($key, $prefix)) {
  241 + if ($config['setcookie']) {
  242 + setcookie(
  243 + $key, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'],
  244 + $config['domain'], $config['secure'], $config['httponly']
  245 + );
  246 + }
  247 +
  248 + unset($_COOKIE[$key]);
  249 + }
  250 + }
  251 + }
  252 + }
  253 +
  254 + /**
  255 + * json 转换时的格式保护
  256 + * @access protected
  257 + * @param mixed $val 要转换的值
  258 + * @param string $key 键名
  259 + * @param string $type 转换类别
  260 + * @return void
  261 + */
  262 + protected static function jsonFormatProtect(&$val, $key, $type = 'encode')
  263 + {
  264 + if (!empty($val) && true !== $val) {
  265 + $val = 'decode' == $type ? urldecode($val) : urlencode($val);
  266 + }
  267 + }
  268 +}
  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\db\Connection;
  15 +use think\db\Query;
  16 +
  17 +/**
  18 + * Class Db
  19 + * @package think
  20 + * @method static Query table(string $table) 指定数据表(含前缀)
  21 + * @method static Query name(string $name) 指定数据表(不含前缀)
  22 + * @method static Query where(mixed $field, string $op = null, mixed $condition = null) 查询条件
  23 + * @method static Query join(mixed $join, mixed $condition = null, string $type = 'INNER') JOIN查询
  24 + * @method static Query union(mixed $union, boolean $all = false) UNION查询
  25 + * @method static Query limit(mixed $offset, integer $length = null) 查询LIMIT
  26 + * @method static Query order(mixed $field, string $order = null) 查询ORDER
  27 + * @method static Query cache(mixed $key = null , integer $expire = null) 设置查询缓存
  28 + * @method static mixed value(string $field) 获取某个字段的值
  29 + * @method static array column(string $field, string $key = '') 获取某个列的值
  30 + * @method static Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') 视图查询
  31 + * @method static mixed find(mixed $data = null) 查询单个记录
  32 + * @method static mixed select(mixed $data = null) 查询多个记录
  33 + * @method static integer insert(array $data, boolean $replace = false, boolean $getLastInsID = false, string $sequence = null) 插入一条记录
  34 + * @method static integer insertGetId(array $data, boolean $replace = false, string $sequence = null) 插入一条记录并返回自增ID
  35 + * @method static integer insertAll(array $dataSet) 插入多条记录
  36 + * @method static integer update(array $data) 更新记录
  37 + * @method static integer delete(mixed $data = null) 删除记录
  38 + * @method static boolean chunk(integer $count, callable $callback, string $column = null) 分块获取数据
  39 + * @method static mixed query(string $sql, array $bind = [], boolean $master = false, bool $pdo = false) SQL查询
  40 + * @method static integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) SQL执行
  41 + * @method static Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) 分页查询
  42 + * @method static mixed transaction(callable $callback) 执行数据库事务
  43 + * @method static void startTrans() 启动事务
  44 + * @method static void commit() 用于非自动提交状态下面的查询提交
  45 + * @method static void rollback() 事务回滚
  46 + * @method static boolean batchQuery(array $sqlArray) 批处理执行SQL语句
  47 + * @method static string quote(string $str) SQL指令安全过滤
  48 + * @method static string getLastInsID($sequence = null) 获取最近插入的ID
  49 + */
  50 +class Db
  51 +{
  52 + /**
  53 + * @var Connection[] 数据库连接实例
  54 + */
  55 + private static $instance = [];
  56 +
  57 + /**
  58 + * @var int 查询次数
  59 + */
  60 + public static $queryTimes = 0;
  61 +
  62 + /**
  63 + * @var int 执行次数
  64 + */
  65 + public static $executeTimes = 0;
  66 +
  67 + /**
  68 + * 数据库初始化,并取得数据库类实例
  69 + * @access public
  70 + * @param mixed $config 连接配置
  71 + * @param bool|string $name 连接标识 true 强制重新连接
  72 + * @return Connection
  73 + * @throws Exception
  74 + */
  75 + public static function connect($config = [], $name = false)
  76 + {
  77 + if (false === $name) {
  78 + $name = md5(serialize($config));
  79 + }
  80 +
  81 + if (true === $name || !isset(self::$instance[$name])) {
  82 + // 解析连接参数 支持数组和字符串
  83 + $options = self::parseConfig($config);
  84 +
  85 + if (empty($options['type'])) {
  86 + throw new \InvalidArgumentException('Undefined db type');
  87 + }
  88 +
  89 + $class = false !== strpos($options['type'], '\\') ?
  90 + $options['type'] :
  91 + '\\think\\db\\connector\\' . ucwords($options['type']);
  92 +
  93 + // 记录初始化信息
  94 + if (App::$debug) {
  95 + Log::record('[ DB ] INIT ' . $options['type'], 'info');
  96 + }
  97 +
  98 + if (true === $name) {
  99 + $name = md5(serialize($config));
  100 + }
  101 +
  102 + self::$instance[$name] = new $class($options);
  103 + }
  104 +
  105 + return self::$instance[$name];
  106 + }
  107 +
  108 + /**
  109 + * 清除连接实例
  110 + * @access public
  111 + * @return void
  112 + */
  113 + public static function clear()
  114 + {
  115 + self::$instance = [];
  116 + }
  117 +
  118 + /**
  119 + * 数据库连接参数解析
  120 + * @access private
  121 + * @param mixed $config 连接参数
  122 + * @return array
  123 + */
  124 + private static function parseConfig($config)
  125 + {
  126 + if (empty($config)) {
  127 + $config = Config::get('database');
  128 + } elseif (is_string($config) && false === strpos($config, '/')) {
  129 + $config = Config::get($config); // 支持读取配置参数
  130 + }
  131 +
  132 + return is_string($config) ? self::parseDsn($config) : $config;
  133 + }
  134 +
  135 + /**
  136 + * DSN 解析
  137 + * 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1&param2=val2#utf8
  138 + * @access private
  139 + * @param string $dsnStr 数据库 DSN 字符串解析
  140 + * @return array
  141 + */
  142 + private static function parseDsn($dsnStr)
  143 + {
  144 + $info = parse_url($dsnStr);
  145 +
  146 + if (!$info) {
  147 + return [];
  148 + }
  149 +
  150 + $dsn = [
  151 + 'type' => $info['scheme'],
  152 + 'username' => isset($info['user']) ? $info['user'] : '',
  153 + 'password' => isset($info['pass']) ? $info['pass'] : '',
  154 + 'hostname' => isset($info['host']) ? $info['host'] : '',
  155 + 'hostport' => isset($info['port']) ? $info['port'] : '',
  156 + 'database' => !empty($info['path']) ? ltrim($info['path'], '/') : '',
  157 + 'charset' => isset($info['fragment']) ? $info['fragment'] : 'utf8',
  158 + ];
  159 +
  160 + if (isset($info['query'])) {
  161 + parse_str($info['query'], $dsn['params']);
  162 + } else {
  163 + $dsn['params'] = [];
  164 + }
  165 +
  166 + return $dsn;
  167 + }
  168 +
  169 + /**
  170 + * 调用驱动类的方法
  171 + * @access public
  172 + * @param string $method 方法名
  173 + * @param array $params 参数
  174 + * @return mixed
  175 + */
  176 + public static function __callStatic($method, $params)
  177 + {
  178 + return call_user_func_array([self::connect(), $method], $params);
  179 + }
  180 +}