作者 潘浩文
1 个管道 的构建 通过 耗费 0 秒

websocket协议测试

要显示太多修改。

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

/vendor
composer.phar
composer.lock
.DS_Store
Thumbs.db
/phpunit.xml
/.idea
/.vscode
\ No newline at end of file
deny from all
\ No newline at end of file
如何贡献我的源代码
===
此文档介绍了 ThinkPHP 团队的组成以及运转机制,您提交的代码将给 ThinkPHP 项目带来什么好处,以及如何才能加入我们的行列。
## 通过 Github 贡献代码
ThinkPHP 目前使用 Git 来控制程序版本,如果你想为 ThinkPHP 贡献源代码,请先大致了解 Git 的使用方法。我们目前把项目托管在 GitHub 上,任何 GitHub 用户都可以向我们贡献代码。
参与的方式很简单,`fork`一份 ThinkPHP 的代码到你的仓库中,修改后提交,并向我们发起`pull request`申请,我们会及时对代码进行审查并处理你的申请并。审查通过后,你的代码将被`merge`进我们的仓库中,这样你就会自动出现在贡献者名单里了,非常方便。
我们希望你贡献的代码符合:
* ThinkPHP 的编码规范
* 适当的注释,能让其他人读懂
* 遵循 Apache2 开源协议
**如果想要了解更多细节或有任何疑问,请继续阅读下面的内容**
### 注意事项
* 本项目代码格式化标准选用 [**PSR-2**](http://www.kancloud.cn/thinkphp/php-fig-psr/3141)
* 类名和类文件名遵循 [**PSR-4**](http://www.kancloud.cn/thinkphp/php-fig-psr/3144)
* 对于 Issues 的处理,请使用诸如 `fix #xxx(Issue ID)` 的 commit title 直接关闭 issue。
* 系统会自动在 PHP 5.4 5.5 5.6 7.0 和 HHVM 上测试修改,其中 HHVM 下的测试容许报错,请确保你的修改符合 PHP 5.4 ~ 5.6 和 PHP 7.0 的语法规范;
* 管理员不会合并造成 CI faild 的修改,若出现 CI faild 请检查自己的源代码或修改相应的[单元测试文件](tests)
## GitHub Issue
GitHub 提供了 Issue 功能,该功能可以用于:
* 提出 bug
* 提出功能改进
* 反馈使用体验
该功能不应该用于:
* 提出修改意见(涉及代码署名和修订追溯问题)
* 不友善的言论
## 快速修改
**GitHub 提供了快速编辑文件的功能**
1. 登录 GitHub 帐号;
2. 浏览项目文件,找到要进行修改的文件;
3. 点击右上角铅笔图标进行修改;
4. 填写 `Commit changes` 相关内容(Title 必填);
5. 提交修改,等待 CI 验证和管理员合并。
**若您需要一次提交大量修改,请继续阅读下面的内容**
## 完整流程
1. `fork`本项目;
2. 克隆(`clone`)你 `fork` 的项目到本地;
3. 新建分支(`branch`)并检出(`checkout`)新分支;
4. 添加本项目到你的本地 git 仓库作为上游(`upstream`);
5. 进行修改,若你的修改包含方法或函数的增减,请记得修改[单元测试文件](tests)
6. 变基(衍合 `rebase`)你的分支到上游 master 分支;
7. `push` 你的本地仓库到 GitHub;
8. 提交 `pull request`
9. 等待 CI 验证(若不通过则重复 5~7,GitHub 会自动更新你的 `pull request`);
10. 等待管理员处理,并及时 `rebase` 你的分支到上游 master 分支(若上游 master 分支有修改)。
*若有必要,可以 `git push -f` 强行推送 rebase 后的分支到自己的 `fork`*
*绝对不可以使用 `git push -f` 强行推送修改到上游*
### 注意事项
* 若对上述流程有任何不清楚的地方,请查阅 GIT 教程,如 [这个](http://backlogtool.com/git-guide/cn/)
* 对于代码**不同方面**的修改,请在自己 `fork` 的项目中**创建不同的分支**(原因参见`完整流程`第9条备注部分);
* 变基及交互式变基操作参见 [Git 交互式变基](http://pakchoi.me/2015/03/17/git-interactive-rebase/)
## 推荐资源
### 开发环境
* XAMPP for Windows 5.5.x
* WampServer (for Windows)
* upupw Apache PHP5.4 ( for Windows)
或自行安装
- Apache / Nginx
- PHP 5.4 ~ 5.6
- MySQL / MariaDB
*Windows 用户推荐添加 PHP bin 目录到 PATH,方便使用 composer*
*Linux 用户自行配置环境, Mac 用户推荐使用内置 Apache 配合 Homebrew 安装 PHP 和 MariaDB*
### 编辑器
Sublime Text 3 + phpfmt 插件
phpfmt 插件参数
```json
{
"autocomplete": true,
"enable_auto_align": true,
"format_on_save": true,
"indent_with_space": true,
"psr1_naming": false,
"psr2": true,
"version": 4
}
```
或其他 编辑器 / IDE 配合 PSR2 自动格式化工具
### Git GUI
* SourceTree
* GitHub Desktop
或其他 Git 图形界面客户端
ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn)
All rights reserved。
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
Apache Licence是著名的非盈利开源组织Apache采用的协议。
该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
允许代码修改,再作为开源或商业软件发布。需要满足
的条件:
1. 需要给代码的用户一份Apache Licence ;
2. 如果你修改了代码,需要在被修改的文件中说明;
3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
带有原来代码中的协议,商标,专利声明和其他原来作者规
定需要包含的说明;
4. 如果再发布的产品中包含一个Notice文件,则在Notice文
件中需要带有本协议内容。你可以在Notice中增加自己的
许可,但不可以表现为对Apache Licence构成更改。
具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
![](http://www.thinkphp.cn/Uploads/editor/2016-06-23/576b4732a6e04.png)
ThinkPHP 5.1 —— 12载初心,你值得信赖的PHP框架
===============
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/top-think/framework/badges/quality-score.png?b=5.1)](https://scrutinizer-ci.com/g/top-think/framework/?branch=5.1)
[![Build Status](https://travis-ci.org/top-think/framework.svg?branch=master)](https://travis-ci.org/top-think/framework)
[![Total Downloads](https://poser.pugx.org/topthink/framework/downloads)](https://packagist.org/packages/topthink/framework)
[![Latest Stable Version](https://poser.pugx.org/topthink/framework/v/stable)](https://packagist.org/packages/topthink/framework)
[![PHP Version](https://img.shields.io/badge/php-%3E%3D5.6-8892BF.svg)](http://www.php.net/)
[![License](https://poser.pugx.org/topthink/framework/license)](https://packagist.org/packages/topthink/framework)
ThinkPHP5.1对底层架构做了进一步的改进,减少依赖,其主要特性包括:
+ 采用容器统一管理对象
+ 支持Facade
+ 更易用的路由
+ 注解路由支持
+ 路由跨域请求支持
+ 验证类增强
+ 配置和路由目录独立
+ 取消系统常量
+ 类库别名机制
+ 模型和数据库增强
+ 依赖注入完善
+ 支持PSR-3日志规范
+ 中间件支持(`V5.1.6+`
+ 支持`Swoole`/`Workerman`运行(`V5.1.18+`
### 废除的功能:
+ 聚合模型
+ 内置控制器扩展类
+ 模型自动验证
> ThinkPHP5.1的运行环境要求PHP5.6+。
## 安装
使用composer安装
~~~
composer create-project topthink/think tp
~~~
启动服务
~~~
cd tp
php think run
~~~
然后就可以在浏览器中访问
~~~
http://localhost:8000
~~~
更新框架
~~~
composer update topthink/framework
~~~
## 在线手册
+ [完全开发手册](https://www.kancloud.cn/manual/thinkphp5_1/content)
+ [升级指导](https://www.kancloud.cn/manual/thinkphp5_1/354155)
## 命名规范
`ThinkPHP5.1`遵循PSR-2命名规范和PSR-4自动加载规范。
## 参与开发
请参阅 [ThinkPHP5 核心框架包](https://github.com/top-think/framework)
## 版权信息
ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
本项目包含的第三方源码和二进制文件之版权信息另行标注。
版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn)
All rights reserved。
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
更多细节参阅 [LICENSE.txt](LICENSE.txt)
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
// 载入Loader类
require __DIR__ . '/library/think/Loader.php';
// 注册自动加载
Loader::register();
// 注册错误和异常处理机制
Error::register();
// 实现日志接口
if (interface_exists('Psr\Log\LoggerInterface')) {
interface LoggerInterface extends \Psr\Log\LoggerInterface
{}
} else {
interface LoggerInterface
{}
}
// 注册类库别名
Loader::addClassAlias([
'App' => facade\App::class,
'Build' => facade\Build::class,
'Cache' => facade\Cache::class,
'Config' => facade\Config::class,
'Cookie' => facade\Cookie::class,
'Db' => Db::class,
'Debug' => facade\Debug::class,
'Env' => facade\Env::class,
'Facade' => Facade::class,
'Hook' => facade\Hook::class,
'Lang' => facade\Lang::class,
'Log' => facade\Log::class,
'Request' => facade\Request::class,
'Response' => facade\Response::class,
'Route' => facade\Route::class,
'Session' => facade\Session::class,
'Url' => facade\Url::class,
'Validate' => facade\Validate::class,
'View' => facade\View::class,
]);
{
"name": "topthink/framework",
"description": "the new thinkphp framework",
"type": "think-framework",
"keywords": [
"framework",
"thinkphp",
"ORM"
],
"homepage": "http://thinkphp.cn/",
"license": "Apache-2.0",
"authors": [
{
"name": "liu21st",
"email": "liu21st@gmail.com"
},
{
"name": "yunwuxin",
"email": "448901948@qq.com"
}
],
"require": {
"php": ">=5.6.0",
"topthink/think-installer": "2.*"
},
"require-dev": {
"phpunit/phpunit": "^5.0|^6.0",
"johnkary/phpunit-speedtrap": "^1.0",
"mikey179/vfsStream": "~1.6",
"phploc/phploc": "2.*",
"sebastian/phpcpd": "2.*",
"squizlabs/php_codesniffer": "2.*",
"phpdocumentor/reflection-docblock": "^2.0"
}
}
<?php
return [
// +----------------------------------------------------------------------
// | 应用设置
// +----------------------------------------------------------------------
'app' => [
// 应用名称
'app_name' => '',
// 应用地址
'app_host' => '',
// 应用调试模式
'app_debug' => false,
// 应用Trace
'app_trace' => false,
// 应用模式状态
'app_status' => '',
// 是否HTTPS
'is_https' => false,
// 入口自动绑定模块
'auto_bind_module' => false,
// 注册的根命名空间
'root_namespace' => [],
// 默认输出类型
'default_return_type' => 'html',
// 默认AJAX 数据返回格式,可选json xml ...
'default_ajax_return' => 'json',
// 默认JSONP格式返回的处理方法
'default_jsonp_handler' => 'jsonpReturn',
// 默认JSONP处理方法
'var_jsonp_handler' => 'callback',
// 默认时区
'default_timezone' => 'Asia/Shanghai',
// 是否开启多语言
'lang_switch_on' => false,
// 默认验证器
'default_validate' => '',
// 默认语言
'default_lang' => 'zh-cn',
// +----------------------------------------------------------------------
// | 模块设置
// +----------------------------------------------------------------------
// 自动搜索控制器
'controller_auto_search' => false,
// 操作方法前缀
'use_action_prefix' => false,
// 操作方法后缀
'action_suffix' => '',
// 默认的空控制器名
'empty_controller' => 'Error',
// 默认的空模块名
'empty_module' => '',
// 默认模块名
'default_module' => 'index',
// 是否支持多模块
'app_multi_module' => true,
// 禁止访问模块
'deny_module_list' => ['common'],
// 默认控制器名
'default_controller' => 'Index',
// 默认操作名
'default_action' => 'index',
// 是否自动转换URL中的控制器和操作名
'url_convert' => true,
// 默认的访问控制器层
'url_controller_layer' => 'controller',
// 应用类库后缀
'class_suffix' => false,
// 控制器类后缀
'controller_suffix' => false,
// +----------------------------------------------------------------------
// | URL请求设置
// +----------------------------------------------------------------------
// 默认全局过滤方法 用逗号分隔多个
'default_filter' => '',
// PATHINFO变量名 用于兼容模式
'var_pathinfo' => 's',
// 兼容PATH_INFO获取
'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
// HTTPS代理标识
'https_agent_name' => '',
// IP代理获取标识
'http_agent_ip' => 'HTTP_X_REAL_IP',
// URL伪静态后缀
'url_html_suffix' => 'html',
// 域名根,如thinkphp.cn
'url_domain_root' => '',
// 表单请求类型伪装变量
'var_method' => '_method',
// 表单ajax伪装变量
'var_ajax' => '_ajax',
// 表单pjax伪装变量
'var_pjax' => '_pjax',
// 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
'request_cache' => false,
// 请求缓存有效期
'request_cache_expire' => null,
// 全局请求缓存排除规则
'request_cache_except' => [],
// +----------------------------------------------------------------------
// | 路由设置
// +----------------------------------------------------------------------
// pathinfo分隔符
'pathinfo_depr' => '/',
// URL普通方式参数 用于自动生成
'url_common_param' => false,
// URL参数方式 0 按名称成对解析 1 按顺序解析
'url_param_type' => 0,
// 是否开启路由延迟解析
'url_lazy_route' => false,
// 是否强制使用路由
'url_route_must' => false,
// 合并路由规则
'route_rule_merge' => false,
// 路由是否完全匹配
'route_complete_match' => false,
// 使用注解路由
'route_annotation' => false,
// 默认的路由变量规则
'default_route_pattern' => '\w+',
// 是否开启路由缓存
'route_check_cache' => false,
// 路由缓存的Key自定义设置(闭包),默认为当前URL和请求类型的md5
'route_check_cache_key' => '',
// 路由缓存的设置
'route_cache_option' => [],
// +----------------------------------------------------------------------
// | 异常及错误设置
// +----------------------------------------------------------------------
// 默认跳转页面对应的模板文件
'dispatch_success_tmpl' => __DIR__ . '/tpl/dispatch_jump.tpl',
'dispatch_error_tmpl' => __DIR__ . '/tpl/dispatch_jump.tpl',
// 异常页面的模板文件
'exception_tmpl' => __DIR__ . '/tpl/think_exception.tpl',
// 错误显示信息,非调试模式有效
'error_message' => '页面错误!请稍后再试~',
// 显示错误信息
'show_error_msg' => false,
// 异常处理handle类 留空使用 \think\exception\Handle
'exception_handle' => '',
],
// +----------------------------------------------------------------------
// | 模板设置
// +----------------------------------------------------------------------
'template' => [
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写
'auto_rule' => 1,
// 模板引擎类型 支持 php think 支持扩展
'type' => 'Think',
// 视图基础目录,配置目录为所有模块的视图起始目录
'view_base' => '',
// 当前模板的视图目录 留空为自动获取
'view_path' => '',
// 模板后缀
'view_suffix' => 'html',
// 模板文件名分隔符
'view_depr' => DIRECTORY_SEPARATOR,
// 模板引擎普通标签开始标记
'tpl_begin' => '{',
// 模板引擎普通标签结束标记
'tpl_end' => '}',
// 标签库标签开始标记
'taglib_begin' => '{',
// 标签库标签结束标记
'taglib_end' => '}',
],
// +----------------------------------------------------------------------
// | 日志设置
// +----------------------------------------------------------------------
'log' => [
// 日志记录方式,内置 file socket 支持扩展
'type' => 'File',
// 日志保存目录
//'path' => LOG_PATH,
// 日志记录级别
'level' => [],
// 是否记录trace信息到日志
'record_trace' => false,
// 是否JSON格式记录
'json' => false,
],
// +----------------------------------------------------------------------
// | Trace设置 开启 app_trace 后 有效
// +----------------------------------------------------------------------
'trace' => [
// 内置Html Console 支持扩展
'type' => 'Html',
'file' => __DIR__ . '/tpl/page_trace.tpl',
],
// +----------------------------------------------------------------------
// | 缓存设置
// +----------------------------------------------------------------------
'cache' => [
// 驱动方式
'type' => 'File',
// 缓存保存目录
//'path' => CACHE_PATH,
// 缓存前缀
'prefix' => '',
// 缓存有效期 0表示永久缓存
'expire' => 0,
],
// +----------------------------------------------------------------------
// | 会话设置
// +----------------------------------------------------------------------
'session' => [
'id' => '',
// SESSION_ID的提交变量,解决flash上传跨域
'var_session_id' => '',
// SESSION 前缀
'prefix' => 'think',
// 驱动方式 支持redis memcache memcached
'type' => '',
// 是否自动开启 SESSION
'auto_start' => true,
'httponly' => true,
'secure' => false,
],
// +----------------------------------------------------------------------
// | Cookie设置
// +----------------------------------------------------------------------
'cookie' => [
// cookie 名称前缀
'prefix' => '',
// cookie 保存时间
'expire' => 0,
// cookie 保存路径
'path' => '/',
// cookie 有效域名
'domain' => '',
// cookie 启用安全传输
'secure' => false,
// httponly设置
'httponly' => '',
// 是否使用 setcookie
'setcookie' => true,
],
// +----------------------------------------------------------------------
// | 数据库设置
// +----------------------------------------------------------------------
'database' => [
// 数据库类型
'type' => 'mysql',
// 数据库连接DSN配置
'dsn' => '',
// 服务器地址
'hostname' => '127.0.0.1',
// 数据库名
'database' => '',
// 数据库用户名
'username' => 'root',
// 数据库密码
'password' => '',
// 数据库连接端口
'hostport' => '',
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => 'utf8',
// 数据库表前缀
'prefix' => '',
// 数据库调试模式
'debug' => false,
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 数据集返回类型
'resultset_type' => 'array',
// 自动写入时间戳字段
'auto_timestamp' => false,
// 时间字段取出后的默认时间格式
'datetime_format' => 'Y-m-d H:i:s',
// 是否需要进行SQL性能分析
'sql_explain' => false,
// 查询对象
'query' => '\\think\\db\\Query',
],
//分页配置
'paginate' => [
'type' => 'bootstrap',
'var_page' => 'page',
'list_rows' => 15,
],
//控制台配置
'console' => [
'name' => 'Think Console',
'version' => '0.1',
'user' => null,
'auto_path' => '',
],
// 中间件配置
'middleware' => [
'default_namespace' => 'app\\http\\middleware\\',
],
];
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
//------------------------
// ThinkPHP 助手函数
//-------------------------
use think\Container;
use think\Db;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\facade\Cache;
use think\facade\Config;
use think\facade\Cookie;
use think\facade\Debug;
use think\facade\Env;
use think\facade\Hook;
use think\facade\Lang;
use think\facade\Log;
use think\facade\Request;
use think\facade\Route;
use think\facade\Session;
use think\facade\Url;
use think\Response;
use think\route\RuleItem;
if (!function_exists('abort')) {
/**
* 抛出HTTP异常
* @param integer|Response $code 状态码 或者 Response对象实例
* @param string $message 错误信息
* @param array $header 参数
*/
function abort($code, $message = null, $header = [])
{
if ($code instanceof Response) {
throw new HttpResponseException($code);
} else {
throw new HttpException($code, $message, null, $header);
}
}
}
if (!function_exists('action')) {
/**
* 调用模块的操作方法 参数格式 [模块/控制器/]操作
* @param string $url 调用地址
* @param string|array $vars 调用参数 支持字符串和数组
* @param string $layer 要调用的控制层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return mixed
*/
function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
{
return app()->action($url, $vars, $layer, $appendSuffix);
}
}
if (!function_exists('app')) {
/**
* 快速获取容器中的实例 支持依赖注入
* @param string $name 类名或标识 默认获取当前应用实例
* @param array $args 参数
* @param bool $newInstance 是否每次创建新的实例
* @return mixed|\think\App
*/
function app($name = 'think\App', $args = [], $newInstance = false)
{
return Container::get($name, $args, $newInstance);
}
}
if (!function_exists('behavior')) {
/**
* 执行某个行为(run方法) 支持依赖注入
* @param mixed $behavior 行为类名或者别名
* @param mixed $args 参数
* @return mixed
*/
function behavior($behavior, $args = null)
{
return Hook::exec($behavior, $args);
}
}
if (!function_exists('bind')) {
/**
* 绑定一个类到容器
* @access public
* @param string $abstract 类标识、接口
* @param mixed $concrete 要绑定的类、闭包或者实例
* @return Container
*/
function bind($abstract, $concrete = null)
{
return Container::getInstance()->bindTo($abstract, $concrete);
}
}
if (!function_exists('cache')) {
/**
* 缓存管理
* @param mixed $name 缓存名称,如果为数组表示进行缓存设置
* @param mixed $value 缓存值
* @param mixed $options 缓存参数
* @param string $tag 缓存标签
* @return mixed
*/
function cache($name, $value = '', $options = null, $tag = null)
{
if (is_array($options)) {
// 缓存操作的同时初始化
Cache::connect($options);
} elseif (is_array($name)) {
// 缓存初始化
return Cache::connect($name);
}
if ('' === $value) {
// 获取缓存
return 0 === strpos($name, '?') ? Cache::has(substr($name, 1)) : Cache::get($name);
} elseif (is_null($value)) {
// 删除缓存
return Cache::rm($name);
}
// 缓存数据
if (is_array($options)) {
$expire = isset($options['expire']) ? $options['expire'] : null; //修复查询缓存无法设置过期时间
} else {
$expire = is_numeric($options) ? $options : null; //默认快捷缓存设置过期时间
}
if (is_null($tag)) {
return Cache::set($name, $value, $expire);
} else {
return Cache::tag($tag)->set($name, $value, $expire);
}
}
}
if (!function_exists('call')) {
/**
* 调用反射执行callable 支持依赖注入
* @param mixed $callable 支持闭包等callable写法
* @param array $args 参数
* @return mixed
*/
function call($callable, $args = [])
{
return Container::getInstance()->invoke($callable, $args);
}
}
if (!function_exists('class_basename')) {
/**
* 获取类名(不包含命名空间)
*
* @param string|object $class
* @return string
*/
function class_basename($class)
{
$class = is_object($class) ? get_class($class) : $class;
return basename(str_replace('\\', '/', $class));
}
}
if (!function_exists('class_uses_recursive')) {
/**
*获取一个类里所有用到的trait,包括父类的
*
* @param $class
* @return array
*/
function class_uses_recursive($class)
{
if (is_object($class)) {
$class = get_class($class);
}
$results = [];
$classes = array_merge([$class => $class], class_parents($class));
foreach ($classes as $class) {
$results += trait_uses_recursive($class);
}
return array_unique($results);
}
}
if (!function_exists('config')) {
/**
* 获取和设置配置参数
* @param string|array $name 参数名
* @param mixed $value 参数值
* @return mixed
*/
function config($name = '', $value = null)
{
if (is_null($value) && is_string($name)) {
if ('.' == substr($name, -1)) {
return Config::pull(substr($name, 0, -1));
}
return 0 === strpos($name, '?') ? Config::has(substr($name, 1)) : Config::get($name);
} else {
return Config::set($name, $value);
}
}
}
if (!function_exists('container')) {
/**
* 获取容器对象实例
* @return Container
*/
function container()
{
return Container::getInstance();
}
}
if (!function_exists('controller')) {
/**
* 实例化控制器 格式:[模块/]控制器
* @param string $name 资源地址
* @param string $layer 控制层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return \think\Controller
*/
function controller($name, $layer = 'controller', $appendSuffix = false)
{
return app()->controller($name, $layer, $appendSuffix);
}
}
if (!function_exists('cookie')) {
/**
* Cookie管理
* @param string|array $name cookie名称,如果为数组表示进行cookie设置
* @param mixed $value cookie值
* @param mixed $option 参数
* @return mixed
*/
function cookie($name, $value = '', $option = null)
{
if (is_array($name)) {
// 初始化
Cookie::init($name);
} elseif (is_null($name)) {
// 清除
Cookie::clear($value);
} elseif ('' === $value) {
// 获取
return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1), $option) : Cookie::get($name);
} elseif (is_null($value)) {
// 删除
return Cookie::delete($name);
} else {
// 设置
return Cookie::set($name, $value, $option);
}
}
}
if (!function_exists('db')) {
/**
* 实例化数据库类
* @param string $name 操作的数据表名称(不含前缀)
* @param array|string $config 数据库配置参数
* @param bool $force 是否强制重新连接
* @return \think\db\Query
*/
function db($name = '', $config = [], $force = true)
{
return Db::connect($config, $force)->name($name);
}
}
if (!function_exists('debug')) {
/**
* 记录时间(微秒)和内存使用情况
* @param string $start 开始标签
* @param string $end 结束标签
* @param integer|string $dec 小数位 如果是m 表示统计内存占用
* @return mixed
*/
function debug($start, $end = '', $dec = 6)
{
if ('' == $end) {
Debug::remark($start);
} else {
return 'm' == $dec ? Debug::getRangeMem($start, $end) : Debug::getRangeTime($start, $end, $dec);
}
}
}
if (!function_exists('download')) {
/**
* 获取\think\response\Download对象实例
* @param string $filename 要下载的文件
* @param string $name 显示文件名
* @param bool $content 是否为内容
* @param integer $expire 有效期(秒)
* @return \think\response\Download
*/
function download($filename, $name = '', $content = false, $expire = 180)
{
return Response::create($filename, 'download')->name($name)->isContent($content)->expire($expire);
}
}
if (!function_exists('dump')) {
/**
* 浏览器友好的变量输出
* @param mixed $var 变量
* @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串
* @param string $label 标签 默认为空
* @return void|string
*/
function dump($var, $echo = true, $label = null)
{
return Debug::dump($var, $echo, $label);
}
}
if (!function_exists('env')) {
/**
* 获取环境变量值
* @access public
* @param string $name 环境变量名(支持二级 .号分割)
* @param string $default 默认值
* @return mixed
*/
function env($name = null, $default = null)
{
return Env::get($name, $default);
}
}
if (!function_exists('exception')) {
/**
* 抛出异常处理
*
* @param string $msg 异常消息
* @param integer $code 异常代码 默认为0
* @param string $exception 异常类
*
* @throws Exception
*/
function exception($msg, $code = 0, $exception = '')
{
$e = $exception ?: '\think\Exception';
throw new $e($msg, $code);
}
}
if (!function_exists('halt')) {
/**
* 调试变量并且中断输出
* @param mixed $var 调试变量或者信息
*/
function halt($var)
{
dump($var);
throw new HttpResponseException(new Response);
}
}
if (!function_exists('input')) {
/**
* 获取输入数据 支持默认值和过滤
* @param string $key 获取的变量名
* @param mixed $default 默认值
* @param string $filter 过滤方法
* @return mixed
*/
function input($key = '', $default = null, $filter = '')
{
if (0 === strpos($key, '?')) {
$key = substr($key, 1);
$has = true;
}
if ($pos = strpos($key, '.')) {
// 指定参数来源
$method = substr($key, 0, $pos);
if (in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'route', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) {
$key = substr($key, $pos + 1);
} else {
$method = 'param';
}
} else {
// 默认为自动判断
$method = 'param';
}
if (isset($has)) {
return request()->has($key, $method, $default);
} else {
return request()->$method($key, $default, $filter);
}
}
}
if (!function_exists('json')) {
/**
* 获取\think\response\Json对象实例
* @param mixed $data 返回的数据
* @param integer $code 状态码
* @param array $header 头部
* @param array $options 参数
* @return \think\response\Json
*/
function json($data = [], $code = 200, $header = [], $options = [])
{
return Response::create($data, 'json', $code, $header, $options);
}
}
if (!function_exists('jsonp')) {
/**
* 获取\think\response\Jsonp对象实例
* @param mixed $data 返回的数据
* @param integer $code 状态码
* @param array $header 头部
* @param array $options 参数
* @return \think\response\Jsonp
*/
function jsonp($data = [], $code = 200, $header = [], $options = [])
{
return Response::create($data, 'jsonp', $code, $header, $options);
}
}
if (!function_exists('lang')) {
/**
* 获取语言变量值
* @param string $name 语言变量名
* @param array $vars 动态变量值
* @param string $lang 语言
* @return mixed
*/
function lang($name, $vars = [], $lang = '')
{
return Lang::get($name, $vars, $lang);
}
}
if (!function_exists('model')) {
/**
* 实例化Model
* @param string $name Model名称
* @param string $layer 业务层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return \think\Model
*/
function model($name = '', $layer = 'model', $appendSuffix = false)
{
return app()->model($name, $layer, $appendSuffix);
}
}
if (!function_exists('parse_name')) {
/**
* 字符串命名风格转换
* type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
* @param string $name 字符串
* @param integer $type 转换类型
* @param bool $ucfirst 首字母是否大写(驼峰规则)
* @return string
*/
function parse_name($name, $type = 0, $ucfirst = true)
{
if ($type) {
$name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
return strtoupper($match[1]);
}, $name);
return $ucfirst ? ucfirst($name) : lcfirst($name);
} else {
return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
}
}
}
if (!function_exists('redirect')) {
/**
* 获取\think\response\Redirect对象实例
* @param mixed $url 重定向地址 支持Url::build方法的地址
* @param array|integer $params 额外参数
* @param integer $code 状态码
* @return \think\response\Redirect
*/
function redirect($url = [], $params = [], $code = 302)
{
if (is_integer($params)) {
$code = $params;
$params = [];
}
return Response::create($url, 'redirect', $code)->params($params);
}
}
if (!function_exists('request')) {
/**
* 获取当前Request对象实例
* @return Request
*/
function request()
{
return app('request');
}
}
if (!function_exists('response')) {
/**
* 创建普通 Response 对象实例
* @param mixed $data 输出数据
* @param int|string $code 状态码
* @param array $header 头信息
* @param string $type
* @return Response
*/
function response($data = [], $code = 200, $header = [], $type = 'html')
{
return Response::create($data, $type, $code, $header);
}
}
if (!function_exists('route')) {
/**
* 路由注册
* @param string $rule 路由规则
* @param mixed $route 路由地址
* @param array $option 路由参数
* @param array $pattern 变量规则
* @return RuleItem
*/
function route($rule, $route, $option = [], $pattern = [])
{
return Route::rule($rule, $route, '*', $option, $pattern);
}
}
if (!function_exists('session')) {
/**
* Session管理
* @param string|array $name session名称,如果为数组表示进行session设置
* @param mixed $value session值
* @param string $prefix 前缀
* @return mixed
*/
function session($name, $value = '', $prefix = null)
{
if (is_array($name)) {
// 初始化
Session::init($name);
} elseif (is_null($name)) {
// 清除
Session::clear($value);
} elseif ('' === $value) {
// 判断或获取
return 0 === strpos($name, '?') ? Session::has(substr($name, 1), $prefix) : Session::get($name, $prefix);
} elseif (is_null($value)) {
// 删除
return Session::delete($name, $prefix);
} else {
// 设置
return Session::set($name, $value, $prefix);
}
}
}
if (!function_exists('token')) {
/**
* 生成表单令牌
* @param string $name 令牌名称
* @param mixed $type 令牌生成方法
* @return string
*/
function token($name = '__token__', $type = 'md5')
{
$token = Request::token($name, $type);
return '<input type="hidden" name="' . $name . '" value="' . $token . '" />';
}
}
if (!function_exists('trace')) {
/**
* 记录日志信息
* @param mixed $log log信息 支持字符串和数组
* @param string $level 日志级别
* @return array|void
*/
function trace($log = '[think]', $level = 'log')
{
if ('[think]' === $log) {
return Log::getLog();
} else {
Log::record($log, $level);
}
}
}
if (!function_exists('trait_uses_recursive')) {
/**
* 获取一个trait里所有引用到的trait
*
* @param string $trait
* @return array
*/
function trait_uses_recursive($trait)
{
$traits = class_uses($trait);
foreach ($traits as $trait) {
$traits += trait_uses_recursive($trait);
}
return $traits;
}
}
if (!function_exists('url')) {
/**
* Url生成
* @param string $url 路由地址
* @param string|array $vars 变量
* @param bool|string $suffix 生成的URL后缀
* @param bool|string $domain 域名
* @return string
*/
function url($url = '', $vars = '', $suffix = true, $domain = false)
{
return Url::build($url, $vars, $suffix, $domain);
}
}
if (!function_exists('validate')) {
/**
* 实例化验证器
* @param string $name 验证器名称
* @param string $layer 业务层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return \think\Validate
*/
function validate($name = '', $layer = 'validate', $appendSuffix = false)
{
return app()->validate($name, $layer, $appendSuffix);
}
}
if (!function_exists('view')) {
/**
* 渲染模板输出
* @param string $template 模板文件
* @param array $vars 模板变量
* @param integer $code 状态码
* @param callable $filter 内容过滤
* @return \think\response\View
*/
function view($template = '', $vars = [], $code = 200, $filter = null)
{
return Response::create($template, 'view', $code)->assign($vars)->filter($filter);
}
}
if (!function_exists('widget')) {
/**
* 渲染输出Widget
* @param string $name Widget名称
* @param array $data 传入的参数
* @return mixed
*/
function widget($name, $data = [])
{
return app()->action($name, $data, 'widget');
}
}
if (!function_exists('xml')) {
/**
* 获取\think\response\Xml对象实例
* @param mixed $data 返回的数据
* @param integer $code 状态码
* @param array $header 头部
* @param array $options 参数
* @return \think\response\Xml
*/
function xml($data = [], $code = 200, $header = [], $options = [])
{
return Response::create($data, 'xml', $code, $header, $options);
}
}
if (!function_exists('yaconf')) {
/**
* 获取yaconf配置
*
* @param string $name 配置参数名
* @param mixed $default 默认值
* @return mixed
*/
function yaconf($name, $default = null)
{
return Config::yaconf($name, $default);
}
}
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
// 核心中文语言包
return [
// 系统错误提示
'Undefined variable' => '未定义变量',
'Undefined index' => '未定义数组索引',
'Undefined offset' => '未定义数组下标',
'Parse error' => '语法解析错误',
'Type error' => '类型错误',
'Fatal error' => '致命错误',
'syntax error' => '语法错误',
// 框架核心错误提示
'dispatch type not support' => '不支持的调度类型',
'method param miss' => '方法参数错误',
'method not exists' => '方法不存在',
'function not exists' => '函数不存在',
'file not exists' => '文件不存在',
'module not exists' => '模块不存在',
'controller not exists' => '控制器不存在',
'class not exists' => '类不存在',
'property not exists' => '类的属性不存在',
'template not exists' => '模板文件不存在',
'illegal controller name' => '非法的控制器名称',
'illegal action name' => '非法的操作名称',
'url suffix deny' => '禁止的URL后缀访问',
'Route Not Found' => '当前访问路由未定义或不匹配',
'Undefined db type' => '未定义数据库类型',
'variable type error' => '变量类型错误',
'PSR-4 error' => 'PSR-4 规范错误',
'not support total' => '简洁模式下不能获取数据总数',
'not support last' => '简洁模式下不能获取最后一页',
'error session handler' => '错误的SESSION处理器类',
'not allow php tag' => '模板不允许使用PHP语法',
'not support' => '不支持',
'redisd master' => 'Redisd 主服务器错误',
'redisd slave' => 'Redisd 从服务器错误',
'must run at sae' => '必须在SAE运行',
'memcache init error' => '未开通Memcache服务,请在SAE管理平台初始化Memcache服务',
'KVDB init error' => '没有初始化KVDB,请在SAE管理平台初始化KVDB服务',
'fields not exists' => '数据表字段不存在',
'where express error' => '查询表达式错误',
'order express error' => '排序表达式错误',
'no data to update' => '没有任何数据需要更新',
'miss data to insert' => '缺少需要写入的数据',
'not support data' => '不支持的数据表达式',
'miss complex primary data' => '缺少复合主键数据',
'miss update condition' => '缺少更新条件',
'model data Not Found' => '模型数据不存在',
'table data not Found' => '表数据不存在',
'delete without condition' => '没有条件不会执行删除操作',
'miss relation data' => '缺少关联表数据',
'tag attr must' => '模板标签属性必须',
'tag error' => '模板标签错误',
'cache write error' => '缓存写入失败',
'sae mc write error' => 'SAE mc 写入错误',
'route name not exists' => '路由标识不存在(或参数不够)',
'invalid request' => '非法请求',
'bind attr has exists' => '模型的属性已经存在',
'relation data not exists' => '关联数据不存在',
'relation not support' => '关联不支持',
'chunk not support order' => 'Chunk不支持调用order方法',
'route pattern error' => '路由变量规则定义错误',
'route behavior will not support' => '路由行为废弃(使用中间件替代)',
'closure not support cache(true)' => '使用闭包查询不支持cache(true),请指定缓存Key',
// 上传错误信息
'unknown upload error' => '未知上传错误!',
'file write error' => '文件写入失败!',
'upload temp dir not found' => '找不到临时文件夹!',
'no file to uploaded' => '没有文件被上传!',
'only the portion of file is uploaded' => '文件只有部分被上传!',
'upload File size exceeds the maximum value' => '上传文件大小超过了最大值!',
'upload write error' => '文件上传保存错误!',
'has the same filename: {:filename}' => '存在同名文件:{:filename}',
'upload illegal files' => '非法上传文件',
'illegal image files' => '非法图片文件',
'extensions to upload is not allowed' => '上传文件后缀不允许',
'mimetype to upload is not allowed' => '上传文件MIME类型不允许!',
'filesize not match' => '上传文件大小不符!',
'directory {:path} creation failed' => '目录 {:path} 创建失败!',
'The middleware must return Response instance' => '中间件方法必须返回Response对象实例',
'The queue was exhausted, with no response returned' => '中间件队列为空',
// Validate Error Message
':attribute require' => ':attribute不能为空',
':attribute must' => ':attribute必须',
':attribute must be numeric' => ':attribute必须是数字',
':attribute must be integer' => ':attribute必须是整数',
':attribute must be float' => ':attribute必须是浮点数',
':attribute must be bool' => ':attribute必须是布尔值',
':attribute not a valid email address' => ':attribute格式不符',
':attribute not a valid mobile' => ':attribute格式不符',
':attribute must be a array' => ':attribute必须是数组',
':attribute must be yes,on or 1' => ':attribute必须是yes、on或者1',
':attribute not a valid datetime' => ':attribute不是一个有效的日期或时间格式',
':attribute not a valid file' => ':attribute不是有效的上传文件',
':attribute not a valid image' => ':attribute不是有效的图像文件',
':attribute must be alpha' => ':attribute只能是字母',
':attribute must be alpha-numeric' => ':attribute只能是字母和数字',
':attribute must be alpha-numeric, dash, underscore' => ':attribute只能是字母、数字和下划线_及破折号-',
':attribute not a valid domain or ip' => ':attribute不是有效的域名或者IP',
':attribute must be chinese' => ':attribute只能是汉字',
':attribute must be chinese or alpha' => ':attribute只能是汉字、字母',
':attribute must be chinese,alpha-numeric' => ':attribute只能是汉字、字母和数字',
':attribute must be chinese,alpha-numeric,underscore, dash' => ':attribute只能是汉字、字母、数字和下划线_及破折号-',
':attribute not a valid url' => ':attribute不是有效的URL地址',
':attribute not a valid ip' => ':attribute不是有效的IP地址',
':attribute must be dateFormat of :rule' => ':attribute必须使用日期格式 :rule',
':attribute must be in :rule' => ':attribute必须在 :rule 范围内',
':attribute be notin :rule' => ':attribute不能在 :rule 范围内',
':attribute must between :1 - :2' => ':attribute只能在 :1 - :2 之间',
':attribute not between :1 - :2' => ':attribute不能在 :1 - :2 之间',
'size of :attribute must be :rule' => ':attribute长度不符合要求 :rule',
'max size of :attribute must be :rule' => ':attribute长度不能超过 :rule',
'min size of :attribute must be :rule' => ':attribute长度不能小于 :rule',
':attribute cannot be less than :rule' => ':attribute日期不能小于 :rule',
':attribute cannot exceed :rule' => ':attribute日期不能超过 :rule',
':attribute not within :rule' => '不在有效期内 :rule',
'access IP is not allowed' => '不允许的IP访问',
'access IP denied' => '禁止的IP访问',
':attribute out of accord with :2' => ':attribute和确认字段:2不一致',
':attribute cannot be same with :2' => ':attribute和比较字段:2不能相同',
':attribute must greater than or equal :rule' => ':attribute必须大于等于 :rule',
':attribute must greater than :rule' => ':attribute必须大于 :rule',
':attribute must less than or equal :rule' => ':attribute必须小于等于 :rule',
':attribute must less than :rule' => ':attribute必须小于 :rule',
':attribute must equal :rule' => ':attribute必须等于 :rule',
':attribute has exists' => ':attribute已存在',
':attribute not conform to the rules' => ':attribute不符合指定规则',
'invalid Request method' => '无效的请求类型',
'invalid token' => '令牌数据无效',
'not conform to the rules' => '规则错误',
];
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
use think\exception\ClassNotFoundException;
use think\exception\HttpResponseException;
use think\route\Dispatch;
/**
* App 应用管理
*/
class App extends Container
{
const VERSION = '5.1.27 LTS';
/**
* 当前模块路径
* @var string
*/
protected $modulePath;
/**
* 应用调试模式
* @var bool
*/
protected $appDebug = true;
/**
* 应用开始时间
* @var float
*/
protected $beginTime;
/**
* 应用内存初始占用
* @var integer
*/
protected $beginMem;
/**
* 应用类库命名空间
* @var string
*/
protected $namespace = 'app';
/**
* 应用类库后缀
* @var bool
*/
protected $suffix = false;
/**
* 严格路由检测
* @var bool
*/
protected $routeMust;
/**
* 应用类库目录
* @var string
*/
protected $appPath;
/**
* 框架目录
* @var string
*/
protected $thinkPath;
/**
* 应用根目录
* @var string
*/
protected $rootPath;
/**
* 运行时目录
* @var string
*/
protected $runtimePath;
/**
* 配置目录
* @var string
*/
protected $configPath;
/**
* 路由目录
* @var string
*/
protected $routePath;
/**
* 配置后缀
* @var string
*/
protected $configExt;
/**
* 应用调度实例
* @var Dispatch
*/
protected $dispatch;
/**
* 绑定模块(控制器)
* @var string
*/
protected $bindModule;
/**
* 初始化
* @var bool
*/
protected $initialized = false;
public function __construct($appPath = '')
{
$this->thinkPath = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
$this->path($appPath);
}
/**
* 绑定模块或者控制器
* @access public
* @param string $bind
* @return $this
*/
public function bind($bind)
{
$this->bindModule = $bind;
return $this;
}
/**
* 设置应用类库目录
* @access public
* @param string $path 路径
* @return $this
*/
public function path($path)
{
$this->appPath = $path ? realpath($path) . DIRECTORY_SEPARATOR : $this->getAppPath();
return $this;
}
/**
* 初始化应用
* @access public
* @return void
*/
public function initialize()
{
if ($this->initialized) {
return;
}
$this->initialized = true;
$this->beginTime = microtime(true);
$this->beginMem = memory_get_usage();
$this->rootPath = dirname($this->appPath) . DIRECTORY_SEPARATOR;
$this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
$this->routePath = $this->rootPath . 'route' . DIRECTORY_SEPARATOR;
$this->configPath = $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
static::setInstance($this);
$this->instance('app', $this);
$this->configExt = $this->env->get('config_ext', '.php');
// 加载惯例配置文件
$this->config->set(include $this->thinkPath . 'convention.php');
// 设置路径环境变量
$this->env->set([
'think_path' => $this->thinkPath,
'root_path' => $this->rootPath,
'app_path' => $this->appPath,
'config_path' => $this->configPath,
'route_path' => $this->routePath,
'runtime_path' => $this->runtimePath,
'extend_path' => $this->rootPath . 'extend' . DIRECTORY_SEPARATOR,
'vendor_path' => $this->rootPath . 'vendor' . DIRECTORY_SEPARATOR,
]);
// 加载环境变量配置文件
if (is_file($this->rootPath . '.env')) {
$this->env->load($this->rootPath . '.env');
}
$this->namespace = $this->env->get('app_namespace', $this->namespace);
$this->env->set('app_namespace', $this->namespace);
// 注册应用命名空间
Loader::addNamespace($this->namespace, $this->appPath);
// 初始化应用
$this->init();
// 开启类名后缀
$this->suffix = $this->config('app.class_suffix');
// 应用调试模式
$this->appDebug = $this->env->get('app_debug', $this->config('app.app_debug'));
$this->env->set('app_debug', $this->appDebug);
if (!$this->appDebug) {
ini_set('display_errors', 'Off');
} elseif (PHP_SAPI != 'cli') {
//重新申请一块比较大的buffer
if (ob_get_level() > 0) {
$output = ob_get_clean();
}
ob_start();
if (!empty($output)) {
echo $output;
}
}
// 注册异常处理类
if ($this->config('app.exception_handle')) {
Error::setExceptionHandler($this->config('app.exception_handle'));
}
// 注册根命名空间
if (!empty($this->config('app.root_namespace'))) {
Loader::addNamespace($this->config('app.root_namespace'));
}
// 加载composer autofile文件
Loader::loadComposerAutoloadFiles();
// 注册类库别名
Loader::addClassAlias($this->config->pull('alias'));
// 数据库配置初始化
Db::init($this->config->pull('database'));
// 设置系统时区
date_default_timezone_set($this->config('app.default_timezone'));
// 读取语言包
$this->loadLangPack();
// 路由初始化
$this->routeInit();
}
/**
* 初始化应用或模块
* @access public
* @param string $module 模块名
* @return void
*/
public function init($module = '')
{
// 定位模块目录
$module = $module ? $module . DIRECTORY_SEPARATOR : '';
$path = $this->appPath . $module;
// 加载初始化文件
if (is_file($path . 'init.php')) {
include $path . 'init.php';
} elseif (is_file($this->runtimePath . $module . 'init.php')) {
include $this->runtimePath . $module . 'init.php';
} else {
// 加载行为扩展文件
if (is_file($path . 'tags.php')) {
$tags = include $path . 'tags.php';
if (is_array($tags)) {
$this->hook->import($tags);
}
}
// 加载公共文件
if (is_file($path . 'common.php')) {
include_once $path . 'common.php';
}
if ('' == $module) {
// 加载系统助手函数
include $this->thinkPath . 'helper.php';
}
// 加载中间件
if (is_file($path . 'middleware.php')) {
$middleware = include $path . 'middleware.php';
if (is_array($middleware)) {
$this->middleware->import($middleware);
}
}
// 注册服务的容器对象实例
if (is_file($path . 'provider.php')) {
$provider = include $path . 'provider.php';
if (is_array($provider)) {
$this->bindTo($provider);
}
}
// 自动读取配置文件
if (is_dir($path . 'config')) {
$dir = $path . 'config' . DIRECTORY_SEPARATOR;
} elseif (is_dir($this->configPath . $module)) {
$dir = $this->configPath . $module;
}
$files = isset($dir) ? scandir($dir) : [];
foreach ($files as $file) {
if ('.' . pathinfo($file, PATHINFO_EXTENSION) === $this->configExt) {
$this->config->load($dir . $file, pathinfo($file, PATHINFO_FILENAME));
}
}
}
$this->setModulePath($path);
if ($module) {
// 对容器中的对象实例进行配置更新
$this->containerConfigUpdate($module);
}
}
protected function containerConfigUpdate($module)
{
$config = $this->config->get();
// 注册异常处理类
if ($config['app']['exception_handle']) {
Error::setExceptionHandler($config['app']['exception_handle']);
}
Db::init($config['database']);
$this->middleware->setConfig($config['middleware']);
$this->route->setConfig($config['app']);
$this->request->init($config['app']);
$this->cookie->init($config['cookie']);
$this->view->init($config['template']);
$this->log->init($config['log']);
$this->session->setConfig($config['session']);
$this->debug->setConfig($config['trace']);
$this->cache->init($config['cache'], true);
// 加载当前模块语言包
$this->lang->load($this->appPath . $module . DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php');
// 模块请求缓存检查
$this->checkRequestCache(
$config['app']['request_cache'],
$config['app']['request_cache_expire'],
$config['app']['request_cache_except']
);
}
/**
* 执行应用程序
* @access public
* @return Response
* @throws Exception
*/
public function run()
{
try {
// 初始化应用
$this->initialize();
// 监听app_init
$this->hook->listen('app_init');
if ($this->bindModule) {
// 模块/控制器绑定
$this->route->bind($this->bindModule);
} elseif ($this->config('app.auto_bind_module')) {
// 入口自动绑定
$name = pathinfo($this->request->baseFile(), PATHINFO_FILENAME);
if ($name && 'index' != $name && is_dir($this->appPath . $name)) {
$this->route->bind($name);
}
}
// 监听app_dispatch
$this->hook->listen('app_dispatch');
$dispatch = $this->dispatch;
if (empty($dispatch)) {
// 路由检测
$dispatch = $this->routeCheck()->init();
}
// 记录当前调度信息
$this->request->dispatch($dispatch);
// 记录路由和请求信息
if ($this->appDebug) {
$this->log('[ ROUTE ] ' . var_export($this->request->routeInfo(), true));
$this->log('[ HEADER ] ' . var_export($this->request->header(), true));
$this->log('[ PARAM ] ' . var_export($this->request->param(), true));
}
// 监听app_begin
$this->hook->listen('app_begin');
// 请求缓存检查
$this->checkRequestCache(
$this->config('request_cache'),
$this->config('request_cache_expire'),
$this->config('request_cache_except')
);
$data = null;
} catch (HttpResponseException $exception) {
$dispatch = null;
$data = $exception->getResponse();
}
$this->middleware->add(function (Request $request, $next) use ($dispatch, $data) {
return is_null($data) ? $dispatch->run() : $data;
});
$response = $this->middleware->dispatch($this->request);
// 监听app_end
$this->hook->listen('app_end', $response);
return $response;
}
protected function getRouteCacheKey()
{
if ($this->config->get('route_check_cache_key')) {
$closure = $this->config->get('route_check_cache_key');
$routeKey = $closure($this->request);
} else {
$routeKey = md5($this->request->baseUrl(true) . ':' . $this->request->method());
}
return $routeKey;
}
protected function loadLangPack()
{
// 读取默认语言
$this->lang->range($this->config('app.default_lang'));
if ($this->config('app.lang_switch_on')) {
// 开启多语言机制 检测当前语言
$this->lang->detect();
}
$this->request->setLangset($this->lang->range());
// 加载系统语言包
$this->lang->load([
$this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php',
$this->appPath . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php',
]);
}
/**
* 设置当前地址的请求缓存
* @access public
* @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id
* @param mixed $expire 缓存有效期
* @param array $except 缓存排除
* @param string $tag 缓存标签
* @return void
*/
public function checkRequestCache($key, $expire = null, $except = [], $tag = null)
{
$cache = $this->request->cache($key, $expire, $except, $tag);
if ($cache) {
$this->setResponseCache($cache);
}
}
public function setResponseCache($cache)
{
list($key, $expire, $tag) = $cache;
if (strtotime($this->request->server('HTTP_IF_MODIFIED_SINCE')) + $expire > $this->request->server('REQUEST_TIME')) {
// 读取缓存
$response = Response::create()->code(304);
throw new HttpResponseException($response);
} elseif ($this->cache->has($key)) {
list($content, $header) = $this->cache->get($key);
$response = Response::create($content)->header($header);
throw new HttpResponseException($response);
}
}
/**
* 设置当前请求的调度信息
* @access public
* @param Dispatch $dispatch 调度信息
* @return $this
*/
public function dispatch(Dispatch $dispatch)
{
$this->dispatch = $dispatch;
return $this;
}
/**
* 记录调试信息
* @access public
* @param mixed $msg 调试信息
* @param string $type 信息类型
* @return void
*/
public function log($msg, $type = 'info')
{
$this->appDebug && $this->log->record($msg, $type);
}
/**
* 获取配置参数 为空则获取所有配置
* @access public
* @param string $name 配置参数名(支持二级配置 .号分割)
* @return mixed
*/
public function config($name = '')
{
return $this->config->get($name);
}
/**
* 路由初始化 导入路由定义规则
* @access public
* @return void
*/
public function routeInit()
{
// 路由检测
$files = scandir($this->routePath);
foreach ($files as $file) {
if (strpos($file, '.php')) {
$filename = $this->routePath . $file;
// 导入路由配置
$rules = include $filename;
if (is_array($rules)) {
$this->route->import($rules);
}
}
}
if ($this->route->config('route_annotation')) {
// 自动生成路由定义
if ($this->appDebug) {
$suffix = $this->route->config('controller_suffix') || $this->route->config('class_suffix');
$this->build->buildRoute($suffix);
}
$filename = $this->runtimePath . 'build_route.php';
if (is_file($filename)) {
include $filename;
}
}
}
/**
* URL路由检测(根据PATH_INFO)
* @access public
* @return Dispatch
*/
public function routeCheck()
{
// 检测路由缓存
if (!$this->appDebug && $this->config->get('route_check_cache')) {
$routeKey = $this->getRouteCacheKey();
$option = $this->config->get('route_cache_option');
if ($option && $this->cache->connect($option)->has($routeKey)) {
return $this->cache->connect($option)->get($routeKey);
} elseif ($this->cache->has($routeKey)) {
return $this->cache->get($routeKey);
}
}
// 获取应用调度信息
$path = $this->request->path();
// 是否强制路由模式
$must = !is_null($this->routeMust) ? $this->routeMust : $this->route->config('url_route_must');
// 路由检测 返回一个Dispatch对象
$dispatch = $this->route->check($path, $must);
if (!empty($routeKey)) {
try {
if ($option) {
$this->cache->connect($option)->tag('route_cache')->set($routeKey, $dispatch);
} else {
$this->cache->tag('route_cache')->set($routeKey, $dispatch);
}
} catch (\Exception $e) {
// 存在闭包的时候缓存无效
}
}
return $dispatch;
}
/**
* 设置应用的路由检测机制
* @access public
* @param bool $must 是否强制检测路由
* @return $this
*/
public function routeMust($must = false)
{
$this->routeMust = $must;
return $this;
}
/**
* 解析模块和类名
* @access protected
* @param string $name 资源地址
* @param string $layer 验证层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return array
*/
protected function parseModuleAndClass($name, $layer, $appendSuffix)
{
if (false !== strpos($name, '\\')) {
$class = $name;
$module = $this->request->module();
} else {
if (strpos($name, '/')) {
list($module, $name) = explode('/', $name, 2);
} else {
$module = $this->request->module();
}
$class = $this->parseClass($module, $layer, $name, $appendSuffix);
}
return [$module, $class];
}
/**
* 实例化应用类库
* @access public
* @param string $name 类名称
* @param string $layer 业务层名称
* @param bool $appendSuffix 是否添加类名后缀
* @param string $common 公共模块名
* @return object
* @throws ClassNotFoundException
*/
public function create($name, $layer, $appendSuffix = false, $common = 'common')
{
$guid = $name . $layer;
if ($this->__isset($guid)) {
return $this->__get($guid);
}
list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);
if (class_exists($class)) {
$object = $this->__get($class);
} else {
$class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
if (class_exists($class)) {
$object = $this->__get($class);
} else {
throw new ClassNotFoundException('class not exists:' . $class, $class);
}
}
$this->__set($guid, $class);
return $object;
}
/**
* 实例化(分层)模型
* @access public
* @param string $name Model名称
* @param string $layer 业务层名称
* @param bool $appendSuffix 是否添加类名后缀
* @param string $common 公共模块名
* @return Model
* @throws ClassNotFoundException
*/
public function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common')
{
return $this->create($name, $layer, $appendSuffix, $common);
}
/**
* 实例化(分层)控制器 格式:[模块名/]控制器名
* @access public
* @param string $name 资源地址
* @param string $layer 控制层名称
* @param bool $appendSuffix 是否添加类名后缀
* @param string $empty 空控制器名称
* @return object
* @throws ClassNotFoundException
*/
public function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
{
list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);
if (class_exists($class)) {
return $this->__get($class);
} elseif ($empty && class_exists($emptyClass = $this->parseClass($module, $layer, $empty, $appendSuffix))) {
return $this->__get($emptyClass);
}
throw new ClassNotFoundException('class not exists:' . $class, $class);
}
/**
* 实例化验证类 格式:[模块名/]验证器名
* @access public
* @param string $name 资源地址
* @param string $layer 验证层名称
* @param bool $appendSuffix 是否添加类名后缀
* @param string $common 公共模块名
* @return Validate
* @throws ClassNotFoundException
*/
public function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common')
{
$name = $name ?: $this->config('default_validate');
if (empty($name)) {
return new Validate;
}
return $this->create($name, $layer, $appendSuffix, $common);
}
/**
* 数据库初始化
* @access public
* @param mixed $config 数据库配置
* @param bool|string $name 连接标识 true 强制重新连接
* @return \think\db\Query
*/
public function db($config = [], $name = false)
{
return Db::connect($config, $name);
}
/**
* 远程调用模块的操作方法 参数格式 [模块/控制器/]操作
* @access public
* @param string $url 调用地址
* @param string|array $vars 调用参数 支持字符串和数组
* @param string $layer 要调用的控制层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return mixed
* @throws ClassNotFoundException
*/
public function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
{
$info = pathinfo($url);
$action = $info['basename'];
$module = '.' != $info['dirname'] ? $info['dirname'] : $this->request->controller();
$class = $this->controller($module, $layer, $appendSuffix);
if (is_scalar($vars)) {
if (strpos($vars, '=')) {
parse_str($vars, $vars);
} else {
$vars = [$vars];
}
}
return $this->invokeMethod([$class, $action . $this->config('action_suffix')], $vars);
}
/**
* 解析应用类的类名
* @access public
* @param string $module 模块名
* @param string $layer 层名 controller model ...
* @param string $name 类名
* @param bool $appendSuffix
* @return string
*/
public function parseClass($module, $layer, $name, $appendSuffix = false)
{
$name = str_replace(['/', '.'], '\\', $name);
$array = explode('\\', $name);
$class = Loader::parseName(array_pop($array), 1) . ($this->suffix || $appendSuffix ? ucfirst($layer) : '');
$path = $array ? implode('\\', $array) . '\\' : '';
return $this->namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $path . $class;
}
/**
* 获取框架版本
* @access public
* @return string
*/
public function version()
{
return static::VERSION;
}
/**
* 是否为调试模式
* @access public
* @return bool
*/
public function isDebug()
{
return $this->appDebug;
}
/**
* 获取模块路径
* @access public
* @return string
*/
public function getModulePath()
{
return $this->modulePath;
}
/**
* 设置模块路径
* @access public
* @param string $path 路径
* @return void
*/
public function setModulePath($path)
{
$this->modulePath = $path;
$this->env->set('module_path', $path);
}
/**
* 获取应用根目录
* @access public
* @return string
*/
public function getRootPath()
{
return $this->rootPath;
}
/**
* 获取应用类库目录
* @access public
* @return string
*/
public function getAppPath()
{
if (is_null($this->appPath)) {
$this->appPath = Loader::getRootPath() . 'application' . DIRECTORY_SEPARATOR;
}
return $this->appPath;
}
/**
* 获取应用运行时目录
* @access public
* @return string
*/
public function getRuntimePath()
{
return $this->runtimePath;
}
/**
* 获取核心框架目录
* @access public
* @return string
*/
public function getThinkPath()
{
return $this->thinkPath;
}
/**
* 获取路由目录
* @access public
* @return string
*/
public function getRoutePath()
{
return $this->routePath;
}
/**
* 获取应用配置目录
* @access public
* @return string
*/
public function getConfigPath()
{
return $this->configPath;
}
/**
* 获取配置后缀
* @access public
* @return string
*/
public function getConfigExt()
{
return $this->configExt;
}
/**
* 获取应用类库命名空间
* @access public
* @return string
*/
public function getNamespace()
{
return $this->namespace;
}
/**
* 设置应用类库命名空间
* @access public
* @param string $namespace 命名空间名称
* @return $this
*/
public function setNamespace($namespace)
{
$this->namespace = $namespace;
return $this;
}
/**
* 是否启用类库后缀
* @access public
* @return bool
*/
public function getSuffix()
{
return $this->suffix;
}
/**
* 获取应用开启时间
* @access public
* @return float
*/
public function getBeginTime()
{
return $this->beginTime;
}
/**
* 获取应用初始内存占用
* @access public
* @return integer
*/
public function getBeginMem()
{
return $this->beginMem;
}
}
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
class Build
{
/**
* 应用对象
* @var App
*/
protected $app;
/**
* 应用目录
* @var string
*/
protected $basePath;
public function __construct(App $app)
{
$this->app = $app;
$this->basePath = $this->app->getAppPath();
}
/**
* 根据传入的build资料创建目录和文件
* @access public
* @param array $build build列表
* @param string $namespace 应用类库命名空间
* @param bool $suffix 类库后缀
* @return void
*/
public function run(array $build = [], $namespace = 'app', $suffix = false)
{
// 锁定
$lockfile = $this->basePath . 'build.lock';
if (is_writable($lockfile)) {
return;
} elseif (!touch($lockfile)) {
throw new Exception('应用目录[' . $this->basePath . ']不可写,目录无法自动生成!<BR>请手动生成项目目录~', 10006);
}
foreach ($build as $module => $list) {
if ('__dir__' == $module) {
// 创建目录列表
$this->buildDir($list);
} elseif ('__file__' == $module) {
// 创建文件列表
$this->buildFile($list);
} else {
// 创建模块
$this->module($module, $list, $namespace, $suffix);
}
}
// 解除锁定
unlink($lockfile);
}
/**
* 创建目录
* @access protected
* @param array $list 目录列表
* @return void
*/
protected function buildDir($list)
{
foreach ($list as $dir) {
$this->checkDirBuild($this->basePath . $dir);
}
}
/**
* 创建文件
* @access protected
* @param array $list 文件列表
* @return void
*/
protected function buildFile($list)
{
foreach ($list as $file) {
if (!is_dir($this->basePath . dirname($file))) {
// 创建目录
mkdir($this->basePath . dirname($file), 0755, true);
}
if (!is_file($this->basePath . $file)) {
file_put_contents($this->basePath . $file, 'php' == pathinfo($file, PATHINFO_EXTENSION) ? "<?php\n" : '');
}
}
}
/**
* 创建模块
* @access public
* @param string $module 模块名
* @param array $list build列表
* @param string $namespace 应用类库命名空间
* @param bool $suffix 类库后缀
* @return void
*/
public function module($module = '', $list = [], $namespace = 'app', $suffix = false)
{
$module = $module ? $module : '';
if (!is_dir($this->basePath . $module)) {
// 创建模块目录
mkdir($this->basePath . $module);
}
if (basename($this->app->getRuntimePath()) != $module) {
// 创建配置文件和公共文件
$this->buildCommon($module);
// 创建模块的默认页面
$this->buildHello($module, $namespace, $suffix);
}
if (empty($list)) {
// 创建默认的模块目录和文件
$list = [
'__file__' => ['common.php'],
'__dir__' => ['controller', 'model', 'view', 'config'],
];
}
// 创建子目录和文件
foreach ($list as $path => $file) {
$modulePath = $this->basePath . $module . DIRECTORY_SEPARATOR;
if ('__dir__' == $path) {
// 生成子目录
foreach ($file as $dir) {
$this->checkDirBuild($modulePath . $dir);
}
} elseif ('__file__' == $path) {
// 生成(空白)文件
foreach ($file as $name) {
if (!is_file($modulePath . $name)) {
file_put_contents($modulePath . $name, 'php' == pathinfo($name, PATHINFO_EXTENSION) ? "<?php\n" : '');
}
}
} else {
// 生成相关MVC文件
foreach ($file as $val) {
$val = trim($val);
$filename = $modulePath . $path . DIRECTORY_SEPARATOR . $val . ($suffix ? ucfirst($path) : '') . '.php';
$space = $namespace . '\\' . ($module ? $module . '\\' : '') . $path;
$class = $val . ($suffix ? ucfirst($path) : '');
switch ($path) {
case 'controller': // 控制器
$content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
break;
case 'model': // 模型
$content = "<?php\nnamespace {$space};\n\nuse think\Model;\n\nclass {$class} extends Model\n{\n\n}";
break;
case 'view': // 视图
$filename = $modulePath . $path . DIRECTORY_SEPARATOR . $val . '.html';
$this->checkDirBuild(dirname($filename));
$content = '';
break;
default:
// 其他文件
$content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
}
if (!is_file($filename)) {
file_put_contents($filename, $content);
}
}
}
}
}
/**
* 根据注释自动生成路由规则
* @access public
* @param bool $suffix 类库后缀
* @param string $layer 控制器层目录名
* @return string
*/
public function buildRoute($suffix = false, $layer = '')
{
$namespace = $this->app->getNameSpace();
$content = '<?php ' . PHP_EOL . '//根据 Annotation 自动生成的路由规则';
if (!$layer) {
$layer = $this->app->config('app.url_controller_layer');
}
if ($this->app->config('app.app_multi_module')) {
$modules = glob($this->basePath . '*', GLOB_ONLYDIR);
foreach ($modules as $module) {
$module = basename($module);
if (in_array($module, $this->app->config('app.deny_module_list'))) {
continue;
}
$path = $this->basePath . $module . DIRECTORY_SEPARATOR . $layer . DIRECTORY_SEPARATOR;
$content .= $this->buildDirRoute($path, $namespace, $module, $suffix, $layer);
}
} else {
$path = $this->basePath . $layer . DIRECTORY_SEPARATOR;
$content .= $this->buildDirRoute($path, $namespace, '', $suffix, $layer);
}
$filename = $this->app->getRuntimePath() . 'build_route.php';
file_put_contents($filename, $content);
return $filename;
}
/**
* 生成子目录控制器类的路由规则
* @access protected
* @param string $path 控制器目录
* @param string $namespace 应用命名空间
* @param string $module 模块
* @param bool $suffix 类库后缀
* @param string $layer 控制器层目录名
* @return string
*/
protected function buildDirRoute($path, $namespace, $module, $suffix, $layer)
{
$content = '';
$controllers = glob($path . '*.php');
foreach ($controllers as $controller) {
$controller = basename($controller, '.php');
$class = new \ReflectionClass($namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $controller);
if (strpos($layer, '\\')) {
// 多级控制器
$level = str_replace(DIRECTORY_SEPARATOR, '.', substr($layer, 11));
$controller = $level . '.' . $controller;
$length = strlen(strstr($layer, '\\', true));
} else {
$length = strlen($layer);
}
if ($suffix) {
$controller = substr($controller, 0, -$length);
}
$content .= $this->getControllerRoute($class, $module, $controller);
}
$subDir = glob($path . '*', GLOB_ONLYDIR);
foreach ($subDir as $dir) {
$content .= $this->buildDirRoute($dir . DIRECTORY_SEPARATOR, $namespace, $module, $suffix, $layer . '\\' . basename($dir));
}
return $content;
}
/**
* 生成控制器类的路由规则
* @access protected
* @param string $class 控制器完整类名
* @param string $module 模块名
* @param string $controller 控制器名
* @return string
*/
protected function getControllerRoute($class, $module, $controller)
{
$content = '';
$comment = $class->getDocComment();
if (false !== strpos($comment, '@route(')) {
$comment = $this->parseRouteComment($comment);
$route = ($module ? $module . '/' : '') . $controller;
$comment = preg_replace('/route\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\)/is', 'Route::resource(\1,\'' . $route . '\')', $comment);
$content .= PHP_EOL . $comment;
} elseif (false !== strpos($comment, '@alias(')) {
$comment = $this->parseRouteComment($comment, '@alias(');
$route = ($module ? $module . '/' : '') . $controller;
$comment = preg_replace('/alias\(\s?([\'\"][\-\_\/\w]+[\'\"])\s?\)/is', 'Route::alias(\1,\'' . $route . '\')', $comment);
$content .= PHP_EOL . $comment;
}
$methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method) {
$comment = $this->getMethodRouteComment($module, $controller, $method);
if ($comment) {
$content .= PHP_EOL . $comment;
}
}
return $content;
}
/**
* 解析路由注释
* @access protected
* @param string $comment
* @param string $tag
* @return string
*/
protected function parseRouteComment($comment, $tag = '@route(')
{
$comment = substr($comment, 3, -2);
$comment = explode(PHP_EOL, substr(strstr(trim($comment), $tag), 1));
$comment = array_map(function ($item) {return trim(trim($item), ' \t*');}, $comment);
if (count($comment) > 1) {
$key = array_search('', $comment);
$comment = array_slice($comment, 0, false === $key ? 1 : $key);
}
$comment = implode(PHP_EOL . "\t", $comment) . ';';
if (strpos($comment, '{')) {
$comment = preg_replace_callback('/\{\s?.*?\s?\}/s', function ($matches) {
return false !== strpos($matches[0], '"') ? '[' . substr(var_export(json_decode($matches[0], true), true), 7, -1) . ']' : $matches[0];
}, $comment);
}
return $comment;
}
/**
* 获取方法的路由注释
* @access protected
* @param string $module 模块
* @param string $controller 控制器名
* @param \ReflectMethod $reflectMethod
* @return string|void
*/
protected function getMethodRouteComment($module, $controller, $reflectMethod)
{
$comment = $reflectMethod->getDocComment();
if (false !== strpos($comment, '@route(')) {
$comment = $this->parseRouteComment($comment);
$action = $reflectMethod->getName();
if ($suffix = $this->app->config('app.action_suffix')) {
$action = substr($action, 0, -strlen($suffix));
}
$route = ($module ? $module . '/' : '') . $controller . '/' . $action;
$comment = preg_replace('/route\s?\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\,?\s?[\'\"]?(\w+?)[\'\"]?\s?\)/is', 'Route::\2(\1,\'' . $route . '\')', $comment);
$comment = preg_replace('/route\s?\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\)/is', 'Route::rule(\1,\'' . $route . '\')', $comment);
return $comment;
}
}
/**
* 创建模块的欢迎页面
* @access protected
* @param string $module 模块名
* @param string $namespace 应用类库命名空间
* @param bool $suffix 类库后缀
* @return void
*/
protected function buildHello($module, $namespace, $suffix = false)
{
$filename = $this->basePath . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'controller' . DIRECTORY_SEPARATOR . 'Index' . ($suffix ? 'Controller' : '') . '.php';
if (!is_file($filename)) {
$content = file_get_contents($this->app->getThinkPath() . 'tpl' . DIRECTORY_SEPARATOR . 'default_index.tpl');
$content = str_replace(['{$app}', '{$module}', '{layer}', '{$suffix}'], [$namespace, $module ? $module . '\\' : '', 'controller', $suffix ? 'Controller' : ''], $content);
$this->checkDirBuild(dirname($filename));
file_put_contents($filename, $content);
}
}
/**
* 创建模块的公共文件
* @access protected
* @param string $module 模块名
* @return void
*/
protected function buildCommon($module)
{
$filename = $this->app->getConfigPath() . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'app.php';
$this->checkDirBuild(dirname($filename));
if (!is_file($filename)) {
file_put_contents($filename, "<?php\n//配置文件\nreturn [\n\n];");
}
$filename = $this->basePath . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'common.php';
if (!is_file($filename)) {
file_put_contents($filename, "<?php\n");
}
}
/**
* 创建目录
* @access protected
* @param string $dirname 目录名称
* @return void
*/
protected function checkDirBuild($dirname)
{
if (!is_dir($dirname)) {
mkdir($dirname, 0755, true);
}
}
}
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
use think\cache\Driver;
/**
* Class Cache
*
* @package think
*
* @mixin Driver
* @mixin \think\cache\driver\File
*/
class Cache
{
/**
* 缓存实例
* @var array
*/
protected $instance = [];
/**
* 缓存配置
* @var array
*/
protected $config = [];
/**
* 操作句柄
* @var object
*/
protected $handler;
public function __construct(array $config = [])
{
$this->config = $config;
$this->init($config);
}
/**
* 连接缓存
* @access public
* @param array $options 配置数组
* @param bool|string $name 缓存连接标识 true 强制重新连接
* @return Driver
*/
public function connect(array $options = [], $name = false)
{
if (false === $name) {
$name = md5(serialize($options));
}
if (true === $name || !isset($this->instance[$name])) {
$type = !empty($options['type']) ? $options['type'] : 'File';
if (true === $name) {
$name = md5(serialize($options));
}
$this->instance[$name] = Loader::factory($type, '\\think\\cache\\driver\\', $options);
}
return $this->instance[$name];
}
/**
* 自动初始化缓存
* @access public
* @param array $options 配置数组
* @param bool $force 强制更新
* @return Driver
*/
public function init(array $options = [], $force = false)
{
if (is_null($this->handler) || $force) {
if ('complex' == $options['type']) {
$default = $options['default'];
$options = isset($options[$default['type']]) ? $options[$default['type']] : $default;
}
$this->handler = $this->connect($options);
}
return $this->handler;
}
public static function __make(Config $config)
{
return new static($config->pull('cache'));
}
public function getConfig()
{
return $this->config;
}
public function setConfig(array $config)
{
$this->config = array_merge($this->config, $config);
}
/**
* 切换缓存类型 需要配置 cache.type 为 complex
* @access public
* @param string $name 缓存标识
* @return Driver
*/
public function store($name = '')
{
if ('' !== $name && 'complex' == $this->config['type']) {
return $this->connect($this->config[$name], strtolower($name));
}
return $this->init();
}
public function __call($method, $args)
{
return call_user_func_array([$this->init(), $method], $args);
}
}
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: zhangyajun <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think;
use ArrayAccess;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use JsonSerializable;
class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
{
/**
* 数据集数据
* @var array
*/
protected $items = [];
public function __construct($items = [])
{
$this->items = $this->convertToArray($items);
}
public static function make($items = [])
{
return new static($items);
}
/**
* 是否为空
* @access public
* @return bool
*/
public function isEmpty()
{
return empty($this->items);
}
public function toArray()
{
return array_map(function ($value) {
return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value;
}, $this->items);
}
public function all()
{
return $this->items;
}
/**
* 合并数组
*
* @access public
* @param mixed $items
* @return static
*/
public function merge($items)
{
return new static(array_merge($this->items, $this->convertToArray($items)));
}
/**
* 交换数组中的键和值
*
* @access public
* @return static
*/
public function flip()
{
return new static(array_flip($this->items));
}
/**
* 按指定键整理数据
*
* @access public
* @param mixed $items 数据
* @param string $indexKey 键名
* @return array
*/
public function dictionary($items = null, &$indexKey = null)
{
if ($items instanceof self || $items instanceof Paginator) {
$items = $items->all();
}
$items = is_null($items) ? $this->items : $items;
if ($items && empty($indexKey)) {
$indexKey = is_array($items[0]) ? 'id' : $items[0]->getPk();
}
if (isset($indexKey) && is_string($indexKey)) {
return array_column($items, null, $indexKey);
}
return $items;
}
/**
* 比较数组,返回差集
*
* @access public
* @param mixed $items 数据
* @param string $indexKey 指定比较的键名
* @return static
*/
public function diff($items, $indexKey = null)
{
if ($this->isEmpty() || is_scalar($this->items[0])) {
return new static(array_diff($this->items, $this->convertToArray($items)));
}
$diff = [];
$dictionary = $this->dictionary($items, $indexKey);
if (is_string($indexKey)) {
foreach ($this->items as $item) {
if (!isset($dictionary[$item[$indexKey]])) {
$diff[] = $item;
}
}
}
return new static($diff);
}
/**
* 比较数组,返回交集
*
* @access public
* @param mixed $items 数据
* @param string $indexKey 指定比较的键名
* @return static
*/
public function intersect($items, $indexKey = null)
{
if ($this->isEmpty() || is_scalar($this->items[0])) {
return new static(array_diff($this->items, $this->convertToArray($items)));
}
$intersect = [];
$dictionary = $this->dictionary($items, $indexKey);
if (is_string($indexKey)) {
foreach ($this->items as $item) {
if (isset($dictionary[$item[$indexKey]])) {
$intersect[] = $item;
}
}
}
return new static($intersect);
}
/**
* 返回数组中所有的键名
*
* @access public
* @return array
*/
public function keys()
{
$current = current($this->items);
if (is_scalar($current)) {
$array = $this->items;
} elseif (is_array($current)) {
$array = $current;
} else {
$array = $current->toArray();
}
return array_keys($array);
}
/**
* 删除数组的最后一个元素(出栈)
*
* @access public
* @return mixed
*/
public function pop()
{
return array_pop($this->items);
}
/**
* 通过使用用户自定义函数,以字符串返回数组
*
* @access public
* @param callable $callback
* @param mixed $initial
* @return mixed
*/
public function reduce(callable $callback, $initial = null)
{
return array_reduce($this->items, $callback, $initial);
}
/**
* 以相反的顺序返回数组。
*
* @access public
* @return static
*/
public function reverse()
{
return new static(array_reverse($this->items));
}
/**
* 删除数组中首个元素,并返回被删除元素的值
*
* @access public
* @return mixed
*/
public function shift()
{
return array_shift($this->items);
}
/**
* 在数组结尾插入一个元素
* @access public
* @param mixed $value
* @param mixed $key
* @return void
*/
public function push($value, $key = null)
{
if (is_null($key)) {
$this->items[] = $value;
} else {
$this->items[$key] = $value;
}
}
/**
* 把一个数组分割为新的数组块.
*
* @access public
* @param int $size
* @param bool $preserveKeys
* @return static
*/
public function chunk($size, $preserveKeys = false)
{
$chunks = [];
foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) {
$chunks[] = new static($chunk);
}
return new static($chunks);
}
/**
* 在数组开头插入一个元素
* @access public
* @param mixed $value
* @param mixed $key
* @return void
*/
public function unshift($value, $key = null)
{
if (is_null($key)) {
array_unshift($this->items, $value);
} else {
$this->items = [$key => $value] + $this->items;
}
}
/**
* 给每个元素执行个回调
*
* @access public
* @param callable $callback
* @return $this
*/
public function each(callable $callback)
{
foreach ($this->items as $key => $item) {
$result = $callback($item, $key);
if (false === $result) {
break;
} elseif (!is_object($item)) {
$this->items[$key] = $result;
}
}
return $this;
}
/**
* 用回调函数处理数组中的元素
* @access public
* @param callable|null $callback
* @return static
*/
public function map(callable $callback)
{
return new static(array_map($callback, $this->items));
}
/**
* 用回调函数过滤数组中的元素
* @access public
* @param callable|null $callback
* @return static
*/
public function filter(callable $callback = null)
{
if ($callback) {
return new static(array_filter($this->items, $callback));
}
return new static(array_filter($this->items));
}
/**
* 根据字段条件过滤数组中的元素
* @access public
* @param string $field 字段名
* @param mixed $operator 操作符
* @param mixed $value 数据
* @return static
*/
public function where($field, $operator, $value = null)
{
if (is_null($value)) {
$value = $operator;
$operator = '=';
}
return $this->filter(function ($data) use ($field, $operator, $value) {
if (strpos($field, '.')) {
list($field, $relation) = explode('.', $field);
$result = isset($data[$field][$relation]) ? $data[$field][$relation] : null;
} else {
$result = isset($data[$field]) ? $data[$field] : null;
}
switch ($operator) {
case '===':
return $result === $value;
case '!==':
return $result !== $value;
case '!=':
case '<>':
return $result != $value;
case '>':
return $result > $value;
case '>=':
return $result >= $value;
case '<':
return $result < $value;
case '<=':
return $result <= $value;
case 'like':
return is_string($result) && false !== strpos($result, $value);
case 'not like':
return is_string($result) && false === strpos($result, $value);
case 'in':
return is_scalar($result) && in_array($result, $value, true);
case 'not in':
return is_scalar($result) && !in_array($result, $value, true);
case 'between':
list($min, $max) = is_string($value) ? explode(',', $value) : $value;
return is_scalar($result) && $result >= $min && $result <= $max;
case 'not between':
list($min, $max) = is_string($value) ? explode(',', $value) : $value;
return is_scalar($result) && $result > $max || $result < $min;
case '==':
case '=':
default:
return $result == $value;
}
});
}
/**
* 返回数据中指定的一列
* @access public
* @param mixed $columnKey 键名
* @param mixed $indexKey 作为索引值的列
* @return array
*/
public function column($columnKey, $indexKey = null)
{
return array_column($this->items, $columnKey, $indexKey);
}
/**
* 对数组排序
*
* @access public
* @param callable|null $callback
* @return static
*/
public function sort(callable $callback = null)
{
$items = $this->items;
$callback = $callback ?: function ($a, $b) {
return $a == $b ? 0 : (($a < $b) ? -1 : 1);
};
uasort($items, $callback);
return new static($items);
}
/**
* 指定字段排序
* @access public
* @param string $field 排序字段
* @param string $order 排序
* @param bool $intSort 是否为数字排序
* @return $this
*/
public function order($field, $order = null, $intSort = true)
{
return $this->sort(function ($a, $b) use ($field, $order, $intSort) {
$fieldA = isset($a[$field]) ? $a[$field] : null;
$fieldB = isset($b[$field]) ? $b[$field] : null;
if ($intSort) {
return 'desc' == strtolower($order) ? $fieldB >= $fieldA : $fieldA >= $fieldB;
} else {
return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB);
}
});
}
/**
* 将数组打乱
*
* @access public
* @return static
*/
public function shuffle()
{
$items = $this->items;
shuffle($items);
return new static($items);
}
/**
* 截取数组
*
* @access public
* @param int $offset
* @param int $length
* @param bool $preserveKeys
* @return static
*/
public function slice($offset, $length = null, $preserveKeys = false)
{
return new static(array_slice($this->items, $offset, $length, $preserveKeys));
}
// ArrayAccess
public function offsetExists($offset)
{
return array_key_exists($offset, $this->items);
}
public function offsetGet($offset)
{
return $this->items[$offset];
}
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->items[] = $value;
} else {
$this->items[$offset] = $value;
}
}
public function offsetUnset($offset)
{
unset($this->items[$offset]);
}
//Countable
public function count()
{
return count($this->items);
}
//IteratorAggregate
public function getIterator()
{
return new ArrayIterator($this->items);
}
//JsonSerializable
public function jsonSerialize()
{
return $this->toArray();
}
/**
* 转换当前数据集为JSON字符串
* @access public
* @param integer $options json参数
* @return string
*/
public function toJson($options = JSON_UNESCAPED_UNICODE)
{
return json_encode($this->toArray(), $options);
}
public function __toString()
{
return $this->toJson();
}
/**
* 转换成数组
*
* @access public
* @param mixed $items
* @return array
*/
protected function convertToArray($items)
{
if ($items instanceof self) {
return $items->all();
}
return (array) $items;
}
}
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
use Yaconf;
class Config implements \ArrayAccess
{
/**
* 配置参数
* @var array
*/
protected $config = [];
/**
* 配置前缀
* @var string
*/
protected $prefix = 'app';
/**
* 配置文件目录
* @var string
*/
protected $path;
/**
* 配置文件后缀
* @var string
*/
protected $ext;
/**
* 是否支持Yaconf
* @var bool
*/
protected $yaconf;
/**
* 构造方法
* @access public
*/
public function __construct($path = '', $ext = '.php')
{
$this->path = $path;
$this->ext = $ext;
$this->yaconf = class_exists('Yaconf');
}
public static function __make(App $app)
{
$path = $app->getConfigPath();
$ext = $app->getConfigExt();
return new static($path, $ext);
}
/**
* 设置开启Yaconf
* @access public
* @param bool|string $yaconf 是否使用Yaconf
* @return void
*/
public function setYaconf($yaconf)
{
if ($this->yaconf) {
$this->yaconf = $yaconf;
}
}
/**
* 设置配置参数默认前缀
* @access public
* @param string $prefix 前缀
* @return void
*/
public function setDefaultPrefix($prefix)
{
$this->prefix = $prefix;
}
/**
* 解析配置文件或内容
* @access public
* @param string $config 配置文件路径或内容
* @param string $type 配置解析类型
* @param string $name 配置名(如设置即表示二级配置)
* @return mixed
*/
public function parse($config, $type = '', $name = '')
{
if (empty($type)) {
$type = pathinfo($config, PATHINFO_EXTENSION);
}
$object = Loader::factory($type, '\\think\\config\\driver\\', $config);
return $this->set($object->parse(), $name);
}
/**
* 加载配置文件(多种格式)
* @access public
* @param string $file 配置文件名
* @param string $name 一级配置名
* @return mixed
*/
public function load($file, $name = '')
{
if (is_file($file)) {
$filename = $file;
} elseif (is_file($this->path . $file . $this->ext)) {
$filename = $this->path . $file . $this->ext;
}
if (isset($filename)) {
return $this->loadFile($filename, $name);
} elseif ($this->yaconf && Yaconf::has($file)) {
return $this->set(Yaconf::get($file), $name);
}
return $this->config;
}
/**
* 获取实际的yaconf配置参数
* @access protected
* @param string $name 配置参数名
* @return string
*/
protected function getYaconfName($name)
{
if ($this->yaconf && is_string($this->yaconf)) {
return $this->yaconf . '.' . $name;
}
return $name;
}
/**
* 获取yaconf配置
* @access public
* @param string $name 配置参数名
* @param mixed $default 默认值
* @return mixed
*/
public function yaconf($name, $default = null)
{
if ($this->yaconf) {
$yaconfName = $this->getYaconfName($name);
if (Yaconf::has($yaconfName)) {
return Yaconf::get($yaconfName);
}
}
return $default;
}
protected function loadFile($file, $name)
{
$name = strtolower($name);
$type = pathinfo($file, PATHINFO_EXTENSION);
if ('php' == $type) {
return $this->set(include $file, $name);
} elseif ('yaml' == $type && function_exists('yaml_parse_file')) {
return $this->set(yaml_parse_file($file), $name);
}
return $this->parse($file, $type, $name);
}
/**
* 检测配置是否存在
* @access public
* @param string $name 配置参数名(支持多级配置 .号分割)
* @return bool
*/
public function has($name)
{
if (!strpos($name, '.')) {
$name = $this->prefix . '.' . $name;
}
return !is_null($this->get($name));
}
/**
* 获取一级配置
* @access public
* @param string $name 一级配置名
* @return array
*/
public function pull($name)
{
$name = strtolower($name);
if ($this->yaconf) {
$yaconfName = $this->getYaconfName($name);
if (Yaconf::has($yaconfName)) {
$config = Yaconf::get($yaconfName);
return isset($this->config[$name]) ? array_merge($this->config[$name], $config) : $config;
}
}
return isset($this->config[$name]) ? $this->config[$name] : [];
}
/**
* 获取配置参数 为空则获取所有配置
* @access public
* @param string $name 配置参数名(支持多级配置 .号分割)
* @param mixed $default 默认值
* @return mixed
*/
public function get($name = null, $default = null)
{
if ($name && !strpos($name, '.')) {
$name = $this->prefix . '.' . $name;
}
// 无参数时获取所有
if (empty($name)) {
return $this->config;
}
if ('.' == substr($name, -1)) {
return $this->pull(substr($name, 0, -1));
}
if ($this->yaconf) {
$yaconfName = $this->getYaconfName($name);
if (Yaconf::has($yaconfName)) {
return Yaconf::get($yaconfName);
}
}
$name = explode('.', $name);
$name[0] = strtolower($name[0]);
$config = $this->config;
// 按.拆分成多维数组进行判断
foreach ($name as $val) {
if (isset($config[$val])) {
$config = $config[$val];
} else {
return $default;
}
}
return $config;
}
/**
* 设置配置参数 name为数组则为批量设置
* @access public
* @param string|array $name 配置参数名(支持三级配置 .号分割)
* @param mixed $value 配置值
* @return mixed
*/
public function set($name, $value = null)
{
if (is_string($name)) {
if (!strpos($name, '.')) {
$name = $this->prefix . '.' . $name;
}
$name = explode('.', $name, 3);
if (count($name) == 2) {
$this->config[strtolower($name[0])][$name[1]] = $value;
} else {
$this->config[strtolower($name[0])][$name[1]][$name[2]] = $value;
}
return $value;
} elseif (is_array($name)) {
// 批量设置
if (!empty($value)) {
if (isset($this->config[$value])) {
$result = array_merge($this->config[$value], $name);
} else {
$result = $name;
}
$this->config[$value] = $result;
} else {
$result = $this->config = array_merge($this->config, $name);
}
} else {
// 为空直接返回 已有配置
$result = $this->config;
}
return $result;
}
/**
* 移除配置
* @access public
* @param string $name 配置参数名(支持三级配置 .号分割)
* @return void
*/
public function remove($name)
{
if (!strpos($name, '.')) {
$name = $this->prefix . '.' . $name;
}
$name = explode('.', $name, 3);
if (count($name) == 2) {
unset($this->config[strtolower($name[0])][$name[1]]);
} else {
unset($this->config[strtolower($name[0])][$name[1]][$name[2]]);
}
}
/**
* 重置配置参数
* @access public
* @param string $prefix 配置前缀名
* @return void
*/
public function reset($prefix = '')
{
if ('' === $prefix) {
$this->config = [];
} else {
$this->config[$prefix] = [];
}
}
/**
* 设置配置
* @access public
* @param string $name 参数名
* @param mixed $value 值
*/
public function __set($name, $value)
{
return $this->set($name, $value);
}
/**
* 获取配置参数
* @access public
* @param string $name 参数名
* @return mixed
*/
public function __get($name)
{
return $this->get($name);
}
/**
* 检测是否存在参数
* @access public
* @param string $name 参数名
* @return bool
*/
public function __isset($name)
{
return $this->has($name);
}
// ArrayAccess
public function offsetSet($name, $value)
{
$this->set($name, $value);
}
public function offsetExists($name)
{
return $this->has($name);
}
public function offsetUnset($name)
{
$this->remove($name);
}
public function offsetGet($name)
{
return $this->get($name);
}
}
<?php
// +----------------------------------------------------------------------
// | TopThink [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2015 http://www.topthink.com All rights reserved.
// +----------------------------------------------------------------------
// | Author: zhangyajun <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think;
use think\console\Command;
use think\console\command\Help as HelpCommand;
use think\console\Input;
use think\console\input\Argument as InputArgument;
use think\console\input\Definition as InputDefinition;
use think\console\input\Option as InputOption;
use think\console\Output;
use think\console\output\driver\Buffer;
class Console
{
private $name;
private $version;
/** @var Command[] */
private $commands = [];
private $wantHelps = false;
private $catchExceptions = true;
private $autoExit = true;
private $definition;
private $defaultCommand;
private static $defaultCommands = [
'help' => "think\\console\\command\\Help",
'list' => "think\\console\\command\\Lists",
'build' => "think\\console\\command\\Build",
'clear' => "think\\console\\command\\Clear",
'make:command' => "think\\console\\command\\make\\Command",
'make:controller' => "think\\console\\command\\make\\Controller",
'make:model' => "think\\console\\command\\make\\Model",
'make:middleware' => "think\\console\\command\\make\\Middleware",
'make:validate' => "think\\console\\command\\make\\Validate",
'optimize:autoload' => "think\\console\\command\\optimize\\Autoload",
'optimize:config' => "think\\console\\command\\optimize\\Config",
'optimize:schema' => "think\\console\\command\\optimize\\Schema",
'optimize:route' => "think\\console\\command\\optimize\\Route",
'run' => "think\\console\\command\\RunServer",
'version' => "think\\console\\command\\Version",
'route:list' => "think\\console\\command\\RouteList",
];
/**
* Console constructor.
* @access public
* @param string $name 名称
* @param string $version 版本
* @param null|string $user 执行用户
*/
public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN', $user = null)
{
$this->name = $name;
$this->version = $version;
if ($user) {
$this->setUser($user);
}
$this->defaultCommand = 'list';
$this->definition = $this->getDefaultInputDefinition();
}
/**
* 设置执行用户
* @param $user
*/
public function setUser($user)
{
if (DIRECTORY_SEPARATOR == '\\') {
return;
}
$user = posix_getpwnam($user);
if ($user) {
posix_setuid($user['uid']);
posix_setgid($user['gid']);
}
}
/**
* 初始化 Console
* @access public
* @param bool $run 是否运行 Console
* @return int|Console
*/
public static function init($run = true)
{
static $console;
if (!$console) {
$config = Container::get('config')->pull('console');
$console = new self($config['name'], $config['version'], $config['user']);
$commands = $console->getDefinedCommands($config);
// 添加指令集
$console->addCommands($commands);
}
if ($run) {
// 运行
return $console->run();
} else {
return $console;
}
}
/**
* @access public
* @param array $config
* @return array
*/
public function getDefinedCommands(array $config = [])
{
$commands = self::$defaultCommands;
if (!empty($config['auto_path']) && is_dir($config['auto_path'])) {
// 自动加载指令类
$files = scandir($config['auto_path']);
if (count($files) > 2) {
$beforeClass = get_declared_classes();
foreach ($files as $file) {
if (pathinfo($file, PATHINFO_EXTENSION) == 'php') {
include $config['auto_path'] . $file;
}
}
$afterClass = get_declared_classes();
$commands = array_merge($commands, array_diff($afterClass, $beforeClass));
}
}
$file = Container::get('env')->get('app_path') . 'command.php';
if (is_file($file)) {
$appCommands = include $file;
if (is_array($appCommands)) {
$commands = array_merge($commands, $appCommands);
}
}
return $commands;
}
/**
* @access public
* @param string $command
* @param array $parameters
* @param string $driver
* @return Output|Buffer
*/
public static function call($command, array $parameters = [], $driver = 'buffer')
{
$console = self::init(false);
array_unshift($parameters, $command);
$input = new Input($parameters);
$output = new Output($driver);
$console->setCatchExceptions(false);
$console->find($command)->run($input, $output);
return $output;
}
/**
* 执行当前的指令
* @access public
* @return int
* @throws \Exception
* @api
*/
public function run()
{
$input = new Input();
$output = new Output();
$this->configureIO($input, $output);
try {
$exitCode = $this->doRun($input, $output);
} catch (\Exception $e) {
if (!$this->catchExceptions) {
throw $e;
}
$output->renderException($e);
$exitCode = $e->getCode();
if (is_numeric($exitCode)) {
$exitCode = (int) $exitCode;
if (0 === $exitCode) {
$exitCode = 1;
}
} else {
$exitCode = 1;
}
}
if ($this->autoExit) {
if ($exitCode > 255) {
$exitCode = 255;
}
exit($exitCode);
}
return $exitCode;
}
/**
* 执行指令
* @access public
* @param Input $input
* @param Output $output
* @return int
*/
public function doRun(Input $input, Output $output)
{
if (true === $input->hasParameterOption(['--version', '-V'])) {
$output->writeln($this->getLongVersion());
return 0;
}
$name = $this->getCommandName($input);
if (true === $input->hasParameterOption(['--help', '-h'])) {
if (!$name) {
$name = 'help';
$input = new Input(['help']);
} else {
$this->wantHelps = true;
}
}
if (!$name) {
$name = $this->defaultCommand;
$input = new Input([$this->defaultCommand]);
}
$command = $this->find($name);
$exitCode = $this->doRunCommand($command, $input, $output);
return $exitCode;
}
/**
* 设置输入参数定义
* @access public
* @param InputDefinition $definition
*/
public function setDefinition(InputDefinition $definition)
{
$this->definition = $definition;
}
/**
* 获取输入参数定义
* @access public
* @return InputDefinition The InputDefinition instance
*/
public function getDefinition()
{
return $this->definition;
}
/**
* Gets the help message.
* @access public
* @return string A help message.
*/
public function getHelp()
{
return $this->getLongVersion();
}
/**
* 是否捕获异常
* @access public
* @param bool $boolean
* @api
*/
public function setCatchExceptions($boolean)
{
$this->catchExceptions = (bool) $boolean;
}
/**
* 是否自动退出
* @access public
* @param bool $boolean
* @api
*/
public function setAutoExit($boolean)
{
$this->autoExit = (bool) $boolean;
}
/**
* 获取名称
* @access public
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* 设置名称
* @access public
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* 获取版本
* @access public
* @return string
* @api
*/
public function getVersion()
{
return $this->version;
}
/**
* 设置版本
* @access public
* @param string $version
*/
public function setVersion($version)
{
$this->version = $version;
}
/**
* 获取完整的版本号
* @access public
* @return string
*/
public function getLongVersion()
{
if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
}
return '<info>Console Tool</info>';
}
/**
* 注册一个指令 (便于动态创建指令)
* @access public
* @param string $name 指令名
* @return Command
*/
public function register($name)
{
return $this->add(new Command($name));
}
/**
* 添加指令集
* @access public
* @param array $commands
*/
public function addCommands(array $commands)
{
foreach ($commands as $key => $command) {
if (is_subclass_of($command, "\\think\\console\\Command")) {
// 注册指令
$this->add($command, is_numeric($key) ? '' : $key);
}
}
}
/**
* 注册一个指令(对象)
* @access public
* @param mixed $command 指令对象或者指令类名
* @param string $name 指令名 留空则自动获取
* @return mixed
*/
public function add($command, $name)
{
if ($name) {
$this->commands[$name] = $command;
return;
}
if (is_string($command)) {
$command = new $command();
}
$command->setConsole($this);
if (!$command->isEnabled()) {
$command->setConsole(null);
return;
}
if (null === $command->getDefinition()) {
throw new \LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command)));
}
$this->commands[$command->getName()] = $command;
foreach ($command->getAliases() as $alias) {
$this->commands[$alias] = $command;
}
return $command;
}
/**
* 获取指令
* @access public
* @param string $name 指令名称
* @return Command
* @throws \InvalidArgumentException
*/
public function get($name)
{
if (!isset($this->commands[$name])) {
throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name));
}
$command = $this->commands[$name];
if (is_string($command)) {
$command = new $command();
}
$command->setConsole($this);
if ($this->wantHelps) {
$this->wantHelps = false;
/** @var HelpCommand $helpCommand */
$helpCommand = $this->get('help');
$helpCommand->setCommand($command);
return $helpCommand;
}
return $command;
}
/**
* 某个指令是否存在
* @access public
* @param string $name 指令名称
* @return bool
*/
public function has($name)
{
return isset($this->commands[$name]);
}
/**
* 获取所有的命名空间
* @access public
* @return array
*/
public function getNamespaces()
{
$namespaces = [];
foreach ($this->commands as $command) {
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
foreach ($command->getAliases() as $alias) {
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
}
}
return array_values(array_unique(array_filter($namespaces)));
}
/**
* 查找注册命名空间中的名称或缩写。
* @access public
* @param string $namespace
* @return string
* @throws \InvalidArgumentException
*/
public function findNamespace($namespace)
{
$allNamespaces = $this->getNamespaces();
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
return preg_quote($matches[1]) . '[^:]*';
}, $namespace);
$namespaces = preg_grep('{^' . $expr . '}', $allNamespaces);
if (empty($namespaces)) {
$message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
if (1 == count($alternatives)) {
$message .= "\n\nDid you mean this?\n ";
} else {
$message .= "\n\nDid you mean one of these?\n ";
}
$message .= implode("\n ", $alternatives);
}
throw new \InvalidArgumentException($message);
}
$exact = in_array($namespace, $namespaces, true);
if (count($namespaces) > 1 && !$exact) {
throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))));
}
return $exact ? $namespace : reset($namespaces);
}
/**
* 查找指令
* @access public
* @param string $name 名称或者别名
* @return Command
* @throws \InvalidArgumentException
*/
public function find($name)
{
$allCommands = array_keys($this->commands);
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
return preg_quote($matches[1]) . '[^:]*';
}, $name);
$commands = preg_grep('{^' . $expr . '}', $allCommands);
if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) {
if (false !== $pos = strrpos($name, ':')) {
$this->findNamespace(substr($name, 0, $pos));
}
$message = sprintf('Command "%s" is not defined.', $name);
if ($alternatives = $this->findAlternatives($name, $allCommands)) {
if (1 == count($alternatives)) {
$message .= "\n\nDid you mean this?\n ";
} else {
$message .= "\n\nDid you mean one of these?\n ";
}
$message .= implode("\n ", $alternatives);
}
throw new \InvalidArgumentException($message);
}
$exact = in_array($name, $commands, true);
if (count($commands) > 1 && !$exact) {
$suggestions = $this->getAbbreviationSuggestions(array_values($commands));
throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions));
}
return $this->get($exact ? $name : reset($commands));
}
/**
* 获取所有的指令
* @access public
* @param string $namespace 命名空间
* @return Command[]
* @api
*/
public function all($namespace = null)
{
if (null === $namespace) {
return $this->commands;
}
$commands = [];
foreach ($this->commands as $name => $command) {
if ($this->extractNamespace($name, substr_count($namespace, ':') + 1) === $namespace) {
$commands[$name] = $command;
}
}
return $commands;
}
/**
* 获取可能的指令名
* @access public
* @param array $names
* @return array
*/
public static function getAbbreviations($names)
{
$abbrevs = [];
foreach ($names as $name) {
for ($len = strlen($name); $len > 0; --$len) {
$abbrev = substr($name, 0, $len);
$abbrevs[$abbrev][] = $name;
}
}
return $abbrevs;
}
/**
* 配置基于用户的参数和选项的输入和输出实例。
* @access protected
* @param Input $input 输入实例
* @param Output $output 输出实例
*/
protected function configureIO(Input $input, Output $output)
{
if (true === $input->hasParameterOption(['--ansi'])) {
$output->setDecorated(true);
} elseif (true === $input->hasParameterOption(['--no-ansi'])) {
$output->setDecorated(false);
}
if (true === $input->hasParameterOption(['--no-interaction', '-n'])) {
$input->setInteractive(false);
}
if (true === $input->hasParameterOption(['--quiet', '-q'])) {
$output->setVerbosity(Output::VERBOSITY_QUIET);
} else {
if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) {
$output->setVerbosity(Output::VERBOSITY_DEBUG);
} elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) {
$output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE);
} elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) {
$output->setVerbosity(Output::VERBOSITY_VERBOSE);
}
}
}
/**
* 执行指令
* @access protected
* @param Command $command 指令实例
* @param Input $input 输入实例
* @param Output $output 输出实例
* @return int
* @throws \Exception
*/
protected function doRunCommand(Command $command, Input $input, Output $output)
{
return $command->run($input, $output);
}
/**
* 获取指令的基础名称
* @access protected
* @param Input $input
* @return string
*/
protected function getCommandName(Input $input)
{
return $input->getFirstArgument();
}
/**
* 获取默认输入定义
* @access protected
* @return InputDefinition
*/
protected function getDefaultInputDefinition()
{
return new InputDefinition([
new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'),
new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this console version'),
new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
]);
}
public static function addDefaultCommands(array $classnames)
{
self::$defaultCommands = array_merge(self::$defaultCommands, $classnames);
}
/**
* 获取可能的建议
* @access private
* @param array $abbrevs
* @return string
*/
private function getAbbreviationSuggestions($abbrevs)
{
return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '');
}
/**
* 返回命名空间部分
* @access public
* @param string $name 指令
* @param string $limit 部分的命名空间的最大数量
* @return string
*/
public function extractNamespace($name, $limit = null)
{
$parts = explode(':', $name);
array_pop($parts);
return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit));
}
/**
* 查找可替代的建议
* @access private
* @param string $name
* @param array|\Traversable $collection
* @return array
*/
private function findAlternatives($name, $collection)
{
$threshold = 1e3;
$alternatives = [];
$collectionParts = [];
foreach ($collection as $item) {
$collectionParts[$item] = explode(':', $item);
}
foreach (explode(':', $name) as $i => $subname) {
foreach ($collectionParts as $collectionName => $parts) {
$exists = isset($alternatives[$collectionName]);
if (!isset($parts[$i]) && $exists) {
$alternatives[$collectionName] += $threshold;
continue;
} elseif (!isset($parts[$i])) {
continue;
}
$lev = levenshtein($subname, $parts[$i]);
if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
$alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
} elseif ($exists) {
$alternatives[$collectionName] += $threshold;
}
}
}
foreach ($collection as $item) {
$lev = levenshtein($name, $item);
if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
$alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
}
}
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) {
return $lev < 2 * $threshold;
});
asort($alternatives);
return array_keys($alternatives);
}
/**
* 设置默认的指令
* @access public
* @param string $commandName The Command name
*/
public function setDefaultCommand($commandName)
{
$this->defaultCommand = $commandName;
}
/**
* 返回所有的命名空间
* @access private
* @param string $name
* @return array
*/
private function extractAllNamespaces($name)
{
$parts = explode(':', $name, -1);
$namespaces = [];
foreach ($parts as $part) {
if (count($namespaces)) {
$namespaces[] = end($namespaces) . ':' . $part;
} else {
$namespaces[] = $part;
}
}
return $namespaces;
}
public function __debugInfo()
{
$data = get_object_vars($this);
unset($data['commands'], $data['definition']);
return $data;
}
}