feat: 本次提交更新内容如下
php有问题,覆盖下
This commit is contained in:
8
Server/thinkphp/.gitignore
vendored
8
Server/thinkphp/.gitignore
vendored
@@ -1,4 +1,8 @@
|
||||
/composer.lock
|
||||
/vendor
|
||||
.idea
|
||||
composer.phar
|
||||
composer.lock
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
/phpunit.xml
|
||||
/.idea
|
||||
/.vscode
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
ThinkPHP 目前使用 Git 来控制程序版本,如果你想为 ThinkPHP 贡献源代码,请先大致了解 Git 的使用方法。我们目前把项目托管在 GitHub 上,任何 GitHub 用户都可以向我们贡献代码。
|
||||
|
||||
参与的方式很简单,`fork`一份 ThinkPHP 的代码到你的仓库中,修改后提交,并向我们发起`pull request`申请,我们会及时对代码进行审查并处理你的申请。审查通过后,你的代码将被`merge`进我们的仓库中,这样你就会自动出现在贡献者名单里了,非常方便。
|
||||
参与的方式很简单,`fork`一份 ThinkPHP 的代码到你的仓库中,修改后提交,并向我们发起`pull request`申请,我们会及时对代码进行审查并处理你的申请并。审查通过后,你的代码将被`merge`进我们的仓库中,这样你就会自动出现在贡献者名单里了,非常方便。
|
||||
|
||||
我们希望你贡献的代码符合:
|
||||
|
||||
@@ -60,7 +60,7 @@ GitHub 提供了 Issue 功能,该功能可以用于:
|
||||
6. 变基(衍合 `rebase`)你的分支到上游 master 分支;
|
||||
7. `push` 你的本地仓库到 GitHub;
|
||||
8. 提交 `pull request`;
|
||||
9. 等待 CI 验证(若不通过则重复 5~7,不需要重新提交 `pull request`,GitHub 会自动更新你的 `pull request`);
|
||||
9. 等待 CI 验证(若不通过则重复 5~7,GitHub 会自动更新你的 `pull request`);
|
||||
10. 等待管理员处理,并及时 `rebase` 你的分支到上游 master 分支(若上游 master 分支有修改)。
|
||||
|
||||
*若有必要,可以 `git push -f` 强行推送 rebase 后的分支到自己的 `fork`*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
|
||||
版权所有Copyright © 2006-2017 by ThinkPHP (http://thinkphp.cn)
|
||||
版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn)
|
||||
All rights reserved。
|
||||
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
|
||||
|
||||
|
||||
@@ -1,103 +1,88 @@
|
||||
ThinkPHP 5.0
|
||||

|
||||
|
||||
ThinkPHP 5.1(LTS) —— 12载初心,你值得信赖的PHP框架
|
||||
===============
|
||||
|
||||
[](https://styleci.io/repos/48530411)
|
||||
[](https://scrutinizer-ci.com/g/top-think/framework/?branch=5.1)
|
||||
[](https://travis-ci.org/top-think/framework)
|
||||
[](http://codecov.io/github/github/top-think/framework?branch=master)
|
||||
[](https://packagist.org/packages/topthink/framework)
|
||||
[](https://packagist.org/packages/topthink/framework)
|
||||
[](https://packagist.org/packages/topthink/framework)
|
||||
[](http://www.php.net/)
|
||||
[](https://packagist.org/packages/topthink/framework)
|
||||
|
||||
ThinkPHP5在保持快速开发和大道至简的核心理念不变的同时,PHP版本要求提升到5.4,优化核心,减少依赖,基于全新的架构思想和命名空间实现,是ThinkPHP突破原有框架思路的颠覆之作,其主要特性包括:
|
||||
ThinkPHP5.1对底层架构做了进一步的改进,减少依赖,其主要特性包括:
|
||||
|
||||
+ 基于命名空间和众多PHP新特性
|
||||
+ 核心功能组件化
|
||||
+ 强化路由功能
|
||||
+ 更灵活的控制器
|
||||
+ 重构的模型和数据库类
|
||||
+ 配置文件可分离
|
||||
+ 重写的自动验证和完成
|
||||
+ 简化扩展机制
|
||||
+ API支持完善
|
||||
+ 改进的Log类
|
||||
+ 命令行访问支持
|
||||
+ REST支持
|
||||
+ 引导文件支持
|
||||
+ 方便的自动生成定义
|
||||
+ 真正惰性加载
|
||||
+ 分布式环境支持
|
||||
+ 支持Composer
|
||||
+ 支持MongoDb
|
||||
+ 采用容器统一管理对象
|
||||
+ 支持Facade
|
||||
+ 更易用的路由
|
||||
+ 注解路由支持
|
||||
+ 路由跨域请求支持
|
||||
+ 验证类增强
|
||||
+ 配置和路由目录独立
|
||||
+ 取消系统常量
|
||||
+ 类库别名机制
|
||||
+ 模型和数据库增强
|
||||
+ 依赖注入完善
|
||||
+ 支持PSR-3日志规范
|
||||
+ 中间件支持(`V5.1.6+`)
|
||||
+ 支持`Swoole`/`Workerman`运行(`V5.1.18+`)
|
||||
|
||||
> ThinkPHP5的运行环境要求PHP5.4以上。
|
||||
官方已经正式宣布`5.1.27`版本为LTS版本。
|
||||
|
||||
详细开发文档参考 [ThinkPHP5完全开发手册](http://www.kancloud.cn/manual/thinkphp5) 以及[ThinkPHP5入门系列教程](http://www.kancloud.cn/special/thinkphp5_quickstart)
|
||||
### 废除的功能:
|
||||
|
||||
## 目录结构
|
||||
+ 聚合模型
|
||||
+ 内置控制器扩展类
|
||||
+ 模型自动验证
|
||||
|
||||
初始的目录结构如下:
|
||||
> ThinkPHP5.1的运行环境要求PHP5.6+ 兼容PHP8.0。
|
||||
|
||||
|
||||
## 安装
|
||||
|
||||
使用composer安装
|
||||
|
||||
~~~
|
||||
www WEB部署目录(或者子目录)
|
||||
├─application 应用目录
|
||||
│ ├─common 公共模块目录(可以更改)
|
||||
│ ├─module_name 模块目录
|
||||
│ │ ├─config.php 模块配置文件
|
||||
│ │ ├─common.php 模块函数文件
|
||||
│ │ ├─controller 控制器目录
|
||||
│ │ ├─model 模型目录
|
||||
│ │ ├─view 视图目录
|
||||
│ │ └─ ... 更多类库目录
|
||||
│ │
|
||||
│ ├─command.php 命令行工具配置文件
|
||||
│ ├─common.php 公共函数文件
|
||||
│ ├─config.php 公共配置文件
|
||||
│ ├─route.php 路由配置文件
|
||||
│ ├─tags.php 应用行为扩展定义文件
|
||||
│ └─database.php 数据库配置文件
|
||||
│
|
||||
├─public WEB目录(对外访问目录)
|
||||
│ ├─index.php 入口文件
|
||||
│ ├─router.php 快速测试文件
|
||||
│ └─.htaccess 用于apache的重写
|
||||
│
|
||||
├─thinkphp 框架系统目录
|
||||
│ ├─lang 语言文件目录
|
||||
│ ├─library 框架类库目录
|
||||
│ │ ├─think Think类库包目录
|
||||
│ │ └─traits 系统Trait目录
|
||||
│ │
|
||||
│ ├─tpl 系统模板目录
|
||||
│ ├─base.php 基础定义文件
|
||||
│ ├─console.php 控制台入口文件
|
||||
│ ├─convention.php 框架惯例配置文件
|
||||
│ ├─helper.php 助手函数文件
|
||||
│ ├─phpunit.xml phpunit配置文件
|
||||
│ └─start.php 框架入口文件
|
||||
│
|
||||
├─extend 扩展类库目录
|
||||
├─runtime 应用的运行时目录(可写,可定制)
|
||||
├─vendor 第三方类库目录(Composer依赖库)
|
||||
├─build.php 自动生成定义文件(参考)
|
||||
├─composer.json composer 定义文件
|
||||
├─LICENSE.txt 授权说明文件
|
||||
├─README.md README 文件
|
||||
├─think 命令行入口文件
|
||||
composer create-project topthink/think tp
|
||||
~~~
|
||||
|
||||
> router.php用于php自带webserver支持,可用于快速测试
|
||||
> 切换到public目录后,启动命令:php -S localhost:8888 router.php
|
||||
> 上面的目录结构和名称是可以改变的,这取决于你的入口文件和配置参数。
|
||||
启动服务
|
||||
|
||||
~~~
|
||||
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)
|
||||
|
||||
|
||||
## 官方服务
|
||||
|
||||
+ [应用服务市场](https://market.topthink.com/)
|
||||
+ [ThinkAPI——统一API服务](https://docs.topthink.com/think-api)
|
||||
|
||||
## 命名规范
|
||||
|
||||
ThinkPHP5的命名规范遵循`PSR-2`规范以及`PSR-4`自动加载规范。
|
||||
`ThinkPHP5.1`遵循PSR-2命名规范和PSR-4自动加载规范。
|
||||
|
||||
## 参与开发
|
||||
注册并登录 Github 帐号, fork 本项目并进行改动。
|
||||
|
||||
更多细节参阅 [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
请参阅 [ThinkPHP5 核心框架包](https://github.com/top-think/framework)。
|
||||
|
||||
## 版权信息
|
||||
|
||||
|
||||
@@ -8,58 +8,45 @@
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
define('THINK_VERSION', '5.0.24');
|
||||
define('THINK_START_TIME', microtime(true));
|
||||
define('THINK_START_MEM', memory_get_usage());
|
||||
define('EXT', '.php');
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
defined('THINK_PATH') or define('THINK_PATH', __DIR__ . DS);
|
||||
define('LIB_PATH', THINK_PATH . 'library' . DS);
|
||||
define('CORE_PATH', LIB_PATH . 'think' . DS);
|
||||
define('TRAIT_PATH', LIB_PATH . 'traits' . DS);
|
||||
defined('APP_PATH') or define('APP_PATH', dirname($_SERVER['SCRIPT_FILENAME']) . DS);
|
||||
defined('ROOT_PATH') or define('ROOT_PATH', dirname(realpath(APP_PATH)) . DS);
|
||||
defined('EXTEND_PATH') or define('EXTEND_PATH', ROOT_PATH . 'extend' . DS);
|
||||
defined('VENDOR_PATH') or define('VENDOR_PATH', ROOT_PATH . 'vendor' . DS);
|
||||
defined('RUNTIME_PATH') or define('RUNTIME_PATH', ROOT_PATH . 'runtime' . DS);
|
||||
defined('LOG_PATH') or define('LOG_PATH', RUNTIME_PATH . 'log' . DS);
|
||||
defined('CACHE_PATH') or define('CACHE_PATH', RUNTIME_PATH . 'cache' . DS);
|
||||
defined('TEMP_PATH') or define('TEMP_PATH', RUNTIME_PATH . 'temp' . DS);
|
||||
defined('CONF_PATH') or define('CONF_PATH', APP_PATH); // 配置文件目录
|
||||
defined('CONF_EXT') or define('CONF_EXT', EXT); // 配置文件后缀
|
||||
defined('ENV_PREFIX') or define('ENV_PREFIX', 'PHP_'); // 环境变量的配置前缀
|
||||
|
||||
// 环境常量
|
||||
define('IS_CLI', PHP_SAPI == 'cli' ? true : false);
|
||||
define('IS_WIN', strpos(PHP_OS, 'WIN') !== false);
|
||||
namespace think;
|
||||
|
||||
// 载入Loader类
|
||||
require CORE_PATH . 'Loader.php';
|
||||
|
||||
// 加载环境变量配置文件
|
||||
if (is_file(ROOT_PATH . '.env')) {
|
||||
$env = parse_ini_file(ROOT_PATH . '.env', true);
|
||||
|
||||
foreach ($env as $key => $val) {
|
||||
$name = ENV_PREFIX . strtoupper($key);
|
||||
|
||||
if (is_array($val)) {
|
||||
foreach ($val as $k => $v) {
|
||||
$item = $name . '_' . strtoupper($k);
|
||||
putenv("$item=$v");
|
||||
}
|
||||
} else {
|
||||
putenv("$name=$val");
|
||||
}
|
||||
}
|
||||
}
|
||||
require __DIR__ . '/library/think/Loader.php';
|
||||
|
||||
// 注册自动加载
|
||||
\think\Loader::register();
|
||||
Loader::register();
|
||||
|
||||
// 注册错误和异常处理机制
|
||||
\think\Error::register();
|
||||
Error::register();
|
||||
|
||||
// 加载惯例配置文件
|
||||
\think\Config::set(include THINK_PATH . 'convention' . EXT);
|
||||
// 实现日志接口
|
||||
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,
|
||||
]);
|
||||
|
||||
@@ -13,23 +13,23 @@
|
||||
{
|
||||
"name": "liu21st",
|
||||
"email": "liu21st@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "yunwuxin",
|
||||
"email": "448901948@qq.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"topthink/think-installer": "~1.0"
|
||||
"php": ">=5.6.0",
|
||||
"topthink/think-installer": "2.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.8.*",
|
||||
"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"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"think\\": "library/think"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,118 +4,155 @@ return [
|
||||
// +----------------------------------------------------------------------
|
||||
// | 应用设置
|
||||
// +----------------------------------------------------------------------
|
||||
// 默认Host地址
|
||||
'app_host' => '',
|
||||
// 应用调试模式
|
||||
'app_debug' => false,
|
||||
// 应用Trace
|
||||
'app_trace' => false,
|
||||
// 应用模式状态
|
||||
'app_status' => '',
|
||||
// 是否支持多模块
|
||||
'app_multi_module' => true,
|
||||
// 入口自动绑定模块
|
||||
'auto_bind_module' => false,
|
||||
// 注册的根命名空间
|
||||
'root_namespace' => [],
|
||||
// 扩展函数文件
|
||||
'extra_file_list' => [THINK_PATH . 'helper' . EXT],
|
||||
// 默认输出类型
|
||||
'default_return_type' => 'html',
|
||||
// 默认AJAX 数据返回格式,可选json xml ...
|
||||
'default_ajax_return' => 'json',
|
||||
// 默认JSONP格式返回的处理方法
|
||||
'default_jsonp_handler' => 'jsonpReturn',
|
||||
// 默认JSONP处理方法
|
||||
'var_jsonp_handler' => 'callback',
|
||||
// 默认时区
|
||||
'default_timezone' => 'PRC',
|
||||
// 是否开启多语言
|
||||
'lang_switch_on' => false,
|
||||
// 默认全局过滤方法 用逗号分隔多个
|
||||
'default_filter' => '',
|
||||
// 默认语言
|
||||
'default_lang' => 'zh-cn',
|
||||
// 应用类库后缀
|
||||
'class_suffix' => false,
|
||||
// 控制器类后缀
|
||||
'controller_suffix' => false,
|
||||
'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',
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 模块设置
|
||||
// +----------------------------------------------------------------------
|
||||
// +----------------------------------------------------------------------
|
||||
// | 模块设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
// 默认模块名
|
||||
'default_module' => 'index',
|
||||
// 禁止访问模块
|
||||
'deny_module_list' => ['common'],
|
||||
// 默认控制器名
|
||||
'default_controller' => 'Index',
|
||||
// 默认操作名
|
||||
'default_action' => 'index',
|
||||
// 默认验证器
|
||||
'default_validate' => '',
|
||||
// 默认的空控制器名
|
||||
'empty_controller' => 'Error',
|
||||
// 操作方法前缀
|
||||
'use_action_prefix' => false,
|
||||
// 操作方法后缀
|
||||
'action_suffix' => '',
|
||||
// 自动搜索控制器
|
||||
'controller_auto_search' => false,
|
||||
// 自动搜索控制器
|
||||
'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设置
|
||||
// +----------------------------------------------------------------------
|
||||
// +----------------------------------------------------------------------
|
||||
// | URL请求设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
// PATHINFO变量名 用于兼容模式
|
||||
'var_pathinfo' => 's',
|
||||
// 兼容PATH_INFO获取
|
||||
'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
|
||||
// pathinfo分隔符
|
||||
'pathinfo_depr' => '/',
|
||||
// HTTPS代理标识
|
||||
'https_agent_name' => '',
|
||||
// URL伪静态后缀
|
||||
'url_html_suffix' => 'html',
|
||||
// URL普通方式参数 用于自动生成
|
||||
'url_common_param' => false,
|
||||
// URL参数方式 0 按名称成对解析 1 按顺序解析
|
||||
'url_param_type' => 0,
|
||||
// 是否开启路由
|
||||
'url_route_on' => true,
|
||||
// 路由配置文件(支持配置多个)
|
||||
'route_config_file' => ['route'],
|
||||
// 路由使用完整匹配
|
||||
'route_complete_match' => false,
|
||||
// 是否强制使用路由
|
||||
'url_route_must' => false,
|
||||
// 域名部署
|
||||
'url_domain_deploy' => false,
|
||||
// 域名根,如thinkphp.cn
|
||||
'url_domain_root' => '',
|
||||
// 是否自动转换URL中的控制器和操作名
|
||||
'url_convert' => true,
|
||||
// 默认的访问控制器层
|
||||
'url_controller_layer' => 'controller',
|
||||
// 表单请求类型伪装变量
|
||||
'var_method' => '_method',
|
||||
// 表单ajax伪装变量
|
||||
'var_ajax' => '_ajax',
|
||||
// 表单pjax伪装变量
|
||||
'var_pjax' => '_pjax',
|
||||
// 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
|
||||
'request_cache' => false,
|
||||
// 请求缓存有效期
|
||||
'request_cache_expire' => null,
|
||||
// 全局请求缓存排除规则
|
||||
'request_cache_except' => [],
|
||||
// 默认全局过滤方法 用逗号分隔多个
|
||||
'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' => [
|
||||
'template' => [
|
||||
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写
|
||||
'auto_rule' => 1,
|
||||
// 模板引擎类型 支持 php think 支持扩展
|
||||
@@ -127,7 +164,7 @@ return [
|
||||
// 模板后缀
|
||||
'view_suffix' => 'html',
|
||||
// 模板文件名分隔符
|
||||
'view_depr' => DS,
|
||||
'view_depr' => DIRECTORY_SEPARATOR,
|
||||
// 模板引擎普通标签开始标记
|
||||
'tpl_begin' => '{',
|
||||
// 模板引擎普通标签结束标记
|
||||
@@ -138,58 +175,42 @@ return [
|
||||
'taglib_end' => '}',
|
||||
],
|
||||
|
||||
// 视图输出字符串内容替换
|
||||
'view_replace_str' => [],
|
||||
// 默认跳转页面对应的模板文件
|
||||
'dispatch_success_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl',
|
||||
'dispatch_error_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl',
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 异常及错误设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
// 异常页面的模板文件
|
||||
'exception_tmpl' => THINK_PATH . 'tpl' . DS . 'think_exception.tpl',
|
||||
|
||||
// 错误显示信息,非调试模式有效
|
||||
'error_message' => '页面错误!请稍后再试~',
|
||||
// 显示错误信息
|
||||
'show_error_msg' => false,
|
||||
// 异常处理handle类 留空使用 \think\exception\Handle
|
||||
'exception_handle' => '',
|
||||
// 是否记录trace信息到日志
|
||||
'record_trace' => false,
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 日志设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
'log' => [
|
||||
'log' => [
|
||||
// 日志记录方式,内置 file socket 支持扩展
|
||||
'type' => 'File',
|
||||
'type' => 'File',
|
||||
// 日志保存目录
|
||||
'path' => LOG_PATH,
|
||||
//'path' => LOG_PATH,
|
||||
// 日志记录级别
|
||||
'level' => [],
|
||||
'level' => [],
|
||||
// 是否记录trace信息到日志
|
||||
'record_trace' => false,
|
||||
// 是否JSON格式记录
|
||||
'json' => false,
|
||||
],
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Trace设置 开启 app_trace 后 有效
|
||||
// +----------------------------------------------------------------------
|
||||
'trace' => [
|
||||
|
||||
'trace' => [
|
||||
// 内置Html Console 支持扩展
|
||||
'type' => 'Html',
|
||||
'file' => __DIR__ . '/tpl/page_trace.tpl',
|
||||
],
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 缓存设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
'cache' => [
|
||||
'cache' => [
|
||||
// 驱动方式
|
||||
'type' => 'File',
|
||||
// 缓存保存目录
|
||||
'path' => CACHE_PATH,
|
||||
//'path' => CACHE_PATH,
|
||||
// 缓存前缀
|
||||
'prefix' => '',
|
||||
// 缓存有效期 0表示永久缓存
|
||||
@@ -200,7 +221,7 @@ return [
|
||||
// | 会话设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
'session' => [
|
||||
'session' => [
|
||||
'id' => '',
|
||||
// SESSION_ID的提交变量,解决flash上传跨域
|
||||
'var_session_id' => '',
|
||||
@@ -217,7 +238,8 @@ return [
|
||||
// +----------------------------------------------------------------------
|
||||
// | Cookie设置
|
||||
// +----------------------------------------------------------------------
|
||||
'cookie' => [
|
||||
|
||||
'cookie' => [
|
||||
// cookie 名称前缀
|
||||
'prefix' => '',
|
||||
// cookie 保存时间
|
||||
@@ -238,7 +260,7 @@ return [
|
||||
// | 数据库设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
'database' => [
|
||||
'database' => [
|
||||
// 数据库类型
|
||||
'type' => 'mysql',
|
||||
// 数据库连接DSN配置
|
||||
@@ -279,20 +301,27 @@ return [
|
||||
'datetime_format' => 'Y-m-d H:i:s',
|
||||
// 是否需要进行SQL性能分析
|
||||
'sql_explain' => false,
|
||||
// 查询对象
|
||||
'query' => '\\think\\db\\Query',
|
||||
],
|
||||
|
||||
//分页配置
|
||||
'paginate' => [
|
||||
'paginate' => [
|
||||
'type' => 'bootstrap',
|
||||
'var_page' => 'page',
|
||||
'list_rows' => 15,
|
||||
],
|
||||
|
||||
//控制台配置
|
||||
'console' => [
|
||||
'name' => 'Think Console',
|
||||
'version' => '0.1',
|
||||
'user' => null,
|
||||
'console' => [
|
||||
'name' => 'Think Console',
|
||||
'version' => '0.1',
|
||||
'user' => null,
|
||||
'auto_path' => '',
|
||||
],
|
||||
|
||||
// 中间件配置
|
||||
'middleware' => [
|
||||
'default_namespace' => 'app\\http\\middleware\\',
|
||||
],
|
||||
];
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,8 @@ return [
|
||||
'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' => '类不存在',
|
||||
@@ -32,7 +34,7 @@ return [
|
||||
'illegal controller name' => '非法的控制器名称',
|
||||
'illegal action name' => '非法的操作名称',
|
||||
'url suffix deny' => '禁止的URL后缀访问',
|
||||
'Route Not Found' => '当前访问路由未定义',
|
||||
'Route Not Found' => '当前访问路由未定义或不匹配',
|
||||
'Undefined db type' => '未定义数据库类型',
|
||||
'variable type error' => '变量类型错误',
|
||||
'PSR-4 error' => 'PSR-4 规范错误',
|
||||
@@ -48,9 +50,10 @@ return [
|
||||
'KVDB init error' => '没有初始化KVDB,请在SAE管理平台初始化KVDB服务',
|
||||
'fields not exists' => '数据表字段不存在',
|
||||
'where express error' => '查询表达式错误',
|
||||
'not support data' => '不支持的数据表达式',
|
||||
'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' => '模型数据不存在',
|
||||
@@ -67,6 +70,8 @@ return [
|
||||
'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',
|
||||
|
||||
// 上传错误信息
|
||||
@@ -85,8 +90,11 @@ return [
|
||||
'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必须是浮点数',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,44 +14,57 @@ namespace think;
|
||||
class Build
|
||||
{
|
||||
/**
|
||||
* 根据传入的 build 资料创建目录和文件
|
||||
* @access public
|
||||
* @param array $build build 列表
|
||||
* @param string $namespace 应用类库命名空间
|
||||
* @param bool $suffix 类库后缀
|
||||
* @return void
|
||||
* @throws Exception
|
||||
* 应用对象
|
||||
* @var App
|
||||
*/
|
||||
public static function run(array $build = [], $namespace = 'app', $suffix = false)
|
||||
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)
|
||||
{
|
||||
// 锁定
|
||||
$lock = APP_PATH . 'build.lock';
|
||||
$lockfile = $this->basePath . 'build.lock';
|
||||
|
||||
// 如果锁定文件不可写(不存在)则进行处理,否则表示已经有程序在处理了
|
||||
if (!is_writable($lock)) {
|
||||
if (!touch($lock)) {
|
||||
throw new Exception(
|
||||
'应用目录[' . APP_PATH . ']不可写,目录无法自动生成!<BR>请手动生成项目目录~',
|
||||
10006
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($build as $module => $list) {
|
||||
if ('__dir__' == $module) {
|
||||
// 创建目录列表
|
||||
self::buildDir($list);
|
||||
} elseif ('__file__' == $module) {
|
||||
// 创建文件列表
|
||||
self::buildFile($list);
|
||||
} else {
|
||||
// 创建模块
|
||||
self::module($module, $list, $namespace, $suffix);
|
||||
}
|
||||
}
|
||||
|
||||
// 解除锁定
|
||||
unlink($lock);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,11 +73,10 @@ class Build
|
||||
* @param array $list 目录列表
|
||||
* @return void
|
||||
*/
|
||||
protected static function buildDir($list)
|
||||
protected function buildDir($list)
|
||||
{
|
||||
foreach ($list as $dir) {
|
||||
// 目录不存在则创建目录
|
||||
!is_dir(APP_PATH . $dir) && mkdir(APP_PATH . $dir, 0755, true);
|
||||
$this->checkDirBuild($this->basePath . $dir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,20 +86,16 @@ class Build
|
||||
* @param array $list 文件列表
|
||||
* @return void
|
||||
*/
|
||||
protected static function buildFile($list)
|
||||
protected function buildFile($list)
|
||||
{
|
||||
foreach ($list as $file) {
|
||||
// 先创建目录
|
||||
if (!is_dir(APP_PATH . dirname($file))) {
|
||||
mkdir(APP_PATH . dirname($file), 0755, true);
|
||||
if (!is_dir($this->basePath . dirname($file))) {
|
||||
// 创建目录
|
||||
mkdir($this->basePath . dirname($file), 0755, true);
|
||||
}
|
||||
|
||||
// 再创建文件
|
||||
if (!is_file(APP_PATH . $file)) {
|
||||
file_put_contents(
|
||||
APP_PATH . $file,
|
||||
'php' == pathinfo($file, PATHINFO_EXTENSION) ? "<?php\n" : ''
|
||||
);
|
||||
if (!is_file($this->basePath . $file)) {
|
||||
file_put_contents($this->basePath . $file, 'php' == pathinfo($file, PATHINFO_EXTENSION) ? "<?php\n" : '');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,60 +103,58 @@ class Build
|
||||
/**
|
||||
* 创建模块
|
||||
* @access public
|
||||
* @param string $module 模块名
|
||||
* @param array $list build 列表
|
||||
* @param string $module 模块名
|
||||
* @param array $list build列表
|
||||
* @param string $namespace 应用类库命名空间
|
||||
* @param bool $suffix 类库后缀
|
||||
* @param bool $suffix 类库后缀
|
||||
* @return void
|
||||
*/
|
||||
public static function module($module = '', $list = [], $namespace = 'app', $suffix = false)
|
||||
public function module($module = '', $list = [], $namespace = 'app', $suffix = false)
|
||||
{
|
||||
$module = $module ?: '';
|
||||
$module = $module ? $module : '';
|
||||
|
||||
// 创建模块目录
|
||||
!is_dir(APP_PATH . $module) && mkdir(APP_PATH . $module);
|
||||
|
||||
// 如果不是 runtime 目录则需要创建配置文件和公共文件、创建模块的默认页面
|
||||
if (basename(RUNTIME_PATH) != $module) {
|
||||
self::buildCommon($module);
|
||||
self::buildHello($module, $namespace, $suffix);
|
||||
if (!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__' => ['config.php', 'common.php'],
|
||||
'__dir__' => ['controller', 'model', 'view'],
|
||||
'__file__' => ['common.php'],
|
||||
'__dir__' => ['controller', 'model', 'view', 'config'],
|
||||
];
|
||||
}
|
||||
|
||||
// 创建子目录和文件
|
||||
foreach ($list as $path => $file) {
|
||||
$modulePath = APP_PATH . $module . DS;
|
||||
|
||||
$modulePath = $this->basePath . $module . DIRECTORY_SEPARATOR;
|
||||
if ('__dir__' == $path) {
|
||||
// 生成子目录
|
||||
foreach ($file as $dir) {
|
||||
self::checkDirBuild($modulePath . $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" : ''
|
||||
);
|
||||
file_put_contents($modulePath . $name, 'php' == pathinfo($name, PATHINFO_EXTENSION) ? "<?php\n" : '');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 生成相关 MVC 文件
|
||||
// 生成相关MVC文件
|
||||
foreach ($file as $val) {
|
||||
$val = trim($val);
|
||||
$filename = $modulePath . $path . DS . $val . ($suffix ? ucfirst($path) : '') . EXT;
|
||||
$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}";
|
||||
@@ -157,8 +163,8 @@ class Build
|
||||
$content = "<?php\nnamespace {$space};\n\nuse think\Model;\n\nclass {$class} extends Model\n{\n\n}";
|
||||
break;
|
||||
case 'view': // 视图
|
||||
$filename = $modulePath . $path . DS . $val . '.html';
|
||||
self::checkDirBuild(dirname($filename));
|
||||
$filename = $modulePath . $path . DIRECTORY_SEPARATOR . $val . '.html';
|
||||
$this->checkDirBuild(dirname($filename));
|
||||
$content = '';
|
||||
break;
|
||||
default:
|
||||
@@ -174,30 +180,200 @@ class Build
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据注释自动生成路由规则
|
||||
* @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 $module 模块名
|
||||
* @param string $namespace 应用类库命名空间
|
||||
* @param bool $suffix 类库后缀
|
||||
* @param bool $suffix 类库后缀
|
||||
* @return void
|
||||
*/
|
||||
protected static function buildHello($module, $namespace, $suffix = false)
|
||||
protected function buildHello($module, $namespace, $suffix = false)
|
||||
{
|
||||
$filename = APP_PATH . ($module ? $module . DS : '') .
|
||||
'controller' . DS . 'Index' .
|
||||
($suffix ? 'Controller' : '') . EXT;
|
||||
|
||||
$filename = $this->basePath . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'controller' . DIRECTORY_SEPARATOR . 'Index' . ($suffix ? 'Controller' : '') . '.php';
|
||||
if (!is_file($filename)) {
|
||||
$module = $module ? $module . '\\' : '';
|
||||
$suffix = $suffix ? 'Controller' : '';
|
||||
$content = str_replace(
|
||||
['{$app}', '{$module}', '{layer}', '{$suffix}'],
|
||||
[$namespace, $module, 'controller', $suffix],
|
||||
file_get_contents(THINK_PATH . 'tpl' . DS . 'default_index.tpl')
|
||||
);
|
||||
$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));
|
||||
|
||||
self::checkDirBuild(dirname($filename));
|
||||
file_put_contents($filename, $content);
|
||||
}
|
||||
}
|
||||
@@ -208,18 +384,20 @@ class Build
|
||||
* @param string $module 模块名
|
||||
* @return void
|
||||
*/
|
||||
protected static function buildCommon($module)
|
||||
protected function buildCommon($module)
|
||||
{
|
||||
$config = CONF_PATH . ($module ? $module . DS : '') . 'config.php';
|
||||
$filename = $this->app->getConfigPath() . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'app.php';
|
||||
$this->checkDirBuild(dirname($filename));
|
||||
|
||||
self::checkDirBuild(dirname($config));
|
||||
|
||||
if (!is_file($config)) {
|
||||
file_put_contents($config, "<?php\n//配置文件\nreturn [\n\n];");
|
||||
if (!is_file($filename)) {
|
||||
file_put_contents($filename, "<?php\n//配置文件\nreturn [\n\n];");
|
||||
}
|
||||
|
||||
$common = APP_PATH . ($module ? $module . DS : '') . 'common.php';
|
||||
if (!is_file($common)) file_put_contents($common, "<?php\n");
|
||||
$filename = $this->basePath . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'common.php';
|
||||
|
||||
if (!is_file($filename)) {
|
||||
file_put_contents($filename, "<?php\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,8 +406,10 @@ class Build
|
||||
* @param string $dirname 目录名称
|
||||
* @return void
|
||||
*/
|
||||
protected static function checkDirBuild($dirname)
|
||||
protected function checkDirBuild($dirname)
|
||||
{
|
||||
!is_dir($dirname) && mkdir($dirname, 0755, true);
|
||||
if (!is_dir($dirname)) {
|
||||
mkdir($dirname, 0755, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,82 +13,101 @@ namespace think;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
/**
|
||||
* Class Cache
|
||||
*
|
||||
* @package think
|
||||
*
|
||||
* @mixin Driver
|
||||
* @mixin \think\cache\driver\File
|
||||
*/
|
||||
class Cache
|
||||
{
|
||||
/**
|
||||
* @var array 缓存的实例
|
||||
* 缓存实例
|
||||
* @var array
|
||||
*/
|
||||
public static $instance = [];
|
||||
protected $instance = [];
|
||||
|
||||
/**
|
||||
* @var int 缓存读取次数
|
||||
* 缓存配置
|
||||
* @var array
|
||||
*/
|
||||
public static $readTimes = 0;
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* @var int 缓存写入次数
|
||||
* 操作句柄
|
||||
* @var object
|
||||
*/
|
||||
public static $writeTimes = 0;
|
||||
protected $handler;
|
||||
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->init($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var object 操作句柄
|
||||
*/
|
||||
public static $handler;
|
||||
|
||||
/**
|
||||
* 连接缓存驱动
|
||||
* 连接缓存
|
||||
* @access public
|
||||
* @param array $options 配置数组
|
||||
* @param bool|string $name 缓存连接标识 true 强制重新连接
|
||||
* @param array $options 配置数组
|
||||
* @param bool|string $name 缓存连接标识 true 强制重新连接
|
||||
* @return Driver
|
||||
*/
|
||||
public static function connect(array $options = [], $name = false)
|
||||
public function connect(array $options = [], $name = false)
|
||||
{
|
||||
$type = !empty($options['type']) ? $options['type'] : 'File';
|
||||
|
||||
if (false === $name) {
|
||||
$name = md5(serialize($options));
|
||||
}
|
||||
|
||||
if (true === $name || !isset(self::$instance[$name])) {
|
||||
$class = false === strpos($type, '\\') ?
|
||||
'\\think\\cache\\driver\\' . ucwords($type) :
|
||||
$type;
|
||||
|
||||
// 记录初始化信息
|
||||
App::$debug && Log::record('[ CACHE ] INIT ' . $type, 'info');
|
||||
if (true === $name || !isset($this->instance[$name])) {
|
||||
$type = !empty($options['type']) ? $options['type'] : 'File';
|
||||
|
||||
if (true === $name) {
|
||||
return new $class($options);
|
||||
$name = md5(serialize($options));
|
||||
}
|
||||
|
||||
self::$instance[$name] = new $class($options);
|
||||
$this->instance[$name] = Loader::factory($type, '\\think\\cache\\driver\\', $options);
|
||||
}
|
||||
|
||||
return self::$instance[$name];
|
||||
return $this->instance[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动初始化缓存
|
||||
* @access public
|
||||
* @param array $options 配置数组
|
||||
* @param array $options 配置数组
|
||||
* @param bool $force 强制更新
|
||||
* @return Driver
|
||||
*/
|
||||
public static function init(array $options = [])
|
||||
public function init(array $options = [], $force = false)
|
||||
{
|
||||
if (is_null(self::$handler)) {
|
||||
if (empty($options) && 'complex' == Config::get('cache.type')) {
|
||||
$default = Config::get('cache.default');
|
||||
// 获取默认缓存配置,并连接
|
||||
$options = Config::get('cache.' . $default['type']) ?: $default;
|
||||
} elseif (empty($options)) {
|
||||
$options = Config::get('cache');
|
||||
if (is_null($this->handler) || $force) {
|
||||
|
||||
if ('complex' == $options['type']) {
|
||||
$default = $options['default'];
|
||||
$options = isset($options[$default['type']]) ? $options[$default['type']] : $default;
|
||||
}
|
||||
|
||||
self::$handler = self::connect($options);
|
||||
$this->handler = $this->connect($options);
|
||||
}
|
||||
|
||||
return self::$handler;
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,151 +116,18 @@ class Cache
|
||||
* @param string $name 缓存标识
|
||||
* @return Driver
|
||||
*/
|
||||
public static function store($name = '')
|
||||
public function store($name = '')
|
||||
{
|
||||
if ('' !== $name && 'complex' == Config::get('cache.type')) {
|
||||
return self::connect(Config::get('cache.' . $name), strtolower($name));
|
||||
if ('' !== $name && 'complex' == $this->config['type']) {
|
||||
return $this->connect($this->config[$name], strtolower($name));
|
||||
}
|
||||
|
||||
return self::init();
|
||||
return $this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存是否存在
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public static function has($name)
|
||||
public function __call($method, $args)
|
||||
{
|
||||
self::$readTimes++;
|
||||
|
||||
return self::init()->has($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存标识
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($name, $default = false)
|
||||
{
|
||||
self::$readTimes++;
|
||||
|
||||
return self::init()->get($name, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存标识
|
||||
* @param mixed $value 存储数据
|
||||
* @param int|null $expire 有效时间 0为永久
|
||||
* @return boolean
|
||||
*/
|
||||
public static function set($name, $value, $expire = null)
|
||||
{
|
||||
self::$writeTimes++;
|
||||
|
||||
return self::init()->set($name, $value, $expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public static function inc($name, $step = 1)
|
||||
{
|
||||
self::$writeTimes++;
|
||||
|
||||
return self::init()->inc($name, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public static function dec($name, $step = 1)
|
||||
{
|
||||
self::$writeTimes++;
|
||||
|
||||
return self::init()->dec($name, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存标识
|
||||
* @return boolean
|
||||
*/
|
||||
public static function rm($name)
|
||||
{
|
||||
self::$writeTimes++;
|
||||
|
||||
return self::init()->rm($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public static function clear($tag = null)
|
||||
{
|
||||
self::$writeTimes++;
|
||||
|
||||
return self::init()->clear($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存并删除
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return mixed
|
||||
*/
|
||||
public static function pull($name)
|
||||
{
|
||||
self::$readTimes++;
|
||||
self::$writeTimes++;
|
||||
|
||||
return self::init()->pull($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果不存在则写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int $expire 有效时间 0为永久
|
||||
* @return mixed
|
||||
*/
|
||||
public static function remember($name, $value, $expire = null)
|
||||
{
|
||||
self::$readTimes++;
|
||||
|
||||
return self::init()->remember($name, $value, $expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存标签
|
||||
* @access public
|
||||
* @param string $name 标签名
|
||||
* @param string|array $keys 缓存标识
|
||||
* @param bool $overlay 是否覆盖
|
||||
* @return Driver
|
||||
*/
|
||||
public static function tag($name, $keys = null, $overlay = false)
|
||||
{
|
||||
return self::init()->tag($name, $keys, $overlay);
|
||||
return call_user_func_array([$this->init(), $method], $args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,33 +20,23 @@ use JsonSerializable;
|
||||
class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var array 数据
|
||||
* 数据集数据
|
||||
* @var array
|
||||
*/
|
||||
protected $items = [];
|
||||
|
||||
/**
|
||||
* Collection constructor.
|
||||
* @access public
|
||||
* @param array $items 数据
|
||||
*/
|
||||
public function __construct($items = [])
|
||||
{
|
||||
$this->items = $this->convertToArray($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 Collection 实例
|
||||
* @access public
|
||||
* @param array $items 数据
|
||||
* @return static
|
||||
*/
|
||||
public static function make($items = [])
|
||||
{
|
||||
return new static($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断数据是否为空
|
||||
* 是否为空
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
@@ -55,32 +45,33 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
||||
return empty($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数据转成数组
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return array_map(function ($value) {
|
||||
return ($value instanceof Model || $value instanceof self) ?
|
||||
$value->toArray() :
|
||||
$value;
|
||||
return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value;
|
||||
}, $this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部的数据
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并数组
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@@ -90,60 +81,112 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数组中所有的键名组成的新 Collection 实例
|
||||
* 按指定键整理数据
|
||||
*
|
||||
* @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()
|
||||
{
|
||||
return new static(array_keys($this->items));
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数组中所有的值组成的新 Collection 实例
|
||||
* @access public
|
||||
* @return static
|
||||
*/
|
||||
public function values()
|
||||
{
|
||||
return new static(array_values($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并数组并返回一个新的 Collection 实例
|
||||
* @access public
|
||||
* @param mixed $items 新的数据
|
||||
* @return static
|
||||
*/
|
||||
public function merge($items)
|
||||
{
|
||||
return new static(array_merge($this->items, $this->convertToArray($items)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较数组,返回差集生成的新 Collection 实例
|
||||
* @access public
|
||||
* @param mixed $items 做比较的数据
|
||||
* @return static
|
||||
*/
|
||||
public function diff($items)
|
||||
{
|
||||
return new static(array_diff($this->items, $this->convertToArray($items)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较数组,返回交集组成的 Collection 新实例
|
||||
* @access public
|
||||
* @param mixed $items 比较数据
|
||||
* @return static
|
||||
*/
|
||||
public function intersect($items)
|
||||
{
|
||||
return new static(array_intersect($this->items, $this->convertToArray($items)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回并删除数据中的的最后一个元素(出栈)
|
||||
* 删除数组的最后一个元素(出栈)
|
||||
*
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -153,7 +196,32 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回并删除数据中首个元素
|
||||
* 通过使用用户自定义函数,以字符串返回数组
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@@ -162,27 +230,11 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
||||
return array_shift($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在数组开头插入一个元素
|
||||
* @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 mixed $value 值
|
||||
* @param mixed $key 键名
|
||||
* @param mixed $value
|
||||
* @param mixed $key
|
||||
* @return void
|
||||
*/
|
||||
public function push($value, $key = null)
|
||||
@@ -195,32 +247,11 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过使用用户自定义函数,以字符串返回数组
|
||||
* 把一个数组分割为新的数组块.
|
||||
*
|
||||
* @access public
|
||||
* @param callable $callback 回调函数
|
||||
* @param mixed $initial 初始值
|
||||
* @return mixed
|
||||
*/
|
||||
public function reduce(callable $callback, $initial = null)
|
||||
{
|
||||
return array_reduce($this->items, $callback, $initial);
|
||||
}
|
||||
|
||||
/**
|
||||
* 以相反的顺序创建一个新的 Collection 实例
|
||||
* @access public
|
||||
* @return static
|
||||
*/
|
||||
public function reverse()
|
||||
{
|
||||
return new static(array_reverse($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* 把数据分割为新的数组块
|
||||
* @access public
|
||||
* @param int $size 分隔长度
|
||||
* @param bool $preserveKeys 是否保持原数据索引
|
||||
* @param int $size
|
||||
* @param bool $preserveKeys
|
||||
* @return static
|
||||
*/
|
||||
public function chunk($size, $preserveKeys = false)
|
||||
@@ -235,9 +266,26 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
||||
}
|
||||
|
||||
/**
|
||||
* 给数据中的每个元素执行回调
|
||||
* 在数组开头插入一个元素
|
||||
* @access public
|
||||
* @param callable $callback 回调函数
|
||||
* @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)
|
||||
@@ -247,9 +295,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
||||
|
||||
if (false === $result) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_object($item)) {
|
||||
} elseif (!is_object($item)) {
|
||||
$this->items[$key] = $result;
|
||||
}
|
||||
}
|
||||
@@ -258,78 +304,151 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
||||
}
|
||||
|
||||
/**
|
||||
* 用回调函数过滤数据中的元素
|
||||
* 用回调函数处理数组中的元素
|
||||
* @access public
|
||||
* @param callable|null $callback 回调函数
|
||||
* @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)
|
||||
{
|
||||
return new static(array_filter($this->items, $callback ?: null));
|
||||
if ($callback) {
|
||||
return new static(array_filter($this->items, $callback));
|
||||
}
|
||||
|
||||
return new static(array_filter($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字段条件过滤数组中的元素
|
||||
* @access public
|
||||
* @param string $field 字段名
|
||||
* @param mixed $operator 操作符
|
||||
* @param mixed $value 数据
|
||||
* @return static
|
||||
*/
|
||||
public function where($field, $operator, $value = null)
|
||||
{
|
||||
if (is_null($value)) {
|
||||
$value = $operator;
|
||||
$operator = '=';
|
||||
}
|
||||
|
||||
return $this->filter(function ($data) use ($field, $operator, $value) {
|
||||
if (strpos($field, '.')) {
|
||||
list($field, $relation) = explode('.', $field);
|
||||
|
||||
$result = isset($data[$field][$relation]) ? $data[$field][$relation] : null;
|
||||
} else {
|
||||
$result = isset($data[$field]) ? $data[$field] : null;
|
||||
}
|
||||
|
||||
switch (strtolower($operator)) {
|
||||
case '===':
|
||||
return $result === $value;
|
||||
case '!==':
|
||||
return $result !== $value;
|
||||
case '!=':
|
||||
case '<>':
|
||||
return $result != $value;
|
||||
case '>':
|
||||
return $result > $value;
|
||||
case '>=':
|
||||
return $result >= $value;
|
||||
case '<':
|
||||
return $result < $value;
|
||||
case '<=':
|
||||
return $result <= $value;
|
||||
case 'like':
|
||||
return is_string($result) && false !== strpos($result, $value);
|
||||
case 'not like':
|
||||
return is_string($result) && false === strpos($result, $value);
|
||||
case 'in':
|
||||
return is_scalar($result) && in_array($result, $value, true);
|
||||
case 'not in':
|
||||
return is_scalar($result) && !in_array($result, $value, true);
|
||||
case 'between':
|
||||
list($min, $max) = is_string($value) ? explode(',', $value) : $value;
|
||||
return is_scalar($result) && $result >= $min && $result <= $max;
|
||||
case 'not between':
|
||||
list($min, $max) = is_string($value) ? explode(',', $value) : $value;
|
||||
return is_scalar($result) && $result > $max || $result < $min;
|
||||
case '==':
|
||||
case '=':
|
||||
default:
|
||||
return $result == $value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数据中指定的一列
|
||||
* @access public
|
||||
* @param mixed $columnKey 键名
|
||||
* @param null $indexKey 作为索引值的列
|
||||
* @param mixed $indexKey 作为索引值的列
|
||||
* @return array
|
||||
*/
|
||||
public function column($columnKey, $indexKey = null)
|
||||
{
|
||||
if (function_exists('array_column')) {
|
||||
return array_column($this->items, $columnKey, $indexKey);
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($this->items as $row) {
|
||||
$key = $value = null;
|
||||
$keySet = $valueSet = false;
|
||||
|
||||
if (null !== $indexKey && array_key_exists($indexKey, $row)) {
|
||||
$key = (string) $row[$indexKey];
|
||||
$keySet = true;
|
||||
}
|
||||
|
||||
if (null === $columnKey) {
|
||||
$valueSet = true;
|
||||
$value = $row;
|
||||
} elseif (is_array($row) && array_key_exists($columnKey, $row)) {
|
||||
$valueSet = true;
|
||||
$value = $row[$columnKey];
|
||||
}
|
||||
|
||||
if ($valueSet) {
|
||||
if ($keySet) {
|
||||
$result[$key] = $value;
|
||||
} else {
|
||||
$result[] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
return array_column($this->toArray(), $columnKey, $indexKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对数据排序,并返回排序后的数据组成的新 Collection 实例
|
||||
* 对数组排序
|
||||
*
|
||||
* @access public
|
||||
* @param callable|null $callback 回调函数
|
||||
* @param callable|null $callback
|
||||
* @return static
|
||||
*/
|
||||
public function sort(callable $callback = null)
|
||||
{
|
||||
$items = $this->items;
|
||||
$items = $this->items;
|
||||
|
||||
$callback = $callback ?: function ($a, $b) {
|
||||
return $a == $b ? 0 : (($a < $b) ? -1 : 1);
|
||||
|
||||
};
|
||||
|
||||
uasort($items, $callback);
|
||||
|
||||
return new static($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数据打乱后组成新的 Collection 实例
|
||||
* 指定字段排序
|
||||
* @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
|
||||
*/
|
||||
@@ -338,15 +457,17 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
||||
$items = $this->items;
|
||||
|
||||
shuffle($items);
|
||||
|
||||
return new static($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取数据并返回新的 Collection 实例
|
||||
* 截取数组
|
||||
*
|
||||
* @access public
|
||||
* @param int $offset 起始位置
|
||||
* @param int $length 截取长度
|
||||
* @param bool $preserveKeys 是否保持原先的键名
|
||||
* @param int $offset
|
||||
* @param int $length
|
||||
* @param bool $preserveKeys
|
||||
* @return static
|
||||
*/
|
||||
public function slice($offset, $length = null, $preserveKeys = false)
|
||||
@@ -354,35 +475,17 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
||||
return new static(array_slice($this->items, $offset, $length, $preserveKeys));
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定的键是否存在
|
||||
* @access public
|
||||
* @param mixed $offset 键名
|
||||
* @return bool
|
||||
*/
|
||||
// ArrayAccess
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return array_key_exists($offset, $this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定键对应的值
|
||||
* @access public
|
||||
* @param mixed $offset 键名
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->items[$offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置键值
|
||||
* @access public
|
||||
* @param mixed $offset 键名
|
||||
* @param mixed $value 值
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (is_null($offset)) {
|
||||
@@ -392,51 +495,33 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定键值
|
||||
* @access public
|
||||
* @param mixed $offset 键名
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->items[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计数据的个数
|
||||
* @access public
|
||||
* @return int
|
||||
*/
|
||||
//Countable
|
||||
public function count()
|
||||
{
|
||||
return count($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据的迭代器
|
||||
* @access public
|
||||
* @return ArrayIterator
|
||||
*/
|
||||
//IteratorAggregate
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数据反序列化成数组
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
//JsonSerializable
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换当前数据集为 JSON 字符串
|
||||
* 转换当前数据集为JSON字符串
|
||||
* @access public
|
||||
* @param integer $options json 参数
|
||||
* @param integer $options json参数
|
||||
* @return string
|
||||
*/
|
||||
public function toJson($options = JSON_UNESCAPED_UNICODE)
|
||||
@@ -444,24 +529,24 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
|
||||
return json_encode($this->toArray(), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数据转换成字符串
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->toJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数据转换成数组
|
||||
* @access protected
|
||||
* @param mixed $items 数据
|
||||
* 转换成数组
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $items
|
||||
* @return array
|
||||
*/
|
||||
protected function convertToArray($items)
|
||||
{
|
||||
return $items instanceof self ? $items->all() : (array) $items;
|
||||
if ($items instanceof self) {
|
||||
return $items->all();
|
||||
}
|
||||
|
||||
return (array) $items;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,204 +11,388 @@
|
||||
|
||||
namespace think;
|
||||
|
||||
class Config
|
||||
use Yaconf;
|
||||
|
||||
class Config implements \ArrayAccess
|
||||
{
|
||||
/**
|
||||
* @var array 配置参数
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
private static $config = [];
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* @var string 参数作用域
|
||||
* 配置前缀
|
||||
* @var string
|
||||
*/
|
||||
private static $range = '_sys_';
|
||||
protected $prefix = 'app';
|
||||
|
||||
/**
|
||||
* 设定配置参数的作用域
|
||||
* 配置文件目录
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* 配置文件后缀
|
||||
* @var string
|
||||
*/
|
||||
protected $ext;
|
||||
|
||||
/**
|
||||
* 是否支持Yaconf
|
||||
* @var bool
|
||||
*/
|
||||
protected $yaconf;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @access public
|
||||
* @param string $range 作用域
|
||||
*/
|
||||
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 static function range($range)
|
||||
public function setYaconf($yaconf)
|
||||
{
|
||||
self::$range = $range;
|
||||
if ($this->yaconf) {
|
||||
$this->yaconf = $yaconf;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset(self::$config[$range])) self::$config[$range] = [];
|
||||
/**
|
||||
* 设置配置参数默认前缀
|
||||
* @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 配置名(如设置即表示二级配置)
|
||||
* @param string $range 作用域
|
||||
* @param string $config 配置文件路径或内容
|
||||
* @param string $type 配置解析类型
|
||||
* @param string $name 配置名(如设置即表示二级配置)
|
||||
* @return mixed
|
||||
*/
|
||||
public static function parse($config, $type = '', $name = '', $range = '')
|
||||
public function parse($config, $type = '', $name = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
if (empty($type)) {
|
||||
$type = pathinfo($config, PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
if (empty($type)) $type = pathinfo($config, PATHINFO_EXTENSION);
|
||||
$object = Loader::factory($type, '\\think\\config\\driver\\', $config);
|
||||
|
||||
$class = false !== strpos($type, '\\') ?
|
||||
$type :
|
||||
'\\think\\config\\driver\\' . ucwords($type);
|
||||
|
||||
return self::set((new $class())->parse($config), $name, $range);
|
||||
return $this->set($object->parse(), $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载配置文件(PHP格式)
|
||||
* 加载配置文件(多种格式)
|
||||
* @access public
|
||||
* @param string $file 配置文件名
|
||||
* @param string $name 配置名(如设置即表示二级配置)
|
||||
* @param string $range 作用域
|
||||
* @param string $file 配置文件名
|
||||
* @param string $name 一级配置名
|
||||
* @return mixed
|
||||
*/
|
||||
public static function load($file, $name = '', $range = '')
|
||||
public function load($file, $name = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
|
||||
if (!isset(self::$config[$range])) self::$config[$range] = [];
|
||||
|
||||
if (is_file($file)) {
|
||||
$name = strtolower($name);
|
||||
$type = pathinfo($file, PATHINFO_EXTENSION);
|
||||
|
||||
if ('php' == $type) {
|
||||
return self::set(include $file, $name, $range);
|
||||
}
|
||||
|
||||
if ('yaml' == $type && function_exists('yaml_parse_file')) {
|
||||
return self::set(yaml_parse_file($file), $name, $range);
|
||||
}
|
||||
|
||||
return self::parse($file, $type, $name, $range);
|
||||
$filename = $file;
|
||||
} elseif (is_file($this->path . $file . $this->ext)) {
|
||||
$filename = $this->path . $file . $this->ext;
|
||||
}
|
||||
|
||||
return self::$config[$range];
|
||||
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 配置参数名(支持二级配置 . 号分割)
|
||||
* @param string $range 作用域
|
||||
* @param string $name 配置参数名(支持多级配置 .号分割)
|
||||
* @return bool
|
||||
*/
|
||||
public static function has($name, $range = '')
|
||||
public function has($name)
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
|
||||
if (!strpos($name, '.')) {
|
||||
return isset(self::$config[$range][strtolower($name)]);
|
||||
if (false === strpos($name, '.')) {
|
||||
$name = $this->prefix . '.' . $name;
|
||||
}
|
||||
|
||||
// 二维数组设置和获取支持
|
||||
$name = explode('.', $name, 2);
|
||||
return isset(self::$config[$range][strtolower($name[0])][$name[1]]);
|
||||
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 string $range 作用域
|
||||
* @param string $name 配置参数名(支持多级配置 .号分割)
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($name = null, $range = '')
|
||||
public function get($name = null, $default = null)
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
if ($name && false === strpos($name, '.')) {
|
||||
$name = $this->prefix . '.' . $name;
|
||||
}
|
||||
|
||||
// 无参数时获取所有
|
||||
if (empty($name) && isset(self::$config[$range])) {
|
||||
return self::$config[$range];
|
||||
if (empty($name)) {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
// 非二级配置时直接返回
|
||||
if (!strpos($name, '.')) {
|
||||
$name = strtolower($name);
|
||||
return isset(self::$config[$range][$name]) ? self::$config[$range][$name] : null;
|
||||
if ('.' == substr($name, -1)) {
|
||||
return $this->pull(substr($name, 0, -1));
|
||||
}
|
||||
|
||||
// 二维数组设置和获取支持
|
||||
$name = explode('.', $name, 2);
|
||||
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;
|
||||
|
||||
if (!isset(self::$config[$range][$name[0]])) {
|
||||
// 动态载入额外配置
|
||||
$module = Request::instance()->module();
|
||||
$file = CONF_PATH . ($module ? $module . DS : '') . 'extra' . DS . $name[0] . CONF_EXT;
|
||||
|
||||
is_file($file) && self::load($file, $name[0]);
|
||||
// 按.拆分成多维数组进行判断
|
||||
foreach ($name as $val) {
|
||||
if (isset($config[$val])) {
|
||||
$config = $config[$val];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
return isset(self::$config[$range][$name[0]][$name[1]]) ?
|
||||
self::$config[$range][$name[0]][$name[1]] :
|
||||
null;
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置参数 name 为数组则为批量设置
|
||||
* 设置配置参数 name为数组则为批量设置
|
||||
* @access public
|
||||
* @param string|array $name 配置参数名(支持二级配置 . 号分割)
|
||||
* @param mixed $value 配置值
|
||||
* @param string $range 作用域
|
||||
* @param string|array $name 配置参数名(支持三级配置 .号分割)
|
||||
* @param mixed $value 配置值
|
||||
* @return mixed
|
||||
*/
|
||||
public static function set($name, $value = null, $range = '')
|
||||
public function set($name, $value = null)
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
|
||||
if (!isset(self::$config[$range])) self::$config[$range] = [];
|
||||
|
||||
// 字符串则表示单个配置设置
|
||||
if (is_string($name)) {
|
||||
if (!strpos($name, '.')) {
|
||||
self::$config[$range][strtolower($name)] = $value;
|
||||
if (false === strpos($name, '.')) {
|
||||
$name = $this->prefix . '.' . $name;
|
||||
}
|
||||
|
||||
$name = explode('.', $name, 3);
|
||||
|
||||
if (count($name) == 2) {
|
||||
$this->config[strtolower($name[0])][$name[1]] = $value;
|
||||
} else {
|
||||
// 二维数组
|
||||
$name = explode('.', $name, 2);
|
||||
self::$config[$range][strtolower($name[0])][$name[1]] = $value;
|
||||
$this->config[strtolower($name[0])][$name[1]][$name[2]] = $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
// 数组则表示批量设置
|
||||
if (is_array($name)) {
|
||||
} elseif (is_array($name)) {
|
||||
// 批量设置
|
||||
if (!empty($value)) {
|
||||
self::$config[$range][$value] = isset(self::$config[$range][$value]) ?
|
||||
array_merge(self::$config[$range][$value], $name) :
|
||||
$name;
|
||||
if (isset($this->config[$value])) {
|
||||
$result = array_merge($this->config[$value], $name);
|
||||
} else {
|
||||
$result = $name;
|
||||
}
|
||||
|
||||
return self::$config[$range][$value];
|
||||
$this->config[$value] = $result;
|
||||
} else {
|
||||
$result = $this->config = array_merge($this->config, $name);
|
||||
}
|
||||
|
||||
return self::$config[$range] = array_merge(
|
||||
self::$config[$range], array_change_key_case($name)
|
||||
);
|
||||
} else {
|
||||
// 为空直接返回 已有配置
|
||||
$result = $this->config;
|
||||
}
|
||||
|
||||
// 为空直接返回已有配置
|
||||
return self::$config[$range];
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除配置
|
||||
* @access public
|
||||
* @param string $name 配置参数名(支持三级配置 .号分割)
|
||||
* @return void
|
||||
*/
|
||||
public function remove($name)
|
||||
{
|
||||
if (false === strpos($name, '.')) {
|
||||
$name = $this->prefix . '.' . $name;
|
||||
}
|
||||
|
||||
$name = explode('.', $name, 3);
|
||||
|
||||
if (count($name) == 2) {
|
||||
unset($this->config[strtolower($name[0])][$name[1]]);
|
||||
} else {
|
||||
unset($this->config[strtolower($name[0])][$name[1]][$name[2]]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置配置参数
|
||||
* @access public
|
||||
* @param string $range 作用域
|
||||
* @param string $prefix 配置前缀名
|
||||
* @return void
|
||||
*/
|
||||
public static function reset($range = '')
|
||||
public function reset($prefix = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
|
||||
if (true === $range) {
|
||||
self::$config = [];
|
||||
if ('' === $prefix) {
|
||||
$this->config = [];
|
||||
} else {
|
||||
self::$config[$range] = [];
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,60 +20,37 @@ use think\console\output\driver\Buffer;
|
||||
|
||||
class Console
|
||||
{
|
||||
/**
|
||||
* @var string 命令名称
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var string 命令版本
|
||||
*/
|
||||
private $name;
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* @var Command[] 命令
|
||||
*/
|
||||
/** @var Command[] */
|
||||
private $commands = [];
|
||||
|
||||
/**
|
||||
* @var bool 是否需要帮助信息
|
||||
*/
|
||||
private $wantHelps = false;
|
||||
|
||||
/**
|
||||
* @var bool 是否捕获异常
|
||||
*/
|
||||
private $catchExceptions = true;
|
||||
|
||||
/**
|
||||
* @var bool 是否自动退出执行
|
||||
*/
|
||||
private $autoExit = true;
|
||||
|
||||
/**
|
||||
* @var InputDefinition 输入定义
|
||||
*/
|
||||
private $autoExit = true;
|
||||
private $definition;
|
||||
|
||||
/**
|
||||
* @var string 默认执行的命令
|
||||
*/
|
||||
private $defaultCommand;
|
||||
|
||||
/**
|
||||
* @var array 默认提供的命令
|
||||
*/
|
||||
private static $defaultCommands = [
|
||||
"think\\console\\command\\Help",
|
||||
"think\\console\\command\\Lists",
|
||||
"think\\console\\command\\Build",
|
||||
"think\\console\\command\\Clear",
|
||||
"think\\console\\command\\make\\Controller",
|
||||
"think\\console\\command\\make\\Model",
|
||||
"think\\console\\command\\optimize\\Autoload",
|
||||
"think\\console\\command\\optimize\\Config",
|
||||
"think\\console\\command\\optimize\\Route",
|
||||
"think\\console\\command\\optimize\\Schema",
|
||||
'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",
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -94,10 +71,6 @@ class Console
|
||||
|
||||
$this->defaultCommand = 'list';
|
||||
$this->definition = $this->getDefaultInputDefinition();
|
||||
|
||||
foreach ($this->getDefaultCommands() as $command) {
|
||||
$this->add($command);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,6 +79,10 @@ class Console
|
||||
*/
|
||||
public function setUser($user)
|
||||
{
|
||||
if (DIRECTORY_SEPARATOR == '\\') {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = posix_getpwnam($user);
|
||||
if ($user) {
|
||||
posix_setuid($user['uid']);
|
||||
@@ -124,34 +101,69 @@ class Console
|
||||
static $console;
|
||||
|
||||
if (!$console) {
|
||||
$config = Config::get('console');
|
||||
// 实例化 console
|
||||
$config = Container::get('config')->pull('console');
|
||||
$console = new self($config['name'], $config['version'], $config['user']);
|
||||
|
||||
// 读取指令集
|
||||
if (is_file(CONF_PATH . 'command' . EXT)) {
|
||||
$commands = include CONF_PATH . 'command' . EXT;
|
||||
$commands = $console->getDefinedCommands($config);
|
||||
|
||||
if (is_array($commands)) {
|
||||
foreach ($commands as $command) {
|
||||
class_exists($command) &&
|
||||
is_subclass_of($command, "\\think\\console\\Command") &&
|
||||
$console->add(new $command()); // 注册指令
|
||||
}
|
||||
}
|
||||
}
|
||||
// 添加指令集
|
||||
$console->addCommands($commands);
|
||||
}
|
||||
|
||||
return $run ? $console->run() : $console;
|
||||
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
|
||||
* @return Output|Buffer
|
||||
*/
|
||||
public static function call($command, array $parameters = [], $driver = 'buffer')
|
||||
{
|
||||
@@ -173,6 +185,7 @@ class Console
|
||||
* @access public
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
* @api
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
@@ -184,21 +197,27 @@ class Console
|
||||
try {
|
||||
$exitCode = $this->doRun($input, $output);
|
||||
} catch (\Exception $e) {
|
||||
if (!$this->catchExceptions) throw $e;
|
||||
if (!$this->catchExceptions) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$output->renderException($e);
|
||||
|
||||
$exitCode = $e->getCode();
|
||||
|
||||
if (is_numeric($exitCode)) {
|
||||
$exitCode = ((int) $exitCode) ?: 1;
|
||||
$exitCode = (int) $exitCode;
|
||||
if (0 === $exitCode) {
|
||||
$exitCode = 1;
|
||||
}
|
||||
} else {
|
||||
$exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->autoExit) {
|
||||
if ($exitCode > 255) $exitCode = 255;
|
||||
if ($exitCode > 255) {
|
||||
$exitCode = 255;
|
||||
}
|
||||
|
||||
exit($exitCode);
|
||||
}
|
||||
@@ -209,13 +228,12 @@ class Console
|
||||
/**
|
||||
* 执行指令
|
||||
* @access public
|
||||
* @param Input $input 输入
|
||||
* @param Output $output 输出
|
||||
* @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());
|
||||
|
||||
@@ -224,7 +242,6 @@ class Console
|
||||
|
||||
$name = $this->getCommandName($input);
|
||||
|
||||
// 获取帮助信息
|
||||
if (true === $input->hasParameterOption(['--help', '-h'])) {
|
||||
if (!$name) {
|
||||
$name = 'help';
|
||||
@@ -239,26 +256,27 @@ class Console
|
||||
$input = new Input([$this->defaultCommand]);
|
||||
}
|
||||
|
||||
return $this->doRunCommand($this->find($name), $input, $output);
|
||||
$command = $this->find($name);
|
||||
|
||||
$exitCode = $this->doRunCommand($command, $input, $output);
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输入参数定义
|
||||
* @access public
|
||||
* @param InputDefinition $definition 输入定义
|
||||
* @return $this;
|
||||
* @param InputDefinition $definition
|
||||
*/
|
||||
public function setDefinition(InputDefinition $definition)
|
||||
{
|
||||
$this->definition = $definition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输入参数定义
|
||||
* @access public
|
||||
* @return InputDefinition
|
||||
* @return InputDefinition The InputDefinition instance
|
||||
*/
|
||||
public function getDefinition()
|
||||
{
|
||||
@@ -266,9 +284,9 @@ class Console
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取帮助信息
|
||||
* Gets the help message.
|
||||
* @access public
|
||||
* @return string
|
||||
* @return string A help message.
|
||||
*/
|
||||
public function getHelp()
|
||||
{
|
||||
@@ -276,29 +294,25 @@ class Console
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否捕获异常
|
||||
* 是否捕获异常
|
||||
* @access public
|
||||
* @param bool $boolean 是否捕获
|
||||
* @return $this
|
||||
* @param bool $boolean
|
||||
* @api
|
||||
*/
|
||||
public function setCatchExceptions($boolean)
|
||||
{
|
||||
$this->catchExceptions = (bool) $boolean;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否自动退出
|
||||
* 是否自动退出
|
||||
* @access public
|
||||
* @param bool $boolean 是否自动退出
|
||||
* @return $this
|
||||
* @param bool $boolean
|
||||
* @api
|
||||
*/
|
||||
public function setAutoExit($boolean)
|
||||
{
|
||||
$this->autoExit = (bool) $boolean;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,20 +328,18 @@ class Console
|
||||
/**
|
||||
* 设置名称
|
||||
* @access public
|
||||
* @param string $name 名称
|
||||
* @return $this
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本
|
||||
* @access public
|
||||
* @return string
|
||||
* @api
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
@@ -337,14 +349,11 @@ class Console
|
||||
/**
|
||||
* 设置版本
|
||||
* @access public
|
||||
* @param string $version 版本信息
|
||||
* @return $this
|
||||
* @param string $version
|
||||
*/
|
||||
public function setVersion($version)
|
||||
{
|
||||
$this->version = $version;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,20 +364,16 @@ class Console
|
||||
public function getLongVersion()
|
||||
{
|
||||
if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
|
||||
return sprintf(
|
||||
'<info>%s</info> version <comment>%s</comment>',
|
||||
$this->getName(),
|
||||
$this->getVersion()
|
||||
);
|
||||
return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
|
||||
}
|
||||
|
||||
return '<info>Console Tool</info>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册一个指令
|
||||
* 注册一个指令 (便于动态创建指令)
|
||||
* @access public
|
||||
* @param string $name 指令名称
|
||||
* @param string $name 指令名
|
||||
* @return Command
|
||||
*/
|
||||
public function register($name)
|
||||
@@ -377,37 +382,47 @@ class Console
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量添加指令
|
||||
* 添加指令集
|
||||
* @access public
|
||||
* @param Command[] $commands 指令实例
|
||||
* @return $this
|
||||
* @param array $commands
|
||||
*/
|
||||
public function addCommands(array $commands)
|
||||
{
|
||||
foreach ($commands as $command) $this->add($command);
|
||||
|
||||
return $this;
|
||||
foreach ($commands as $key => $command) {
|
||||
if (is_subclass_of($command, "\\think\\console\\Command")) {
|
||||
// 注册指令
|
||||
$this->add($command, is_numeric($key) ? '' : $key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个指令
|
||||
* 注册一个指令(对象)
|
||||
* @access public
|
||||
* @param Command $command 命令实例
|
||||
* @return Command|bool
|
||||
* @param mixed $command 指令对象或者指令类名
|
||||
* @param string $name 指令名 留空则自动获取
|
||||
* @return mixed
|
||||
*/
|
||||
public function add(Command $command)
|
||||
public function add($command, $name)
|
||||
{
|
||||
if (!$command->isEnabled()) {
|
||||
$command->setConsole(null);
|
||||
return false;
|
||||
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))
|
||||
);
|
||||
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;
|
||||
@@ -429,13 +444,17 @@ class Console
|
||||
public function get($name)
|
||||
{
|
||||
if (!isset($this->commands[$name])) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('The command "%s" does not exist.', $name)
|
||||
);
|
||||
throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name));
|
||||
}
|
||||
|
||||
$command = $this->commands[$name];
|
||||
|
||||
if (is_string($command)) {
|
||||
$command = new $command();
|
||||
}
|
||||
|
||||
$command->setConsole($this);
|
||||
|
||||
if ($this->wantHelps) {
|
||||
$this->wantHelps = false;
|
||||
|
||||
@@ -468,42 +487,39 @@ class Console
|
||||
public function getNamespaces()
|
||||
{
|
||||
$namespaces = [];
|
||||
foreach ($this->commands as $name => $command) {
|
||||
if (is_string($command)) {
|
||||
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($name));
|
||||
} else {
|
||||
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
|
||||
|
||||
foreach ($this->commands as $command) {
|
||||
$namespaces = array_merge(
|
||||
$namespaces, $this->extractAllNamespaces($command->getName())
|
||||
);
|
||||
|
||||
foreach ($command->getAliases() as $alias) {
|
||||
$namespaces = array_merge(
|
||||
$namespaces, $this->extractAllNamespaces($alias)
|
||||
);
|
||||
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
|
||||
* @param string $namespace
|
||||
* @return string
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function findNamespace($namespace)
|
||||
{
|
||||
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
|
||||
$allNamespaces = $this->getNamespaces();
|
||||
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
|
||||
return preg_quote($matches[1]) . '[^:]*';
|
||||
}, $namespace);
|
||||
|
||||
$allNamespaces = $this->getNamespaces();
|
||||
$namespaces = preg_grep('{^' . $expr . '}', $allNamespaces);
|
||||
$namespaces = preg_grep('{^' . $expr . '}', $allNamespaces);
|
||||
|
||||
if (empty($namespaces)) {
|
||||
$message = sprintf(
|
||||
'There are no commands defined in the "%s" namespace.', $namespace
|
||||
);
|
||||
$message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
|
||||
|
||||
if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
|
||||
if (1 == count($alternatives)) {
|
||||
@@ -519,14 +535,8 @@ class Console
|
||||
}
|
||||
|
||||
$exact = in_array($namespace, $namespaces, true);
|
||||
|
||||
if (count($namespaces) > 1 && !$exact) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf(
|
||||
'The namespace "%s" is ambiguous (%s).',
|
||||
$namespace,
|
||||
$this->getAbbreviationSuggestions(array_values($namespaces)))
|
||||
);
|
||||
throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))));
|
||||
}
|
||||
|
||||
return $exact ? $namespace : reset($namespaces);
|
||||
@@ -541,15 +551,16 @@ class Console
|
||||
*/
|
||||
public function find($name)
|
||||
{
|
||||
$allCommands = array_keys($this->commands);
|
||||
|
||||
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
|
||||
return preg_quote($matches[1]) . '[^:]*';
|
||||
}, $name);
|
||||
|
||||
$allCommands = array_keys($this->commands);
|
||||
$commands = preg_grep('{^' . $expr . '}', $allCommands);
|
||||
$commands = preg_grep('{^' . $expr . '}', $allCommands);
|
||||
|
||||
if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) {
|
||||
if (false !== ($pos = strrpos($name, ':'))) {
|
||||
if (false !== $pos = strrpos($name, ':')) {
|
||||
$this->findNamespace(substr($name, 0, $pos));
|
||||
}
|
||||
|
||||
@@ -567,22 +578,11 @@ class Console
|
||||
throw new \InvalidArgumentException($message);
|
||||
}
|
||||
|
||||
if (count($commands) > 1) {
|
||||
$commandList = $this->commands;
|
||||
$commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) {
|
||||
$commandName = $commandList[$nameOrAlias]->getName();
|
||||
|
||||
return $commandName === $nameOrAlias || !in_array($commandName, $commands);
|
||||
});
|
||||
}
|
||||
|
||||
$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)
|
||||
);
|
||||
throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions));
|
||||
}
|
||||
|
||||
return $this->get($exact ? $name : reset($commands));
|
||||
@@ -593,17 +593,19 @@ class Console
|
||||
* @access public
|
||||
* @param string $namespace 命名空间
|
||||
* @return Command[]
|
||||
* @api
|
||||
*/
|
||||
public function all($namespace = null)
|
||||
{
|
||||
if (null === $namespace) return $this->commands;
|
||||
if (null === $namespace) {
|
||||
return $this->commands;
|
||||
}
|
||||
|
||||
$commands = [];
|
||||
|
||||
foreach ($this->commands as $name => $command) {
|
||||
$ext = $this->extractNamespace($name, substr_count($namespace, ':') + 1);
|
||||
|
||||
if ($ext === $namespace) $commands[$name] = $command;
|
||||
if ($this->extractNamespace($name, substr_count($namespace, ':') + 1) === $namespace) {
|
||||
$commands[$name] = $command;
|
||||
}
|
||||
}
|
||||
|
||||
return $commands;
|
||||
@@ -612,7 +614,7 @@ class Console
|
||||
/**
|
||||
* 获取可能的指令名
|
||||
* @access public
|
||||
* @param array $names 指令名
|
||||
* @param array $names
|
||||
* @return array
|
||||
*/
|
||||
public static function getAbbreviations($names)
|
||||
@@ -629,11 +631,10 @@ class Console
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置基于用户的参数和选项的输入和输出实例
|
||||
* 配置基于用户的参数和选项的输入和输出实例。
|
||||
* @access protected
|
||||
* @param Input $input 输入实例
|
||||
* @param Output $output 输出实例
|
||||
* @return void
|
||||
*/
|
||||
protected function configureIO(Input $input, Output $output)
|
||||
{
|
||||
@@ -675,9 +676,9 @@ class Console
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指令的名称
|
||||
* 获取指令的基础名称
|
||||
* @access protected
|
||||
* @param Input $input 输入实例
|
||||
* @param Input $input
|
||||
* @return string
|
||||
*/
|
||||
protected function getCommandName(Input $input)
|
||||
@@ -704,33 +705,9 @@ class Console
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认命令
|
||||
* @access protected
|
||||
* @return Command[]
|
||||
*/
|
||||
protected function getDefaultCommands()
|
||||
public static function addDefaultCommands(array $classnames)
|
||||
{
|
||||
$defaultCommands = [];
|
||||
|
||||
foreach (self::$defaultCommands as $class) {
|
||||
if (class_exists($class) && is_subclass_of($class, "think\\console\\Command")) {
|
||||
$defaultCommands[] = new $class();
|
||||
}
|
||||
}
|
||||
|
||||
return $defaultCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加默认指令
|
||||
* @access public
|
||||
* @param array $classes 指令
|
||||
* @return void
|
||||
*/
|
||||
public static function addDefaultCommands(array $classes)
|
||||
{
|
||||
self::$defaultCommands = array_merge(self::$defaultCommands, $classes);
|
||||
self::$defaultCommands = array_merge(self::$defaultCommands, $classnames);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -741,18 +718,13 @@ class Console
|
||||
*/
|
||||
private function getAbbreviationSuggestions($abbrevs)
|
||||
{
|
||||
return sprintf(
|
||||
'%s, %s%s',
|
||||
$abbrevs[0],
|
||||
$abbrevs[1],
|
||||
count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''
|
||||
);
|
||||
return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回指令的命名空间部分
|
||||
* 返回命名空间部分
|
||||
* @access public
|
||||
* @param string $name 指令名称
|
||||
* @param string $name 指令
|
||||
* @param string $limit 部分的命名空间的最大数量
|
||||
* @return string
|
||||
*/
|
||||
@@ -767,16 +739,16 @@ class Console
|
||||
/**
|
||||
* 查找可替代的建议
|
||||
* @access private
|
||||
* @param string $name 指令名称
|
||||
* @param array|\Traversable $collection 建议集合
|
||||
* @param string $name
|
||||
* @param array|\Traversable $collection
|
||||
* @return array
|
||||
*/
|
||||
private function findAlternatives($name, $collection)
|
||||
{
|
||||
$threshold = 1e3;
|
||||
$alternatives = [];
|
||||
$collectionParts = [];
|
||||
$threshold = 1e3;
|
||||
$alternatives = [];
|
||||
|
||||
$collectionParts = [];
|
||||
foreach ($collection as $item) {
|
||||
$collectionParts[$item] = explode(':', $item);
|
||||
}
|
||||
@@ -784,7 +756,6 @@ class Console
|
||||
foreach (explode(':', $name) as $i => $subname) {
|
||||
foreach ($collectionParts as $collectionName => $parts) {
|
||||
$exists = isset($alternatives[$collectionName]);
|
||||
|
||||
if (!isset($parts[$i]) && $exists) {
|
||||
$alternatives[$collectionName] += $threshold;
|
||||
continue;
|
||||
@@ -793,14 +764,8 @@ class Console
|
||||
}
|
||||
|
||||
$lev = levenshtein($subname, $parts[$i]);
|
||||
|
||||
if ($lev <= strlen($subname) / 3 ||
|
||||
'' !== $subname &&
|
||||
false !== strpos($parts[$i], $subname)
|
||||
) {
|
||||
$alternatives[$collectionName] = $exists ?
|
||||
$alternatives[$collectionName] + $lev :
|
||||
$lev;
|
||||
if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
|
||||
$alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
|
||||
} elseif ($exists) {
|
||||
$alternatives[$collectionName] += $threshold;
|
||||
}
|
||||
@@ -809,18 +774,14 @@ class Console
|
||||
|
||||
foreach ($collection as $item) {
|
||||
$lev = levenshtein($name, $item);
|
||||
|
||||
if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
|
||||
$alternatives[$item] = isset($alternatives[$item]) ?
|
||||
$alternatives[$item] - $lev :
|
||||
$lev;
|
||||
$alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
|
||||
}
|
||||
}
|
||||
|
||||
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) {
|
||||
return $lev < 2 * $threshold;
|
||||
});
|
||||
|
||||
asort($alternatives);
|
||||
|
||||
return array_keys($alternatives);
|
||||
@@ -829,27 +790,25 @@ class Console
|
||||
/**
|
||||
* 设置默认的指令
|
||||
* @access public
|
||||
* @param string $commandName 指令名称
|
||||
* @return $this
|
||||
* @param string $commandName The Command name
|
||||
*/
|
||||
public function setDefaultCommand($commandName)
|
||||
{
|
||||
$this->defaultCommand = $commandName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回所有的命名空间
|
||||
* @access private
|
||||
* @param string $name 指令名称
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
private function extractAllNamespaces($name)
|
||||
{
|
||||
$parts = explode(':', $name, -1);
|
||||
$namespaces = [];
|
||||
|
||||
foreach (explode(':', $name, -1) as $part) {
|
||||
foreach ($parts as $part) {
|
||||
if (count($namespaces)) {
|
||||
$namespaces[] = end($namespaces) . ':' . $part;
|
||||
} else {
|
||||
@@ -860,4 +819,11 @@ class Console
|
||||
return $namespaces;
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['commands'], $data['definition']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,74 +14,108 @@ namespace think;
|
||||
use think\exception\ValidateException;
|
||||
use traits\controller\Jump;
|
||||
|
||||
Loader::import('controller/Jump', TRAIT_PATH, EXT);
|
||||
|
||||
class Controller
|
||||
{
|
||||
use Jump;
|
||||
|
||||
/**
|
||||
* @var \think\View 视图类实例
|
||||
* 视图类实例
|
||||
* @var \think\View
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var \think\Request Request 实例
|
||||
* Request实例
|
||||
* @var \think\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* @var bool 验证失败是否抛出异常
|
||||
* 验证失败是否抛出异常
|
||||
* @var bool
|
||||
*/
|
||||
protected $failException = false;
|
||||
|
||||
/**
|
||||
* @var bool 是否批量验证
|
||||
* 是否批量验证
|
||||
* @var bool
|
||||
*/
|
||||
protected $batchValidate = false;
|
||||
|
||||
/**
|
||||
* @var array 前置操作方法列表
|
||||
* 前置操作方法列表(即将废弃)
|
||||
* @var array $beforeActionList
|
||||
*/
|
||||
protected $beforeActionList = [];
|
||||
|
||||
/**
|
||||
* 控制器中间件
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [];
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @access public
|
||||
* @param Request $request Request 对象
|
||||
*/
|
||||
public function __construct(Request $request = null)
|
||||
public function __construct(App $app = null)
|
||||
{
|
||||
$this->view = View::instance(Config::get('template'), Config::get('view_replace_str'));
|
||||
$this->request = is_null($request) ? Request::instance() : $request;
|
||||
$this->app = $app ?: Container::get('app');
|
||||
$this->request = $this->app['request'];
|
||||
$this->view = $this->app['view'];
|
||||
|
||||
// 控制器初始化
|
||||
$this->_initialize();
|
||||
$this->initialize();
|
||||
|
||||
// 前置操作方法
|
||||
if ($this->beforeActionList) {
|
||||
foreach ($this->beforeActionList as $method => $options) {
|
||||
is_numeric($method) ?
|
||||
$this->beforeAction($options) :
|
||||
$this->beforeAction($method, $options);
|
||||
}
|
||||
$this->registerMiddleware();
|
||||
|
||||
// 前置操作方法 即将废弃
|
||||
foreach ((array) $this->beforeActionList as $method => $options) {
|
||||
is_numeric($method) ?
|
||||
$this->beforeAction($options) :
|
||||
$this->beforeAction($method, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化操作
|
||||
* @access protected
|
||||
*/
|
||||
protected function _initialize()
|
||||
// 初始化
|
||||
protected function initialize()
|
||||
{}
|
||||
|
||||
// 注册控制器中间件
|
||||
public function registerMiddleware()
|
||||
{
|
||||
foreach ($this->middleware as $key => $val) {
|
||||
if (!is_int($key)) {
|
||||
$only = $except = null;
|
||||
|
||||
if (isset($val['only'])) {
|
||||
$only = array_map(function ($item) {
|
||||
return strtolower($item);
|
||||
}, $val['only']);
|
||||
} elseif (isset($val['except'])) {
|
||||
$except = array_map(function ($item) {
|
||||
return strtolower($item);
|
||||
}, $val['except']);
|
||||
}
|
||||
|
||||
if (isset($only) && !in_array($this->request->action(), $only)) {
|
||||
continue;
|
||||
} elseif (isset($except) && in_array($this->request->action(), $except)) {
|
||||
continue;
|
||||
} else {
|
||||
$val = $key;
|
||||
}
|
||||
}
|
||||
|
||||
$this->app['middleware']->controller($val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 前置操作
|
||||
* @access protected
|
||||
* @param string $method 前置操作方法名
|
||||
* @param array $options 调用参数 ['only'=>[...]] 或者 ['except'=>[...]]
|
||||
* @return void
|
||||
* @param array $options 调用参数 ['only'=>[...]] 或者['except'=>[...]]
|
||||
*/
|
||||
protected function beforeAction($method, $options = [])
|
||||
{
|
||||
@@ -90,7 +124,11 @@ class Controller
|
||||
$options['only'] = explode(',', $options['only']);
|
||||
}
|
||||
|
||||
if (!in_array($this->request->action(), $options['only'])) {
|
||||
$only = array_map(function ($val) {
|
||||
return strtolower($val);
|
||||
}, $options['only']);
|
||||
|
||||
if (!in_array($this->request->action(), $only)) {
|
||||
return;
|
||||
}
|
||||
} elseif (isset($options['except'])) {
|
||||
@@ -98,7 +136,11 @@ class Controller
|
||||
$options['except'] = explode(',', $options['except']);
|
||||
}
|
||||
|
||||
if (in_array($this->request->action(), $options['except'])) {
|
||||
$except = array_map(function ($val) {
|
||||
return strtolower($val);
|
||||
}, $options['except']);
|
||||
|
||||
if (in_array($this->request->action(), $except)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -111,13 +153,12 @@ class Controller
|
||||
* @access protected
|
||||
* @param string $template 模板文件名
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $replace 模板替换
|
||||
* @param array $config 模板参数
|
||||
* @return mixed
|
||||
*/
|
||||
protected function fetch($template = '', $vars = [], $replace = [], $config = [])
|
||||
protected function fetch($template = '', $vars = [], $config = [])
|
||||
{
|
||||
return $this->view->fetch($template, $vars, $replace, $config);
|
||||
return Response::create($template, 'view')->assign($vars)->config($config);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,13 +166,12 @@ class Controller
|
||||
* @access protected
|
||||
* @param string $content 模板内容
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $replace 替换内容
|
||||
* @param array $config 模板参数
|
||||
* @return mixed
|
||||
*/
|
||||
protected function display($content = '', $vars = [], $replace = [], $config = [])
|
||||
protected function display($content = '', $vars = [], $config = [])
|
||||
{
|
||||
return $this->view->display($content, $vars, $replace, $config);
|
||||
return Response::create($content, 'view')->assign($vars)->config($config)->isContent(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,10 +188,23 @@ class Controller
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 视图过滤
|
||||
* @access protected
|
||||
* @param Callable $filter 过滤方法或闭包
|
||||
* @return $this
|
||||
*/
|
||||
protected function filter($filter)
|
||||
{
|
||||
$this->view->filter($filter);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化模板引擎
|
||||
* @access protected
|
||||
* @param array|string $engine 引擎参数
|
||||
* @param array|string $engine 引擎参数
|
||||
* @return $this
|
||||
*/
|
||||
protected function engine($engine)
|
||||
@@ -164,7 +217,7 @@ class Controller
|
||||
/**
|
||||
* 设置验证失败后是否抛出异常
|
||||
* @access protected
|
||||
* @param bool $fail 是否抛出异常
|
||||
* @param bool $fail 是否抛出异常
|
||||
* @return $this
|
||||
*/
|
||||
protected function validateFailException($fail = true)
|
||||
@@ -188,30 +241,28 @@ class Controller
|
||||
protected function validate($data, $validate, $message = [], $batch = false, $callback = null)
|
||||
{
|
||||
if (is_array($validate)) {
|
||||
$v = Loader::validate();
|
||||
$v = $this->app->validate();
|
||||
$v->rule($validate);
|
||||
} else {
|
||||
// 支持场景
|
||||
if (strpos($validate, '.')) {
|
||||
// 支持场景
|
||||
list($validate, $scene) = explode('.', $validate);
|
||||
}
|
||||
|
||||
$v = Loader::validate($validate);
|
||||
|
||||
!empty($scene) && $v->scene($scene);
|
||||
$v = $this->app->validate($validate);
|
||||
if (!empty($scene)) {
|
||||
$v->scene($scene);
|
||||
}
|
||||
}
|
||||
|
||||
// 批量验证
|
||||
// 是否批量验证
|
||||
if ($batch || $this->batchValidate) {
|
||||
$v->batch(true);
|
||||
}
|
||||
|
||||
// 设置错误信息
|
||||
if (is_array($message)) {
|
||||
$v->message($message);
|
||||
}
|
||||
|
||||
// 使用回调验证
|
||||
if ($callback && is_callable($callback)) {
|
||||
call_user_func_array($callback, [$v, &$data]);
|
||||
}
|
||||
@@ -220,10 +271,17 @@ class Controller
|
||||
if ($this->failException) {
|
||||
throw new ValidateException($v->getError());
|
||||
}
|
||||
|
||||
return $v->getError();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['app'], $data['request']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,71 +14,81 @@ namespace think;
|
||||
class Cookie
|
||||
{
|
||||
/**
|
||||
* @var array cookie 设置参数
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected static $config = [
|
||||
'prefix' => '', // cookie 名称前缀
|
||||
'expire' => 0, // cookie 保存时间
|
||||
'path' => '/', // cookie 保存路径
|
||||
'domain' => '', // cookie 有效域名
|
||||
'secure' => false, // cookie 启用安全传输
|
||||
'httponly' => false, // httponly 设置
|
||||
'setcookie' => true, // 是否使用 setcookie
|
||||
protected $config = [
|
||||
// cookie 名称前缀
|
||||
'prefix' => '',
|
||||
// cookie 保存时间
|
||||
'expire' => 0,
|
||||
// cookie 保存路径
|
||||
'path' => '/',
|
||||
// cookie 有效域名
|
||||
'domain' => '',
|
||||
// cookie 启用安全传输
|
||||
'secure' => false,
|
||||
// httponly设置
|
||||
'httponly' => false,
|
||||
// 是否使用 setcookie
|
||||
'setcookie' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var bool 是否完成初始化了
|
||||
* 构造方法
|
||||
* @access public
|
||||
*/
|
||||
protected static $init;
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$this->init($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie初始化
|
||||
* @access public
|
||||
* @param array $config 配置参数
|
||||
* @param array $config
|
||||
* @return void
|
||||
*/
|
||||
public static function init(array $config = [])
|
||||
public function init(array $config = [])
|
||||
{
|
||||
if (empty($config)) {
|
||||
$config = Config::get('cookie');
|
||||
}
|
||||
$this->config = array_merge($this->config, array_change_key_case($config));
|
||||
|
||||
self::$config = array_merge(self::$config, array_change_key_case($config));
|
||||
|
||||
if (!empty(self::$config['httponly'])) {
|
||||
if (!empty($this->config['httponly']) && PHP_SESSION_ACTIVE != session_status()) {
|
||||
ini_set('session.cookie_httponly', 1);
|
||||
}
|
||||
}
|
||||
|
||||
self::$init = true;
|
||||
public static function __make(Config $config)
|
||||
{
|
||||
return new static($config->pull('cookie'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置或者获取 cookie 作用域(前缀)
|
||||
* 设置或者获取cookie作用域(前缀)
|
||||
* @access public
|
||||
* @param string $prefix 前缀
|
||||
* @return string|
|
||||
* @param string $prefix
|
||||
* @return string|void
|
||||
*/
|
||||
public static function prefix($prefix = '')
|
||||
public function prefix($prefix = '')
|
||||
{
|
||||
if (empty($prefix)) {
|
||||
return self::$config['prefix'];
|
||||
return $this->config['prefix'];
|
||||
}
|
||||
|
||||
return self::$config['prefix'] = $prefix;
|
||||
$this->config['prefix'] = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie 设置、获取、删除
|
||||
*
|
||||
* @access public
|
||||
* @param string $name cookie 名称
|
||||
* @param mixed $value cookie 值
|
||||
* @param string $name cookie名称
|
||||
* @param mixed $value cookie值
|
||||
* @param mixed $option 可选参数 可能会是 null|integer|string
|
||||
* @return void
|
||||
*/
|
||||
public static function set($name, $value = '', $option = null)
|
||||
public function set($name, $value = '', $option = null)
|
||||
{
|
||||
!isset(self::$init) && self::init();
|
||||
|
||||
// 参数设置(会覆盖黙认设置)
|
||||
if (!is_null($option)) {
|
||||
if (is_numeric($option)) {
|
||||
@@ -87,42 +97,51 @@ class Cookie
|
||||
parse_str($option, $option);
|
||||
}
|
||||
|
||||
$config = array_merge(self::$config, array_change_key_case($option));
|
||||
$config = array_merge($this->config, array_change_key_case($option));
|
||||
} else {
|
||||
$config = self::$config;
|
||||
$config = $this->config;
|
||||
}
|
||||
|
||||
$name = $config['prefix'] . $name;
|
||||
|
||||
// 设置 cookie
|
||||
// 设置cookie
|
||||
if (is_array($value)) {
|
||||
array_walk_recursive($value, 'self::jsonFormatProtect', 'encode');
|
||||
array_walk_recursive($value, [$this, 'jsonFormatProtect'], 'encode');
|
||||
$value = 'think:' . json_encode($value);
|
||||
}
|
||||
|
||||
$expire = !empty($config['expire']) ?
|
||||
$_SERVER['REQUEST_TIME'] + intval($config['expire']) :
|
||||
0;
|
||||
$expire = !empty($config['expire']) ? $_SERVER['REQUEST_TIME'] + intval($config['expire']) : 0;
|
||||
|
||||
if ($config['setcookie']) {
|
||||
setcookie(
|
||||
$name, $value, $expire, $config['path'], $config['domain'],
|
||||
$config['secure'], $config['httponly']
|
||||
);
|
||||
$this->setCookie($name, $value, $expire, $config);
|
||||
}
|
||||
|
||||
$_COOKIE[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 永久保存 Cookie 数据
|
||||
* Cookie 设置保存
|
||||
*
|
||||
* @access public
|
||||
* @param string $name cookie 名称
|
||||
* @param mixed $value cookie 值
|
||||
* @param string $name cookie名称
|
||||
* @param mixed $value cookie值
|
||||
* @param array $option 可选参数
|
||||
* @return void
|
||||
*/
|
||||
protected function setCookie($name, $value, $expire, $option = [])
|
||||
{
|
||||
setcookie($name, $value, $expire, $option['path'], $option['domain'], $option['secure'], $option['httponly']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 永久保存Cookie数据
|
||||
* @access public
|
||||
* @param string $name cookie名称
|
||||
* @param mixed $value cookie值
|
||||
* @param mixed $option 可选参数 可能会是 null|integer|string
|
||||
* @return void
|
||||
*/
|
||||
public static function forever($name, $value = '', $option = null)
|
||||
public function forever($name, $value = '', $option = null)
|
||||
{
|
||||
if (is_null($option) || is_numeric($option)) {
|
||||
$option = [];
|
||||
@@ -130,49 +149,43 @@ class Cookie
|
||||
|
||||
$option['expire'] = 315360000;
|
||||
|
||||
self::set($name, $value, $option);
|
||||
$this->set($name, $value, $option);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否有 Cookie 数据
|
||||
* 判断Cookie数据
|
||||
* @access public
|
||||
* @param string $name cookie 名称
|
||||
* @param string|null $prefix cookie 前缀
|
||||
* @param string $name cookie名称
|
||||
* @param string|null $prefix cookie前缀
|
||||
* @return bool
|
||||
*/
|
||||
public static function has($name, $prefix = null)
|
||||
public function has($name, $prefix = null)
|
||||
{
|
||||
!isset(self::$init) && self::init();
|
||||
$prefix = !is_null($prefix) ? $prefix : $this->config['prefix'];
|
||||
$name = $prefix . $name;
|
||||
|
||||
$prefix = !is_null($prefix) ? $prefix : self::$config['prefix'];
|
||||
|
||||
return isset($_COOKIE[$prefix . $name]);
|
||||
return isset($_COOKIE[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Cookie 的值
|
||||
* Cookie获取
|
||||
* @access public
|
||||
* @param string $name cookie 名称
|
||||
* @param string|null $prefix cookie 前缀
|
||||
* @param string $name cookie名称 留空获取全部
|
||||
* @param string|null $prefix cookie前缀
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($name = '', $prefix = null)
|
||||
public function get($name = '', $prefix = null)
|
||||
{
|
||||
!isset(self::$init) && self::init();
|
||||
|
||||
$prefix = !is_null($prefix) ? $prefix : self::$config['prefix'];
|
||||
$prefix = !is_null($prefix) ? $prefix : $this->config['prefix'];
|
||||
$key = $prefix . $name;
|
||||
|
||||
if ('' == $name) {
|
||||
// 获取全部
|
||||
if ($prefix) {
|
||||
$value = [];
|
||||
|
||||
foreach ($_COOKIE as $k => $val) {
|
||||
if (0 === strpos($k, $prefix)) {
|
||||
$value[$k] = $val;
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
$value = $_COOKIE;
|
||||
@@ -181,8 +194,9 @@ class Cookie
|
||||
$value = $_COOKIE[$key];
|
||||
|
||||
if (0 === strpos($value, 'think:')) {
|
||||
$value = json_decode(substr($value, 6), true);
|
||||
array_walk_recursive($value, 'self::jsonFormatProtect', 'decode');
|
||||
$value = substr($value, 6);
|
||||
$value = json_decode($value, true);
|
||||
array_walk_recursive($value, [$this, 'jsonFormatProtect'], 'decode');
|
||||
}
|
||||
} else {
|
||||
$value = null;
|
||||
@@ -192,77 +206,63 @@ class Cookie
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 Cookie
|
||||
* Cookie删除
|
||||
* @access public
|
||||
* @param string $name cookie 名称
|
||||
* @param string|null $prefix cookie 前缀
|
||||
* @param string $name cookie名称
|
||||
* @param string|null $prefix cookie前缀
|
||||
* @return void
|
||||
*/
|
||||
public static function delete($name, $prefix = null)
|
||||
public function delete($name, $prefix = null)
|
||||
{
|
||||
!isset(self::$init) && self::init();
|
||||
|
||||
$config = self::$config;
|
||||
$config = $this->config;
|
||||
$prefix = !is_null($prefix) ? $prefix : $config['prefix'];
|
||||
$name = $prefix . $name;
|
||||
|
||||
if ($config['setcookie']) {
|
||||
setcookie(
|
||||
$name, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'],
|
||||
$config['domain'], $config['secure'], $config['httponly']
|
||||
);
|
||||
$this->setcookie($name, '', $_SERVER['REQUEST_TIME'] - 3600, $config);
|
||||
}
|
||||
|
||||
// 删除指定 cookie
|
||||
// 删除指定cookie
|
||||
unset($_COOKIE[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定前缀的所有 cookie
|
||||
* Cookie清空
|
||||
* @access public
|
||||
* @param string|null $prefix cookie 前缀
|
||||
* @param string|null $prefix cookie前缀
|
||||
* @return void
|
||||
*/
|
||||
public static function clear($prefix = null)
|
||||
public function clear($prefix = null)
|
||||
{
|
||||
// 清除指定前缀的所有cookie
|
||||
if (empty($_COOKIE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
!isset(self::$init) && self::init();
|
||||
|
||||
// 要删除的 cookie 前缀,不指定则删除 config 设置的指定前缀
|
||||
$config = self::$config;
|
||||
// 要删除的cookie前缀,不指定则删除config设置的指定前缀
|
||||
$config = $this->config;
|
||||
$prefix = !is_null($prefix) ? $prefix : $config['prefix'];
|
||||
|
||||
if ($prefix) {
|
||||
// 如果前缀为空字符串将不作处理直接返回
|
||||
foreach ($_COOKIE as $key => $val) {
|
||||
if (0 === strpos($key, $prefix)) {
|
||||
if ($config['setcookie']) {
|
||||
setcookie(
|
||||
$key, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'],
|
||||
$config['domain'], $config['secure'], $config['httponly']
|
||||
);
|
||||
$this->setcookie($key, '', $_SERVER['REQUEST_TIME'] - 3600, $config);
|
||||
}
|
||||
|
||||
unset($_COOKIE[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* json 转换时的格式保护
|
||||
* @access protected
|
||||
* @param mixed $val 要转换的值
|
||||
* @param string $key 键名
|
||||
* @param string $type 转换类别
|
||||
* @return void
|
||||
*/
|
||||
protected static function jsonFormatProtect(&$val, $key, $type = 'encode')
|
||||
private function jsonFormatProtect(&$val, $key, $type = 'encode')
|
||||
{
|
||||
if (!empty($val) && true !== $val) {
|
||||
$val = 'decode' == $type ? urldecode($val) : urlencode($val);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,22 +12,31 @@
|
||||
namespace think;
|
||||
|
||||
use think\db\Connection;
|
||||
use think\db\Query;
|
||||
|
||||
/**
|
||||
* Class Db
|
||||
* @package think
|
||||
* @method Query table(string $table) static 指定数据表(含前缀)
|
||||
* @method Query name(string $name) static 指定数据表(不含前缀)
|
||||
* @method Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件
|
||||
* @method Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询
|
||||
* @method Query union(mixed $union, boolean $all = false) static UNION查询
|
||||
* @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT
|
||||
* @method Query order(mixed $field, string $order = null) static 查询ORDER
|
||||
* @method Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存
|
||||
* @method \think\db\Query master() static 从主服务器读取数据
|
||||
* @method \think\db\Query readMaster(bool $all = false) static 后续从主服务器读取数据
|
||||
* @method \think\db\Query table(string $table) static 指定数据表(含前缀)
|
||||
* @method \think\db\Query name(string $name) static 指定数据表(不含前缀)
|
||||
* @method \think\db\Expression raw(string $value) static 使用表达式设置数据
|
||||
* @method \think\db\Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件
|
||||
* @method \think\db\Query whereRaw(string $where, array $bind = []) static 表达式查询
|
||||
* @method \think\db\Query whereExp(string $field, string $condition, array $bind = []) static 字段表达式查询
|
||||
* @method \think\db\Query when(mixed $condition, mixed $query, mixed $otherwise = null) static 条件查询
|
||||
* @method \think\db\Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询
|
||||
* @method \think\db\Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询
|
||||
* @method \think\db\Query field(mixed $field, boolean $except = false) static 指定查询字段
|
||||
* @method \think\db\Query fieldRaw(string $field, array $bind = []) static 指定查询字段
|
||||
* @method \think\db\Query union(mixed $union, boolean $all = false) static UNION查询
|
||||
* @method \think\db\Query limit(mixed $offset, integer $length = null) static 查询LIMIT
|
||||
* @method \think\db\Query order(mixed $field, string $order = null) static 查询ORDER
|
||||
* @method \think\db\Query orderRaw(string $field, array $bind = []) static 查询ORDER
|
||||
* @method \think\db\Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存
|
||||
* @method \think\db\Query withAttr(string $name,callable $callback = null) static 使用获取器获取数据
|
||||
* @method mixed value(string $field) static 获取某个字段的值
|
||||
* @method array column(string $field, string $key = '') static 获取某个列的值
|
||||
* @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询
|
||||
* @method mixed find(mixed $data = null) static 查询单个记录
|
||||
* @method mixed select(mixed $data = null) static 查询多个记录
|
||||
* @method integer insert(array $data, boolean $replace = false, boolean $getLastInsID = false, string $sequence = null) static 插入一条记录
|
||||
@@ -36,110 +45,125 @@ use think\db\Query;
|
||||
* @method integer update(array $data) static 更新记录
|
||||
* @method integer delete(mixed $data = null) static 删除记录
|
||||
* @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据
|
||||
* @method \Generator cursor(mixed $data = null) static 使用游标查找记录
|
||||
* @method mixed query(string $sql, array $bind = [], boolean $master = false, bool $pdo = false) static SQL查询
|
||||
* @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行
|
||||
* @method Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询
|
||||
* @method \think\Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询
|
||||
* @method mixed transaction(callable $callback) static 执行数据库事务
|
||||
* @method void startTrans() static 启动事务
|
||||
* @method void commit() static 用于非自动提交状态下面的查询提交
|
||||
* @method void rollback() static 事务回滚
|
||||
* @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句
|
||||
* @method string quote(string $str) static SQL指令安全过滤
|
||||
* @method string getLastInsID($sequence = null) static 获取最近插入的ID
|
||||
* @method string getLastInsID(string $sequence = null) static 获取最近插入的ID
|
||||
*/
|
||||
class Db
|
||||
{
|
||||
/**
|
||||
* @var Connection[] 数据库连接实例
|
||||
* 当前数据库连接对象
|
||||
* @var Connection
|
||||
*/
|
||||
private static $instance = [];
|
||||
protected static $connection;
|
||||
|
||||
/**
|
||||
* @var int 查询次数
|
||||
* 数据库配置
|
||||
* @var array
|
||||
*/
|
||||
protected static $config = [];
|
||||
|
||||
/**
|
||||
* 查询次数
|
||||
* @var integer
|
||||
*/
|
||||
public static $queryTimes = 0;
|
||||
|
||||
/**
|
||||
* @var int 执行次数
|
||||
* 执行次数
|
||||
* @var integer
|
||||
*/
|
||||
public static $executeTimes = 0;
|
||||
|
||||
/**
|
||||
* 数据库初始化,并取得数据库类实例
|
||||
* 配置
|
||||
* @access public
|
||||
* @param mixed $config 连接配置
|
||||
* @param bool|string $name 连接标识 true 强制重新连接
|
||||
* @return Connection
|
||||
* @throws Exception
|
||||
* @param mixed $config
|
||||
* @return void
|
||||
*/
|
||||
public static function connect($config = [], $name = false)
|
||||
public static function init($config = [])
|
||||
{
|
||||
if (false === $name) {
|
||||
$name = md5(serialize($config));
|
||||
self::$config = $config;
|
||||
|
||||
if (empty($config['query'])) {
|
||||
self::$config['query'] = '\\think\\db\\Query';
|
||||
}
|
||||
|
||||
if (true === $name || !isset(self::$instance[$name])) {
|
||||
// 解析连接参数 支持数组和字符串
|
||||
$options = self::parseConfig($config);
|
||||
|
||||
if (empty($options['type'])) {
|
||||
throw new \InvalidArgumentException('Undefined db type');
|
||||
}
|
||||
|
||||
$class = false !== strpos($options['type'], '\\') ?
|
||||
$options['type'] :
|
||||
'\\think\\db\\connector\\' . ucwords($options['type']);
|
||||
|
||||
// 记录初始化信息
|
||||
if (App::$debug) {
|
||||
Log::record('[ DB ] INIT ' . $options['type'], 'info');
|
||||
}
|
||||
|
||||
if (true === $name) {
|
||||
$name = md5(serialize($config));
|
||||
}
|
||||
|
||||
self::$instance[$name] = new $class($options);
|
||||
}
|
||||
|
||||
return self::$instance[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除连接实例
|
||||
* 获取数据库配置
|
||||
* @access public
|
||||
* @return void
|
||||
* @param string $config 配置名称
|
||||
* @return mixed
|
||||
*/
|
||||
public static function clear()
|
||||
public static function getConfig($name = '')
|
||||
{
|
||||
self::$instance = [];
|
||||
if ('' === $name) {
|
||||
return self::$config;
|
||||
}
|
||||
|
||||
return isset(self::$config[$name]) ? self::$config[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换数据库连接
|
||||
* @access public
|
||||
* @param mixed $config 连接配置
|
||||
* @param bool|string $name 连接标识 true 强制重新连接
|
||||
* @param string $query 查询对象类名
|
||||
* @return mixed 返回查询对象实例
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function connect($config = [], $name = false, $query = '')
|
||||
{
|
||||
// 解析配置参数
|
||||
$options = self::parseConfig($config ?: self::$config);
|
||||
|
||||
$query = $query ?: $options['query'];
|
||||
|
||||
// 创建数据库连接对象实例
|
||||
self::$connection = Connection::instance($options, $name);
|
||||
|
||||
return new $query(self::$connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库连接参数解析
|
||||
* @access private
|
||||
* @param mixed $config 连接参数
|
||||
* @param mixed $config
|
||||
* @return array
|
||||
*/
|
||||
private static function parseConfig($config)
|
||||
{
|
||||
if (empty($config)) {
|
||||
$config = Config::get('database');
|
||||
} elseif (is_string($config) && false === strpos($config, '/')) {
|
||||
$config = Config::get($config); // 支持读取配置参数
|
||||
if (is_string($config) && false === strpos($config, '/')) {
|
||||
// 支持读取配置参数
|
||||
$config = isset(self::$config[$config]) ? self::$config[$config] : self::$config;
|
||||
}
|
||||
|
||||
return is_string($config) ? self::parseDsn($config) : $config;
|
||||
$result = is_string($config) ? self::parseDsnConfig($config) : $config;
|
||||
|
||||
if (empty($result['query'])) {
|
||||
$result['query'] = self::$config['query'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* DSN 解析
|
||||
* DSN解析
|
||||
* 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1¶m2=val2#utf8
|
||||
* @access private
|
||||
* @param string $dsnStr 数据库 DSN 字符串解析
|
||||
* @param string $dsnStr
|
||||
* @return array
|
||||
*/
|
||||
private static function parseDsn($dsnStr)
|
||||
private static function parseDsnConfig($dsnStr)
|
||||
{
|
||||
$info = parse_url($dsnStr);
|
||||
|
||||
@@ -166,15 +190,8 @@ class Db
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用驱动类的方法
|
||||
* @access public
|
||||
* @param string $method 方法名
|
||||
* @param array $params 参数
|
||||
* @return mixed
|
||||
*/
|
||||
public static function __callStatic($method, $params)
|
||||
public static function __callStatic($method, $args)
|
||||
{
|
||||
return call_user_func_array([self::connect(), $method], $params);
|
||||
return call_user_func_array([static::connect(), $method], $args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,64 +11,95 @@
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\exception\ClassNotFoundException;
|
||||
use think\model\Collection as ModelCollection;
|
||||
use think\response\Redirect;
|
||||
|
||||
class Debug
|
||||
{
|
||||
/**
|
||||
* @var array 区间时间信息
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected static $info = [];
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* @var array 区间内存信息
|
||||
* 区间时间信息
|
||||
* @var array
|
||||
*/
|
||||
protected static $mem = [];
|
||||
protected $info = [];
|
||||
|
||||
/**
|
||||
* 区间内存信息
|
||||
* @var array
|
||||
*/
|
||||
protected $mem = [];
|
||||
|
||||
/**
|
||||
* 应用对象
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
public function __construct(App $app, array $config = [])
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public static function __make(App $app, Config $config)
|
||||
{
|
||||
return new static($app, $config->pull('trace'));
|
||||
}
|
||||
|
||||
public function setConfig(array $config)
|
||||
{
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录时间(微秒)和内存使用情况
|
||||
* @access public
|
||||
* @param string $name 标记位置
|
||||
* @param mixed $value 标记值(留空则取当前 time 表示仅记录时间 否则同时记录时间和内存)
|
||||
* @param string $name 标记位置
|
||||
* @param mixed $value 标记值 留空则取当前 time 表示仅记录时间 否则同时记录时间和内存
|
||||
* @return void
|
||||
*/
|
||||
public static function remark($name, $value = '')
|
||||
public function remark($name, $value = '')
|
||||
{
|
||||
self::$info[$name] = is_float($value) ? $value : microtime(true);
|
||||
// 记录时间和内存使用
|
||||
$this->info[$name] = is_float($value) ? $value : microtime(true);
|
||||
|
||||
if ('time' != $value) {
|
||||
self::$mem['mem'][$name] = is_float($value) ? $value : memory_get_usage();
|
||||
self::$mem['peak'][$name] = memory_get_peak_usage();
|
||||
$this->mem['mem'][$name] = is_float($value) ? $value : memory_get_usage();
|
||||
$this->mem['peak'][$name] = memory_get_peak_usage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计某个区间的时间(微秒)使用情况 返回值以秒为单位
|
||||
* 统计某个区间的时间(微秒)使用情况
|
||||
* @access public
|
||||
* @param string $start 开始标签
|
||||
* @param string $end 结束标签
|
||||
* @param integer $dec 小数位
|
||||
* @return string
|
||||
* @param string $start 开始标签
|
||||
* @param string $end 结束标签
|
||||
* @param integer|string $dec 小数位
|
||||
* @return integer
|
||||
*/
|
||||
public static function getRangeTime($start, $end, $dec = 6)
|
||||
public function getRangeTime($start, $end, $dec = 6)
|
||||
{
|
||||
if (!isset(self::$info[$end])) {
|
||||
self::$info[$end] = microtime(true);
|
||||
if (!isset($this->info[$end])) {
|
||||
$this->info[$end] = microtime(true);
|
||||
}
|
||||
|
||||
return number_format((self::$info[$end] - self::$info[$start]), $dec);
|
||||
return number_format(($this->info[$end] - $this->info[$start]), $dec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计从开始到统计时的时间(微秒)使用情况 返回值以秒为单位
|
||||
* 统计从开始到统计时的时间(微秒)使用情况
|
||||
* @access public
|
||||
* @param integer $dec 小数位
|
||||
* @return string
|
||||
* @param integer|string $dec 小数位
|
||||
* @return integer
|
||||
*/
|
||||
public static function getUseTime($dec = 6)
|
||||
public function getUseTime($dec = 6)
|
||||
{
|
||||
return number_format((microtime(true) - THINK_START_TIME), $dec);
|
||||
return number_format((microtime(true) - $this->app->getBeginTime()), $dec);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,26 +107,26 @@ class Debug
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public static function getThroughputRate()
|
||||
public function getThroughputRate()
|
||||
{
|
||||
return number_format(1 / self::getUseTime(), 2) . 'req/s';
|
||||
return number_format(1 / $this->getUseTime(), 2) . 'req/s';
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录区间的内存使用情况
|
||||
* @access public
|
||||
* @param string $start 开始标签
|
||||
* @param string $end 结束标签
|
||||
* @param integer $dec 小数位
|
||||
* @param string $start 开始标签
|
||||
* @param string $end 结束标签
|
||||
* @param integer|string $dec 小数位
|
||||
* @return string
|
||||
*/
|
||||
public static function getRangeMem($start, $end, $dec = 2)
|
||||
public function getRangeMem($start, $end, $dec = 2)
|
||||
{
|
||||
if (!isset(self::$mem['mem'][$end])) {
|
||||
self::$mem['mem'][$end] = memory_get_usage();
|
||||
if (!isset($this->mem['mem'][$end])) {
|
||||
$this->mem['mem'][$end] = memory_get_usage();
|
||||
}
|
||||
|
||||
$size = self::$mem['mem'][$end] - self::$mem['mem'][$start];
|
||||
$size = $this->mem['mem'][$end] - $this->mem['mem'][$start];
|
||||
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
$pos = 0;
|
||||
|
||||
@@ -110,12 +141,12 @@ class Debug
|
||||
/**
|
||||
* 统计从开始到统计时的内存使用情况
|
||||
* @access public
|
||||
* @param integer $dec 小数位
|
||||
* @param integer|string $dec 小数位
|
||||
* @return string
|
||||
*/
|
||||
public static function getUseMem($dec = 2)
|
||||
public function getUseMem($dec = 2)
|
||||
{
|
||||
$size = memory_get_usage() - THINK_START_MEM;
|
||||
$size = memory_get_usage() - $this->app->getBeginMem();
|
||||
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
$pos = 0;
|
||||
|
||||
@@ -130,18 +161,18 @@ class Debug
|
||||
/**
|
||||
* 统计区间的内存峰值情况
|
||||
* @access public
|
||||
* @param string $start 开始标签
|
||||
* @param string $end 结束标签
|
||||
* @param integer $dec 小数位
|
||||
* @param string $start 开始标签
|
||||
* @param string $end 结束标签
|
||||
* @param integer|string $dec 小数位
|
||||
* @return string
|
||||
*/
|
||||
public static function getMemPeak($start, $end, $dec = 2)
|
||||
public function getMemPeak($start, $end, $dec = 2)
|
||||
{
|
||||
if (!isset(self::$mem['peak'][$end])) {
|
||||
self::$mem['peak'][$end] = memory_get_peak_usage();
|
||||
if (!isset($this->mem['peak'][$end])) {
|
||||
$this->mem['peak'][$end] = memory_get_peak_usage();
|
||||
}
|
||||
|
||||
$size = self::$mem['peak'][$end] - self::$mem['peak'][$start];
|
||||
$size = $this->mem['peak'][$end] - $this->mem['peak'][$start];
|
||||
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
$pos = 0;
|
||||
|
||||
@@ -156,90 +187,77 @@ class Debug
|
||||
/**
|
||||
* 获取文件加载信息
|
||||
* @access public
|
||||
* @param bool $detail 是否显示详细
|
||||
* @param bool $detail 是否显示详细
|
||||
* @return integer|array
|
||||
*/
|
||||
public static function getFile($detail = false)
|
||||
public function getFile($detail = false)
|
||||
{
|
||||
$files = get_included_files();
|
||||
|
||||
if ($detail) {
|
||||
$info = [];
|
||||
$files = get_included_files();
|
||||
$info = [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
foreach ($files as $key => $file) {
|
||||
$info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )';
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
return count($files);
|
||||
return count(get_included_files());
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览器友好的变量输出
|
||||
* @access public
|
||||
* @param mixed $var 变量
|
||||
* @param boolean $echo 是否输出(默认为 true,为 false 则返回输出字符串)
|
||||
* @param string|null $label 标签(默认为空)
|
||||
* @param integer $flags htmlspecialchars 的标志
|
||||
* @return null|string
|
||||
* @param mixed $var 变量
|
||||
* @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串
|
||||
* @param string $label 标签 默认为空
|
||||
* @param integer $flags htmlspecialchars flags
|
||||
* @return void|string
|
||||
*/
|
||||
public static function dump($var, $echo = true, $label = null, $flags = ENT_SUBSTITUTE)
|
||||
public function dump($var, $echo = true, $label = null, $flags = ENT_SUBSTITUTE)
|
||||
{
|
||||
$label = (null === $label) ? '' : rtrim($label) . ':';
|
||||
if ($var instanceof Model || $var instanceof ModelCollection) {
|
||||
$var = $var->toArray();
|
||||
}
|
||||
|
||||
ob_start();
|
||||
var_dump($var);
|
||||
$output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', ob_get_clean());
|
||||
|
||||
if (IS_CLI) {
|
||||
$output = ob_get_clean();
|
||||
$output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output);
|
||||
|
||||
if (PHP_SAPI == 'cli') {
|
||||
$output = PHP_EOL . $label . $output . PHP_EOL;
|
||||
} else {
|
||||
if (!extension_loaded('xdebug')) {
|
||||
$output = htmlspecialchars($output, $flags);
|
||||
}
|
||||
|
||||
$output = '<pre>' . $label . $output . '</pre>';
|
||||
}
|
||||
|
||||
if ($echo) {
|
||||
echo($output);
|
||||
return;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试信息注入到响应中
|
||||
* @access public
|
||||
* @param Response $response 响应实例
|
||||
* @param string $content 返回的字符串
|
||||
* @return void
|
||||
*/
|
||||
public static function inject(Response $response, &$content)
|
||||
public function inject(Response $response, &$content)
|
||||
{
|
||||
$config = Config::get('trace');
|
||||
$config = $this->config;
|
||||
$type = isset($config['type']) ? $config['type'] : 'Html';
|
||||
$class = false !== strpos($type, '\\') ? $type : '\\think\\debug\\' . ucwords($type);
|
||||
|
||||
unset($config['type']);
|
||||
|
||||
if (!class_exists($class)) {
|
||||
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
||||
}
|
||||
|
||||
/** @var \think\debug\Console|\think\debug\Html $trace */
|
||||
$trace = new $class($config);
|
||||
$trace = Loader::factory($type, '\\think\\debug\\', $config);
|
||||
|
||||
if ($response instanceof Redirect) {
|
||||
// TODO 记录
|
||||
//TODO 记录
|
||||
} else {
|
||||
$output = $trace->output($response, Log::getLog());
|
||||
|
||||
$output = $trace->output($response, $this->app['log']->getLog());
|
||||
if (is_string($output)) {
|
||||
// trace 调试信息注入
|
||||
// trace调试信息注入
|
||||
$pos = strripos($content, '</body>');
|
||||
if (false !== $pos) {
|
||||
$content = substr($content, 0, $pos) . $output . substr($content, $pos);
|
||||
@@ -249,4 +267,12 @@ class Debug
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['app']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,27 +13,101 @@ namespace think;
|
||||
|
||||
class Env
|
||||
{
|
||||
/**
|
||||
* 环境变量数据
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->data = $_ENV;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取环境变量定义文件
|
||||
* @access public
|
||||
* @param string $file 环境变量定义文件
|
||||
* @return void
|
||||
*/
|
||||
public function load($file)
|
||||
{
|
||||
$env = parse_ini_file($file, true);
|
||||
$this->set($env);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取环境变量值
|
||||
* @access public
|
||||
* @param string $name 环境变量名(支持二级 . 号分割)
|
||||
* @param string $default 默认值
|
||||
* @param string $name 环境变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($name, $default = null)
|
||||
public function get($name = null, $default = null, $php_prefix = true)
|
||||
{
|
||||
$result = getenv(ENV_PREFIX . strtoupper(str_replace('.', '_', $name)));
|
||||
|
||||
if (false !== $result) {
|
||||
if ('false' === $result) {
|
||||
$result = false;
|
||||
} elseif ('true' === $result) {
|
||||
$result = true;
|
||||
}
|
||||
|
||||
return $result;
|
||||
if (is_null($name)) {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
return $default;
|
||||
$name = strtoupper(str_replace('.', '_', $name));
|
||||
|
||||
if (isset($this->data[$name])) {
|
||||
return $this->data[$name];
|
||||
}
|
||||
|
||||
return $this->getEnv($name, $default, $php_prefix);
|
||||
}
|
||||
|
||||
protected function getEnv($name, $default = null, $php_prefix = true)
|
||||
{
|
||||
if ($php_prefix) {
|
||||
$name = 'PHP_' . $name;
|
||||
}
|
||||
|
||||
$result = getenv($name);
|
||||
|
||||
if (false === $result) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if ('false' === $result) {
|
||||
$result = false;
|
||||
} elseif ('true' === $result) {
|
||||
$result = true;
|
||||
}
|
||||
|
||||
if (!isset($this->data[$name])) {
|
||||
$this->data[$name] = $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置环境变量值
|
||||
* @access public
|
||||
* @param string|array $env 环境变量
|
||||
* @param mixed $value 值
|
||||
* @return void
|
||||
*/
|
||||
public function set($env, $value = null)
|
||||
{
|
||||
if (is_array($env)) {
|
||||
$env = array_change_key_case($env, CASE_UPPER);
|
||||
|
||||
foreach ($env as $key => $val) {
|
||||
if (is_array($val)) {
|
||||
foreach ($val as $k => $v) {
|
||||
$this->data[$key . '_' . strtoupper($k)] = $v;
|
||||
}
|
||||
} else {
|
||||
$this->data[$key] = $val;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$name = strtoupper(str_replace('.', '_', $env));
|
||||
|
||||
$this->data[$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,12 @@ use think\exception\ThrowableError;
|
||||
|
||||
class Error
|
||||
{
|
||||
/**
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected static $exceptionHandler;
|
||||
|
||||
/**
|
||||
* 注册异常处理
|
||||
* @access public
|
||||
@@ -32,10 +38,9 @@ class Error
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常处理
|
||||
* Exception Handler
|
||||
* @access public
|
||||
* @param \Exception|\Throwable $e 异常
|
||||
* @return void
|
||||
* @param \Exception|\Throwable $e
|
||||
*/
|
||||
public static function appException($e)
|
||||
{
|
||||
@@ -43,32 +48,29 @@ class Error
|
||||
$e = new ThrowableError($e);
|
||||
}
|
||||
|
||||
$handler = self::getExceptionHandler();
|
||||
$handler->report($e);
|
||||
self::getExceptionHandler()->report($e);
|
||||
|
||||
if (IS_CLI) {
|
||||
$handler->renderForConsole(new ConsoleOutput, $e);
|
||||
if (PHP_SAPI == 'cli') {
|
||||
self::getExceptionHandler()->renderForConsole(new ConsoleOutput, $e);
|
||||
} else {
|
||||
$handler->render($e)->send();
|
||||
self::getExceptionHandler()->render($e)->send();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误处理
|
||||
* Error Handler
|
||||
* @access public
|
||||
* @param integer $errno 错误编号
|
||||
* @param integer $errstr 详细错误信息
|
||||
* @param string $errfile 出错的文件
|
||||
* @param integer $errline 出错行号
|
||||
* @return void
|
||||
* @param integer $errno 错误编号
|
||||
* @param integer $errstr 详细错误信息
|
||||
* @param string $errfile 出错的文件
|
||||
* @param integer $errline 出错行号
|
||||
* @throws ErrorException
|
||||
*/
|
||||
public static function appError($errno, $errstr, $errfile = '', $errline = 0)
|
||||
{
|
||||
$exception = new ErrorException($errno, $errstr, $errfile, $errline);
|
||||
|
||||
// 符合异常处理的则将错误信息托管至 think\exception\ErrorException
|
||||
if (error_reporting() & $errno) {
|
||||
// 将错误信息托管至 think\exception\ErrorException
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
@@ -76,27 +78,27 @@ class Error
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常中止处理
|
||||
* Shutdown Handler
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function appShutdown()
|
||||
{
|
||||
// 将错误信息托管至 think\ErrorException
|
||||
if (!is_null($error = error_get_last()) && self::isFatal($error['type'])) {
|
||||
self::appException(new ErrorException(
|
||||
$error['type'], $error['message'], $error['file'], $error['line']
|
||||
));
|
||||
// 将错误信息托管至think\ErrorException
|
||||
$exception = new ErrorException($error['type'], $error['message'], $error['file'], $error['line']);
|
||||
|
||||
self::appException($exception);
|
||||
}
|
||||
|
||||
// 写入日志
|
||||
Log::save();
|
||||
Container::get('log')->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 确定错误类型是否致命
|
||||
*
|
||||
* @access protected
|
||||
* @param int $type 错误类型
|
||||
* @param int $type
|
||||
* @return bool
|
||||
*/
|
||||
protected static function isFatal($type)
|
||||
@@ -105,7 +107,20 @@ class Error
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取异常处理的实例
|
||||
* 设置异常处理类
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $handle
|
||||
* @return void
|
||||
*/
|
||||
public static function setExceptionHandler($handle)
|
||||
{
|
||||
self::$exceptionHandler = $handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the exception handler.
|
||||
*
|
||||
* @access public
|
||||
* @return Handle
|
||||
*/
|
||||
@@ -114,20 +129,16 @@ class Error
|
||||
static $handle;
|
||||
|
||||
if (!$handle) {
|
||||
// 异常处理 handle
|
||||
$class = Config::get('exception_handle');
|
||||
// 异常处理handle
|
||||
$class = self::$exceptionHandler;
|
||||
|
||||
if ($class && is_string($class) && class_exists($class) &&
|
||||
is_subclass_of($class, "\\think\\exception\\Handle")
|
||||
) {
|
||||
if ($class && is_string($class) && class_exists($class) && is_subclass_of($class, "\\think\\exception\\Handle")) {
|
||||
$handle = new $class;
|
||||
} else {
|
||||
$handle = new Handle;
|
||||
|
||||
if ($class instanceof \Closure) {
|
||||
$handle->setRender($class);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,13 +13,15 @@ namespace think;
|
||||
|
||||
class Exception extends \Exception
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array 保存异常页面显示的额外 Debug 数据
|
||||
* 保存异常页面显示的额外Debug数据
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* 设置异常额外的 Debug 数据
|
||||
* 设置异常额外的Debug数据
|
||||
* 数据将会显示为下面的格式
|
||||
*
|
||||
* Exception Data
|
||||
@@ -34,7 +36,6 @@ class Exception extends \Exception
|
||||
* @access protected
|
||||
* @param string $label 数据分类,用于异常页面显示
|
||||
* @param array $data 需要显示的数据,必须为关联数组
|
||||
* @return void
|
||||
*/
|
||||
final protected function setData($label, array $data)
|
||||
{
|
||||
@@ -42,10 +43,10 @@ class Exception extends \Exception
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取异常额外 Debug 数据
|
||||
* 获取异常额外Debug数据
|
||||
* 主要用于输出到异常页面便于调试
|
||||
* @access public
|
||||
* @return array
|
||||
* @return array 由setData设置的Debug数据
|
||||
*/
|
||||
final public function getData()
|
||||
{
|
||||
|
||||
@@ -16,61 +16,64 @@ use SplFileObject;
|
||||
class File extends SplFileObject
|
||||
{
|
||||
/**
|
||||
* @var string 错误信息
|
||||
* 错误信息
|
||||
* @var string
|
||||
*/
|
||||
private $error = '';
|
||||
|
||||
/**
|
||||
* @var string 当前完整文件名
|
||||
* 当前完整文件名
|
||||
* @var string
|
||||
*/
|
||||
protected $filename;
|
||||
|
||||
/**
|
||||
* @var string 上传文件名
|
||||
* 上传文件名
|
||||
* @var string
|
||||
*/
|
||||
protected $saveName;
|
||||
|
||||
/**
|
||||
* @var string 文件上传命名规则
|
||||
* 上传文件命名规则
|
||||
* @var string
|
||||
*/
|
||||
protected $rule = 'date';
|
||||
|
||||
/**
|
||||
* @var array 文件上传验证规则
|
||||
* 上传文件验证规则
|
||||
* @var array
|
||||
*/
|
||||
protected $validate = [];
|
||||
|
||||
/**
|
||||
* @var bool 单元测试
|
||||
* 是否单元测试
|
||||
* @var bool
|
||||
*/
|
||||
protected $isTest;
|
||||
|
||||
/**
|
||||
* @var array 上传文件信息
|
||||
* 上传文件信息
|
||||
* @var array
|
||||
*/
|
||||
protected $info;
|
||||
protected $info = [];
|
||||
|
||||
/**
|
||||
* @var array 文件 hash 信息
|
||||
* 文件hash规则
|
||||
* @var array
|
||||
*/
|
||||
protected $hash = [];
|
||||
|
||||
/**
|
||||
* File constructor.
|
||||
* @access public
|
||||
* @param string $filename 文件名称
|
||||
* @param string $mode 访问模式
|
||||
*/
|
||||
public function __construct($filename, $mode = 'r')
|
||||
{
|
||||
parent::__construct($filename, $mode);
|
||||
|
||||
$this->filename = $this->getRealPath() ?: $this->getPathname();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否是单元测试
|
||||
* 是否测试
|
||||
* @access public
|
||||
* @param bool $test 是否是测试
|
||||
* @param bool $test 是否测试
|
||||
* @return $this
|
||||
*/
|
||||
public function isTest($test = false)
|
||||
@@ -83,7 +86,7 @@ class File extends SplFileObject
|
||||
/**
|
||||
* 设置上传信息
|
||||
* @access public
|
||||
* @param array $info 上传文件信息
|
||||
* @param array $info 上传文件信息
|
||||
* @return $this
|
||||
*/
|
||||
public function setUploadInfo($info)
|
||||
@@ -96,7 +99,7 @@ class File extends SplFileObject
|
||||
/**
|
||||
* 获取上传文件的信息
|
||||
* @access public
|
||||
* @param string $name 信息名称
|
||||
* @param string $name
|
||||
* @return array|string
|
||||
*/
|
||||
public function getInfo($name = '')
|
||||
@@ -117,7 +120,7 @@ class File extends SplFileObject
|
||||
/**
|
||||
* 设置上传文件的保存文件名
|
||||
* @access public
|
||||
* @param string $saveName 保存名称
|
||||
* @param string $saveName
|
||||
* @return $this
|
||||
*/
|
||||
public function setSaveName($saveName)
|
||||
@@ -130,7 +133,7 @@ class File extends SplFileObject
|
||||
/**
|
||||
* 获取文件的哈希散列值
|
||||
* @access public
|
||||
* @param string $type 类型
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
public function hash($type = 'sha1')
|
||||
@@ -145,17 +148,20 @@ class File extends SplFileObject
|
||||
/**
|
||||
* 检查目录是否可写
|
||||
* @access protected
|
||||
* @param string $path 目录
|
||||
* @param string $path 目录
|
||||
* @return boolean
|
||||
*/
|
||||
protected function checkPath($path)
|
||||
{
|
||||
if (is_dir($path) || mkdir($path, 0755, true)) {
|
||||
if (is_dir($path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mkdir($path, 0755, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->error = ['directory {:path} creation failed', ['path' => $path]];
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -174,7 +180,7 @@ class File extends SplFileObject
|
||||
/**
|
||||
* 设置文件的命名规则
|
||||
* @access public
|
||||
* @param string $rule 文件命名规则
|
||||
* @param string $rule 文件命名规则
|
||||
* @return $this
|
||||
*/
|
||||
public function rule($rule)
|
||||
@@ -187,10 +193,10 @@ class File extends SplFileObject
|
||||
/**
|
||||
* 设置上传文件的验证规则
|
||||
* @access public
|
||||
* @param array $rule 验证规则
|
||||
* @param array $rule 验证规则
|
||||
* @return $this
|
||||
*/
|
||||
public function validate(array $rule = [])
|
||||
public function validate($rule = [])
|
||||
{
|
||||
$this->validate = $rule;
|
||||
|
||||
@@ -204,40 +210,27 @@ class File extends SplFileObject
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
return $this->isTest ? is_file($this->filename) : is_uploaded_file($this->filename);
|
||||
if ($this->isTest) {
|
||||
return is_file($this->filename);
|
||||
}
|
||||
|
||||
return is_uploaded_file($this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测上传文件
|
||||
* @access public
|
||||
* @param array $rule 验证规则
|
||||
* @param array $rule 验证规则
|
||||
* @return bool
|
||||
*/
|
||||
public function check($rule = [])
|
||||
{
|
||||
$rule = $rule ?: $this->validate;
|
||||
|
||||
/* 检查文件大小 */
|
||||
if (isset($rule['size']) && !$this->checkSize($rule['size'])) {
|
||||
$this->error = 'filesize not match';
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 检查文件 Mime 类型 */
|
||||
if (isset($rule['type']) && !$this->checkMime($rule['type'])) {
|
||||
$this->error = 'mimetype to upload is not allowed';
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 检查文件后缀 */
|
||||
if (isset($rule['ext']) && !$this->checkExt($rule['ext'])) {
|
||||
$this->error = 'extensions to upload is not allowed';
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 检查图像文件 */
|
||||
if (!$this->checkImg()) {
|
||||
$this->error = 'illegal image files';
|
||||
if ((isset($rule['size']) && !$this->checkSize($rule['size']))
|
||||
|| (isset($rule['type']) && !$this->checkMime($rule['type']))
|
||||
|| (isset($rule['ext']) && !$this->checkExt($rule['ext']))
|
||||
|| !$this->checkImg()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -247,7 +240,7 @@ class File extends SplFileObject
|
||||
/**
|
||||
* 检测上传文件后缀
|
||||
* @access public
|
||||
* @param array|string $ext 允许后缀
|
||||
* @param array|string $ext 允许后缀
|
||||
* @return bool
|
||||
*/
|
||||
public function checkExt($ext)
|
||||
@@ -258,7 +251,12 @@ class File extends SplFileObject
|
||||
|
||||
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
|
||||
|
||||
return in_array($extension, $ext);
|
||||
if (!in_array($extension, $ext)) {
|
||||
$this->error = 'extensions to upload is not allowed';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,16 +268,16 @@ class File extends SplFileObject
|
||||
{
|
||||
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
|
||||
|
||||
// 如果上传的不是图片,或者是图片而且后缀确实符合图片类型则返回 true
|
||||
return !in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) || in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6, 13]);
|
||||
/* 对图像文件进行严格检测 */
|
||||
if (in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) && !in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6, 13])) {
|
||||
$this->error = 'illegal image files';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断图像类型
|
||||
* @access protected
|
||||
* @param string $image 图片名称
|
||||
* @return bool|int
|
||||
*/
|
||||
// 判断图像类型
|
||||
protected function getImageType($image)
|
||||
{
|
||||
if (function_exists('exif_imagetype')) {
|
||||
@@ -297,36 +295,49 @@ class File extends SplFileObject
|
||||
/**
|
||||
* 检测上传文件大小
|
||||
* @access public
|
||||
* @param integer $size 最大大小
|
||||
* @param integer $size 最大大小
|
||||
* @return bool
|
||||
*/
|
||||
public function checkSize($size)
|
||||
{
|
||||
return $this->getSize() <= $size;
|
||||
if ($this->getSize() > (int) $size) {
|
||||
$this->error = 'filesize not match';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测上传文件类型
|
||||
* @access public
|
||||
* @param array|string $mime 允许类型
|
||||
* @param array|string $mime 允许类型
|
||||
* @return bool
|
||||
*/
|
||||
public function checkMime($mime)
|
||||
{
|
||||
$mime = is_string($mime) ? explode(',', $mime) : $mime;
|
||||
if (is_string($mime)) {
|
||||
$mime = explode(',', $mime);
|
||||
}
|
||||
|
||||
return in_array(strtolower($this->getMime()), $mime);
|
||||
if (!in_array(strtolower($this->getMime()), $mime)) {
|
||||
$this->error = 'mimetype to upload is not allowed';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动文件
|
||||
* @access public
|
||||
* @param string $path 保存路径
|
||||
* @param string|bool $savename 保存的文件名 默认自动生成
|
||||
* @param boolean $replace 同名文件是否覆盖
|
||||
* @return false|File
|
||||
* @param string $path 保存路径
|
||||
* @param string|bool $savename 保存的文件名 默认自动生成
|
||||
* @param boolean $replace 同名文件是否覆盖
|
||||
* @param bool $autoAppendExt 自动补充扩展名
|
||||
* @return false|File false-失败 否则返回File实例
|
||||
*/
|
||||
public function move($path, $savename = true, $replace = true)
|
||||
public function move($path, $savename = true, $replace = true, $autoAppendExt = true)
|
||||
{
|
||||
// 文件上传失败,捕获错误代码
|
||||
if (!empty($this->info['error'])) {
|
||||
@@ -345,9 +356,9 @@ class File extends SplFileObject
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = rtrim($path, DS) . DS;
|
||||
$path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
||||
// 文件保存命名规则
|
||||
$saveName = $this->buildSaveName($savename);
|
||||
$saveName = $this->buildSaveName($savename, $autoAppendExt);
|
||||
$filename = $path . $saveName;
|
||||
|
||||
// 检测目录
|
||||
@@ -355,7 +366,7 @@ class File extends SplFileObject
|
||||
return false;
|
||||
}
|
||||
|
||||
// 不覆盖同名文件
|
||||
/* 不覆盖同名文件 */
|
||||
if (!$replace && is_file($filename)) {
|
||||
$this->error = ['has the same filename: {:filename}', ['filename' => $filename]];
|
||||
return false;
|
||||
@@ -369,9 +380,10 @@ class File extends SplFileObject
|
||||
return false;
|
||||
}
|
||||
|
||||
// 返回 File 对象实例
|
||||
// 返回 File对象实例
|
||||
$file = new self($filename);
|
||||
$file->setSaveName($saveName)->setUploadInfo($this->info);
|
||||
$file->setSaveName($saveName);
|
||||
$file->setUploadInfo($this->info);
|
||||
|
||||
return $file;
|
||||
}
|
||||
@@ -379,47 +391,60 @@ class File extends SplFileObject
|
||||
/**
|
||||
* 获取保存文件名
|
||||
* @access protected
|
||||
* @param string|bool $savename 保存的文件名 默认自动生成
|
||||
* @param string|bool $savename 保存的文件名 默认自动生成
|
||||
* @param bool $autoAppendExt 自动补充扩展名
|
||||
* @return string
|
||||
*/
|
||||
protected function buildSaveName($savename)
|
||||
protected function buildSaveName($savename, $autoAppendExt = true)
|
||||
{
|
||||
// 自动生成文件名
|
||||
if (true === $savename) {
|
||||
if ($this->rule instanceof \Closure) {
|
||||
$savename = call_user_func_array($this->rule, [$this]);
|
||||
} else {
|
||||
switch ($this->rule) {
|
||||
case 'date':
|
||||
$savename = date('Ymd') . DS . md5(microtime(true));
|
||||
break;
|
||||
default:
|
||||
if (in_array($this->rule, hash_algos())) {
|
||||
$hash = $this->hash($this->rule);
|
||||
$savename = substr($hash, 0, 2) . DS . substr($hash, 2);
|
||||
} elseif (is_callable($this->rule)) {
|
||||
$savename = call_user_func($this->rule);
|
||||
} else {
|
||||
$savename = date('Ymd') . DS . md5(microtime(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
// 自动生成文件名
|
||||
$savename = $this->autoBuildName();
|
||||
} elseif ('' === $savename || false === $savename) {
|
||||
// 保留原文件名
|
||||
$savename = $this->getInfo('name');
|
||||
}
|
||||
|
||||
if (!strpos($savename, '.')) {
|
||||
if ($autoAppendExt && false === strpos($savename, '.')) {
|
||||
$savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
return $savename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动生成文件名
|
||||
* @access protected
|
||||
* @return string
|
||||
*/
|
||||
protected function autoBuildName()
|
||||
{
|
||||
if ($this->rule instanceof \Closure) {
|
||||
$savename = call_user_func_array($this->rule, [$this]);
|
||||
} else {
|
||||
switch ($this->rule) {
|
||||
case 'date':
|
||||
$savename = date('Ymd') . DIRECTORY_SEPARATOR . md5(microtime(true));
|
||||
break;
|
||||
default:
|
||||
if (in_array($this->rule, hash_algos())) {
|
||||
$hash = $this->hash($this->rule);
|
||||
$savename = substr($hash, 0, 2) . DIRECTORY_SEPARATOR . substr($hash, 2);
|
||||
} elseif (is_callable($this->rule)) {
|
||||
$savename = call_user_func($this->rule);
|
||||
} else {
|
||||
$savename = date('Ymd') . DIRECTORY_SEPARATOR . md5(microtime(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $savename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误代码信息
|
||||
* @access private
|
||||
* @param int $errorNo 错误号
|
||||
* @return $this
|
||||
* @param int $errorNo 错误号
|
||||
*/
|
||||
private function error($errorNo)
|
||||
{
|
||||
@@ -443,8 +468,6 @@ class File extends SplFileObject
|
||||
default:
|
||||
$this->error = 'unknown upload error';
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -454,6 +477,8 @@ class File extends SplFileObject
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
$lang = Container::get('lang');
|
||||
|
||||
if (is_array($this->error)) {
|
||||
list($msg, $vars) = $this->error;
|
||||
} else {
|
||||
@@ -461,16 +486,9 @@ class File extends SplFileObject
|
||||
$vars = [];
|
||||
}
|
||||
|
||||
return Lang::has($msg) ? Lang::get($msg, $vars) : $msg;
|
||||
return $lang->has($msg) ? $lang->get($msg, $vars) : $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 魔法方法,获取文件的 hash 值
|
||||
* @access public
|
||||
* @param string $method 方法名
|
||||
* @param mixed $args 调用参数
|
||||
* @return string
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
return $this->hash($method);
|
||||
|
||||
@@ -14,68 +14,122 @@ namespace think;
|
||||
class Hook
|
||||
{
|
||||
/**
|
||||
* @var array 标签
|
||||
* 钩子行为定义
|
||||
* @var array
|
||||
*/
|
||||
private static $tags = [];
|
||||
private $tags = [];
|
||||
|
||||
/**
|
||||
* 绑定行为列表
|
||||
* @var array
|
||||
*/
|
||||
protected $bind = [];
|
||||
|
||||
/**
|
||||
* 入口方法名称
|
||||
* @var string
|
||||
*/
|
||||
private static $portal = 'run';
|
||||
|
||||
/**
|
||||
* 应用对象
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定入口方法名称
|
||||
* @access public
|
||||
* @param string $name 方法名
|
||||
* @return $this
|
||||
*/
|
||||
public function portal($name)
|
||||
{
|
||||
self::$portal = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定行为标识 便于调用
|
||||
* @access public
|
||||
* @param string|array $name 行为标识
|
||||
* @param mixed $behavior 行为
|
||||
* @return $this
|
||||
*/
|
||||
public function alias($name, $behavior = null)
|
||||
{
|
||||
if (is_array($name)) {
|
||||
$this->bind = array_merge($this->bind, $name);
|
||||
} else {
|
||||
$this->bind[$name] = $behavior;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态添加行为扩展到某个标签
|
||||
* @access public
|
||||
* @param string $tag 标签名称
|
||||
* @param mixed $behavior 行为名称
|
||||
* @param bool $first 是否放到开头执行
|
||||
* @param string $tag 标签名称
|
||||
* @param mixed $behavior 行为名称
|
||||
* @param bool $first 是否放到开头执行
|
||||
* @return void
|
||||
*/
|
||||
public static function add($tag, $behavior, $first = false)
|
||||
public function add($tag, $behavior, $first = false)
|
||||
{
|
||||
isset(self::$tags[$tag]) || self::$tags[$tag] = [];
|
||||
isset($this->tags[$tag]) || $this->tags[$tag] = [];
|
||||
|
||||
if (is_array($behavior) && !is_callable($behavior)) {
|
||||
if (!array_key_exists('_overlay', $behavior) || !$behavior['_overlay']) {
|
||||
unset($behavior['_overlay']);
|
||||
self::$tags[$tag] = array_merge(self::$tags[$tag], $behavior);
|
||||
if (!array_key_exists('_overlay', $behavior)) {
|
||||
$this->tags[$tag] = array_merge($this->tags[$tag], $behavior);
|
||||
} else {
|
||||
unset($behavior['_overlay']);
|
||||
self::$tags[$tag] = $behavior;
|
||||
$this->tags[$tag] = $behavior;
|
||||
}
|
||||
} elseif ($first) {
|
||||
array_unshift(self::$tags[$tag], $behavior);
|
||||
array_unshift($this->tags[$tag], $behavior);
|
||||
} else {
|
||||
self::$tags[$tag][] = $behavior;
|
||||
$this->tags[$tag][] = $behavior;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量导入插件
|
||||
* @access public
|
||||
* @param array $tags 插件信息
|
||||
* @param boolean $recursive 是否递归合并
|
||||
* @param array $tags 插件信息
|
||||
* @param bool $recursive 是否递归合并
|
||||
* @return void
|
||||
*/
|
||||
public static function import(array $tags, $recursive = true)
|
||||
public function import(array $tags, $recursive = true)
|
||||
{
|
||||
if ($recursive) {
|
||||
foreach ($tags as $tag => $behavior) {
|
||||
self::add($tag, $behavior);
|
||||
$this->add($tag, $behavior);
|
||||
}
|
||||
} else {
|
||||
self::$tags = $tags + self::$tags;
|
||||
$this->tags = $tags + $this->tags;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件信息
|
||||
* @access public
|
||||
* @param string $tag 插件位置(留空获取全部)
|
||||
* @param string $tag 插件位置 留空获取全部
|
||||
* @return array
|
||||
*/
|
||||
public static function get($tag = '')
|
||||
public function get($tag = '')
|
||||
{
|
||||
if (empty($tag)) {
|
||||
return self::$tags;
|
||||
//获取全部的插件信息
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : [];
|
||||
return array_key_exists($tag, $this->tags) ? $this->tags[$tag] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,18 +137,17 @@ class Hook
|
||||
* @access public
|
||||
* @param string $tag 标签名称
|
||||
* @param mixed $params 传入参数
|
||||
* @param mixed $extra 额外参数
|
||||
* @param bool $once 只获取一个有效返回值
|
||||
* @return mixed
|
||||
*/
|
||||
public static function listen($tag, &$params = null, $extra = null, $once = false)
|
||||
public function listen($tag, $params = null, $once = false)
|
||||
{
|
||||
$results = [];
|
||||
$tags = $this->get($tag);
|
||||
|
||||
foreach (static::get($tag) as $key => $name) {
|
||||
$results[$key] = self::exec($name, $tag, $params, $extra);
|
||||
foreach ($tags as $key => $name) {
|
||||
$results[$key] = $this->execTag($name, $tag, $params);
|
||||
|
||||
// 如果返回 false,或者仅获取一个有效返回则中断行为执行
|
||||
if (false === $results[$key] || (!is_null($results[$key]) && $once)) {
|
||||
break;
|
||||
}
|
||||
@@ -104,45 +157,64 @@ class Hook
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行某个行为
|
||||
* 执行行为
|
||||
* @access public
|
||||
* @param mixed $class 要执行的行为
|
||||
* @param string $tag 方法名(标签名)
|
||||
* @param mixed $params 传人的参数
|
||||
* @param mixed $extra 额外参数
|
||||
* @param mixed $class 行为
|
||||
* @param mixed $params 参数
|
||||
* @return mixed
|
||||
*/
|
||||
public static function exec($class, $tag = '', &$params = null, $extra = null)
|
||||
public function exec($class, $params = null)
|
||||
{
|
||||
App::$debug && Debug::remark('behavior_start', 'time');
|
||||
if ($class instanceof \Closure || is_array($class)) {
|
||||
$method = $class;
|
||||
} else {
|
||||
if (isset($this->bind[$class])) {
|
||||
$class = $this->bind[$class];
|
||||
}
|
||||
$method = [$class, self::$portal];
|
||||
}
|
||||
|
||||
return $this->app->invoke($method, [$params]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行某个标签的行为
|
||||
* @access protected
|
||||
* @param mixed $class 要执行的行为
|
||||
* @param string $tag 方法名(标签名)
|
||||
* @param mixed $params 参数
|
||||
* @return mixed
|
||||
*/
|
||||
protected function execTag($class, $tag = '', $params = null)
|
||||
{
|
||||
$method = Loader::parseName($tag, 1, false);
|
||||
|
||||
if ($class instanceof \Closure) {
|
||||
$result = call_user_func_array($class, [ & $params, $extra]);
|
||||
$class = 'Closure';
|
||||
} elseif (is_array($class)) {
|
||||
list($class, $method) = $class;
|
||||
|
||||
$result = (new $class())->$method($params, $extra);
|
||||
$class = $class . '->' . $method;
|
||||
} elseif (is_object($class)) {
|
||||
$result = $class->$method($params, $extra);
|
||||
$class = get_class($class);
|
||||
} elseif (strpos($class, '::')) {
|
||||
$result = call_user_func_array($class, [ & $params, $extra]);
|
||||
$call = $class;
|
||||
$class = 'Closure';
|
||||
} elseif (is_array($class) || strpos($class, '::')) {
|
||||
$call = $class;
|
||||
} else {
|
||||
$obj = new $class();
|
||||
$method = ($tag && is_callable([$obj, $method])) ? $method : 'run';
|
||||
$result = $obj->$method($params, $extra);
|
||||
$obj = Container::get($class);
|
||||
|
||||
if (!is_callable([$obj, $method])) {
|
||||
$method = self::$portal;
|
||||
}
|
||||
|
||||
$call = [$class, $method];
|
||||
$class = $class . '->' . $method;
|
||||
}
|
||||
|
||||
if (App::$debug) {
|
||||
Debug::remark('behavior_end', 'time');
|
||||
Log::record('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info');
|
||||
}
|
||||
$result = $this->app->invoke($call, [$params]);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['app']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,92 +14,104 @@ namespace think;
|
||||
class Lang
|
||||
{
|
||||
/**
|
||||
* @var array 语言数据
|
||||
* 多语言信息
|
||||
* @var array
|
||||
*/
|
||||
private static $lang = [];
|
||||
private $lang = [];
|
||||
|
||||
/**
|
||||
* @var string 语言作用域
|
||||
* 当前语言
|
||||
* @var string
|
||||
*/
|
||||
private static $range = 'zh-cn';
|
||||
private $range = 'zh-cn';
|
||||
|
||||
/**
|
||||
* @var string 语言自动侦测的变量
|
||||
* 多语言自动侦测变量名
|
||||
* @var string
|
||||
*/
|
||||
protected static $langDetectVar = 'lang';
|
||||
protected $langDetectVar = 'lang';
|
||||
|
||||
/**
|
||||
* @var string 语言 Cookie 变量
|
||||
* 多语言cookie变量
|
||||
* @var string
|
||||
*/
|
||||
protected static $langCookieVar = 'think_var';
|
||||
protected $langCookieVar = 'think_var';
|
||||
|
||||
/**
|
||||
* @var int 语言 Cookie 的过期时间
|
||||
* 允许的多语言列表
|
||||
* @var array
|
||||
*/
|
||||
protected static $langCookieExpire = 3600;
|
||||
protected $allowLangList = [];
|
||||
|
||||
/**
|
||||
* @var array 允许语言列表
|
||||
* Accept-Language转义为对应语言包名称 系统默认配置
|
||||
* @var string
|
||||
*/
|
||||
protected static $allowLangList = [];
|
||||
protected $acceptLanguage = [
|
||||
'zh-hans-cn' => 'zh-cn',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array Accept-Language 转义为对应语言包名称 系统默认配置
|
||||
* 应用对象
|
||||
* @var App
|
||||
*/
|
||||
protected static $acceptLanguage = ['zh-hans-cn' => 'zh-cn'];
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* 设定当前的语言
|
||||
* @access public
|
||||
* @param string $range 语言作用域
|
||||
* @return string
|
||||
*/
|
||||
public static function range($range = '')
|
||||
public function __construct(App $app)
|
||||
{
|
||||
if ($range) {
|
||||
self::$range = $range;
|
||||
}
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
return self::$range;
|
||||
// 设定当前的语言
|
||||
public function range($range = '')
|
||||
{
|
||||
if ('' == $range) {
|
||||
return $this->range;
|
||||
} else {
|
||||
$this->range = $range;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置语言定义(不区分大小写)
|
||||
* @access public
|
||||
* @param string|array $name 语言变量
|
||||
* @param string|array $name 语言变量
|
||||
* @param string $value 语言值
|
||||
* @param string $range 语言作用域
|
||||
* @return mixed
|
||||
*/
|
||||
public static function set($name, $value = null, $range = '')
|
||||
public function set($name, $value = null, $range = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
|
||||
if (!isset(self::$lang[$range])) {
|
||||
self::$lang[$range] = [];
|
||||
$range = $range ?: $this->range;
|
||||
// 批量定义
|
||||
if (!isset($this->lang[$range])) {
|
||||
$this->lang[$range] = [];
|
||||
}
|
||||
|
||||
if (is_array($name)) {
|
||||
return self::$lang[$range] = array_change_key_case($name) + self::$lang[$range];
|
||||
return $this->lang[$range] = array_change_key_case($name) + $this->lang[$range];
|
||||
}
|
||||
|
||||
return self::$lang[$range][strtolower($name)] = $value;
|
||||
return $this->lang[$range][strtolower($name)] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载语言定义(不区分大小写)
|
||||
* @access public
|
||||
* @param array|string $file 语言文件
|
||||
* @param string $range 语言作用域
|
||||
* @return mixed
|
||||
* @param string|array $file 语言文件
|
||||
* @param string $range 语言作用域
|
||||
* @return array
|
||||
*/
|
||||
public static function load($file, $range = '')
|
||||
public function load($file, $range = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
$file = is_string($file) ? [$file] : $file;
|
||||
$range = $range ?: $this->range;
|
||||
if (!isset($this->lang[$range])) {
|
||||
$this->lang[$range] = [];
|
||||
}
|
||||
|
||||
if (!isset(self::$lang[$range])) {
|
||||
self::$lang[$range] = [];
|
||||
// 批量定义
|
||||
if (is_string($file)) {
|
||||
$file = [$file];
|
||||
}
|
||||
|
||||
$lang = [];
|
||||
@@ -107,10 +119,8 @@ class Lang
|
||||
foreach ($file as $_file) {
|
||||
if (is_file($_file)) {
|
||||
// 记录加载信息
|
||||
App::$debug && Log::record('[ LANG ] ' . $_file, 'info');
|
||||
|
||||
$this->app->log('[ LANG ] ' . $_file);
|
||||
$_lang = include $_file;
|
||||
|
||||
if (is_array($_lang)) {
|
||||
$lang = array_change_key_case($_lang) + $lang;
|
||||
}
|
||||
@@ -118,45 +128,45 @@ class Lang
|
||||
}
|
||||
|
||||
if (!empty($lang)) {
|
||||
self::$lang[$range] = $lang + self::$lang[$range];
|
||||
$this->lang[$range] = $lang + $this->lang[$range];
|
||||
}
|
||||
|
||||
return self::$lang[$range];
|
||||
return $this->lang[$range];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取语言定义(不区分大小写)
|
||||
* @access public
|
||||
* @param string|null $name 语言变量
|
||||
* @param string $range 语言作用域
|
||||
* @return mixed
|
||||
* @param string|null $name 语言变量
|
||||
* @param string $range 语言作用域
|
||||
* @return bool
|
||||
*/
|
||||
public static function has($name, $range = '')
|
||||
public function has($name, $range = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
$range = $range ?: $this->range;
|
||||
|
||||
return isset(self::$lang[$range][strtolower($name)]);
|
||||
return isset($this->lang[$range][strtolower($name)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取语言定义(不区分大小写)
|
||||
* @access public
|
||||
* @param string|null $name 语言变量
|
||||
* @param array $vars 变量替换
|
||||
* @param string $range 语言作用域
|
||||
* @param string|null $name 语言变量
|
||||
* @param array $vars 变量替换
|
||||
* @param string $range 语言作用域
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($name = null, $vars = [], $range = '')
|
||||
public function get($name = null, $vars = [], $range = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
$range = $range ?: $this->range;
|
||||
|
||||
// 空参数返回所有定义
|
||||
if (empty($name)) {
|
||||
return self::$lang[$range];
|
||||
if (is_null($name)) {
|
||||
return $this->lang[$range];
|
||||
}
|
||||
|
||||
$key = strtolower($name);
|
||||
$value = isset(self::$lang[$range][$key]) ? self::$lang[$range][$key] : $name;
|
||||
$value = isset($this->lang[$range][$key]) ? $this->lang[$range][$key] : $name;
|
||||
|
||||
// 变量解析
|
||||
if (!empty($vars) && is_array($vars)) {
|
||||
@@ -177,7 +187,6 @@ class Lang
|
||||
}
|
||||
$value = str_replace($replace, $vars, $value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $value;
|
||||
@@ -188,41 +197,51 @@ class Lang
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public static function detect()
|
||||
public function detect()
|
||||
{
|
||||
// 自动侦测设置获取语言选择
|
||||
$langSet = '';
|
||||
|
||||
if (isset($_GET[self::$langDetectVar])) {
|
||||
// url 中设置了语言变量
|
||||
$langSet = strtolower($_GET[self::$langDetectVar]);
|
||||
} elseif (isset($_COOKIE[self::$langCookieVar])) {
|
||||
// Cookie 中设置了语言变量
|
||||
$langSet = strtolower($_COOKIE[self::$langCookieVar]);
|
||||
if (isset($_GET[$this->langDetectVar])) {
|
||||
// url中设置了语言变量
|
||||
$langSet = strtolower($_GET[$this->langDetectVar]);
|
||||
} elseif (isset($_COOKIE[$this->langCookieVar])) {
|
||||
// Cookie中设置了语言变量
|
||||
$langSet = strtolower($_COOKIE[$this->langCookieVar]);
|
||||
} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||
// 自动侦测浏览器语言
|
||||
preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
|
||||
$langSet = strtolower($matches[1]);
|
||||
$acceptLangs = Config::get('header_accept_lang');
|
||||
|
||||
if (isset($acceptLangs[$langSet])) {
|
||||
$langSet = $acceptLangs[$langSet];
|
||||
} elseif (isset(self::$acceptLanguage[$langSet])) {
|
||||
$langSet = self::$acceptLanguage[$langSet];
|
||||
$langSet = strtolower($matches[1]);
|
||||
if (isset($this->acceptLanguage[$langSet])) {
|
||||
$langSet = $this->acceptLanguage[$langSet];
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/^([a-z\d\-]+)/i', $langSet, $matches)) {
|
||||
$langSet = strtolower($matches[1]);
|
||||
} else {
|
||||
$langSet = self::$range;
|
||||
$langSet = $this->range;
|
||||
}
|
||||
|
||||
// 合法的语言
|
||||
if (empty(self::$allowLangList) || in_array($langSet, self::$allowLangList)) {
|
||||
self::$range = $langSet ?: self::$range;
|
||||
if (empty($this->allowLangList) || in_array($langSet, $this->allowLangList)) {
|
||||
// 合法的语言
|
||||
$this->range = $langSet ?: $this->range;
|
||||
}
|
||||
|
||||
return self::$range;
|
||||
return $this->range;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前语言到Cookie
|
||||
* @access public
|
||||
* @param string $lang 语言
|
||||
* @return void
|
||||
*/
|
||||
public function saveToCookie($lang = null)
|
||||
{
|
||||
$range = $lang ?: $this->range;
|
||||
|
||||
$_COOKIE[$this->langCookieVar] = $range;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,31 +250,20 @@ class Lang
|
||||
* @param string $var 变量名称
|
||||
* @return void
|
||||
*/
|
||||
public static function setLangDetectVar($var)
|
||||
public function setLangDetectVar($var)
|
||||
{
|
||||
self::$langDetectVar = $var;
|
||||
$this->langDetectVar = $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置语言的 cookie 保存变量
|
||||
* 设置语言的cookie保存变量
|
||||
* @access public
|
||||
* @param string $var 变量名称
|
||||
* @return void
|
||||
*/
|
||||
public static function setLangCookieVar($var)
|
||||
public function setLangCookieVar($var)
|
||||
{
|
||||
self::$langCookieVar = $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置语言的 cookie 的过期时间
|
||||
* @access public
|
||||
* @param string $expire 过期时间
|
||||
* @return void
|
||||
*/
|
||||
public static function setLangCookieExpire($expire)
|
||||
{
|
||||
self::$langCookieExpire = $expire;
|
||||
$this->langCookieVar = $var;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,8 +272,19 @@ class Lang
|
||||
* @param array $list 语言列表
|
||||
* @return void
|
||||
*/
|
||||
public static function setAllowLangList($list)
|
||||
public function setAllowLangList(array $list)
|
||||
{
|
||||
self::$allowLangList = $list;
|
||||
$this->allowLangList = $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置转义的语言列表
|
||||
* @access public
|
||||
* @param array $list 语言列表
|
||||
* @return void
|
||||
*/
|
||||
public function setAcceptLanguage(array $list)
|
||||
{
|
||||
$this->acceptLanguage = array_merge($this->acceptLanguage, $list);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,278 +16,76 @@ use think\exception\ClassNotFoundException;
|
||||
class Loader
|
||||
{
|
||||
/**
|
||||
* @var array 实例数组
|
||||
*/
|
||||
protected static $instance = [];
|
||||
|
||||
/**
|
||||
* @var array 类名映射
|
||||
* 类名映射信息
|
||||
* @var array
|
||||
*/
|
||||
protected static $classMap = [];
|
||||
|
||||
/**
|
||||
* @var array 命名空间别名
|
||||
* 类库别名
|
||||
* @var array
|
||||
*/
|
||||
protected static $namespaceAlias = [];
|
||||
protected static $classAlias = [];
|
||||
|
||||
/**
|
||||
* @var array PSR-4 命名空间前缀长度映射
|
||||
* PSR-4
|
||||
* @var array
|
||||
*/
|
||||
private static $prefixLengthsPsr4 = [];
|
||||
private static $prefixDirsPsr4 = [];
|
||||
private static $fallbackDirsPsr4 = [];
|
||||
|
||||
/**
|
||||
* @var array PSR-4 的加载目录
|
||||
*/
|
||||
private static $prefixDirsPsr4 = [];
|
||||
|
||||
/**
|
||||
* @var array PSR-4 加载失败的回退目录
|
||||
*/
|
||||
private static $fallbackDirsPsr4 = [];
|
||||
|
||||
/**
|
||||
* @var array PSR-0 命名空间前缀映射
|
||||
*/
|
||||
private static $prefixesPsr0 = [];
|
||||
|
||||
/**
|
||||
* @var array PSR-0 加载失败的回退目录
|
||||
* PSR-0
|
||||
* @var array
|
||||
*/
|
||||
private static $prefixesPsr0 = [];
|
||||
private static $fallbackDirsPsr0 = [];
|
||||
|
||||
/**
|
||||
* @var array 需要加载的文件
|
||||
* 需要加载的文件
|
||||
* @var array
|
||||
*/
|
||||
private static $files = [];
|
||||
|
||||
/**
|
||||
* 自动加载
|
||||
* @access public
|
||||
* @param string $class 类名
|
||||
* @return bool
|
||||
* Composer安装路径
|
||||
* @var string
|
||||
*/
|
||||
public static function autoload($class)
|
||||
private static $composerPath;
|
||||
|
||||
// 获取应用根目录
|
||||
public static function getRootPath()
|
||||
{
|
||||
// 检测命名空间别名
|
||||
if (!empty(self::$namespaceAlias)) {
|
||||
$namespace = dirname($class);
|
||||
if (isset(self::$namespaceAlias[$namespace])) {
|
||||
$original = self::$namespaceAlias[$namespace] . '\\' . basename($class);
|
||||
if (class_exists($original)) {
|
||||
return class_alias($original, $class, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($file = self::findFile($class)) {
|
||||
// 非 Win 环境不严格区分大小写
|
||||
if (!IS_WIN || pathinfo($file, PATHINFO_FILENAME) == pathinfo(realpath($file), PATHINFO_FILENAME)) {
|
||||
__include_file($file);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找文件
|
||||
* @access private
|
||||
* @param string $class 类名
|
||||
* @return bool|string
|
||||
*/
|
||||
private static function findFile($class)
|
||||
{
|
||||
// 类库映射
|
||||
if (!empty(self::$classMap[$class])) {
|
||||
return self::$classMap[$class];
|
||||
}
|
||||
|
||||
// 查找 PSR-4
|
||||
$logicalPathPsr4 = strtr($class, '\\', DS) . EXT;
|
||||
$first = $class[0];
|
||||
|
||||
if (isset(self::$prefixLengthsPsr4[$first])) {
|
||||
foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach (self::$prefixDirsPsr4[$prefix] as $dir) {
|
||||
if (is_file($file = $dir . DS . substr($logicalPathPsr4, $length))) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查找 PSR-4 fallback dirs
|
||||
foreach (self::$fallbackDirsPsr4 as $dir) {
|
||||
if (is_file($file = $dir . DS . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// 查找 PSR-0
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespace class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DS);
|
||||
if ('cli' == PHP_SAPI) {
|
||||
$scriptName = realpath($_SERVER['argv'][0]);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DS) . EXT;
|
||||
$scriptName = $_SERVER['SCRIPT_FILENAME'];
|
||||
}
|
||||
|
||||
if (isset(self::$prefixesPsr0[$first])) {
|
||||
foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (is_file($file = $dir . DS . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$path = realpath(dirname($scriptName));
|
||||
|
||||
if (!is_file($path . DIRECTORY_SEPARATOR . 'think')) {
|
||||
$path = dirname($path);
|
||||
}
|
||||
|
||||
// 查找 PSR-0 fallback dirs
|
||||
foreach (self::$fallbackDirsPsr0 as $dir) {
|
||||
if (is_file($file = $dir . DS . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// 找不到则设置映射为 false 并返回
|
||||
return self::$classMap[$class] = false;
|
||||
return $path . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册 classmap
|
||||
* @access public
|
||||
* @param string|array $class 类名
|
||||
* @param string $map 映射
|
||||
* @return void
|
||||
*/
|
||||
public static function addClassMap($class, $map = '')
|
||||
{
|
||||
if (is_array($class)) {
|
||||
self::$classMap = array_merge(self::$classMap, $class);
|
||||
} else {
|
||||
self::$classMap[$class] = $map;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册命名空间
|
||||
* @access public
|
||||
* @param string|array $namespace 命名空间
|
||||
* @param string $path 路径
|
||||
* @return void
|
||||
*/
|
||||
public static function addNamespace($namespace, $path = '')
|
||||
{
|
||||
if (is_array($namespace)) {
|
||||
foreach ($namespace as $prefix => $paths) {
|
||||
self::addPsr4($prefix . '\\', rtrim($paths, DS), true);
|
||||
}
|
||||
} else {
|
||||
self::addPsr4($namespace . '\\', rtrim($path, DS), true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 PSR-0 命名空间
|
||||
* @access private
|
||||
* @param array|string $prefix 空间前缀
|
||||
* @param array $paths 路径
|
||||
* @param bool $prepend 预先设置的优先级更高
|
||||
* @return void
|
||||
*/
|
||||
private static function addPsr0($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
self::$fallbackDirsPsr0 = $prepend ?
|
||||
array_merge((array) $paths, self::$fallbackDirsPsr0) :
|
||||
array_merge(self::$fallbackDirsPsr0, (array) $paths);
|
||||
} else {
|
||||
$first = $prefix[0];
|
||||
|
||||
if (!isset(self::$prefixesPsr0[$first][$prefix])) {
|
||||
self::$prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
} else {
|
||||
self::$prefixesPsr0[$first][$prefix] = $prepend ?
|
||||
array_merge((array) $paths, self::$prefixesPsr0[$first][$prefix]) :
|
||||
array_merge(self::$prefixesPsr0[$first][$prefix], (array) $paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 PSR-4 空间
|
||||
* @access private
|
||||
* @param array|string $prefix 空间前缀
|
||||
* @param string $paths 路径
|
||||
* @param bool $prepend 预先设置的优先级更高
|
||||
* @return void
|
||||
*/
|
||||
private static function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
self::$fallbackDirsPsr4 = $prepend ?
|
||||
array_merge((array) $paths, self::$fallbackDirsPsr4) :
|
||||
array_merge(self::$fallbackDirsPsr4, (array) $paths);
|
||||
|
||||
} elseif (!isset(self::$prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException(
|
||||
"A non-empty PSR-4 prefix must end with a namespace separator."
|
||||
);
|
||||
}
|
||||
|
||||
self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
self::$prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
|
||||
} else {
|
||||
self::$prefixDirsPsr4[$prefix] = $prepend ?
|
||||
// Prepend directories for an already registered namespace.
|
||||
array_merge((array) $paths, self::$prefixDirsPsr4[$prefix]) :
|
||||
// Append directories for an already registered namespace.
|
||||
array_merge(self::$prefixDirsPsr4[$prefix], (array) $paths);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册命名空间别名
|
||||
* @access public
|
||||
* @param array|string $namespace 命名空间
|
||||
* @param string $original 源文件
|
||||
* @return void
|
||||
*/
|
||||
public static function addNamespaceAlias($namespace, $original = '')
|
||||
{
|
||||
if (is_array($namespace)) {
|
||||
self::$namespaceAlias = array_merge(self::$namespaceAlias, $namespace);
|
||||
} else {
|
||||
self::$namespaceAlias[$namespace] = $original;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册自动加载机制
|
||||
* @access public
|
||||
* @param callable $autoload 自动加载处理方法
|
||||
* @return void
|
||||
*/
|
||||
public static function register($autoload = null)
|
||||
// 注册自动加载机制
|
||||
public static function register($autoload = '')
|
||||
{
|
||||
// 注册系统自动加载
|
||||
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
|
||||
|
||||
// Composer 自动加载支持
|
||||
if (is_dir(VENDOR_PATH . 'composer')) {
|
||||
if (PHP_VERSION_ID >= 50600 && is_file(VENDOR_PATH . 'composer' . DS . 'autoload_static.php')) {
|
||||
require VENDOR_PATH . 'composer' . DS . 'autoload_static.php';
|
||||
$rootPath = self::getRootPath();
|
||||
|
||||
self::$composerPath = $rootPath . 'vendor' . DIRECTORY_SEPARATOR . 'composer' . DIRECTORY_SEPARATOR;
|
||||
|
||||
// Composer自动加载支持
|
||||
if (is_dir(self::$composerPath)) {
|
||||
if (is_file(self::$composerPath . 'autoload_static.php')) {
|
||||
require self::$composerPath . 'autoload_static.php';
|
||||
|
||||
$declaredClass = get_declared_classes();
|
||||
$composerClass = array_pop($declaredClass);
|
||||
@@ -298,58 +96,255 @@ class Loader
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self::registerComposerLoader();
|
||||
self::registerComposerLoader(self::$composerPath);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册命名空间定义
|
||||
self::addNamespace([
|
||||
'think' => LIB_PATH . 'think' . DS,
|
||||
'behavior' => LIB_PATH . 'behavior' . DS,
|
||||
'traits' => LIB_PATH . 'traits' . DS,
|
||||
'think' => __DIR__,
|
||||
'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits',
|
||||
]);
|
||||
|
||||
// 加载类库映射文件
|
||||
if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {
|
||||
self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));
|
||||
if (is_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php')) {
|
||||
self::addClassMap(__include_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php'));
|
||||
}
|
||||
|
||||
self::loadComposerAutoloadFiles();
|
||||
// 自动加载extend目录
|
||||
self::addAutoLoadDir($rootPath . 'extend');
|
||||
}
|
||||
|
||||
// 自动加载 extend 目录
|
||||
self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);
|
||||
// 自动加载
|
||||
public static function autoload($class)
|
||||
{
|
||||
if (isset(self::$classAlias[$class])) {
|
||||
return class_alias(self::$classAlias[$class], $class);
|
||||
}
|
||||
|
||||
if ($file = self::findFile($class)) {
|
||||
|
||||
// Win环境严格区分大小写
|
||||
if (strpos(PHP_OS, 'WIN') !== false && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
__include_file($file);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册 composer 自动加载
|
||||
* 查找文件
|
||||
* @access private
|
||||
* @return void
|
||||
* @param string $class
|
||||
* @return string|false
|
||||
*/
|
||||
private static function registerComposerLoader()
|
||||
private static function findFile($class)
|
||||
{
|
||||
if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) {
|
||||
$map = require VENDOR_PATH . 'composer/autoload_namespaces.php';
|
||||
if (!empty(self::$classMap[$class])) {
|
||||
// 类库映射
|
||||
return self::$classMap[$class];
|
||||
}
|
||||
|
||||
// 查找 PSR-4
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php';
|
||||
|
||||
$first = $class[0];
|
||||
if (isset(self::$prefixLengthsPsr4[$first])) {
|
||||
foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach (self::$prefixDirsPsr4[$prefix] as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查找 PSR-4 fallback dirs
|
||||
foreach (self::$fallbackDirsPsr4 as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// 查找 PSR-0
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php';
|
||||
}
|
||||
|
||||
if (isset(self::$prefixesPsr0[$first])) {
|
||||
foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查找 PSR-0 fallback dirs
|
||||
foreach (self::$fallbackDirsPsr0 as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
return self::$classMap[$class] = false;
|
||||
}
|
||||
|
||||
// 注册classmap
|
||||
public static function addClassMap($class, $map = '')
|
||||
{
|
||||
if (is_array($class)) {
|
||||
self::$classMap = array_merge(self::$classMap, $class);
|
||||
} else {
|
||||
self::$classMap[$class] = $map;
|
||||
}
|
||||
}
|
||||
|
||||
// 注册命名空间
|
||||
public static function addNamespace($namespace, $path = '')
|
||||
{
|
||||
if (is_array($namespace)) {
|
||||
foreach ($namespace as $prefix => $paths) {
|
||||
self::addPsr4($prefix . '\\', rtrim($paths, DIRECTORY_SEPARATOR), true);
|
||||
}
|
||||
} else {
|
||||
self::addPsr4($namespace . '\\', rtrim($path, DIRECTORY_SEPARATOR), true);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加Ps0空间
|
||||
private static function addPsr0($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
self::$fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
self::$fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
self::$fallbackDirsPsr0 = array_merge(
|
||||
self::$fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset(self::$prefixesPsr0[$first][$prefix])) {
|
||||
self::$prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($prepend) {
|
||||
self::$prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
self::$prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
self::$prefixesPsr0[$first][$prefix] = array_merge(
|
||||
self::$prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加Psr4空间
|
||||
private static function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
self::$fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
self::$fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
self::$fallbackDirsPsr4 = array_merge(
|
||||
self::$fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset(self::$prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
|
||||
self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
self::$prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
self::$prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
self::$prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
self::$prefixDirsPsr4[$prefix] = array_merge(
|
||||
self::$prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册自动加载类库目录
|
||||
public static function addAutoLoadDir($path)
|
||||
{
|
||||
self::$fallbackDirsPsr4[] = $path;
|
||||
}
|
||||
|
||||
// 注册类别名
|
||||
public static function addClassAlias($alias, $class = null)
|
||||
{
|
||||
if (is_array($alias)) {
|
||||
self::$classAlias = array_merge(self::$classAlias, $alias);
|
||||
} else {
|
||||
self::$classAlias[$alias] = $class;
|
||||
}
|
||||
}
|
||||
|
||||
// 注册composer自动加载
|
||||
public static function registerComposerLoader($composerPath)
|
||||
{
|
||||
if (is_file($composerPath . 'autoload_namespaces.php')) {
|
||||
$map = require $composerPath . 'autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
self::addPsr0($namespace, $path);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_file(VENDOR_PATH . 'composer/autoload_psr4.php')) {
|
||||
$map = require VENDOR_PATH . 'composer/autoload_psr4.php';
|
||||
if (is_file($composerPath . 'autoload_psr4.php')) {
|
||||
$map = require $composerPath . 'autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
self::addPsr4($namespace, $path);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_file(VENDOR_PATH . 'composer/autoload_classmap.php')) {
|
||||
$classMap = require VENDOR_PATH . 'composer/autoload_classmap.php';
|
||||
if (is_file($composerPath . 'autoload_classmap.php')) {
|
||||
$classMap = require $composerPath . 'autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
self::addClassMap($classMap);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) {
|
||||
self::$files = require VENDOR_PATH . 'composer/autoload_files.php';
|
||||
if (is_file($composerPath . 'autoload_files.php')) {
|
||||
self::$files = require $composerPath . 'autoload_files.php';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,246 +360,12 @@ class Loader
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入所需的类库 同 Java 的 Import 本函数有缓存功能
|
||||
* @access public
|
||||
* @param string $class 类库命名空间字符串
|
||||
* @param string $baseUrl 起始路径
|
||||
* @param string $ext 导入的文件扩展名
|
||||
* @return bool
|
||||
*/
|
||||
public static function import($class, $baseUrl = '', $ext = EXT)
|
||||
{
|
||||
static $_file = [];
|
||||
$key = $class . $baseUrl;
|
||||
$class = str_replace(['.', '#'], [DS, '.'], $class);
|
||||
|
||||
if (isset($_file[$key])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (empty($baseUrl)) {
|
||||
list($name, $class) = explode(DS, $class, 2);
|
||||
|
||||
if (isset(self::$prefixDirsPsr4[$name . '\\'])) {
|
||||
// 注册的命名空间
|
||||
$baseUrl = self::$prefixDirsPsr4[$name . '\\'];
|
||||
} elseif ('@' == $name) {
|
||||
// 加载当前模块应用类库
|
||||
$baseUrl = App::$modulePath;
|
||||
} elseif (is_dir(EXTEND_PATH . $name)) {
|
||||
$baseUrl = EXTEND_PATH . $name . DS;
|
||||
} else {
|
||||
// 加载其它模块的类库
|
||||
$baseUrl = APP_PATH . $name . DS;
|
||||
}
|
||||
} elseif (substr($baseUrl, -1) != DS) {
|
||||
$baseUrl .= DS;
|
||||
}
|
||||
|
||||
// 如果类存在则导入类库文件
|
||||
if (is_array($baseUrl)) {
|
||||
foreach ($baseUrl as $path) {
|
||||
if (is_file($filename = $path . DS . $class . $ext)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$filename = $baseUrl . $class . $ext;
|
||||
}
|
||||
|
||||
if (!empty($filename) &&
|
||||
is_file($filename) &&
|
||||
(!IS_WIN || pathinfo($filename, PATHINFO_FILENAME) == pathinfo(realpath($filename), PATHINFO_FILENAME))
|
||||
) {
|
||||
__include_file($filename);
|
||||
$_file[$key] = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化(分层)模型
|
||||
* @access public
|
||||
* @param string $name Model名称
|
||||
* @param string $layer 业务层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @param string $common 公共模块名
|
||||
* @return object
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public static function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common')
|
||||
{
|
||||
$uid = $name . $layer;
|
||||
|
||||
if (isset(self::$instance[$uid])) {
|
||||
return self::$instance[$uid];
|
||||
}
|
||||
|
||||
list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
|
||||
|
||||
if (class_exists($class)) {
|
||||
$model = new $class();
|
||||
} else {
|
||||
$class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
|
||||
|
||||
if (class_exists($class)) {
|
||||
$model = new $class();
|
||||
} else {
|
||||
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
||||
}
|
||||
}
|
||||
|
||||
return self::$instance[$uid] = $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化(分层)控制器 格式:[模块名/]控制器名
|
||||
* @access public
|
||||
* @param string $name 资源地址
|
||||
* @param string $layer 控制层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @param string $empty 空控制器名称
|
||||
* @return object
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
|
||||
{
|
||||
list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
|
||||
|
||||
if (class_exists($class)) {
|
||||
return App::invokeClass($class);
|
||||
}
|
||||
|
||||
if ($empty) {
|
||||
$emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix);
|
||||
|
||||
if (class_exists($emptyClass)) {
|
||||
return new $emptyClass(Request::instance());
|
||||
}
|
||||
}
|
||||
|
||||
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化验证类 格式:[模块名/]验证器名
|
||||
* @access public
|
||||
* @param string $name 资源地址
|
||||
* @param string $layer 验证层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @param string $common 公共模块名
|
||||
* @return object|false
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public static function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common')
|
||||
{
|
||||
$name = $name ?: Config::get('default_validate');
|
||||
|
||||
if (empty($name)) {
|
||||
return new Validate;
|
||||
}
|
||||
|
||||
$uid = $name . $layer;
|
||||
if (isset(self::$instance[$uid])) {
|
||||
return self::$instance[$uid];
|
||||
}
|
||||
|
||||
list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
|
||||
|
||||
if (class_exists($class)) {
|
||||
$validate = new $class;
|
||||
} else {
|
||||
$class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
|
||||
|
||||
if (class_exists($class)) {
|
||||
$validate = new $class;
|
||||
} else {
|
||||
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
||||
}
|
||||
}
|
||||
|
||||
return self::$instance[$uid] = $validate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析模块和类名
|
||||
* @access protected
|
||||
* @param string $name 资源地址
|
||||
* @param string $layer 验证层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @return array
|
||||
*/
|
||||
protected static function getModuleAndClass($name, $layer, $appendSuffix)
|
||||
{
|
||||
if (false !== strpos($name, '\\')) {
|
||||
$module = Request::instance()->module();
|
||||
$class = $name;
|
||||
} else {
|
||||
if (strpos($name, '/')) {
|
||||
list($module, $name) = explode('/', $name, 2);
|
||||
} else {
|
||||
$module = Request::instance()->module();
|
||||
}
|
||||
|
||||
$class = self::parseClass($module, $layer, $name, $appendSuffix);
|
||||
}
|
||||
|
||||
return [$module, $class];
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库初始化 并取得数据库类实例
|
||||
* @access public
|
||||
* @param mixed $config 数据库配置
|
||||
* @param bool|string $name 连接标识 true 强制重新连接
|
||||
* @return \think\db\Connection
|
||||
*/
|
||||
public static 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
|
||||
*/
|
||||
public static function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
|
||||
{
|
||||
$info = pathinfo($url);
|
||||
$action = $info['basename'];
|
||||
$module = '.' != $info['dirname'] ? $info['dirname'] : Request::instance()->controller();
|
||||
$class = self::controller($module, $layer, $appendSuffix);
|
||||
|
||||
if ($class) {
|
||||
if (is_scalar($vars)) {
|
||||
if (strpos($vars, '=')) {
|
||||
parse_str($vars, $vars);
|
||||
} else {
|
||||
$vars = [$vars];
|
||||
}
|
||||
}
|
||||
|
||||
return App::invokeMethod([$class, $action . Config::get('action_suffix')], $vars);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串命名风格转换
|
||||
* type 0 将 Java 风格转换为 C 的风格 1 将 C 风格转换为 Java 的风格
|
||||
* type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
|
||||
* @access public
|
||||
* @param string $name 字符串
|
||||
* @param integer $type 转换类型
|
||||
* @param string $name 字符串
|
||||
* @param integer $type 转换类型
|
||||
* @param bool $ucfirst 首字母是否大写(驼峰规则)
|
||||
* @return string
|
||||
*/
|
||||
@@ -614,7 +375,6 @@ class Loader
|
||||
$name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
|
||||
return strtoupper($match[1]);
|
||||
}, $name);
|
||||
|
||||
return $ucfirst ? ucfirst($name) : lcfirst($name);
|
||||
}
|
||||
|
||||
@@ -622,43 +382,28 @@ class Loader
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析应用类的类名
|
||||
* 创建工厂对象实例
|
||||
* @access public
|
||||
* @param string $module 模块名
|
||||
* @param string $layer 层名 controller model ...
|
||||
* @param string $name 类名
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @return string
|
||||
* @param string $name 工厂类名
|
||||
* @param string $namespace 默认命名空间
|
||||
* @return mixed
|
||||
*/
|
||||
public static function parseClass($module, $layer, $name, $appendSuffix = false)
|
||||
public static function factory($name, $namespace = '', ...$args)
|
||||
{
|
||||
$class = false !== strpos($name, '\\') ? $name : $namespace . ucwords($name);
|
||||
|
||||
$array = explode('\\', str_replace(['/', '.'], '\\', $name));
|
||||
$class = self::parseName(array_pop($array), 1);
|
||||
$class = $class . (App::$suffix || $appendSuffix ? ucfirst($layer) : '');
|
||||
$path = $array ? implode('\\', $array) . '\\' : '';
|
||||
|
||||
return App::$namespace . '\\' .
|
||||
($module ? $module . '\\' : '') .
|
||||
$layer . '\\' . $path . $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化类的实例
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function clearInstance()
|
||||
{
|
||||
self::$instance = [];
|
||||
if (class_exists($class)) {
|
||||
return Container::getInstance()->invokeClass($class, $args);
|
||||
} else {
|
||||
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 作用范围隔离
|
||||
|
||||
/**
|
||||
* include
|
||||
* @param string $file 文件路径
|
||||
* 作用范围隔离
|
||||
*
|
||||
* @param $file
|
||||
* @return mixed
|
||||
*/
|
||||
function __include_file($file)
|
||||
@@ -666,11 +411,6 @@ function __include_file($file)
|
||||
return include $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* require
|
||||
* @param string $file 文件路径
|
||||
* @return mixed
|
||||
*/
|
||||
function __require_file($file)
|
||||
{
|
||||
return require $file;
|
||||
|
||||
@@ -11,134 +11,184 @@
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\exception\ClassNotFoundException;
|
||||
|
||||
/**
|
||||
* Class Log
|
||||
* @package think
|
||||
*
|
||||
* @method void log($msg) static 记录一般日志
|
||||
* @method void error($msg) static 记录错误日志
|
||||
* @method void info($msg) static 记录一般信息日志
|
||||
* @method void sql($msg) static 记录 SQL 查询日志
|
||||
* @method void notice($msg) static 记录提示日志
|
||||
* @method void alert($msg) static 记录报警日志
|
||||
*/
|
||||
class Log
|
||||
class Log implements LoggerInterface
|
||||
{
|
||||
const LOG = 'log';
|
||||
const ERROR = 'error';
|
||||
const INFO = 'info';
|
||||
const SQL = 'sql';
|
||||
const NOTICE = 'notice';
|
||||
const ALERT = 'alert';
|
||||
const DEBUG = 'debug';
|
||||
const EMERGENCY = 'emergency';
|
||||
const ALERT = 'alert';
|
||||
const CRITICAL = 'critical';
|
||||
const ERROR = 'error';
|
||||
const WARNING = 'warning';
|
||||
const NOTICE = 'notice';
|
||||
const INFO = 'info';
|
||||
const DEBUG = 'debug';
|
||||
const SQL = 'sql';
|
||||
|
||||
/**
|
||||
* @var array 日志信息
|
||||
* 日志信息
|
||||
* @var array
|
||||
*/
|
||||
protected static $log = [];
|
||||
protected $log = [];
|
||||
|
||||
/**
|
||||
* @var array 配置参数
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected static $config = [];
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* @var array 日志类型
|
||||
* 日志写入驱动
|
||||
* @var object
|
||||
*/
|
||||
protected static $type = ['log', 'error', 'info', 'sql', 'notice', 'alert', 'debug'];
|
||||
protected $driver;
|
||||
|
||||
/**
|
||||
* @var log\driver\File|log\driver\Test|log\driver\Socket 日志写入驱动
|
||||
* 日志授权key
|
||||
* @var string
|
||||
*/
|
||||
protected static $driver;
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* @var string 当前日志授权 key
|
||||
* 是否允许日志写入
|
||||
* @var bool
|
||||
*/
|
||||
protected static $key;
|
||||
protected $allowWrite = true;
|
||||
|
||||
/**
|
||||
* 应用对象
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
public static function __make(App $app, Config $config)
|
||||
{
|
||||
return (new static($app))->init($config->pull('log'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志初始化
|
||||
* @access public
|
||||
* @param array $config 配置参数
|
||||
* @return void
|
||||
* @param array $config
|
||||
* @return $this
|
||||
*/
|
||||
public static function init($config = [])
|
||||
public function init($config = [])
|
||||
{
|
||||
$type = isset($config['type']) ? $config['type'] : 'File';
|
||||
$class = false !== strpos($type, '\\') ? $type : '\\think\\log\\driver\\' . ucwords($type);
|
||||
$type = isset($config['type']) ? $config['type'] : 'File';
|
||||
|
||||
$this->config = $config;
|
||||
|
||||
self::$config = $config;
|
||||
unset($config['type']);
|
||||
|
||||
if (class_exists($class)) {
|
||||
self::$driver = new $class($config);
|
||||
} else {
|
||||
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
||||
if (!empty($config['close'])) {
|
||||
$this->allowWrite = false;
|
||||
}
|
||||
|
||||
// 记录初始化信息
|
||||
App::$debug && Log::record('[ LOG ] INIT ' . $type, 'info');
|
||||
$this->driver = Loader::factory($type, '\\think\\log\\driver\\', $config);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日志信息
|
||||
* @access public
|
||||
* @param string $type 信息类型
|
||||
* @return array|string
|
||||
* @return array
|
||||
*/
|
||||
public static function getLog($type = '')
|
||||
public function getLog($type = '')
|
||||
{
|
||||
return $type ? self::$log[$type] : self::$log;
|
||||
return $type ? $this->log[$type] : $this->log;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录调试信息
|
||||
* 记录日志信息
|
||||
* @access public
|
||||
* @param mixed $msg 调试信息
|
||||
* @param string $type 信息类型
|
||||
* @return void
|
||||
* @param mixed $msg 日志信息
|
||||
* @param string $type 日志级别
|
||||
* @param array $context 替换内容
|
||||
* @return $this
|
||||
*/
|
||||
public static function record($msg, $type = 'log')
|
||||
public function record($msg, $type = 'info', array $context = [])
|
||||
{
|
||||
self::$log[$type][] = $msg;
|
||||
if (!$this->allowWrite) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 命令行下面日志写入改进
|
||||
IS_CLI && self::save();
|
||||
if (is_string($msg) && !empty($context)) {
|
||||
$replace = [];
|
||||
foreach ($context as $key => $val) {
|
||||
$replace['{' . $key . '}'] = $val;
|
||||
}
|
||||
|
||||
$msg = strtr($msg, $replace);
|
||||
}
|
||||
|
||||
if (PHP_SAPI == 'cli') {
|
||||
if (empty($this->config['level']) || in_array($type, $this->config['level'])) {
|
||||
// 命令行日志实时写入
|
||||
$this->write($msg, $type, true);
|
||||
}
|
||||
} else {
|
||||
$this->log[$type][] = $msg;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空日志信息
|
||||
* @access public
|
||||
* @return void
|
||||
* @return $this
|
||||
*/
|
||||
public static function clear()
|
||||
public function clear()
|
||||
{
|
||||
self::$log = [];
|
||||
$this->log = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前日志记录的授权 key
|
||||
* 当前日志记录的授权key
|
||||
* @access public
|
||||
* @param string $key 授权 key
|
||||
* @return void
|
||||
* @param string $key 授权key
|
||||
* @return $this
|
||||
*/
|
||||
public static function key($key)
|
||||
public function key($key)
|
||||
{
|
||||
self::$key = $key;
|
||||
$this->key = $key;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查日志写入权限
|
||||
* @access public
|
||||
* @param array $config 当前日志配置参数
|
||||
* @param array $config 当前日志配置参数
|
||||
* @return bool
|
||||
*/
|
||||
public static function check($config)
|
||||
public function check($config)
|
||||
{
|
||||
return !self::$key || empty($config['allow_key']) || in_array(self::$key, $config['allow_key']);
|
||||
if ($this->key && !empty($config['allow_key']) && !in_array($this->key, $config['allow_key'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭本次请求日志写入
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->allowWrite = false;
|
||||
$this->log = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,41 +196,36 @@ class Log
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public static function save()
|
||||
public function save()
|
||||
{
|
||||
// 没有需要保存的记录则直接返回
|
||||
if (empty(self::$log)) {
|
||||
if (empty($this->log) || !$this->allowWrite) {
|
||||
return true;
|
||||
}
|
||||
|
||||
is_null(self::$driver) && self::init(Config::get('log'));
|
||||
|
||||
// 检测日志写入权限
|
||||
if (!self::check(self::$config)) {
|
||||
if (!$this->check($this->config)) {
|
||||
// 检测日志写入权限
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty(self::$config['level'])) {
|
||||
// 获取全部日志
|
||||
$log = self::$log;
|
||||
if (!App::$debug && isset($log['debug'])) {
|
||||
unset($log['debug']);
|
||||
$log = [];
|
||||
|
||||
foreach ($this->log as $level => $info) {
|
||||
if (!$this->app->isDebug() && 'debug' == $level) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// 记录允许级别
|
||||
$log = [];
|
||||
foreach (self::$config['level'] as $level) {
|
||||
if (isset(self::$log[$level])) {
|
||||
$log[$level] = self::$log[$level];
|
||||
}
|
||||
|
||||
if (empty($this->config['level']) || in_array($level, $this->config['level'])) {
|
||||
$log[$level] = $info;
|
||||
|
||||
$this->app['hook']->listen('log_level', [$level, $info]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($result = self::$driver->save($log, true)) {
|
||||
self::$log = [];
|
||||
}
|
||||
$result = $this->driver->save($log, true);
|
||||
|
||||
Hook::listen('log_write_done', $log);
|
||||
if ($result) {
|
||||
$this->log = [];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
@@ -189,49 +234,156 @@ class Log
|
||||
* 实时写入日志信息 并支持行为
|
||||
* @access public
|
||||
* @param mixed $msg 调试信息
|
||||
* @param string $type 信息类型
|
||||
* @param string $type 日志级别
|
||||
* @param bool $force 是否强制写入
|
||||
* @return bool
|
||||
*/
|
||||
public static function write($msg, $type = 'log', $force = false)
|
||||
public function write($msg, $type = 'info', $force = false)
|
||||
{
|
||||
$log = self::$log;
|
||||
// 封装日志信息
|
||||
if (empty($this->config['level'])) {
|
||||
$force = true;
|
||||
}
|
||||
|
||||
// 如果不是强制写入,而且信息类型不在可记录的类别中则直接返回 false 不做记录
|
||||
if (true !== $force && !empty(self::$config['level']) && !in_array($type, self::$config['level'])) {
|
||||
if (true === $force || in_array($type, $this->config['level'])) {
|
||||
$log[$type][] = $msg;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 封装日志信息
|
||||
$log[$type][] = $msg;
|
||||
|
||||
// 监听 log_write
|
||||
Hook::listen('log_write', $log);
|
||||
|
||||
is_null(self::$driver) && self::init(Config::get('log'));
|
||||
// 监听log_write
|
||||
$this->app['hook']->listen('log_write', $log);
|
||||
|
||||
// 写入日志
|
||||
if ($result = self::$driver->save($log, false)) {
|
||||
self::$log = [];
|
||||
}
|
||||
|
||||
return $result;
|
||||
return $this->driver->save($log, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法调用
|
||||
* 记录日志信息
|
||||
* @access public
|
||||
* @param string $method 调用方法
|
||||
* @param mixed $args 参数
|
||||
* @param string $level 日志级别
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public static function __callStatic($method, $args)
|
||||
public function log($level, $message, array $context = [])
|
||||
{
|
||||
if (in_array($method, self::$type)) {
|
||||
array_push($args, $method);
|
||||
|
||||
call_user_func_array('\\think\\Log::record', $args);
|
||||
}
|
||||
$this->record($message, $level, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录emergency信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function emergency($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录警报信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function alert($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录紧急情况
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function critical($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录错误信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function error($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录warning信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function warning($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录notice信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function notice($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录一般信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function info($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录调试信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function debug($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录sql信息
|
||||
* @access public
|
||||
* @param mixed $message 日志信息
|
||||
* @param array $context 替换内容
|
||||
* @return void
|
||||
*/
|
||||
public function sql($message, array $context = [])
|
||||
{
|
||||
$this->log(__FUNCTION__, $message, $context);
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['app']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,28 +20,52 @@ use Traversable;
|
||||
|
||||
abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
|
||||
{
|
||||
/** @var bool 是否为简洁模式 */
|
||||
/**
|
||||
* 是否简洁模式
|
||||
* @var bool
|
||||
*/
|
||||
protected $simple = false;
|
||||
|
||||
/** @var Collection 数据集 */
|
||||
/**
|
||||
* 数据集
|
||||
* @var Collection
|
||||
*/
|
||||
protected $items;
|
||||
|
||||
/** @var integer 当前页 */
|
||||
/**
|
||||
* 当前页
|
||||
* @var integer
|
||||
*/
|
||||
protected $currentPage;
|
||||
|
||||
/** @var integer 最后一页 */
|
||||
/**
|
||||
* 最后一页
|
||||
* @var integer
|
||||
*/
|
||||
protected $lastPage;
|
||||
|
||||
/** @var integer|null 数据总数 */
|
||||
/**
|
||||
* 数据总数
|
||||
* @var integer|null
|
||||
*/
|
||||
protected $total;
|
||||
|
||||
/** @var integer 每页的数量 */
|
||||
/**
|
||||
* 每页数量
|
||||
* @var integer
|
||||
*/
|
||||
protected $listRows;
|
||||
|
||||
/** @var bool 是否有下一页 */
|
||||
/**
|
||||
* 是否有下一页
|
||||
* @var bool
|
||||
*/
|
||||
protected $hasMore;
|
||||
|
||||
/** @var array 一些配置 */
|
||||
/**
|
||||
* 分页配置
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [
|
||||
'var_page' => 'page',
|
||||
'path' => '/',
|
||||
@@ -49,9 +73,6 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
'fragment' => '',
|
||||
];
|
||||
|
||||
/** @var mixed simple模式下的下个元素 */
|
||||
protected $nextItem;
|
||||
|
||||
public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
|
||||
{
|
||||
$this->options = array_merge($this->options, $options);
|
||||
@@ -68,10 +89,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
if ($simple) {
|
||||
$this->currentPage = $this->setCurrentPage($currentPage);
|
||||
$this->hasMore = count($items) > ($this->listRows);
|
||||
if ($this->hasMore) {
|
||||
$this->nextItem = $items->slice($this->listRows, 1);
|
||||
}
|
||||
$items = $items->slice(0, $this->listRows);
|
||||
$items = $items->slice(0, $this->listRows);
|
||||
} else {
|
||||
$this->total = $total;
|
||||
$this->lastPage = (int) ceil($total / $listRows);
|
||||
@@ -82,11 +100,12 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
}
|
||||
|
||||
/**
|
||||
* @access public
|
||||
* @param $items
|
||||
* @param $listRows
|
||||
* @param null $currentPage
|
||||
* @param bool $simple
|
||||
* @param null $total
|
||||
* @param bool $simple
|
||||
* @param array $options
|
||||
* @return Paginator
|
||||
*/
|
||||
@@ -107,7 +126,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
/**
|
||||
* 获取页码对应的链接
|
||||
*
|
||||
* @param $page
|
||||
* @access protected
|
||||
* @param $page
|
||||
* @return string
|
||||
*/
|
||||
protected function url($page)
|
||||
@@ -123,27 +143,31 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
$parameters = [];
|
||||
$path = str_replace('[PAGE]', $page, $this->options['path']);
|
||||
}
|
||||
|
||||
if (count($this->options['query']) > 0) {
|
||||
$parameters = array_merge($this->options['query'], $parameters);
|
||||
}
|
||||
|
||||
$url = $path;
|
||||
if (!empty($parameters)) {
|
||||
$url .= '?' . http_build_query($parameters, null, '&');
|
||||
}
|
||||
|
||||
return $url . $this->buildFragment();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动获取当前页码
|
||||
* @param string $varPage
|
||||
* @param int $default
|
||||
* @access public
|
||||
* @param string $varPage
|
||||
* @param int $default
|
||||
* @return int
|
||||
*/
|
||||
public static function getCurrentPage($varPage = 'page', $default = 1)
|
||||
{
|
||||
$page = (int) Request::instance()->param($varPage);
|
||||
$page = Container::get('request')->param($varPage);
|
||||
|
||||
if (filter_var($page, FILTER_VALIDATE_INT) !== false && $page >= 1) {
|
||||
if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) {
|
||||
return $page;
|
||||
}
|
||||
|
||||
@@ -152,11 +176,12 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
|
||||
/**
|
||||
* 自动获取当前的path
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public static function getCurrentPath()
|
||||
{
|
||||
return Request::instance()->baseUrl();
|
||||
return Container::get('request')->baseUrl();
|
||||
}
|
||||
|
||||
public function total()
|
||||
@@ -164,6 +189,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
if ($this->simple) {
|
||||
throw new \DomainException('not support total');
|
||||
}
|
||||
|
||||
return $this->total;
|
||||
}
|
||||
|
||||
@@ -182,11 +208,13 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
if ($this->simple) {
|
||||
throw new \DomainException('not support last');
|
||||
}
|
||||
|
||||
return $this->lastPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据是否足够分页
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasPages()
|
||||
@@ -197,6 +225,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
/**
|
||||
* 创建一组分页链接
|
||||
*
|
||||
* @access public
|
||||
* @param int $start
|
||||
* @param int $end
|
||||
* @return array
|
||||
@@ -215,18 +244,21 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
/**
|
||||
* 设置URL锚点
|
||||
*
|
||||
* @access public
|
||||
* @param string|null $fragment
|
||||
* @return $this
|
||||
*/
|
||||
public function fragment($fragment)
|
||||
{
|
||||
$this->options['fragment'] = $fragment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加URL参数
|
||||
*
|
||||
* @access public
|
||||
* @param array|string $key
|
||||
* @param string|null $value
|
||||
* @return $this
|
||||
@@ -251,6 +283,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
/**
|
||||
* 构造锚点字符串
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
protected function buildFragment()
|
||||
@@ -260,6 +293,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
|
||||
/**
|
||||
* 渲染分页html
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function render();
|
||||
@@ -282,6 +316,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
/**
|
||||
* 给每个元素执行个回调
|
||||
*
|
||||
* @access public
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
@@ -289,6 +324,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
{
|
||||
foreach ($this->items as $key => $item) {
|
||||
$result = $callback($item, $key);
|
||||
|
||||
if (false === $result) {
|
||||
break;
|
||||
} elseif (!is_object($item)) {
|
||||
@@ -301,6 +337,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
|
||||
/**
|
||||
* Retrieve an external iterator
|
||||
* @access public
|
||||
* @return Traversable An instance of an object implementing <b>Iterator</b> or
|
||||
* <b>Traversable</b>
|
||||
*/
|
||||
@@ -311,7 +348,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
|
||||
/**
|
||||
* Whether a offset exists
|
||||
* @param mixed $offset
|
||||
* @access public
|
||||
* @param mixed $offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
@@ -321,7 +359,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
|
||||
/**
|
||||
* Offset to retrieve
|
||||
* @param mixed $offset
|
||||
* @access public
|
||||
* @param mixed $offset
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
@@ -331,8 +370,9 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
|
||||
/**
|
||||
* Offset to set
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
* @access public
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
@@ -341,9 +381,10 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
|
||||
/**
|
||||
* Offset to unset
|
||||
* @param mixed $offset
|
||||
* @access public
|
||||
* @param mixed $offset
|
||||
* @return void
|
||||
* @since 5.0.0
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
@@ -365,24 +406,19 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
if ($this->simple) {
|
||||
return [
|
||||
'per_page' => $this->listRows,
|
||||
'current_page' => $this->currentPage,
|
||||
'has_more' => $this->hasMore,
|
||||
'next_item' => $this->nextItem,
|
||||
'data' => $this->items->toArray(),
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'total' => $this->total,
|
||||
'per_page' => $this->listRows,
|
||||
'current_page' => $this->currentPage,
|
||||
'last_page' => $this->lastPage,
|
||||
'data' => $this->items->toArray(),
|
||||
];
|
||||
try {
|
||||
$total = $this->total();
|
||||
} catch (\DomainException $e) {
|
||||
$total = null;
|
||||
}
|
||||
|
||||
return [
|
||||
'total' => $total,
|
||||
'per_page' => $this->listRows(),
|
||||
'current_page' => $this->currentPage(),
|
||||
'last_page' => $this->lastPage,
|
||||
'data' => $this->items->toArray(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -114,12 +114,13 @@ class Process
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param string $commandline 指令
|
||||
* @param string|null $cwd 工作目录
|
||||
* @param array|null $env 环境变量
|
||||
* @param string|null $input 输入
|
||||
* @param int|float|null $timeout 超时时间
|
||||
* @param array $options proc_open的选项
|
||||
* @access public
|
||||
* @param string $commandline 指令
|
||||
* @param string|null $cwd 工作目录
|
||||
* @param array|null $env 环境变量
|
||||
* @param string|null $input 输入
|
||||
* @param int|float|null $timeout 超时时间
|
||||
* @param array $options proc_open的选项
|
||||
* @throws \RuntimeException
|
||||
* @api
|
||||
*/
|
||||
@@ -132,7 +133,7 @@ class Process
|
||||
$this->commandline = $commandline;
|
||||
$this->cwd = $cwd;
|
||||
|
||||
if (null === $this->cwd && (defined('ZEND_THREAD_SAFE') || '\\' === DS)) {
|
||||
if (null === $this->cwd && (defined('ZEND_THREAD_SAFE') || '\\' === DIRECTORY_SEPARATOR)) {
|
||||
$this->cwd = getcwd();
|
||||
}
|
||||
if (null !== $env) {
|
||||
@@ -141,10 +142,10 @@ class Process
|
||||
|
||||
$this->input = $input;
|
||||
$this->setTimeout($timeout);
|
||||
$this->useFileHandles = '\\' === DS;
|
||||
$this->useFileHandles = '\\' === DIRECTORY_SEPARATOR;
|
||||
$this->pty = false;
|
||||
$this->enhanceWindowsCompatibility = true;
|
||||
$this->enhanceSigchildCompatibility = '\\' !== DS && $this->isSigchildEnabled();
|
||||
$this->enhanceSigchildCompatibility = '\\' !== DIRECTORY_SEPARATOR && $this->isSigchildEnabled();
|
||||
$this->options = array_replace([
|
||||
'suppress_errors' => true,
|
||||
'binary_pipes' => true,
|
||||
@@ -163,7 +164,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 运行指令
|
||||
* @param callback|null $callback
|
||||
* @access public
|
||||
* @param callback|null $callback
|
||||
* @return int
|
||||
*/
|
||||
public function run($callback = null)
|
||||
@@ -175,7 +177,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 运行指令
|
||||
* @param callable|null $callback
|
||||
* @access public
|
||||
* @param callable|null $callback
|
||||
* @return self
|
||||
* @throws \RuntimeException
|
||||
* @throws ProcessFailedException
|
||||
@@ -195,7 +198,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 启动进程并写到 STDIN 输入后返回。
|
||||
* @param callable|null $callback
|
||||
* @access public
|
||||
* @param callable|null $callback
|
||||
* @throws \RuntimeException
|
||||
* @throws \RuntimeException
|
||||
* @throws \LogicException
|
||||
@@ -216,7 +220,7 @@ class Process
|
||||
|
||||
$commandline = $this->commandline;
|
||||
|
||||
if ('\\' === DS && $this->enhanceWindowsCompatibility) {
|
||||
if ('\\' === DIRECTORY_SEPARATOR && $this->enhanceWindowsCompatibility) {
|
||||
$commandline = 'cmd /V:ON /E:ON /C "(' . $commandline . ')';
|
||||
foreach ($this->processPipes->getFiles() as $offset => $filename) {
|
||||
$commandline .= ' ' . $offset . '>' . Utils::escapeArgument($filename);
|
||||
@@ -245,7 +249,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 重启进程
|
||||
* @param callable|null $callback
|
||||
* @access public
|
||||
* @param callable|null $callback
|
||||
* @return Process
|
||||
* @throws \RuntimeException
|
||||
* @throws \RuntimeException
|
||||
@@ -264,7 +269,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 等待要终止的进程
|
||||
* @param callable|null $callback
|
||||
* @access public
|
||||
* @param callable|null $callback
|
||||
* @return int
|
||||
*/
|
||||
public function wait($callback = null)
|
||||
@@ -278,8 +284,8 @@ class Process
|
||||
|
||||
do {
|
||||
$this->checkTimeout();
|
||||
$running = '\\' === DS ? $this->isRunning() : $this->processPipes->areOpen();
|
||||
$close = '\\' !== DS || !$running;
|
||||
$running = '\\' === DIRECTORY_SEPARATOR ? $this->isRunning() : $this->processPipes->areOpen();
|
||||
$close = '\\' !== DIRECTORY_SEPARATOR || !$running;
|
||||
$this->readPipes(true, $close);
|
||||
} while ($running);
|
||||
|
||||
@@ -296,6 +302,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 获取PID
|
||||
* @access public
|
||||
* @return int|null
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
@@ -312,7 +319,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 将一个 POSIX 信号发送到进程中
|
||||
* @param int $signal
|
||||
* @access public
|
||||
* @param int $signal
|
||||
* @return Process
|
||||
*/
|
||||
public function signal($signal)
|
||||
@@ -324,6 +332,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 禁用从底层过程获取输出和错误输出。
|
||||
* @access public
|
||||
* @return Process
|
||||
*/
|
||||
public function disableOutput()
|
||||
@@ -342,6 +351,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 开启从底层过程获取输出和错误输出。
|
||||
* @access public
|
||||
* @return Process
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
@@ -358,6 +368,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 输出是否禁用
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isOutputDisabled()
|
||||
@@ -367,9 +378,9 @@ class Process
|
||||
|
||||
/**
|
||||
* 获取当前的输出管道
|
||||
* @access public
|
||||
* @return string
|
||||
* @throws \LogicException
|
||||
* @throws \LogicException
|
||||
* @api
|
||||
*/
|
||||
public function getOutput()
|
||||
@@ -380,13 +391,14 @@ class Process
|
||||
|
||||
$this->requireProcessIsStarted(__FUNCTION__);
|
||||
|
||||
$this->readPipes(false, '\\' === DS ? !$this->processInformation['running'] : true);
|
||||
$this->readPipes(false, '\\' === DIRECTORY_SEPARATOR ? !$this->processInformation['running'] : true);
|
||||
|
||||
return $this->stdout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以增量方式返回的输出结果。
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getIncrementalOutput()
|
||||
@@ -408,6 +420,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 清空输出
|
||||
* @access public
|
||||
* @return Process
|
||||
*/
|
||||
public function clearOutput()
|
||||
@@ -420,6 +433,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 返回当前的错误输出的过程 (STDERR)。
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorOutput()
|
||||
@@ -430,13 +444,14 @@ class Process
|
||||
|
||||
$this->requireProcessIsStarted(__FUNCTION__);
|
||||
|
||||
$this->readPipes(false, '\\' === DS ? !$this->processInformation['running'] : true);
|
||||
$this->readPipes(false, '\\' === DIRECTORY_SEPARATOR ? !$this->processInformation['running'] : true);
|
||||
|
||||
return $this->stderr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以增量方式返回 errorOutput
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getIncrementalErrorOutput()
|
||||
@@ -458,6 +473,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 清空 errorOutput
|
||||
* @access public
|
||||
* @return Process
|
||||
*/
|
||||
public function clearErrorOutput()
|
||||
@@ -470,6 +486,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 获取退出码
|
||||
* @access public
|
||||
* @return null|int
|
||||
*/
|
||||
public function getExitCode()
|
||||
@@ -485,6 +502,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 获取退出文本
|
||||
* @access public
|
||||
* @return null|string
|
||||
*/
|
||||
public function getExitCodeText()
|
||||
@@ -498,6 +516,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 检查是否成功
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isSuccessful()
|
||||
@@ -507,6 +526,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 是否未捕获的信号已被终止子进程
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBeenSignaled()
|
||||
@@ -524,6 +544,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 返回导致子进程终止其执行的数。
|
||||
* @access public
|
||||
* @return int
|
||||
*/
|
||||
public function getTermSignal()
|
||||
@@ -541,6 +562,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 检查子进程信号是否已停止
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBeenStopped()
|
||||
@@ -554,6 +576,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 返回导致子进程停止其执行的数。
|
||||
* @access public
|
||||
* @return int
|
||||
*/
|
||||
public function getStopSignal()
|
||||
@@ -567,6 +590,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 检查是否正在运行
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isRunning()
|
||||
@@ -582,6 +606,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 检查是否已开始
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isStarted()
|
||||
@@ -591,6 +616,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 检查是否已终止
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isTerminated()
|
||||
@@ -602,6 +628,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 获取当前的状态
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getStatus()
|
||||
@@ -613,11 +640,12 @@ class Process
|
||||
|
||||
/**
|
||||
* 终止进程
|
||||
* @access public
|
||||
*/
|
||||
public function stop()
|
||||
{
|
||||
if ($this->isRunning()) {
|
||||
if ('\\' === DS && !$this->isSigchildEnabled()) {
|
||||
if ('\\' === DIRECTORY_SEPARATOR && !$this->isSigchildEnabled()) {
|
||||
exec(sprintf('taskkill /F /T /PID %d 2>&1', $this->getPid()), $output, $exitCode);
|
||||
if ($exitCode > 0) {
|
||||
throw new \RuntimeException('Unable to kill the process');
|
||||
@@ -642,6 +670,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 添加一行输出
|
||||
* @access public
|
||||
* @param string $line
|
||||
*/
|
||||
public function addOutput($line)
|
||||
@@ -652,6 +681,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 添加一行错误输出
|
||||
* @access public
|
||||
* @param string $line
|
||||
*/
|
||||
public function addErrorOutput($line)
|
||||
@@ -662,6 +692,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 获取被执行的指令
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getCommandLine()
|
||||
@@ -671,6 +702,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 设置指令
|
||||
* @access public
|
||||
* @param string $commandline
|
||||
* @return self
|
||||
*/
|
||||
@@ -683,6 +715,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 获取超时时间
|
||||
* @access public
|
||||
* @return float|null
|
||||
*/
|
||||
public function getTimeout()
|
||||
@@ -692,6 +725,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 获取idle超时时间
|
||||
* @access public
|
||||
* @return float|null
|
||||
*/
|
||||
public function getIdleTimeout()
|
||||
@@ -701,7 +735,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 设置超时时间
|
||||
* @param int|float|null $timeout
|
||||
* @access public
|
||||
* @param int|float|null $timeout
|
||||
* @return self
|
||||
*/
|
||||
public function setTimeout($timeout)
|
||||
@@ -713,7 +748,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 设置idle超时时间
|
||||
* @param int|float|null $timeout
|
||||
* @access public
|
||||
* @param int|float|null $timeout
|
||||
* @return self
|
||||
*/
|
||||
public function setIdleTimeout($timeout)
|
||||
@@ -729,12 +765,13 @@ class Process
|
||||
|
||||
/**
|
||||
* 设置TTY
|
||||
* @param bool $tty
|
||||
* @access public
|
||||
* @param bool $tty
|
||||
* @return self
|
||||
*/
|
||||
public function setTty($tty)
|
||||
{
|
||||
if ('\\' === DS && $tty) {
|
||||
if ('\\' === DIRECTORY_SEPARATOR && $tty) {
|
||||
throw new \RuntimeException('TTY mode is not supported on Windows platform.');
|
||||
}
|
||||
if ($tty && (!file_exists('/dev/tty') || !is_readable('/dev/tty'))) {
|
||||
@@ -748,6 +785,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 检查是否是tty模式
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isTty()
|
||||
@@ -757,7 +795,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 设置pty模式
|
||||
* @param bool $bool
|
||||
* @access public
|
||||
* @param bool $bool
|
||||
* @return self
|
||||
*/
|
||||
public function setPty($bool)
|
||||
@@ -769,6 +808,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 是否是pty模式
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isPty()
|
||||
@@ -778,6 +818,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 获取工作目录
|
||||
* @access public
|
||||
* @return string|null
|
||||
*/
|
||||
public function getWorkingDirectory()
|
||||
@@ -791,7 +832,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 设置工作目录
|
||||
* @param string $cwd
|
||||
* @access public
|
||||
* @param string $cwd
|
||||
* @return self
|
||||
*/
|
||||
public function setWorkingDirectory($cwd)
|
||||
@@ -803,6 +845,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 获取环境变量
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getEnv()
|
||||
@@ -812,7 +855,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 设置环境变量
|
||||
* @param array $env
|
||||
* @access public
|
||||
* @param array $env
|
||||
* @return self
|
||||
*/
|
||||
public function setEnv(array $env)
|
||||
@@ -831,6 +875,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 获取输入
|
||||
* @access public
|
||||
* @return null|string
|
||||
*/
|
||||
public function getInput()
|
||||
@@ -840,7 +885,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 设置输入
|
||||
* @param mixed $input
|
||||
* @access public
|
||||
* @param mixed $input
|
||||
* @return self
|
||||
*/
|
||||
public function setInput($input)
|
||||
@@ -856,6 +902,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 获取proc_open的选项
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getOptions()
|
||||
@@ -865,7 +912,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 设置proc_open的选项
|
||||
* @param array $options
|
||||
* @access public
|
||||
* @param array $options
|
||||
* @return self
|
||||
*/
|
||||
public function setOptions(array $options)
|
||||
@@ -877,6 +925,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 是否兼容windows
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function getEnhanceWindowsCompatibility()
|
||||
@@ -886,7 +935,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 设置是否兼容windows
|
||||
* @param bool $enhance
|
||||
* @access public
|
||||
* @param bool $enhance
|
||||
* @return self
|
||||
*/
|
||||
public function setEnhanceWindowsCompatibility($enhance)
|
||||
@@ -898,6 +948,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 返回是否 sigchild 兼容模式激活
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function getEnhanceSigchildCompatibility()
|
||||
@@ -907,7 +958,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 激活 sigchild 兼容性模式。
|
||||
* @param bool $enhance
|
||||
* @access public
|
||||
* @param bool $enhance
|
||||
* @return self
|
||||
*/
|
||||
public function setEnhanceSigchildCompatibility($enhance)
|
||||
@@ -941,6 +993,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 是否支持pty
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public static function isPtySupported()
|
||||
@@ -951,7 +1004,7 @@ class Process
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ('\\' === DS) {
|
||||
if ('\\' === DIRECTORY_SEPARATOR) {
|
||||
return $result = false;
|
||||
}
|
||||
|
||||
@@ -967,11 +1020,12 @@ class Process
|
||||
|
||||
/**
|
||||
* 创建所需的 proc_open 的描述符
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function getDescriptors()
|
||||
{
|
||||
if ('\\' === DS) {
|
||||
if ('\\' === DIRECTORY_SEPARATOR) {
|
||||
$this->processPipes = WindowsPipes::create($this, $this->input);
|
||||
} else {
|
||||
$this->processPipes = UnixPipes::create($this, $this->input);
|
||||
@@ -990,7 +1044,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 建立 wait () 使用的回调。
|
||||
* @param callable|null $callback
|
||||
* @access protected
|
||||
* @param callable|null $callback
|
||||
* @return callable
|
||||
*/
|
||||
protected function buildCallback($callback)
|
||||
@@ -1013,6 +1068,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 更新状态
|
||||
* @access protected
|
||||
* @param bool $blocking
|
||||
*/
|
||||
protected function updateStatus($blocking)
|
||||
@@ -1024,7 +1080,7 @@ class Process
|
||||
$this->processInformation = proc_get_status($this->process);
|
||||
$this->captureExitCode();
|
||||
|
||||
$this->readPipes($blocking, '\\' === DS ? !$this->processInformation['running'] : true);
|
||||
$this->readPipes($blocking, '\\' === DIRECTORY_SEPARATOR ? !$this->processInformation['running'] : true);
|
||||
|
||||
if (!$this->processInformation['running']) {
|
||||
$this->close();
|
||||
@@ -1033,6 +1089,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 是否开启 '--enable-sigchild'
|
||||
* @access protected
|
||||
* @return bool
|
||||
*/
|
||||
protected function isSigchildEnabled()
|
||||
@@ -1053,7 +1110,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 验证是否超时
|
||||
* @param int|float|null $timeout
|
||||
* @access private
|
||||
* @param int|float|null $timeout
|
||||
* @return float|null
|
||||
*/
|
||||
private function validateTimeout($timeout)
|
||||
@@ -1071,8 +1129,9 @@ class Process
|
||||
|
||||
/**
|
||||
* 读取pipes
|
||||
* @param bool $blocking
|
||||
* @param bool $close
|
||||
* @access private
|
||||
* @param bool $blocking
|
||||
* @param bool $close
|
||||
*/
|
||||
private function readPipes($blocking, $close)
|
||||
{
|
||||
@@ -1100,6 +1159,7 @@ class Process
|
||||
|
||||
/**
|
||||
* 关闭资源
|
||||
* @access private
|
||||
* @return int 退出码
|
||||
*/
|
||||
private function close()
|
||||
@@ -1146,8 +1206,9 @@ class Process
|
||||
|
||||
/**
|
||||
* 将一个 POSIX 信号发送到进程中。
|
||||
* @param int $signal
|
||||
* @param bool $throwException
|
||||
* @access private
|
||||
* @param int $signal
|
||||
* @param bool $throwException
|
||||
* @return bool
|
||||
*/
|
||||
private function doSignal($signal, $throwException)
|
||||
@@ -1183,7 +1244,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 确保进程已经开启
|
||||
* @param string $functionName
|
||||
* @access private
|
||||
* @param string $functionName
|
||||
*/
|
||||
private function requireProcessIsStarted($functionName)
|
||||
{
|
||||
@@ -1194,7 +1256,8 @@ class Process
|
||||
|
||||
/**
|
||||
* 确保进程已经终止
|
||||
* @param string $functionName
|
||||
* @access private
|
||||
* @param string $functionName
|
||||
*/
|
||||
private function requireProcessIsTerminated($functionName)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,100 +11,135 @@
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\response\Json as JsonResponse;
|
||||
use think\response\Jsonp as JsonpResponse;
|
||||
use think\response\Redirect as RedirectResponse;
|
||||
use think\response\View as ViewResponse;
|
||||
use think\response\Xml as XmlResponse;
|
||||
|
||||
class Response
|
||||
{
|
||||
// 原始数据
|
||||
/**
|
||||
* 原始数据
|
||||
* @var mixed
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
// 当前的contentType
|
||||
/**
|
||||
* 应用对象实例
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* 当前contentType
|
||||
* @var string
|
||||
*/
|
||||
protected $contentType = 'text/html';
|
||||
|
||||
// 字符集
|
||||
/**
|
||||
* 字符集
|
||||
* @var string
|
||||
*/
|
||||
protected $charset = 'utf-8';
|
||||
|
||||
//状态
|
||||
/**
|
||||
* 状态码
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 200;
|
||||
|
||||
// 输出参数
|
||||
/**
|
||||
* 是否允许请求缓存
|
||||
* @var bool
|
||||
*/
|
||||
protected $allowCache = true;
|
||||
|
||||
/**
|
||||
* 输出参数
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
// header参数
|
||||
|
||||
/**
|
||||
* header参数
|
||||
* @var array
|
||||
*/
|
||||
protected $header = [];
|
||||
|
||||
/**
|
||||
* 输出内容
|
||||
* @var string
|
||||
*/
|
||||
protected $content = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param mixed $data 输出数据
|
||||
* @param int $code
|
||||
* @param array $header
|
||||
* @param array $options 输出参数
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param mixed $data 输出数据
|
||||
* @param int $code
|
||||
* @param array $header
|
||||
* @param array $options 输出参数
|
||||
*/
|
||||
public function __construct($data = '', $code = 200, array $header = [], $options = [])
|
||||
{
|
||||
$this->data($data);
|
||||
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
$this->contentType($this->contentType, $this->charset);
|
||||
$this->header = array_merge($this->header, $header);
|
||||
|
||||
$this->code = $code;
|
||||
$this->app = Container::get('app');
|
||||
$this->header = array_merge($this->header, $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Response对象
|
||||
* @access public
|
||||
* @param mixed $data 输出数据
|
||||
* @param string $type 输出类型
|
||||
* @param int $code
|
||||
* @param array $header
|
||||
* @param array $options 输出参数
|
||||
* @return Response|JsonResponse|ViewResponse|XmlResponse|RedirectResponse|JsonpResponse
|
||||
* @param mixed $data 输出数据
|
||||
* @param string $type 输出类型
|
||||
* @param int $code
|
||||
* @param array $header
|
||||
* @param array $options 输出参数
|
||||
* @return Response
|
||||
*/
|
||||
public static function create($data = '', $type = '', $code = 200, array $header = [], $options = [])
|
||||
{
|
||||
$class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst(strtolower($type));
|
||||
|
||||
if (class_exists($class)) {
|
||||
$response = new $class($data, $code, $header, $options);
|
||||
} else {
|
||||
$response = new static($data, $code, $header, $options);
|
||||
return new $class($data, $code, $header, $options);
|
||||
}
|
||||
|
||||
return $response;
|
||||
return new static($data, $code, $header, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送数据到客户端
|
||||
* @access public
|
||||
* @return mixed
|
||||
* @return void
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function send()
|
||||
{
|
||||
// 监听response_send
|
||||
Hook::listen('response_send', $this);
|
||||
$this->app['hook']->listen('response_send', $this);
|
||||
|
||||
// 处理输出数据
|
||||
$data = $this->getContent();
|
||||
|
||||
// Trace调试注入
|
||||
if (Env::get('app_trace', Config::get('app_trace'))) {
|
||||
Debug::inject($this, $data);
|
||||
if ('cli' != PHP_SAPI && $this->app['env']->get('app_trace', $this->app->config('app.app_trace'))) {
|
||||
$this->app['debug']->inject($this, $data);
|
||||
}
|
||||
|
||||
if (200 == $this->code) {
|
||||
$cache = Request::instance()->getCache();
|
||||
if (200 == $this->code && $this->allowCache) {
|
||||
$cache = $this->app['request']->getCache();
|
||||
if ($cache) {
|
||||
$this->header['Cache-Control'] = 'max-age=' . $cache[1] . ',must-revalidate';
|
||||
$this->header['Last-Modified'] = gmdate('D, d M Y H:i:s') . ' GMT';
|
||||
$this->header['Expires'] = gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME'] + $cache[1]) . ' GMT';
|
||||
Cache::tag($cache[2])->set($cache[0], [$data, $this->header], $cache[1]);
|
||||
|
||||
$this->app['cache']->tag($cache[2])->set($cache[0], [$data, $this->header], $cache[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,15 +148,11 @@ class Response
|
||||
http_response_code($this->code);
|
||||
// 发送头部信息
|
||||
foreach ($this->header as $name => $val) {
|
||||
if (is_null($val)) {
|
||||
header($name);
|
||||
} else {
|
||||
header($name . ':' . $val);
|
||||
}
|
||||
header($name . (!is_null($val) ? ':' . $val : ''));
|
||||
}
|
||||
}
|
||||
|
||||
echo $data;
|
||||
$this->sendData($data);
|
||||
|
||||
if (function_exists('fastcgi_finish_request')) {
|
||||
// 提高页面响应
|
||||
@@ -129,18 +160,18 @@ class Response
|
||||
}
|
||||
|
||||
// 监听response_end
|
||||
Hook::listen('response_end', $this);
|
||||
$this->app['hook']->listen('response_end', $this);
|
||||
|
||||
// 清空当次请求有效的数据
|
||||
if (!($this instanceof RedirectResponse)) {
|
||||
Session::flush();
|
||||
$this->app['session']->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理数据
|
||||
* @access protected
|
||||
* @param mixed $data 要处理的数据
|
||||
* @param mixed $data 要处理的数据
|
||||
* @return mixed
|
||||
*/
|
||||
protected function output($data)
|
||||
@@ -148,35 +179,61 @@ class Response
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出数据
|
||||
* @access protected
|
||||
* @param string $data 要处理的数据
|
||||
* @return void
|
||||
*/
|
||||
protected function sendData($data)
|
||||
{
|
||||
echo $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出的参数
|
||||
* @access public
|
||||
* @param mixed $options 输出参数
|
||||
* @param mixed $options 输出参数
|
||||
* @return $this
|
||||
*/
|
||||
public function options($options = [])
|
||||
{
|
||||
$this->options = array_merge($this->options, $options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出数据设置
|
||||
* @access public
|
||||
* @param mixed $data 输出数据
|
||||
* @param mixed $data 输出数据
|
||||
* @return $this
|
||||
*/
|
||||
public function data($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许请求缓存
|
||||
* @access public
|
||||
* @param bool $cache 允许请求缓存
|
||||
* @return $this
|
||||
*/
|
||||
public function allowCache($cache)
|
||||
{
|
||||
$this->allowCache = $cache;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应头
|
||||
* @access public
|
||||
* @param string|array $name 参数名
|
||||
* @param string $value 参数值
|
||||
* @param string|array $name 参数名
|
||||
* @param string $value 参数值
|
||||
* @return $this
|
||||
*/
|
||||
public function header($name, $value = null)
|
||||
@@ -186,12 +243,14 @@ class Response
|
||||
} else {
|
||||
$this->header[$name] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置页面输出内容
|
||||
* @param $content
|
||||
* @access public
|
||||
* @param mixed $content
|
||||
* @return $this
|
||||
*/
|
||||
public function content($content)
|
||||
@@ -211,87 +270,114 @@ class Response
|
||||
|
||||
/**
|
||||
* 发送HTTP状态
|
||||
* @param integer $code 状态码
|
||||
* @access public
|
||||
* @param integer $code 状态码
|
||||
* @return $this
|
||||
*/
|
||||
public function code($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* LastModified
|
||||
* @param string $time
|
||||
* @access public
|
||||
* @param string $time
|
||||
* @return $this
|
||||
*/
|
||||
public function lastModified($time)
|
||||
{
|
||||
$this->header['Last-Modified'] = $time;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expires
|
||||
* @param string $time
|
||||
* @access public
|
||||
* @param string $time
|
||||
* @return $this
|
||||
*/
|
||||
public function expires($time)
|
||||
{
|
||||
$this->header['Expires'] = $time;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* ETag
|
||||
* @param string $eTag
|
||||
* @access public
|
||||
* @param string $eTag
|
||||
* @return $this
|
||||
*/
|
||||
public function eTag($eTag)
|
||||
{
|
||||
$this->header['ETag'] = $eTag;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面缓存控制
|
||||
* @param string $cache 状态码
|
||||
* @access public
|
||||
* @param string $cache 缓存设置
|
||||
* @return $this
|
||||
*/
|
||||
public function cacheControl($cache)
|
||||
{
|
||||
$this->header['Cache-control'] = $cache;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置页面不做任何缓存
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function noCache()
|
||||
{
|
||||
$this->header['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0';
|
||||
$this->header['Pragma'] = 'no-cache';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面输出类型
|
||||
* @param string $contentType 输出类型
|
||||
* @param string $charset 输出编码
|
||||
* @access public
|
||||
* @param string $contentType 输出类型
|
||||
* @param string $charset 输出编码
|
||||
* @return $this
|
||||
*/
|
||||
public function contentType($contentType, $charset = 'utf-8')
|
||||
{
|
||||
$this->header['Content-Type'] = $contentType . '; charset=' . $charset;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取头部信息
|
||||
* @param string $name 头部名称
|
||||
* @access public
|
||||
* @param string $name 头部名称
|
||||
* @return mixed
|
||||
*/
|
||||
public function getHeader($name = '')
|
||||
{
|
||||
if (!empty($name)) {
|
||||
return isset($this->header[$name]) ? $this->header[$name] : null;
|
||||
} else {
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始数据
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData()
|
||||
@@ -301,6 +387,7 @@ class Response
|
||||
|
||||
/**
|
||||
* 获取输出数据
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function getContent()
|
||||
@@ -318,15 +405,25 @@ class Response
|
||||
|
||||
$this->content = (string) $content;
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态码
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getCode()
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['app']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,37 +15,115 @@ use think\exception\ClassNotFoundException;
|
||||
|
||||
class Session
|
||||
{
|
||||
protected static $prefix = '';
|
||||
protected static $init = null;
|
||||
/**
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* 前缀
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix = '';
|
||||
|
||||
/**
|
||||
* 是否初始化
|
||||
* @var bool
|
||||
*/
|
||||
protected $init = null;
|
||||
|
||||
/**
|
||||
* 锁驱动
|
||||
* @var object
|
||||
*/
|
||||
protected $lockDriver = null;
|
||||
|
||||
/**
|
||||
* 锁key
|
||||
* @var string
|
||||
*/
|
||||
protected $sessKey = 'PHPSESSID';
|
||||
|
||||
/**
|
||||
* 锁超时时间
|
||||
* @var integer
|
||||
*/
|
||||
protected $lockTimeout = 3;
|
||||
|
||||
/**
|
||||
* 是否启用锁机制
|
||||
* @var bool
|
||||
*/
|
||||
protected $lock = false;
|
||||
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置或者获取session作用域(前缀)
|
||||
* @param string $prefix
|
||||
* @access public
|
||||
* @param string $prefix
|
||||
* @return string|void
|
||||
*/
|
||||
public static function prefix($prefix = '')
|
||||
public function prefix($prefix = '')
|
||||
{
|
||||
empty(self::$init) && self::boot();
|
||||
empty($this->init) && $this->boot();
|
||||
|
||||
if (empty($prefix) && null !== $prefix) {
|
||||
return self::$prefix;
|
||||
return $this->prefix;
|
||||
} else {
|
||||
self::$prefix = $prefix;
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
}
|
||||
|
||||
public static function __make(Config $config)
|
||||
{
|
||||
return new static($config->pull('session'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置
|
||||
* @access public
|
||||
* @param array $config
|
||||
* @return void
|
||||
*/
|
||||
public function setConfig(array $config = [])
|
||||
{
|
||||
$this->config = array_merge($this->config, array_change_key_case($config));
|
||||
|
||||
if (isset($config['prefix'])) {
|
||||
$this->prefix = $config['prefix'];
|
||||
}
|
||||
|
||||
if (isset($config['use_lock'])) {
|
||||
$this->lock = $config['use_lock'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置已经初始化
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function inited()
|
||||
{
|
||||
$this->init = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* session初始化
|
||||
* @param array $config
|
||||
* @access public
|
||||
* @param array $config
|
||||
* @return void
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public static function init(array $config = [])
|
||||
public function init(array $config = [])
|
||||
{
|
||||
if (empty($config)) {
|
||||
$config = Config::get('session');
|
||||
}
|
||||
// 记录初始化信息
|
||||
App::$debug && Log::record('[ SESSION ] INIT ' . var_export($config, true), 'info');
|
||||
$config = $config ?: $this->config;
|
||||
|
||||
$isDoStart = false;
|
||||
if (isset($config['use_trans_sid'])) {
|
||||
ini_set('session.use_trans_sid', $config['use_trans_sid'] ? 1 : 0);
|
||||
@@ -57,42 +135,57 @@ class Session
|
||||
$isDoStart = true;
|
||||
}
|
||||
|
||||
if (isset($config['prefix']) && ('' === self::$prefix || null === self::$prefix)) {
|
||||
self::$prefix = $config['prefix'];
|
||||
if (isset($config['prefix'])) {
|
||||
$this->prefix = $config['prefix'];
|
||||
}
|
||||
|
||||
if (isset($config['use_lock'])) {
|
||||
$this->lock = $config['use_lock'];
|
||||
}
|
||||
|
||||
if (isset($config['var_session_id']) && isset($_REQUEST[$config['var_session_id']])) {
|
||||
session_id($_REQUEST[$config['var_session_id']]);
|
||||
} elseif (isset($config['id']) && !empty($config['id'])) {
|
||||
session_id($config['id']);
|
||||
}
|
||||
|
||||
if (isset($config['name'])) {
|
||||
session_name($config['name']);
|
||||
}
|
||||
|
||||
if (isset($config['path'])) {
|
||||
session_save_path($config['path']);
|
||||
}
|
||||
|
||||
if (isset($config['domain'])) {
|
||||
ini_set('session.cookie_domain', $config['domain']);
|
||||
}
|
||||
|
||||
if (isset($config['expire'])) {
|
||||
ini_set('session.gc_maxlifetime', $config['expire']);
|
||||
ini_set('session.cookie_lifetime', $config['expire']);
|
||||
}
|
||||
|
||||
if (isset($config['secure'])) {
|
||||
ini_set('session.cookie_secure', $config['secure']);
|
||||
}
|
||||
|
||||
if (isset($config['httponly'])) {
|
||||
ini_set('session.cookie_httponly', $config['httponly']);
|
||||
}
|
||||
|
||||
if (isset($config['use_cookies'])) {
|
||||
ini_set('session.use_cookies', $config['use_cookies'] ? 1 : 0);
|
||||
}
|
||||
|
||||
if (isset($config['cache_limiter'])) {
|
||||
session_cache_limiter($config['cache_limiter']);
|
||||
}
|
||||
|
||||
if (isset($config['cache_expire'])) {
|
||||
session_cache_expire($config['cache_expire']);
|
||||
}
|
||||
|
||||
if (!empty($config['type'])) {
|
||||
// 读取session驱动
|
||||
$class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\session\\driver\\' . ucwords($config['type']);
|
||||
@@ -102,42 +195,51 @@ class Session
|
||||
throw new ClassNotFoundException('error session handler:' . $class, $class);
|
||||
}
|
||||
}
|
||||
|
||||
if ($isDoStart) {
|
||||
session_start();
|
||||
self::$init = true;
|
||||
$this->start();
|
||||
} else {
|
||||
self::$init = false;
|
||||
$this->init = false;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* session自动启动或者初始化
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function boot()
|
||||
public function boot()
|
||||
{
|
||||
if (is_null(self::$init)) {
|
||||
self::init();
|
||||
} elseif (false === self::$init) {
|
||||
if (is_null($this->init)) {
|
||||
$this->init();
|
||||
}
|
||||
|
||||
if (false === $this->init) {
|
||||
if (PHP_SESSION_ACTIVE != session_status()) {
|
||||
session_start();
|
||||
$this->start();
|
||||
}
|
||||
self::$init = true;
|
||||
$this->init = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* session设置
|
||||
* @param string $name session名称
|
||||
* @param mixed $value session值
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @access public
|
||||
* @param string $name session名称
|
||||
* @param mixed $value session值
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return void
|
||||
*/
|
||||
public static function set($name, $value = '', $prefix = null)
|
||||
public function set($name, $value, $prefix = null)
|
||||
{
|
||||
empty(self::$init) && self::boot();
|
||||
$this->lock();
|
||||
|
||||
empty($this->init) && $this->boot();
|
||||
|
||||
$prefix = !is_null($prefix) ? $prefix : $this->prefix;
|
||||
|
||||
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
|
||||
if (strpos($name, '.')) {
|
||||
// 二维数组赋值
|
||||
list($name1, $name2) = explode('.', $name);
|
||||
@@ -151,51 +253,130 @@ class Session
|
||||
} else {
|
||||
$_SESSION[$name] = $value;
|
||||
}
|
||||
|
||||
$this->unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* session获取
|
||||
* @param string $name session名称
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @access public
|
||||
* @param string $name session名称
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($name = '', $prefix = null)
|
||||
public function get($name = '', $prefix = null)
|
||||
{
|
||||
empty(self::$init) && self::boot();
|
||||
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
|
||||
if ('' == $name) {
|
||||
// 获取全部的session
|
||||
$value = $prefix ? (!empty($_SESSION[$prefix]) ? $_SESSION[$prefix] : []) : $_SESSION;
|
||||
} elseif ($prefix) {
|
||||
// 获取session
|
||||
if (strpos($name, '.')) {
|
||||
list($name1, $name2) = explode('.', $name);
|
||||
$value = isset($_SESSION[$prefix][$name1][$name2]) ? $_SESSION[$prefix][$name1][$name2] : null;
|
||||
} else {
|
||||
$value = isset($_SESSION[$prefix][$name]) ? $_SESSION[$prefix][$name] : null;
|
||||
}
|
||||
} else {
|
||||
if (strpos($name, '.')) {
|
||||
list($name1, $name2) = explode('.', $name);
|
||||
$value = isset($_SESSION[$name1][$name2]) ? $_SESSION[$name1][$name2] : null;
|
||||
} else {
|
||||
$value = isset($_SESSION[$name]) ? $_SESSION[$name] : null;
|
||||
$this->lock();
|
||||
|
||||
empty($this->init) && $this->boot();
|
||||
|
||||
$prefix = !is_null($prefix) ? $prefix : $this->prefix;
|
||||
|
||||
$value = $prefix ? (!empty($_SESSION[$prefix]) ? $_SESSION[$prefix] : []) : $_SESSION;
|
||||
|
||||
if ('' != $name) {
|
||||
$name = explode('.', $name);
|
||||
|
||||
foreach ($name as $val) {
|
||||
if (isset($value[$val])) {
|
||||
$value = $value[$val];
|
||||
} else {
|
||||
$value = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->unlock();
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* session 读写锁驱动实例化
|
||||
*/
|
||||
protected function initDriver()
|
||||
{
|
||||
$config = $this->config;
|
||||
|
||||
if (!empty($config['type']) && isset($config['use_lock']) && $config['use_lock']) {
|
||||
// 读取session驱动
|
||||
$class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\session\\driver\\' . ucwords($config['type']);
|
||||
|
||||
// 检查驱动类及类中是否存在 lock 和 unlock 函数
|
||||
if (class_exists($class) && method_exists($class, 'lock') && method_exists($class, 'unlock')) {
|
||||
$this->lockDriver = new $class($config);
|
||||
}
|
||||
}
|
||||
|
||||
// 通过cookie获得session_id
|
||||
if (isset($config['name']) && $config['name']) {
|
||||
$this->sessKey = $config['name'];
|
||||
}
|
||||
|
||||
if (isset($config['lock_timeout']) && $config['lock_timeout'] > 0) {
|
||||
$this->lockTimeout = $config['lock_timeout'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* session 读写加锁
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function lock()
|
||||
{
|
||||
if (empty($this->lock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->initDriver();
|
||||
|
||||
if (null !== $this->lockDriver && method_exists($this->lockDriver, 'lock')) {
|
||||
$t = time();
|
||||
// 使用 session_id 作为互斥条件,即只对同一 session_id 的会话互斥。第一次请求没有 session_id
|
||||
$sessID = isset($_COOKIE[$this->sessKey]) ? $_COOKIE[$this->sessKey] : '';
|
||||
|
||||
do {
|
||||
if (time() - $t > $this->lockTimeout) {
|
||||
$this->unlock();
|
||||
}
|
||||
} while (!$this->lockDriver->lock($sessID, $this->lockTimeout));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* session 读写解锁
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function unlock()
|
||||
{
|
||||
if (empty($this->lock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->pause();
|
||||
|
||||
if ($this->lockDriver && method_exists($this->lockDriver, 'unlock')) {
|
||||
$sessID = isset($_COOKIE[$this->sessKey]) ? $_COOKIE[$this->sessKey] : '';
|
||||
$this->lockDriver->unlock($sessID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* session获取并删除
|
||||
* @param string $name session名称
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @access public
|
||||
* @param string $name session名称
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return mixed
|
||||
*/
|
||||
public static function pull($name, $prefix = null)
|
||||
public function pull($name, $prefix = null)
|
||||
{
|
||||
$result = self::get($name, $prefix);
|
||||
$result = $this->get($name, $prefix);
|
||||
|
||||
if ($result) {
|
||||
self::delete($name, $prefix);
|
||||
$this->delete($name, $prefix);
|
||||
return $result;
|
||||
} else {
|
||||
return;
|
||||
@@ -204,53 +385,63 @@ class Session
|
||||
|
||||
/**
|
||||
* session设置 下一次请求有效
|
||||
* @param string $name session名称
|
||||
* @param mixed $value session值
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @access public
|
||||
* @param string $name session名称
|
||||
* @param mixed $value session值
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return void
|
||||
*/
|
||||
public static function flash($name, $value)
|
||||
public function flash($name, $value)
|
||||
{
|
||||
self::set($name, $value);
|
||||
if (!self::has('__flash__.__time__')) {
|
||||
self::set('__flash__.__time__', $_SERVER['REQUEST_TIME_FLOAT']);
|
||||
$this->set($name, $value);
|
||||
|
||||
if (!$this->has('__flash__.__time__')) {
|
||||
$this->set('__flash__.__time__', $_SERVER['REQUEST_TIME_FLOAT']);
|
||||
}
|
||||
self::push('__flash__', $name);
|
||||
|
||||
$this->push('__flash__', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空当前请求的session数据
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function flush()
|
||||
public function flush()
|
||||
{
|
||||
if (self::$init) {
|
||||
$item = self::get('__flash__');
|
||||
if (!$this->init) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($item)) {
|
||||
$time = $item['__time__'];
|
||||
if ($_SERVER['REQUEST_TIME_FLOAT'] > $time) {
|
||||
unset($item['__time__']);
|
||||
self::delete($item);
|
||||
self::set('__flash__', []);
|
||||
}
|
||||
$item = $this->get('__flash__');
|
||||
|
||||
if (!empty($item)) {
|
||||
$time = $item['__time__'];
|
||||
|
||||
if ($_SERVER['REQUEST_TIME_FLOAT'] > $time) {
|
||||
unset($item['__time__']);
|
||||
$this->delete($item);
|
||||
$this->set('__flash__', []);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除session数据
|
||||
* @param string|array $name session名称
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @access public
|
||||
* @param string|array $name session名称
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return void
|
||||
*/
|
||||
public static function delete($name, $prefix = null)
|
||||
public function delete($name, $prefix = null)
|
||||
{
|
||||
empty(self::$init) && self::boot();
|
||||
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
|
||||
empty($this->init) && $this->boot();
|
||||
|
||||
$prefix = !is_null($prefix) ? $prefix : $this->prefix;
|
||||
|
||||
if (is_array($name)) {
|
||||
foreach ($name as $key) {
|
||||
self::delete($key, $prefix);
|
||||
$this->delete($key, $prefix);
|
||||
}
|
||||
} elseif (strpos($name, '.')) {
|
||||
list($name1, $name2) = explode('.', $name);
|
||||
@@ -270,13 +461,15 @@ class Session
|
||||
|
||||
/**
|
||||
* 清空session数据
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @access public
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return void
|
||||
*/
|
||||
public static function clear($prefix = null)
|
||||
public function clear($prefix = null)
|
||||
{
|
||||
empty(self::$init) && self::boot();
|
||||
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
|
||||
empty($this->init) && $this->boot();
|
||||
$prefix = !is_null($prefix) ? $prefix : $this->prefix;
|
||||
|
||||
if ($prefix) {
|
||||
unset($_SESSION[$prefix]);
|
||||
} else {
|
||||
@@ -286,81 +479,101 @@ class Session
|
||||
|
||||
/**
|
||||
* 判断session数据
|
||||
* @param string $name session名称
|
||||
* @param string|null $prefix
|
||||
* @access public
|
||||
* @param string $name session名称
|
||||
* @param string|null $prefix
|
||||
* @return bool
|
||||
*/
|
||||
public static function has($name, $prefix = null)
|
||||
public function has($name, $prefix = null)
|
||||
{
|
||||
empty(self::$init) && self::boot();
|
||||
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
|
||||
if (strpos($name, '.')) {
|
||||
// 支持数组
|
||||
list($name1, $name2) = explode('.', $name);
|
||||
return $prefix ? isset($_SESSION[$prefix][$name1][$name2]) : isset($_SESSION[$name1][$name2]);
|
||||
} else {
|
||||
return $prefix ? isset($_SESSION[$prefix][$name]) : isset($_SESSION[$name]);
|
||||
empty($this->init) && $this->boot();
|
||||
|
||||
$prefix = !is_null($prefix) ? $prefix : $this->prefix;
|
||||
$value = $prefix ? (!empty($_SESSION[$prefix]) ? $_SESSION[$prefix] : []) : $_SESSION;
|
||||
|
||||
$name = explode('.', $name);
|
||||
|
||||
foreach ($name as $val) {
|
||||
if (!isset($value[$val])) {
|
||||
return false;
|
||||
} else {
|
||||
$value = $value[$val];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加数据到一个session数组
|
||||
* @access public
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public static function push($key, $value)
|
||||
public function push($key, $value)
|
||||
{
|
||||
$array = self::get($key);
|
||||
$array = $this->get($key);
|
||||
|
||||
if (is_null($array)) {
|
||||
$array = [];
|
||||
}
|
||||
|
||||
$array[] = $value;
|
||||
self::set($key, $array);
|
||||
|
||||
$this->set($key, $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动session
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function start()
|
||||
public function start()
|
||||
{
|
||||
session_start();
|
||||
self::$init = true;
|
||||
|
||||
$this->init = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁session
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function destroy()
|
||||
public function destroy()
|
||||
{
|
||||
if (!empty($_SESSION)) {
|
||||
$_SESSION = [];
|
||||
}
|
||||
|
||||
session_unset();
|
||||
session_destroy();
|
||||
self::$init = null;
|
||||
|
||||
$this->init = null;
|
||||
$this->lockDriver = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新生成session_id
|
||||
* @param bool $delete 是否删除关联会话文件
|
||||
* @access public
|
||||
* @param bool $delete 是否删除关联会话文件
|
||||
* @return void
|
||||
*/
|
||||
public static function regenerate($delete = false)
|
||||
public function regenerate($delete = false)
|
||||
{
|
||||
session_regenerate_id($delete);
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停session
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function pause()
|
||||
public function pause()
|
||||
{
|
||||
// 暂停session
|
||||
session_write_close();
|
||||
self::$init = false;
|
||||
$this->init = false;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,39 +13,88 @@ namespace think;
|
||||
|
||||
class Url
|
||||
{
|
||||
// 生成URL地址的root
|
||||
protected static $root;
|
||||
protected static $bindCheck;
|
||||
/**
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* ROOT地址
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* 绑定检查
|
||||
* @var bool
|
||||
*/
|
||||
protected $bindCheck;
|
||||
|
||||
/**
|
||||
* 应用对象
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
public function __construct(App $app, array $config = [])
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->config = $config;
|
||||
|
||||
if (is_file($app->getRuntimePath() . 'route.php')) {
|
||||
// 读取路由映射文件
|
||||
$app['route']->setName(include $app->getRuntimePath() . 'route.php');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @access public
|
||||
* @param array $config
|
||||
* @return void
|
||||
*/
|
||||
public function init(array $config = [])
|
||||
{
|
||||
$this->config = array_merge($this->config, array_change_key_case($config));
|
||||
}
|
||||
|
||||
public static function __make(App $app, Config $config)
|
||||
{
|
||||
return new static($app, $config->pull('app'));
|
||||
}
|
||||
|
||||
/**
|
||||
* URL生成 支持路由反射
|
||||
* @param string $url 路由地址
|
||||
* @param string|array $vars 参数(支持数组和字符串)a=val&b=val2... ['a'=>'val1', 'b'=>'val2']
|
||||
* @param string|bool $suffix 伪静态后缀,默认为true表示获取配置值
|
||||
* @param boolean|string $domain 是否显示域名 或者直接传入域名
|
||||
* @access public
|
||||
* @param string $url 路由地址
|
||||
* @param string|array $vars 参数(支持数组和字符串)a=val&b=val2... ['a'=>'val1', 'b'=>'val2']
|
||||
* @param string|bool $suffix 伪静态后缀,默认为true表示获取配置值
|
||||
* @param boolean|string $domain 是否显示域名 或者直接传入域名
|
||||
* @return string
|
||||
*/
|
||||
public static function build($url = '', $vars = '', $suffix = true, $domain = false)
|
||||
public function build($url = '', $vars = '', $suffix = true, $domain = false)
|
||||
{
|
||||
if (false === $domain && Route::rules('domain')) {
|
||||
$domain = true;
|
||||
}
|
||||
// 解析URL
|
||||
if (0 === strpos($url, '[') && $pos = strpos($url, ']')) {
|
||||
// [name] 表示使用路由命名标识生成URL
|
||||
$name = substr($url, 1, $pos - 1);
|
||||
$url = 'name' . substr($url, $pos + 1);
|
||||
}
|
||||
|
||||
if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
|
||||
$info = parse_url($url);
|
||||
$url = !empty($info['path']) ? $info['path'] : '';
|
||||
|
||||
if (isset($info['fragment'])) {
|
||||
// 解析锚点
|
||||
$anchor = $info['fragment'];
|
||||
|
||||
if (false !== strpos($anchor, '?')) {
|
||||
// 解析参数
|
||||
list($anchor, $info['query']) = explode('?', $anchor, 2);
|
||||
}
|
||||
|
||||
if (false !== strpos($anchor, '@')) {
|
||||
// 解析域名
|
||||
list($anchor, $domain) = explode('@', $anchor, 2);
|
||||
@@ -63,23 +112,28 @@ class Url
|
||||
}
|
||||
|
||||
if ($url) {
|
||||
$rule = Route::name(isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : ''));
|
||||
$checkName = isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : '');
|
||||
$checkDomain = $domain && is_string($domain) ? $domain : null;
|
||||
|
||||
$rule = $this->app['route']->getName($checkName, $checkDomain);
|
||||
|
||||
if (is_null($rule) && isset($info['query'])) {
|
||||
$rule = Route::name($url);
|
||||
$rule = $this->app['route']->getName($url);
|
||||
// 解析地址里面参数 合并到vars
|
||||
parse_str($info['query'], $params);
|
||||
$vars = array_merge($params, $vars);
|
||||
unset($info['query']);
|
||||
}
|
||||
}
|
||||
if (!empty($rule) && $match = self::getRuleUrl($rule, $vars)) {
|
||||
|
||||
if (!empty($rule) && $match = $this->getRuleUrl($rule, $vars, $domain)) {
|
||||
// 匹配路由命名标识
|
||||
$url = $match[0];
|
||||
// 替换可选分隔符
|
||||
$url = preg_replace(['/(\W)\?$/', '/(\W)\?/'], ['', '\1'], $url);
|
||||
if (!empty($match[1])) {
|
||||
|
||||
if ($domain) {
|
||||
$domain = $match[1];
|
||||
}
|
||||
|
||||
if (!is_null($match[2])) {
|
||||
$suffix = $match[2];
|
||||
}
|
||||
@@ -87,14 +141,14 @@ class Url
|
||||
throw new \InvalidArgumentException('route name not exists:' . $name);
|
||||
} else {
|
||||
// 检查别名路由
|
||||
$alias = Route::rules('alias');
|
||||
$alias = $this->app['route']->getAlias();
|
||||
$matchAlias = false;
|
||||
|
||||
if ($alias) {
|
||||
// 别名路由解析
|
||||
foreach ($alias as $key => $val) {
|
||||
if (is_array($val)) {
|
||||
$val = $val[0];
|
||||
}
|
||||
foreach ($alias as $key => $item) {
|
||||
$val = $item->getRoute();
|
||||
|
||||
if (0 === strpos($url, $val)) {
|
||||
$url = $key . substr($url, strlen($val));
|
||||
$matchAlias = true;
|
||||
@@ -102,10 +156,31 @@ class Url
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$matchAlias) {
|
||||
// 路由标识不存在 直接解析
|
||||
$url = self::parseUrl($url, $domain);
|
||||
$url = $this->parseUrl($url);
|
||||
}
|
||||
|
||||
// 检测URL绑定
|
||||
if (!$this->bindCheck) {
|
||||
$bind = $this->app['route']->getBind($domain && is_string($domain) ? $domain : null);
|
||||
|
||||
if ($bind && 0 === strpos($url, $bind)) {
|
||||
$url = substr($url, strlen($bind) + 1);
|
||||
} else {
|
||||
$binds = $this->app['route']->getBind(true);
|
||||
|
||||
foreach ($binds as $key => $val) {
|
||||
if (is_string($val) && 0 === strpos($url, $val) && substr_count($val, '/') > 1) {
|
||||
$url = substr($url, strlen($val) + 1);
|
||||
$domain = $key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($info['query'])) {
|
||||
// 解析地址里面参数 合并到vars
|
||||
parse_str($info['query'], $params);
|
||||
@@ -113,32 +188,29 @@ class Url
|
||||
}
|
||||
}
|
||||
|
||||
// 检测URL绑定
|
||||
if (!self::$bindCheck) {
|
||||
$type = Route::getBind('type');
|
||||
if ($type) {
|
||||
$bind = Route::getBind($type);
|
||||
if ($bind && 0 === strpos($url, $bind)) {
|
||||
$url = substr($url, strlen($bind) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 还原URL分隔符
|
||||
$depr = Config::get('pathinfo_depr');
|
||||
$depr = $this->config['pathinfo_depr'];
|
||||
$url = str_replace('/', $depr, $url);
|
||||
|
||||
// URL后缀
|
||||
$suffix = in_array($url, ['/', '']) ? '' : self::parseSuffix($suffix);
|
||||
if ('/' == substr($url, -1) || '' == $url) {
|
||||
$suffix = '';
|
||||
} else {
|
||||
$suffix = $this->parseSuffix($suffix);
|
||||
}
|
||||
|
||||
// 锚点
|
||||
$anchor = !empty($anchor) ? '#' . $anchor : '';
|
||||
|
||||
// 参数组装
|
||||
if (!empty($vars)) {
|
||||
// 添加参数
|
||||
if (Config::get('url_common_param')) {
|
||||
if ($this->config['url_common_param']) {
|
||||
$vars = http_build_query($vars);
|
||||
$url .= $suffix . '?' . $vars . $anchor;
|
||||
} else {
|
||||
$paramType = Config::get('url_param_type');
|
||||
$paramType = $this->config['url_param_type'];
|
||||
|
||||
foreach ($vars as $var => $val) {
|
||||
if ('' !== trim($val)) {
|
||||
if ($paramType) {
|
||||
@@ -148,24 +220,29 @@ class Url
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$url .= $suffix . $anchor;
|
||||
}
|
||||
} else {
|
||||
$url .= $suffix . $anchor;
|
||||
}
|
||||
// 检测域名
|
||||
$domain = self::parseDomain($url, $domain);
|
||||
// URL组装
|
||||
$url = $domain . rtrim(self::$root ?: Request::instance()->root(), '/') . '/' . ltrim($url, '/');
|
||||
|
||||
self::$bindCheck = false;
|
||||
// 检测域名
|
||||
$domain = $this->parseDomain($url, $domain);
|
||||
|
||||
// URL组装
|
||||
$url = $domain . rtrim($this->root ?: $this->app['request']->root(), '/') . '/' . ltrim($url, '/');
|
||||
|
||||
$this->bindCheck = false;
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
// 直接解析URL地址
|
||||
protected static function parseUrl($url, &$domain)
|
||||
protected function parseUrl($url)
|
||||
{
|
||||
$request = Request::instance();
|
||||
$request = $this->app['request'];
|
||||
|
||||
if (0 === strpos($url, '/')) {
|
||||
// 直接作为路由地址解析
|
||||
$url = substr($url, 1);
|
||||
@@ -177,42 +254,11 @@ class Url
|
||||
$url = substr($url, 1);
|
||||
} else {
|
||||
// 解析到 模块/控制器/操作
|
||||
$module = $request->module();
|
||||
$domains = Route::rules('domain');
|
||||
if (true === $domain && 2 == substr_count($url, '/')) {
|
||||
$current = $request->host();
|
||||
$match = [];
|
||||
$pos = [];
|
||||
foreach ($domains as $key => $item) {
|
||||
if (isset($item['[bind]']) && 0 === strpos($url, $item['[bind]'][0])) {
|
||||
$pos[$key] = strlen($item['[bind]'][0]) + 1;
|
||||
$match[] = $key;
|
||||
$module = '';
|
||||
}
|
||||
}
|
||||
if ($match) {
|
||||
$domain = current($match);
|
||||
foreach ($match as $item) {
|
||||
if (0 === strpos($current, $item)) {
|
||||
$domain = $item;
|
||||
}
|
||||
}
|
||||
self::$bindCheck = true;
|
||||
$url = substr($url, $pos[$domain]);
|
||||
}
|
||||
} elseif ($domain) {
|
||||
if (isset($domains[$domain]['[bind]'][0])) {
|
||||
$bindModule = $domains[$domain]['[bind]'][0];
|
||||
if ($bindModule && !in_array($bindModule[0], ['\\', '@'])) {
|
||||
$module = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
$module = $module ? $module . '/' : '';
|
||||
|
||||
$module = $request->module();
|
||||
$module = $module ? $module . '/' : '';
|
||||
$controller = $request->controller();
|
||||
|
||||
if ('' == $url) {
|
||||
// 空字符串输出当前的 模块/控制器/操作
|
||||
$action = $request->action();
|
||||
} else {
|
||||
$path = explode('/', $url);
|
||||
@@ -220,28 +266,32 @@ class Url
|
||||
$controller = empty($path) ? $controller : array_pop($path);
|
||||
$module = empty($path) ? $module : array_pop($path) . '/';
|
||||
}
|
||||
if (Config::get('url_convert')) {
|
||||
|
||||
if ($this->config['url_convert']) {
|
||||
$action = strtolower($action);
|
||||
$controller = Loader::parseName($controller);
|
||||
}
|
||||
|
||||
$url = $module . $controller . '/' . $action;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
// 检测域名
|
||||
protected static function parseDomain(&$url, $domain)
|
||||
protected function parseDomain(&$url, $domain)
|
||||
{
|
||||
if (!$domain) {
|
||||
return '';
|
||||
}
|
||||
$request = Request::instance();
|
||||
$rootDomain = Config::get('url_domain_root');
|
||||
|
||||
$rootDomain = $this->app['request']->rootDomain();
|
||||
if (true === $domain) {
|
||||
// 自动判断域名
|
||||
$domain = Config::get('app_host') ?: $request->host();
|
||||
$domain = $this->config['app_host'] ?: $this->app['request']->host();
|
||||
|
||||
$domains = $this->app['route']->getDomains();
|
||||
|
||||
$domains = Route::rules('domain');
|
||||
if ($domains) {
|
||||
$route_domain = array_keys($domains);
|
||||
foreach ($route_domain as $domain_prefix) {
|
||||
@@ -251,6 +301,7 @@ class Url
|
||||
if (is_string($rule) && false === strpos($key, '*') && 0 === strpos($url, $rule)) {
|
||||
$url = ltrim($url, $rule);
|
||||
$domain = $key;
|
||||
|
||||
// 生成对应子域名
|
||||
if (!empty($rootDomain)) {
|
||||
$domain .= $rootDomain;
|
||||
@@ -260,74 +311,102 @@ class Url
|
||||
if (!empty($rootDomain)) {
|
||||
$domain .= $rootDomain;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (empty($rootDomain)) {
|
||||
$host = Config::get('app_host') ?: $request->host();
|
||||
$rootDomain = substr_count($host, '.') > 1 ? substr(strstr($host, '.'), 1) : $host;
|
||||
}
|
||||
if (substr_count($domain, '.') < 2 && !strpos($domain, $rootDomain)) {
|
||||
$domain .= '.' . $rootDomain;
|
||||
}
|
||||
} elseif (0 !== strpos($domain, $rootDomain) && false === strpos($domain, '.')) {
|
||||
$domain .= '.' . $rootDomain;
|
||||
}
|
||||
|
||||
if (false !== strpos($domain, '://')) {
|
||||
$scheme = '';
|
||||
} else {
|
||||
$scheme = $request->isSsl() || Config::get('is_https') ? 'https://' : 'http://';
|
||||
$scheme = $this->app['request']->isSsl() || $this->config['is_https'] ? 'https://' : 'http://';
|
||||
|
||||
}
|
||||
|
||||
return $scheme . $domain;
|
||||
}
|
||||
|
||||
// 解析URL后缀
|
||||
protected static function parseSuffix($suffix)
|
||||
protected function parseSuffix($suffix)
|
||||
{
|
||||
if ($suffix) {
|
||||
$suffix = true === $suffix ? Config::get('url_html_suffix') : $suffix;
|
||||
$suffix = true === $suffix ? $this->config['url_html_suffix'] : $suffix;
|
||||
|
||||
if ($pos = strpos($suffix, '|')) {
|
||||
$suffix = substr($suffix, 0, $pos);
|
||||
}
|
||||
}
|
||||
|
||||
return (empty($suffix) || 0 === strpos($suffix, '.')) ? $suffix : '.' . $suffix;
|
||||
}
|
||||
|
||||
// 匹配路由地址
|
||||
public static function getRuleUrl($rule, &$vars = [])
|
||||
public function getRuleUrl($rule, &$vars = [], $allowDomain = '')
|
||||
{
|
||||
$port = $this->app['request']->port();
|
||||
foreach ($rule as $item) {
|
||||
list($url, $pattern, $domain, $suffix) = $item;
|
||||
if (empty($pattern)) {
|
||||
return [rtrim($url, '$'), $domain, $suffix];
|
||||
list($url, $pattern, $domain, $suffix, $method) = $item;
|
||||
|
||||
if (is_string($allowDomain) && $domain != $allowDomain) {
|
||||
continue;
|
||||
}
|
||||
$type = Config::get('url_common_param');
|
||||
|
||||
if ($port && !in_array($port, [80, 443])) {
|
||||
$domain .= ':' . $port;
|
||||
}
|
||||
|
||||
if (empty($pattern)) {
|
||||
return [rtrim($url, '?/-'), $domain, $suffix];
|
||||
}
|
||||
|
||||
$type = $this->config['url_common_param'];
|
||||
$keys = [];
|
||||
|
||||
foreach ($pattern as $key => $val) {
|
||||
if (isset($vars[$key])) {
|
||||
$url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key . '', '<' . $key . '>'], $type ? $vars[$key] : urlencode($vars[$key]), $url);
|
||||
unset($vars[$key]);
|
||||
$result = [$url, $domain, $suffix];
|
||||
$url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key, '<' . $key . '>'], $type ? $vars[$key] : urlencode($vars[$key]), $url);
|
||||
$keys[] = $key;
|
||||
$url = str_replace(['/?', '-?'], ['/', '-'], $url);
|
||||
$result = [rtrim($url, '?/-'), $domain, $suffix];
|
||||
} elseif (2 == $val) {
|
||||
$url = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url);
|
||||
$result = [$url, $domain, $suffix];
|
||||
$url = str_replace(['/?', '-?'], ['/', '-'], $url);
|
||||
$result = [rtrim($url, '?/-'), $domain, $suffix];
|
||||
} else {
|
||||
$result = null;
|
||||
$keys = [];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$vars = array_diff_key($vars, array_flip($keys));
|
||||
|
||||
if (isset($result)) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 指定当前生成URL地址的root
|
||||
public static function root($root)
|
||||
public function root($root)
|
||||
{
|
||||
self::$root = $root;
|
||||
Request::instance()->root($root);
|
||||
$this->root = $root;
|
||||
$this->app['request']->setRoot($root);
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$data = get_object_vars($this);
|
||||
unset($data['app']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,80 +13,83 @@ namespace think;
|
||||
|
||||
class View
|
||||
{
|
||||
// 视图实例
|
||||
protected static $instance;
|
||||
// 模板引擎实例
|
||||
/**
|
||||
* 模板引擎实例
|
||||
* @var object
|
||||
*/
|
||||
public $engine;
|
||||
// 模板变量
|
||||
protected $data = [];
|
||||
// 用于静态赋值的模板变量
|
||||
protected static $var = [];
|
||||
// 视图输出替换
|
||||
protected $replace = [];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param array $engine 模板引擎参数
|
||||
* @param array $replace 字符串替换参数
|
||||
* 模板变量
|
||||
* @var array
|
||||
*/
|
||||
public function __construct($engine = [], $replace = [])
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* 内容过滤
|
||||
* @var mixed
|
||||
*/
|
||||
protected $filter;
|
||||
|
||||
/**
|
||||
* 全局模板变量
|
||||
* @var array
|
||||
*/
|
||||
protected static $var = [];
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @access public
|
||||
* @param mixed $engine 模板引擎参数
|
||||
* @return $this
|
||||
*/
|
||||
public function init($engine = [])
|
||||
{
|
||||
// 初始化模板引擎
|
||||
$this->engine($engine);
|
||||
// 基础替换字符串
|
||||
$request = Request::instance();
|
||||
$base = $request->root();
|
||||
$root = strpos($base, '.') ? ltrim(dirname($base), DS) : $base;
|
||||
if ('' != $root) {
|
||||
$root = '/' . ltrim($root, '/');
|
||||
}
|
||||
$baseReplace = [
|
||||
'__ROOT__' => $root,
|
||||
'__URL__' => $base . '/' . $request->module() . '/' . Loader::parseName($request->controller()),
|
||||
'__STATIC__' => $root . '/static',
|
||||
'__CSS__' => $root . '/static/css',
|
||||
'__JS__' => $root . '/static/js',
|
||||
];
|
||||
$this->replace = array_merge($baseReplace, (array) $replace);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化视图
|
||||
* @access public
|
||||
* @param array $engine 模板引擎参数
|
||||
* @param array $replace 字符串替换参数
|
||||
* @return object
|
||||
*/
|
||||
public static function instance($engine = [], $replace = [])
|
||||
public static function __make(Config $config)
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self($engine, $replace);
|
||||
}
|
||||
return self::$instance;
|
||||
return (new static())->init($config->pull('template'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板变量静态赋值
|
||||
* @access public
|
||||
* @param mixed $name 变量名
|
||||
* @param mixed $value 变量值
|
||||
* @return void
|
||||
* @param mixed $name 变量名
|
||||
* @param mixed $value 变量值
|
||||
* @return $this
|
||||
*/
|
||||
public static function share($name, $value = '')
|
||||
public function share($name, $value = '')
|
||||
{
|
||||
if (is_array($name)) {
|
||||
self::$var = array_merge(self::$var, $name);
|
||||
} else {
|
||||
self::$var[$name] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理模板变量
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
self::$var = [];
|
||||
$this->data = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板变量赋值
|
||||
* @access public
|
||||
* @param mixed $name 变量名
|
||||
* @param mixed $value 变量值
|
||||
* @param mixed $name 变量名
|
||||
* @param mixed $value 变量值
|
||||
* @return $this
|
||||
*/
|
||||
public function assign($name, $value = '')
|
||||
@@ -96,13 +99,14 @@ class View
|
||||
} else {
|
||||
$this->data[$name] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前模板解析的引擎
|
||||
* @access public
|
||||
* @param array|string $options 引擎参数
|
||||
* @param array|string $options 引擎参数
|
||||
* @return $this
|
||||
*/
|
||||
public function engine($options = [])
|
||||
@@ -114,38 +118,66 @@ class View
|
||||
$type = !empty($options['type']) ? $options['type'] : 'Think';
|
||||
}
|
||||
|
||||
$class = false !== strpos($type, '\\') ? $type : '\\think\\view\\driver\\' . ucfirst($type);
|
||||
if (isset($options['type'])) {
|
||||
unset($options['type']);
|
||||
}
|
||||
$this->engine = new $class($options);
|
||||
|
||||
$this->engine = Loader::factory($type, '\\think\\view\\driver\\', $options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置模板引擎
|
||||
* @access private
|
||||
* @param string|array $name 参数名
|
||||
* @param mixed $value 参数值
|
||||
* @access public
|
||||
* @param string|array $name 参数名
|
||||
* @param mixed $value 参数值
|
||||
* @return $this
|
||||
*/
|
||||
public function config($name, $value = null)
|
||||
{
|
||||
$this->engine->config($name, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查模板是否存在
|
||||
* @access public
|
||||
* @param string|array $name 参数名
|
||||
* @return bool
|
||||
*/
|
||||
public function exists($name)
|
||||
{
|
||||
return $this->engine->exists($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 视图过滤
|
||||
* @access public
|
||||
* @param Callable $filter 过滤方法或闭包
|
||||
* @return $this
|
||||
*/
|
||||
public function filter($filter)
|
||||
{
|
||||
if ($filter) {
|
||||
$this->filter = $filter;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析和获取模板内容 用于输出
|
||||
* @param string $template 模板文件名或者内容
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $replace 替换内容
|
||||
* @param array $config 模板参数
|
||||
* @param bool $renderContent 是否渲染内容
|
||||
* @access public
|
||||
* @param string $template 模板文件名或者内容
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $config 模板参数
|
||||
* @param bool $renderContent 是否渲染内容
|
||||
* @return string
|
||||
* @throws Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function fetch($template = '', $vars = [], $replace = [], $config = [], $renderContent = false)
|
||||
public function fetch($template = '', $vars = [], $config = [], $renderContent = false)
|
||||
{
|
||||
// 模板变量
|
||||
$vars = array_merge(self::$var, $this->data, $vars);
|
||||
@@ -157,9 +189,6 @@ class View
|
||||
// 渲染输出
|
||||
try {
|
||||
$method = $renderContent ? 'display' : 'fetch';
|
||||
// 允许用户自定义模板的字符串替换
|
||||
$replace = array_merge($this->replace, $replace, (array) $this->engine->config('tpl_replace_string'));
|
||||
$this->engine->config('tpl_replace_string', $replace);
|
||||
$this->engine->$method($template, $vars, $config);
|
||||
} catch (\Exception $e) {
|
||||
ob_end_clean();
|
||||
@@ -168,47 +197,32 @@ class View
|
||||
|
||||
// 获取并清空缓存
|
||||
$content = ob_get_clean();
|
||||
// 内容过滤标签
|
||||
Hook::listen('view_filter', $content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 视图内容替换
|
||||
* @access public
|
||||
* @param string|array $content 被替换内容(支持批量替换)
|
||||
* @param string $replace 替换内容
|
||||
* @return $this
|
||||
*/
|
||||
public function replace($content, $replace = '')
|
||||
{
|
||||
if (is_array($content)) {
|
||||
$this->replace = array_merge($this->replace, $content);
|
||||
} else {
|
||||
$this->replace[$content] = $replace;
|
||||
if ($this->filter) {
|
||||
$content = call_user_func_array($this->filter, [$content]);
|
||||
}
|
||||
return $this;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染内容输出
|
||||
* @access public
|
||||
* @param string $content 内容
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $replace 替换内容
|
||||
* @param array $config 模板参数
|
||||
* @param string $content 内容
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $config 模板参数
|
||||
* @return mixed
|
||||
*/
|
||||
public function display($content, $vars = [], $replace = [], $config = [])
|
||||
public function display($content, $vars = [], $config = [])
|
||||
{
|
||||
return $this->fetch($content, $vars, $replace, $config, true);
|
||||
return $this->fetch($content, $vars, $config, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板变量赋值
|
||||
* @access public
|
||||
* @param string $name 变量名
|
||||
* @param mixed $value 变量值
|
||||
* @param string $name 变量名
|
||||
* @param mixed $value 变量值
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
@@ -218,7 +232,7 @@ class View
|
||||
/**
|
||||
* 取得模板显示变量的值
|
||||
* @access protected
|
||||
* @param string $name 模板变量
|
||||
* @param string $name 模板变量
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
@@ -229,7 +243,7 @@ class View
|
||||
/**
|
||||
* 检测模板变量是否设置
|
||||
* @access public
|
||||
* @param string $name 模板变量名
|
||||
* @param string $name 模板变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name)
|
||||
|
||||
197
Server/thinkphp/library/think/cache/Driver.php
vendored
197
Server/thinkphp/library/think/cache/Driver.php
vendored
@@ -11,19 +11,53 @@
|
||||
|
||||
namespace think\cache;
|
||||
|
||||
use think\Container;
|
||||
|
||||
/**
|
||||
* 缓存基础类
|
||||
*/
|
||||
abstract class Driver
|
||||
{
|
||||
/**
|
||||
* 驱动句柄
|
||||
* @var object
|
||||
*/
|
||||
protected $handler = null;
|
||||
|
||||
/**
|
||||
* 缓存读取次数
|
||||
* @var integer
|
||||
*/
|
||||
protected $readTimes = 0;
|
||||
|
||||
/**
|
||||
* 缓存写入次数
|
||||
* @var integer
|
||||
*/
|
||||
protected $writeTimes = 0;
|
||||
|
||||
/**
|
||||
* 缓存参数
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* 缓存标签
|
||||
* @var string
|
||||
*/
|
||||
protected $tag;
|
||||
|
||||
/**
|
||||
* 序列化方法
|
||||
* @var array
|
||||
*/
|
||||
protected static $serialize = ['serialize', 'unserialize', 'think_serialize:', 16];
|
||||
|
||||
/**
|
||||
* 判断缓存是否存在
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function has($name);
|
||||
@@ -31,8 +65,8 @@ abstract class Driver
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function get($name, $default = false);
|
||||
@@ -40,9 +74,9 @@ abstract class Driver
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int $expire 有效时间 0为永久
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int $expire 有效时间 0为永久
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function set($name, $value, $expire = null);
|
||||
@@ -50,8 +84,8 @@ abstract class Driver
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
abstract public function inc($name, $step = 1);
|
||||
@@ -59,8 +93,8 @@ abstract class Driver
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
abstract public function dec($name, $step = 1);
|
||||
@@ -68,7 +102,7 @@ abstract class Driver
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function rm($name);
|
||||
@@ -76,15 +110,30 @@ abstract class Driver
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function clear($tag = null);
|
||||
|
||||
/**
|
||||
* 获取有效期
|
||||
* @access protected
|
||||
* @param integer|\DateTime $expire 有效期
|
||||
* @return integer
|
||||
*/
|
||||
protected function getExpireTime($expire)
|
||||
{
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp() - time();
|
||||
}
|
||||
|
||||
return $expire;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实际的缓存标识
|
||||
* @access public
|
||||
* @param string $name 缓存名
|
||||
* @access protected
|
||||
* @param string $name 缓存名
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheKey($name)
|
||||
@@ -95,12 +144,13 @@ abstract class Driver
|
||||
/**
|
||||
* 读取缓存并删除
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return mixed
|
||||
*/
|
||||
public function pull($name)
|
||||
{
|
||||
$result = $this->get($name, false);
|
||||
|
||||
if ($result) {
|
||||
$this->rm($name);
|
||||
return $result;
|
||||
@@ -112,9 +162,9 @@ abstract class Driver
|
||||
/**
|
||||
* 如果不存在则写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int $expire 有效时间 0为永久
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int $expire 有效时间 0为永久
|
||||
* @return mixed
|
||||
*/
|
||||
public function remember($name, $value, $expire = null)
|
||||
@@ -129,14 +179,18 @@ abstract class Driver
|
||||
try {
|
||||
// 锁定
|
||||
$this->set($name . '_lock', true);
|
||||
|
||||
if ($value instanceof \Closure) {
|
||||
$value = call_user_func($value);
|
||||
// 获取缓存数据
|
||||
$value = Container::getInstance()->invokeFunction($value);
|
||||
}
|
||||
|
||||
// 缓存数据
|
||||
$this->set($name, $value, $expire);
|
||||
|
||||
// 解锁
|
||||
$this->rm($name . '_lock');
|
||||
} catch (\Exception $e) {
|
||||
// 解锁
|
||||
$this->rm($name . '_lock');
|
||||
throw $e;
|
||||
} catch (\throwable $e) {
|
||||
@@ -146,15 +200,16 @@ abstract class Driver
|
||||
} else {
|
||||
$value = $this->get($name);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存标签
|
||||
* @access public
|
||||
* @param string $name 标签名
|
||||
* @param string|array $keys 缓存标识
|
||||
* @param bool $overlay 是否覆盖
|
||||
* @param string $name 标签名
|
||||
* @param string|array $keys 缓存标识
|
||||
* @param bool $overlay 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function tag($name, $keys = null, $overlay = false)
|
||||
@@ -164,53 +219,66 @@ abstract class Driver
|
||||
} elseif (is_null($keys)) {
|
||||
$this->tag = $name;
|
||||
} else {
|
||||
$key = 'tag_' . md5($name);
|
||||
$key = $this->getTagkey($name);
|
||||
|
||||
if (is_string($keys)) {
|
||||
$keys = explode(',', $keys);
|
||||
}
|
||||
|
||||
$keys = array_map([$this, 'getCacheKey'], $keys);
|
||||
|
||||
if ($overlay) {
|
||||
$value = $keys;
|
||||
} else {
|
||||
$value = array_unique(array_merge($this->getTagItem($name), $keys));
|
||||
}
|
||||
|
||||
$this->set($key, implode(',', $value), 0);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新标签
|
||||
* @access public
|
||||
* @param string $name 缓存标识
|
||||
* @access protected
|
||||
* @param string $name 缓存标识
|
||||
* @return void
|
||||
*/
|
||||
protected function setTagItem($name)
|
||||
{
|
||||
if ($this->tag) {
|
||||
$key = 'tag_' . md5($this->tag);
|
||||
$key = $this->getTagkey($this->tag);
|
||||
$this->tag = null;
|
||||
|
||||
if ($this->has($key)) {
|
||||
$value = explode(',', $this->get($key));
|
||||
$value[] = $name;
|
||||
$value = implode(',', array_unique($value));
|
||||
|
||||
if (count($value) > 1000) {
|
||||
array_shift($value);
|
||||
}
|
||||
|
||||
$value = implode(',', array_unique($value));
|
||||
} else {
|
||||
$value = $name;
|
||||
}
|
||||
|
||||
$this->set($key, $value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标签包含的缓存标识
|
||||
* @access public
|
||||
* @param string $tag 缓存标签
|
||||
* @access protected
|
||||
* @param string $tag 缓存标签
|
||||
* @return array
|
||||
*/
|
||||
protected function getTagItem($tag)
|
||||
{
|
||||
$key = 'tag_' . md5($tag);
|
||||
$key = $this->getTagkey($tag);
|
||||
$value = $this->get($key);
|
||||
|
||||
if ($value) {
|
||||
return array_filter(explode(',', $value));
|
||||
} else {
|
||||
@@ -218,6 +286,58 @@ abstract class Driver
|
||||
}
|
||||
}
|
||||
|
||||
protected function getTagKey($tag)
|
||||
{
|
||||
return 'tag_' . md5($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化数据
|
||||
* @access protected
|
||||
* @param mixed $data
|
||||
* @return string
|
||||
*/
|
||||
protected function serialize($data)
|
||||
{
|
||||
if (is_scalar($data) || !$this->options['serialize']) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$serialize = self::$serialize[0];
|
||||
|
||||
return self::$serialize[2] . $serialize($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化数据
|
||||
* @access protected
|
||||
* @param string $data
|
||||
* @return mixed
|
||||
*/
|
||||
protected function unserialize($data)
|
||||
{
|
||||
if ($this->options['serialize'] && 0 === strpos($data, self::$serialize[2])) {
|
||||
$unserialize = self::$serialize[1];
|
||||
|
||||
return $unserialize(substr($data, self::$serialize[3]));
|
||||
} else {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册序列化机制
|
||||
* @access public
|
||||
* @param callable $serialize 序列化方法
|
||||
* @param callable $unserialize 反序列化方法
|
||||
* @param string $prefix 序列化前缀标识
|
||||
* @return $this
|
||||
*/
|
||||
public static function registerSerialize($serialize, $unserialize, $prefix = 'think_serialize:')
|
||||
{
|
||||
self::$serialize = [$serialize, $unserialize, $prefix, strlen($prefix)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回句柄对象,可执行其它高级方法
|
||||
*
|
||||
@@ -228,4 +348,19 @@ abstract class Driver
|
||||
{
|
||||
return $this->handler;
|
||||
}
|
||||
|
||||
public function getReadTimes()
|
||||
{
|
||||
return $this->readTimes;
|
||||
}
|
||||
|
||||
public function getWriteTimes()
|
||||
{
|
||||
return $this->writeTimes;
|
||||
}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
return call_user_func_array([$this->handler, $method], $args);
|
||||
}
|
||||
}
|
||||
|
||||
113
Server/thinkphp/library/think/cache/driver/File.php
vendored
113
Server/thinkphp/library/think/cache/driver/File.php
vendored
@@ -12,6 +12,7 @@
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
use think\Container;
|
||||
|
||||
/**
|
||||
* 文件类型缓存类
|
||||
@@ -23,14 +24,16 @@ class File extends Driver
|
||||
'expire' => 0,
|
||||
'cache_subdir' => true,
|
||||
'prefix' => '',
|
||||
'path' => CACHE_PATH,
|
||||
'path' => '',
|
||||
'hash_type' => 'md5',
|
||||
'data_compress' => false,
|
||||
'serialize' => true,
|
||||
];
|
||||
|
||||
protected $expire;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* 架构函数
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
@@ -38,9 +41,13 @@ class File extends Driver
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
if (substr($this->options['path'], -1) != DS) {
|
||||
$this->options['path'] .= DS;
|
||||
|
||||
if (empty($this->options['path'])) {
|
||||
$this->options['path'] = Container::get('app')->getRuntimePath() . 'cache' . DIRECTORY_SEPARATOR;
|
||||
} elseif (substr($this->options['path'], -1) != DIRECTORY_SEPARATOR) {
|
||||
$this->options['path'] .= DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
@@ -52,11 +59,13 @@ class File extends Driver
|
||||
private function init()
|
||||
{
|
||||
// 创建项目缓存目录
|
||||
if (!is_dir($this->options['path'])) {
|
||||
if (mkdir($this->options['path'], 0755, true)) {
|
||||
try {
|
||||
if (!is_dir($this->options['path']) && mkdir($this->options['path'], 0755, true)) {
|
||||
return true;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -69,62 +78,77 @@ class File extends Driver
|
||||
*/
|
||||
protected function getCacheKey($name, $auto = false)
|
||||
{
|
||||
$name = md5($name);
|
||||
$name = hash($this->options['hash_type'], $name);
|
||||
|
||||
if ($this->options['cache_subdir']) {
|
||||
// 使用子目录
|
||||
$name = substr($name, 0, 2) . DS . substr($name, 2);
|
||||
$name = substr($name, 0, 2) . DIRECTORY_SEPARATOR . substr($name, 2);
|
||||
}
|
||||
|
||||
if ($this->options['prefix']) {
|
||||
$name = $this->options['prefix'] . DS . $name;
|
||||
$name = $this->options['prefix'] . DIRECTORY_SEPARATOR . $name;
|
||||
}
|
||||
|
||||
$filename = $this->options['path'] . $name . '.php';
|
||||
$dir = dirname($filename);
|
||||
|
||||
if ($auto && !is_dir($dir)) {
|
||||
mkdir($dir, 0755, true);
|
||||
try {
|
||||
mkdir($dir, 0755, true);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存是否存在
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return $this->get($name) ? true : false;
|
||||
return false !== $this->get($name) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$filename = $this->getCacheKey($name);
|
||||
|
||||
if (!is_file($filename)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$content = file_get_contents($filename);
|
||||
$this->expire = null;
|
||||
|
||||
if (false !== $content) {
|
||||
$expire = (int) substr($content, 8, 12);
|
||||
if (0 != $expire && time() > filemtime($filename) + $expire) {
|
||||
//缓存过期删除缓存文件
|
||||
$this->unlink($filename);
|
||||
return $default;
|
||||
}
|
||||
|
||||
$this->expire = $expire;
|
||||
$content = substr($content, 32);
|
||||
|
||||
if ($this->options['data_compress'] && function_exists('gzcompress')) {
|
||||
//启用数据压缩
|
||||
$content = gzuncompress($content);
|
||||
}
|
||||
$content = unserialize($content);
|
||||
return $content;
|
||||
return $this->unserialize($content);
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
@@ -133,30 +157,36 @@ class File extends Driver
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int|\DateTime $expire 有效时间 0为永久
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp() - time();
|
||||
}
|
||||
|
||||
$expire = $this->getExpireTime($expire);
|
||||
$filename = $this->getCacheKey($name, true);
|
||||
|
||||
if ($this->tag && !is_file($filename)) {
|
||||
$first = true;
|
||||
}
|
||||
$data = serialize($value);
|
||||
|
||||
$data = $this->serialize($value);
|
||||
|
||||
if ($this->options['data_compress'] && function_exists('gzcompress')) {
|
||||
//数据压缩
|
||||
$data = gzcompress($data, 3);
|
||||
}
|
||||
|
||||
$data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
|
||||
$result = file_put_contents($filename, $data);
|
||||
|
||||
if ($result) {
|
||||
isset($first) && $this->setTagItem($filename);
|
||||
clearstatcache();
|
||||
@@ -169,8 +199,8 @@ class File extends Driver
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
@@ -189,8 +219,8 @@ class File extends Driver
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
@@ -209,14 +239,15 @@ class File extends Driver
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
$filename = $this->getCacheKey($name);
|
||||
$this->writeTimes++;
|
||||
|
||||
try {
|
||||
return $this->unlink($filename);
|
||||
return $this->unlink($this->getCacheKey($name));
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
@@ -224,7 +255,7 @@ class File extends Driver
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
@@ -235,27 +266,35 @@ class File extends Driver
|
||||
foreach ($keys as $key) {
|
||||
$this->unlink($key);
|
||||
}
|
||||
$this->rm('tag_' . md5($tag));
|
||||
$this->rm($this->getTagKey($tag));
|
||||
return true;
|
||||
}
|
||||
$files = (array) glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DS : '') . '*');
|
||||
|
||||
$this->writeTimes++;
|
||||
|
||||
$files = (array) glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DIRECTORY_SEPARATOR : '') . '*');
|
||||
|
||||
foreach ($files as $path) {
|
||||
if (is_dir($path)) {
|
||||
$matches = glob($path . '/*.php');
|
||||
$matches = glob($path . DIRECTORY_SEPARATOR . '*.php');
|
||||
if (is_array($matches)) {
|
||||
array_map('unlink', $matches);
|
||||
array_map(function ($v) {
|
||||
$this->unlink($v);
|
||||
}, $matches);
|
||||
}
|
||||
rmdir($path);
|
||||
} else {
|
||||
unlink($path);
|
||||
$this->unlink($path);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断文件是否存在后,删除
|
||||
* @param $path
|
||||
* @access private
|
||||
* @param string $path
|
||||
* @return bool
|
||||
* @author byron sampson <xiaobo.sun@qq.com>
|
||||
* @return boolean
|
||||
|
||||
@@ -26,18 +26,19 @@ class Lite extends Driver
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* 架构函数
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
if (substr($this->options['path'], -1) != DS) {
|
||||
$this->options['path'] .= DS;
|
||||
|
||||
if (substr($this->options['path'], -1) != DIRECTORY_SEPARATOR) {
|
||||
$this->options['path'] .= DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -45,7 +46,7 @@ class Lite extends Driver
|
||||
/**
|
||||
* 取得变量的存储文件名
|
||||
* @access protected
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheKey($name)
|
||||
@@ -56,7 +57,7 @@ class Lite extends Driver
|
||||
/**
|
||||
* 判断缓存是否存在
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return mixed
|
||||
*/
|
||||
public function has($name)
|
||||
@@ -67,21 +68,26 @@ class Lite extends Driver
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$filename = $this->getCacheKey($name);
|
||||
|
||||
if (is_file($filename)) {
|
||||
// 判断是否过期
|
||||
$mtime = filemtime($filename);
|
||||
|
||||
if ($mtime < time()) {
|
||||
// 清除已经过期的文件
|
||||
unlink($filename);
|
||||
return $default;
|
||||
}
|
||||
|
||||
return include $filename;
|
||||
} else {
|
||||
return $default;
|
||||
@@ -90,41 +96,49 @@ class Lite extends Driver
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int|\DateTime $expire 有效时间 0为永久
|
||||
* @return bool
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp();
|
||||
} else {
|
||||
$expire = 0 === $expire ? 10 * 365 * 24 * 3600 : $expire;
|
||||
$expire = time() + $expire;
|
||||
}
|
||||
|
||||
$filename = $this->getCacheKey($name);
|
||||
|
||||
if ($this->tag && !is_file($filename)) {
|
||||
$first = true;
|
||||
}
|
||||
|
||||
$ret = file_put_contents($filename, ("<?php return " . var_export($value, true) . ";"));
|
||||
|
||||
// 通过设置修改时间实现有效期
|
||||
if ($ret) {
|
||||
isset($first) && $this->setTagItem($filename);
|
||||
touch($filename, $expire);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
@@ -134,14 +148,15 @@ class Lite extends Driver
|
||||
} else {
|
||||
$value = $step;
|
||||
}
|
||||
|
||||
return $this->set($name, $value, 0) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
@@ -151,24 +166,27 @@ class Lite extends Driver
|
||||
} else {
|
||||
$value = -$step;
|
||||
}
|
||||
|
||||
return $this->set($name, $value, 0) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
return unlink($this->getCacheKey($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return bool
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
@@ -179,9 +197,13 @@ class Lite extends Driver
|
||||
foreach ($keys as $key) {
|
||||
unlink($key);
|
||||
}
|
||||
$this->rm('tag_' . md5($tag));
|
||||
|
||||
$this->rm($this->getTagKey($tag));
|
||||
return true;
|
||||
}
|
||||
array_map("unlink", glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DS : '') . '*.php'));
|
||||
|
||||
$this->writeTimes++;
|
||||
|
||||
array_map("unlink", glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DIRECTORY_SEPARATOR : '') . '*.php'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,13 @@ class Memcache extends Driver
|
||||
'timeout' => 0, // 超时时间(单位:毫秒)
|
||||
'persistent' => true,
|
||||
'prefix' => '',
|
||||
'serialize' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options 缓存参数
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param array $options 缓存参数
|
||||
* @throws \BadFunctionCallException
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
@@ -35,16 +36,21 @@ class Memcache extends Driver
|
||||
if (!extension_loaded('memcache')) {
|
||||
throw new \BadFunctionCallException('not support: memcache');
|
||||
}
|
||||
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
$this->handler = new \Memcache;
|
||||
|
||||
// 支持集群
|
||||
$hosts = explode(',', $this->options['host']);
|
||||
$ports = explode(',', $this->options['port']);
|
||||
|
||||
if (empty($ports[0])) {
|
||||
$ports[0] = 11211;
|
||||
}
|
||||
|
||||
// 建立连接
|
||||
foreach ((array) $hosts as $i => $host) {
|
||||
$port = isset($ports[$i]) ? $ports[$i] : $ports[0];
|
||||
@@ -57,99 +63,115 @@ class Memcache extends Driver
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return false !== $this->handler->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$result = $this->handler->get($this->getCacheKey($name));
|
||||
return false !== $result ? $result : $default;
|
||||
|
||||
return false !== $result ? $this->unserialize($result) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int|DateTime $expire 有效时间(秒)
|
||||
* @return bool
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp() - time();
|
||||
}
|
||||
|
||||
if ($this->tag && !$this->has($name)) {
|
||||
$first = true;
|
||||
}
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
$expire = $this->getExpireTime($expire);
|
||||
$value = $this->serialize($value);
|
||||
|
||||
if ($this->handler->set($key, $value, 0, $expire)) {
|
||||
isset($first) && $this->setTagItem($key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
if ($this->handler->get($key)) {
|
||||
return $this->handler->increment($key, $step);
|
||||
}
|
||||
|
||||
return $this->handler->set($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
$value = $this->handler->get($key) - $step;
|
||||
$res = $this->handler->set($key, $value);
|
||||
if (!$res) {
|
||||
return false;
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return !$res ? false : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @param string $name 缓存变量名
|
||||
* @param bool|false $ttl
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param bool|false $ttl
|
||||
* @return bool
|
||||
*/
|
||||
public function rm($name, $ttl = false)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return false === $ttl ?
|
||||
$this->handler->delete($key) :
|
||||
$this->handler->delete($key, $ttl);
|
||||
@@ -158,7 +180,7 @@ class Memcache extends Driver
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @param string $tag 标签名
|
||||
* @return bool
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
@@ -166,12 +188,19 @@ class Memcache extends Driver
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$this->handler->delete($key);
|
||||
}
|
||||
$this->rm('tag_' . md5($tag));
|
||||
|
||||
$tagName = $this->getTagKey($tag);
|
||||
$this->rm($tagName);
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->writeTimes++;
|
||||
|
||||
return $this->handler->flush();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,49 +16,58 @@ use think\cache\Driver;
|
||||
class Memcached extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 11211,
|
||||
'expire' => 0,
|
||||
'timeout' => 0, // 超时时间(单位:毫秒)
|
||||
'prefix' => '',
|
||||
'username' => '', //账号
|
||||
'password' => '', //密码
|
||||
'option' => [],
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 11211,
|
||||
'expire' => 0,
|
||||
'timeout' => 0, // 超时时间(单位:毫秒)
|
||||
'prefix' => '',
|
||||
'username' => '', //账号
|
||||
'password' => '', //密码
|
||||
'option' => [],
|
||||
'serialize' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options 缓存参数
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param array $options 缓存参数
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!extension_loaded('memcached')) {
|
||||
throw new \BadFunctionCallException('not support: memcached');
|
||||
}
|
||||
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
$this->handler = new \Memcached;
|
||||
|
||||
if (!empty($this->options['option'])) {
|
||||
$this->handler->setOptions($this->options['option']);
|
||||
}
|
||||
|
||||
// 设置连接超时时间(单位:毫秒)
|
||||
if ($this->options['timeout'] > 0) {
|
||||
$this->handler->setOption(\Memcached::OPT_CONNECT_TIMEOUT, $this->options['timeout']);
|
||||
}
|
||||
|
||||
// 支持集群
|
||||
$hosts = explode(',', $this->options['host']);
|
||||
$ports = explode(',', $this->options['port']);
|
||||
if (empty($ports[0])) {
|
||||
$ports[0] = 11211;
|
||||
}
|
||||
|
||||
// 建立连接
|
||||
$servers = [];
|
||||
foreach ((array) $hosts as $i => $host) {
|
||||
$servers[] = [$host, (isset($ports[$i]) ? $ports[$i] : $ports[0]), 1];
|
||||
}
|
||||
|
||||
$this->handler->addServers($servers);
|
||||
$this->handler->setOption(\Memcached::OPT_COMPRESSION, false);
|
||||
if ('' != $this->options['username']) {
|
||||
$this->handler->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
|
||||
$this->handler->setSaslAuthData($this->options['username'], $this->options['password']);
|
||||
@@ -68,100 +77,115 @@ class Memcached extends Driver
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return $this->handler->get($key) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$result = $this->handler->get($this->getCacheKey($name));
|
||||
return false !== $result ? $result : $default;
|
||||
|
||||
return false !== $result ? $this->unserialize($result) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return bool
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp() - time();
|
||||
}
|
||||
|
||||
if ($this->tag && !$this->has($name)) {
|
||||
$first = true;
|
||||
}
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
$expire = 0 == $expire ? 0 : $_SERVER['REQUEST_TIME'] + $expire;
|
||||
$expire = $this->getExpireTime($expire);
|
||||
$value = $this->serialize($value);
|
||||
|
||||
if ($this->handler->set($key, $value, $expire)) {
|
||||
isset($first) && $this->setTagItem($key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
if ($this->handler->get($key)) {
|
||||
return $this->handler->increment($key, $step);
|
||||
}
|
||||
|
||||
return $this->handler->set($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
$value = $this->handler->get($key) - $step;
|
||||
$res = $this->handler->set($key, $value);
|
||||
if (!$res) {
|
||||
return false;
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return !$res ? false : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @param string $name 缓存变量名
|
||||
* @param bool|false $ttl
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param bool|false $ttl
|
||||
* @return bool
|
||||
*/
|
||||
public function rm($name, $ttl = false)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return false === $ttl ?
|
||||
$this->handler->delete($key) :
|
||||
$this->handler->delete($key, $ttl);
|
||||
@@ -170,7 +194,7 @@ class Memcached extends Driver
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @param string $tag 标签名
|
||||
* @return bool
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
@@ -178,10 +202,78 @@ class Memcached extends Driver
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
|
||||
$this->handler->deleteMulti($keys);
|
||||
$this->rm('tag_' . md5($tag));
|
||||
$this->rm($this->getTagKey($tag));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->writeTimes++;
|
||||
|
||||
return $this->handler->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存标签
|
||||
* @access public
|
||||
* @param string $name 标签名
|
||||
* @param string|array $keys 缓存标识
|
||||
* @param bool $overlay 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function tag($name, $keys = null, $overlay = false)
|
||||
{
|
||||
if (is_null($keys)) {
|
||||
$this->tag = $name;
|
||||
} else {
|
||||
$tagName = $this->getTagKey($name);
|
||||
if ($overlay) {
|
||||
$this->handler->delete($tagName);
|
||||
}
|
||||
|
||||
if (!$this->has($tagName)) {
|
||||
$this->handler->set($tagName, '');
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$this->handler->append($tagName, ',' . $key);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新标签
|
||||
* @access protected
|
||||
* @param string $name 缓存标识
|
||||
* @return void
|
||||
*/
|
||||
protected function setTagItem($name)
|
||||
{
|
||||
if ($this->tag) {
|
||||
$tagName = $this->getTagKey($this->tag);
|
||||
|
||||
if ($this->has($tagName)) {
|
||||
$this->handler->append($tagName, ',' . $name);
|
||||
} else {
|
||||
$this->handler->set($tagName, $name);
|
||||
}
|
||||
|
||||
$this->tag = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标签包含的缓存标识
|
||||
* @access public
|
||||
* @param string $tag 缓存标签
|
||||
* @return array
|
||||
*/
|
||||
public function getTagItem($tag)
|
||||
{
|
||||
$tagName = $this->getTagKey($tag);
|
||||
return explode(',', trim($this->handler->get($tagName), ','));
|
||||
}
|
||||
}
|
||||
|
||||
170
Server/thinkphp/library/think/cache/driver/Redis.php
vendored
170
Server/thinkphp/library/think/cache/driver/Redis.php
vendored
@@ -31,98 +31,121 @@ class Redis extends Driver
|
||||
'expire' => 0,
|
||||
'persistent' => false,
|
||||
'prefix' => '',
|
||||
'serialize' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options 缓存参数
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param array $options 缓存参数
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!extension_loaded('redis')) {
|
||||
throw new \BadFunctionCallException('not support: redis');
|
||||
}
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
$this->handler = new \Redis;
|
||||
if ($this->options['persistent']) {
|
||||
$this->handler->pconnect($this->options['host'], $this->options['port'], $this->options['timeout'], 'persistent_id_' . $this->options['select']);
|
||||
|
||||
if (extension_loaded('redis')) {
|
||||
$this->handler = new \Redis;
|
||||
|
||||
if ($this->options['persistent']) {
|
||||
$this->handler->pconnect($this->options['host'], $this->options['port'], $this->options['timeout'], 'persistent_id_' . $this->options['select']);
|
||||
} else {
|
||||
$this->handler->connect($this->options['host'], $this->options['port'], $this->options['timeout']);
|
||||
}
|
||||
|
||||
if ('' != $this->options['password']) {
|
||||
$this->handler->auth($this->options['password']);
|
||||
}
|
||||
|
||||
if (0 != $this->options['select']) {
|
||||
$this->handler->select($this->options['select']);
|
||||
}
|
||||
} elseif (class_exists('\Predis\Client')) {
|
||||
$params = [];
|
||||
foreach ($this->options as $key => $val) {
|
||||
if (in_array($key, ['aggregate', 'cluster', 'connections', 'exceptions', 'prefix', 'profile', 'replication', 'parameters'])) {
|
||||
$params[$key] = $val;
|
||||
unset($this->options[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if ('' == $this->options['password']) {
|
||||
unset($this->options['password']);
|
||||
}
|
||||
|
||||
$this->handler = new \Predis\Client($this->options, $params);
|
||||
|
||||
$this->options['prefix'] = '';
|
||||
} else {
|
||||
$this->handler->connect($this->options['host'], $this->options['port'], $this->options['timeout']);
|
||||
}
|
||||
|
||||
if ('' != $this->options['password']) {
|
||||
$this->handler->auth($this->options['password']);
|
||||
}
|
||||
|
||||
if (0 != $this->options['select']) {
|
||||
$this->handler->select($this->options['select']);
|
||||
throw new \BadFunctionCallException('not support: redis');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return (bool) $this->handler->exists($this->getCacheKey($name));
|
||||
return $this->handler->exists($this->getCacheKey($name)) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$value = $this->handler->get($this->getCacheKey($name));
|
||||
|
||||
if (is_null($value) || false === $value) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
try {
|
||||
$result = 0 === strpos($value, 'think_serialize:') ? unserialize(substr($value, 16)) : $value;
|
||||
} catch (\Exception $e) {
|
||||
$result = $default;
|
||||
}
|
||||
|
||||
return $result;
|
||||
return $this->unserialize($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp() - time();
|
||||
}
|
||||
|
||||
if ($this->tag && !$this->has($name)) {
|
||||
$first = true;
|
||||
}
|
||||
$key = $this->getCacheKey($name);
|
||||
$value = is_scalar($value) ? $value : 'think_serialize:' . serialize($value);
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
$expire = $this->getExpireTime($expire);
|
||||
|
||||
$value = $this->serialize($value);
|
||||
|
||||
if ($expire) {
|
||||
$result = $this->handler->setex($key, $expire, $value);
|
||||
} else {
|
||||
$result = $this->handler->set($key, $value);
|
||||
}
|
||||
|
||||
isset($first) && $this->setTagItem($key);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -135,6 +158,8 @@ class Redis extends Driver
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return $this->handler->incrby($key, $step);
|
||||
@@ -149,6 +174,8 @@ class Redis extends Driver
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return $this->handler->decrby($key, $step);
|
||||
@@ -157,18 +184,20 @@ class Redis extends Driver
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
return $this->handler->delete($this->getCacheKey($name));
|
||||
$this->writeTimes++;
|
||||
|
||||
return $this->handler->del($this->getCacheKey($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
@@ -176,13 +205,68 @@ class Redis extends Driver
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
foreach ($keys as $key) {
|
||||
$this->handler->delete($key);
|
||||
}
|
||||
$this->rm('tag_' . md5($tag));
|
||||
|
||||
$this->handler->del($keys);
|
||||
|
||||
$tagName = $this->getTagKey($tag);
|
||||
$this->handler->del($tagName);
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->writeTimes++;
|
||||
|
||||
return $this->handler->flushDB();
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存标签
|
||||
* @access public
|
||||
* @param string $name 标签名
|
||||
* @param string|array $keys 缓存标识
|
||||
* @param bool $overlay 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function tag($name, $keys = null, $overlay = false)
|
||||
{
|
||||
if (is_null($keys)) {
|
||||
$this->tag = $name;
|
||||
} else {
|
||||
$tagName = $this->getTagKey($name);
|
||||
if ($overlay) {
|
||||
$this->handler->del($tagName);
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$this->handler->sAdd($tagName, $key);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新标签
|
||||
* @access protected
|
||||
* @param string $name 缓存标识
|
||||
* @return void
|
||||
*/
|
||||
protected function setTagItem($name)
|
||||
{
|
||||
if ($this->tag) {
|
||||
$tagName = $this->getTagKey($this->tag);
|
||||
$this->handler->sAdd($tagName, $name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标签包含的缓存标识
|
||||
* @access protected
|
||||
* @param string $tag 缓存标签
|
||||
* @return array
|
||||
*/
|
||||
protected function getTagItem($tag)
|
||||
{
|
||||
$tagName = $this->getTagKey($tag);
|
||||
return $this->handler->sMembers($tagName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,30 +25,34 @@ class Sqlite extends Driver
|
||||
'prefix' => '',
|
||||
'expire' => 0,
|
||||
'persistent' => false,
|
||||
'serialize' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options 缓存参数
|
||||
* @throws \BadFunctionCallException
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param array $options 缓存参数
|
||||
* @throws \BadFunctionCallException
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!extension_loaded('sqlite')) {
|
||||
throw new \BadFunctionCallException('not support: sqlite');
|
||||
}
|
||||
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
$func = $this->options['persistent'] ? 'sqlite_popen' : 'sqlite_open';
|
||||
|
||||
$func = $this->options['persistent'] ? 'sqlite_popen' : 'sqlite_open';
|
||||
|
||||
$this->handler = $func($this->options['db']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实际的缓存标识
|
||||
* @access public
|
||||
* @param string $name 缓存名
|
||||
* @param string $name 缓存名
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheKey($name)
|
||||
@@ -59,82 +63,101 @@ class Sqlite extends Driver
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$name = $this->getCacheKey($name);
|
||||
$sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . $_SERVER['REQUEST_TIME'] . ') LIMIT 1';
|
||||
$name = $this->getCacheKey($name);
|
||||
|
||||
$sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . time() . ') LIMIT 1';
|
||||
$result = sqlite_query($this->handler, $sql);
|
||||
|
||||
return sqlite_num_rows($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$name = $this->getCacheKey($name);
|
||||
$sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . $_SERVER['REQUEST_TIME'] . ') LIMIT 1';
|
||||
$this->readTimes++;
|
||||
|
||||
$name = $this->getCacheKey($name);
|
||||
|
||||
$sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . time() . ') LIMIT 1';
|
||||
|
||||
$result = sqlite_query($this->handler, $sql);
|
||||
|
||||
if (sqlite_num_rows($result)) {
|
||||
$content = sqlite_fetch_single($result);
|
||||
if (function_exists('gzcompress')) {
|
||||
//启用数据压缩
|
||||
$content = gzuncompress($content);
|
||||
}
|
||||
return unserialize($content);
|
||||
|
||||
return $this->unserialize($content);
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$name = $this->getCacheKey($name);
|
||||
$value = sqlite_escape_string(serialize($value));
|
||||
$this->writeTimes++;
|
||||
|
||||
$name = $this->getCacheKey($name);
|
||||
|
||||
$value = sqlite_escape_string($this->serialize($value));
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp();
|
||||
} else {
|
||||
$expire = (0 == $expire) ? 0 : (time() + $expire); //缓存有效期为0表示永久缓存
|
||||
}
|
||||
|
||||
if (function_exists('gzcompress')) {
|
||||
//数据压缩
|
||||
$value = gzcompress($value, 3);
|
||||
}
|
||||
|
||||
if ($this->tag) {
|
||||
$tag = $this->tag;
|
||||
$this->tag = null;
|
||||
} else {
|
||||
$tag = '';
|
||||
}
|
||||
|
||||
$sql = 'REPLACE INTO ' . $this->options['table'] . ' (var, value, expire, tag) VALUES (\'' . $name . '\', \'' . $value . '\', \'' . $expire . '\', \'' . $tag . '\')';
|
||||
|
||||
if (sqlite_query($this->handler, $sql)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
@@ -144,14 +167,15 @@ class Sqlite extends Driver
|
||||
} else {
|
||||
$value = $step;
|
||||
}
|
||||
|
||||
return $this->set($name, $value, 0) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
@@ -161,39 +185,49 @@ class Sqlite extends Driver
|
||||
} else {
|
||||
$value = -$step;
|
||||
}
|
||||
|
||||
return $this->set($name, $value, 0) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$name = $this->getCacheKey($name);
|
||||
$sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\'';
|
||||
|
||||
$sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\'';
|
||||
sqlite_query($this->handler, $sql);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
$name = sqlite_escape_string($tag);
|
||||
$name = sqlite_escape_string($this->getTagKey($tag));
|
||||
$sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE tag=\'' . $name . '\'';
|
||||
sqlite_query($this->handler, $sql);
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->writeTimes++;
|
||||
|
||||
$sql = 'DELETE FROM ' . $this->options['table'];
|
||||
|
||||
sqlite_query($this->handler, $sql);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,21 +20,23 @@ use think\cache\Driver;
|
||||
class Wincache extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'prefix' => '',
|
||||
'expire' => 0,
|
||||
'prefix' => '',
|
||||
'expire' => 0,
|
||||
'serialize' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options 缓存参数
|
||||
* @throws \BadFunctionCallException
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param array $options 缓存参数
|
||||
* @throws \BadFunctionCallException
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!function_exists('wincache_ucache_info')) {
|
||||
throw new \BadFunctionCallException('not support: WinCache');
|
||||
}
|
||||
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
@@ -43,110 +45,131 @@ class Wincache extends Driver
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return wincache_ucache_exists($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
return wincache_ucache_exists($key) ? wincache_ucache_get($key) : $default;
|
||||
|
||||
return wincache_ucache_exists($key) ? $this->unserialize(wincache_ucache_get($key)) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp() - time();
|
||||
}
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
$expire = $this->getExpireTime($expire);
|
||||
$value = $this->serialize($value);
|
||||
|
||||
if ($this->tag && !$this->has($name)) {
|
||||
$first = true;
|
||||
}
|
||||
|
||||
if (wincache_ucache_set($key, $value, $expire)) {
|
||||
isset($first) && $this->setTagItem($key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return wincache_ucache_inc($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return wincache_ucache_dec($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
return wincache_ucache_delete($this->getCacheKey($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
$keys = $this->getTagItem($tag);
|
||||
foreach ($keys as $key) {
|
||||
wincache_ucache_delete($key);
|
||||
}
|
||||
$this->rm('tag_' . md5($tag));
|
||||
|
||||
wincache_ucache_delete($keys);
|
||||
|
||||
$tagName = $this->getTagkey($tag);
|
||||
$this->rm($tagName);
|
||||
return true;
|
||||
} else {
|
||||
return wincache_ucache_clear();
|
||||
}
|
||||
|
||||
$this->writeTimes++;
|
||||
return wincache_ucache_clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,14 +20,15 @@ use think\cache\Driver;
|
||||
class Xcache extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'prefix' => '',
|
||||
'expire' => 0,
|
||||
'prefix' => '',
|
||||
'expire' => 0,
|
||||
'serialize' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options 缓存参数
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param array $options 缓存参数
|
||||
* @throws \BadFunctionCallException
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
@@ -35,6 +36,7 @@ class Xcache extends Driver
|
||||
if (!function_exists('xcache_info')) {
|
||||
throw new \BadFunctionCallException('not support: Xcache');
|
||||
}
|
||||
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
@@ -43,96 +45,113 @@ class Xcache extends Driver
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return xcache_isset($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$this->readTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
return xcache_isset($key) ? xcache_get($key) : $default;
|
||||
|
||||
return xcache_isset($key) ? $this->unserialize(xcache_get($key)) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp() - time();
|
||||
}
|
||||
|
||||
if ($this->tag && !$this->has($name)) {
|
||||
$first = true;
|
||||
}
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
$expire = $this->getExpireTime($expire);
|
||||
$value = $this->serialize($value);
|
||||
|
||||
if (xcache_set($key, $value, $expire)) {
|
||||
isset($first) && $this->setTagItem($key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return xcache_inc($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
$key = $this->getCacheKey($name);
|
||||
|
||||
return xcache_dec($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
$this->writeTimes++;
|
||||
|
||||
return xcache_unset($this->getCacheKey($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
@@ -140,12 +159,17 @@ class Xcache extends Driver
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
|
||||
foreach ($keys as $key) {
|
||||
xcache_unset($key);
|
||||
}
|
||||
$this->rm('tag_' . md5($tag));
|
||||
|
||||
$this->rm($this->getTagKey($tag));
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->writeTimes++;
|
||||
|
||||
if (function_exists('xcache_unset_by_prefix')) {
|
||||
return xcache_unset_by_prefix($this->options['prefix']);
|
||||
} else {
|
||||
|
||||
@@ -13,12 +13,19 @@ namespace think\config\driver;
|
||||
|
||||
class Ini
|
||||
{
|
||||
public function parse($config)
|
||||
protected $config;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
if (is_file($config)) {
|
||||
return parse_ini_file($config, true);
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function parse()
|
||||
{
|
||||
if (is_file($this->config)) {
|
||||
return parse_ini_file($this->config, true);
|
||||
} else {
|
||||
return parse_ini_string($config, true);
|
||||
return parse_ini_string($this->config, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,19 @@ namespace think\config\driver;
|
||||
|
||||
class Json
|
||||
{
|
||||
public function parse($config)
|
||||
protected $config;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
if (is_file($config)) {
|
||||
$config = file_get_contents($config);
|
||||
}
|
||||
$result = json_decode($config, true);
|
||||
return $result;
|
||||
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function parse()
|
||||
{
|
||||
return json_decode($this->config, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,19 +13,28 @@ namespace think\config\driver;
|
||||
|
||||
class Xml
|
||||
{
|
||||
public function parse($config)
|
||||
protected $config;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
if (is_file($config)) {
|
||||
$content = simplexml_load_file($config);
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function parse()
|
||||
{
|
||||
if (is_file($this->config)) {
|
||||
$content = simplexml_load_file($this->config);
|
||||
} else {
|
||||
$content = simplexml_load_string($config);
|
||||
$content = simplexml_load_string($this->config);
|
||||
}
|
||||
|
||||
$result = (array) $content;
|
||||
foreach ($result as $key => $val) {
|
||||
if (is_object($val)) {
|
||||
$result[$key] = (array) $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,4 +467,16 @@ class Command
|
||||
throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出表格
|
||||
* @param Table $table
|
||||
* @return string
|
||||
*/
|
||||
protected function table(Table $table)
|
||||
{
|
||||
$content = $table->render();
|
||||
$this->output->writeln($content);
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,11 @@ use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\facade\App;
|
||||
use think\facade\Build as AppBuild;
|
||||
|
||||
class Build extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -35,7 +36,7 @@ class Build extends Command
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
if ($input->hasOption('module')) {
|
||||
\think\Build::module($input->getOption('module'));
|
||||
AppBuild::module($input->getOption('module'));
|
||||
$output->writeln("Successed");
|
||||
return;
|
||||
}
|
||||
@@ -43,13 +44,15 @@ class Build extends Command
|
||||
if ($input->hasOption('config')) {
|
||||
$build = include $input->getOption('config');
|
||||
} else {
|
||||
$build = include APP_PATH . 'build.php';
|
||||
$build = include App::getAppPath() . 'build.php';
|
||||
}
|
||||
|
||||
if (empty($build)) {
|
||||
$output->writeln("Build Config Is Empty");
|
||||
return;
|
||||
}
|
||||
\think\Build::run($build);
|
||||
|
||||
AppBuild::run($build);
|
||||
$output->writeln("Successed");
|
||||
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
// +----------------------------------------------------------------------
|
||||
namespace think\console\command;
|
||||
|
||||
use think\Cache;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\facade\App;
|
||||
use think\facade\Cache;
|
||||
|
||||
class Clear extends Command
|
||||
{
|
||||
@@ -24,39 +24,46 @@ class Clear extends Command
|
||||
// 指令配置
|
||||
$this
|
||||
->setName('clear')
|
||||
->addArgument('type', Argument::OPTIONAL, 'type to clear', null)
|
||||
->addOption('path', 'd', Option::VALUE_OPTIONAL, 'path to clear', null)
|
||||
->addOption('cache', 'c', Option::VALUE_NONE, 'clear cache file')
|
||||
->addOption('route', 'u', Option::VALUE_NONE, 'clear route cache')
|
||||
->addOption('log', 'l', Option::VALUE_NONE, 'clear log file')
|
||||
->addOption('dir', 'r', Option::VALUE_NONE, 'clear empty dir')
|
||||
->setDescription('Clear runtime file');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$path = $input->getOption('path') ?: RUNTIME_PATH;
|
||||
|
||||
$type = $input->getArgument('type');
|
||||
|
||||
if ($type == 'route') {
|
||||
Cache::clear('route_check');
|
||||
if ($input->getOption('route')) {
|
||||
Cache::clear('route_cache');
|
||||
} else {
|
||||
if (is_dir($path)) {
|
||||
$this->clearPath($path);
|
||||
if ($input->getOption('cache')) {
|
||||
$path = App::getRuntimePath() . 'cache';
|
||||
} elseif ($input->getOption('log')) {
|
||||
$path = App::getRuntimePath() . 'log';
|
||||
} else {
|
||||
$path = $input->getOption('path') ?: App::getRuntimePath();
|
||||
}
|
||||
|
||||
$rmdir = $input->getOption('dir') ? true : false;
|
||||
$this->clear(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR, $rmdir);
|
||||
}
|
||||
|
||||
$output->writeln("<info>Clear Successed</info>");
|
||||
}
|
||||
|
||||
protected function clearPath($path)
|
||||
protected function clear($path, $rmdir)
|
||||
{
|
||||
$path = realpath($path) . DS;
|
||||
$files = scandir($path);
|
||||
if ($files) {
|
||||
foreach ($files as $file) {
|
||||
if ('.' != $file && '..' != $file && is_dir($path . $file)) {
|
||||
$this->clearPath($path . $file);
|
||||
} elseif ('.gitignore' != $file && is_file($path . $file)) {
|
||||
unlink($path . $file);
|
||||
$files = is_dir($path) ? scandir($path) : [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ('.' != $file && '..' != $file && is_dir($path . $file)) {
|
||||
array_map('unlink', glob($path . $file . DIRECTORY_SEPARATOR . '*.*'));
|
||||
if ($rmdir) {
|
||||
rmdir($path . $file);
|
||||
}
|
||||
} elseif ('.gitignore' != $file && is_file($path . $file)) {
|
||||
unlink($path . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ use think\console\Output;
|
||||
|
||||
class Help extends Command
|
||||
{
|
||||
|
||||
private $command;
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,14 +13,13 @@ namespace think\console\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
use think\console\input\Argument as InputArgument;
|
||||
use think\console\input\Option as InputOption;
|
||||
use think\console\input\Definition as InputDefinition;
|
||||
use think\console\input\Option as InputOption;
|
||||
use think\console\Output;
|
||||
|
||||
class Lists extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -68,7 +67,7 @@ EOF
|
||||
{
|
||||
return new InputDefinition([
|
||||
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
|
||||
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list')
|
||||
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,16 +11,16 @@
|
||||
|
||||
namespace think\console\command;
|
||||
|
||||
use think\App;
|
||||
use think\Config;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument;
|
||||
use think\console\Output;
|
||||
use think\facade\App;
|
||||
use think\facade\Config;
|
||||
use think\facade\Env;
|
||||
|
||||
abstract class Make extends Command
|
||||
{
|
||||
|
||||
protected $type;
|
||||
|
||||
abstract protected function getStub();
|
||||
@@ -45,7 +45,7 @@ abstract class Make extends Command
|
||||
}
|
||||
|
||||
if (!is_dir(dirname($pathname))) {
|
||||
mkdir(strtolower(dirname($pathname)), 0755, true);
|
||||
mkdir(dirname($pathname), 0755, true);
|
||||
}
|
||||
|
||||
file_put_contents($pathname, $this->buildClass($classname));
|
||||
@@ -62,26 +62,26 @@ abstract class Make extends Command
|
||||
|
||||
$class = str_replace($namespace . '\\', '', $name);
|
||||
|
||||
return str_replace(['{%className%}', '{%namespace%}', '{%app_namespace%}'], [
|
||||
return str_replace(['{%className%}', '{%actionSuffix%}', '{%namespace%}', '{%app_namespace%}'], [
|
||||
$class,
|
||||
Config::get('action_suffix'),
|
||||
$namespace,
|
||||
App::$namespace,
|
||||
App::getNamespace(),
|
||||
], $stub);
|
||||
|
||||
}
|
||||
|
||||
protected function getPathName($name)
|
||||
{
|
||||
$name = str_replace(App::$namespace . '\\', '', $name);
|
||||
$name = str_replace(App::getNamespace() . '\\', '', $name);
|
||||
|
||||
return APP_PATH . str_replace('\\', '/', $name) . '.php';
|
||||
return Env::get('app_path') . ltrim(str_replace('\\', '/', $name), '/') . '.php';
|
||||
}
|
||||
|
||||
protected function getClassName($name)
|
||||
{
|
||||
$appNamespace = App::$namespace;
|
||||
$appNamespace = App::getNamespace();
|
||||
|
||||
if (strpos($name, $appNamespace . '\\') === 0) {
|
||||
if (strpos($name, $appNamespace . '\\') !== false) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,30 +11,36 @@
|
||||
|
||||
namespace think\console\command\make;
|
||||
|
||||
use think\Config;
|
||||
use think\console\command\Make;
|
||||
use think\console\input\Option;
|
||||
use think\facade\Config;
|
||||
|
||||
class Controller extends Make
|
||||
{
|
||||
|
||||
protected $type = "Controller";
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
parent::configure();
|
||||
$this->setName('make:controller')
|
||||
->addOption('api', null, Option::VALUE_NONE, 'Generate an api controller class.')
|
||||
->addOption('plain', null, Option::VALUE_NONE, 'Generate an empty controller class.')
|
||||
->setDescription('Create a new resource controller class');
|
||||
}
|
||||
|
||||
protected function getStub()
|
||||
{
|
||||
if ($this->input->getOption('plain')) {
|
||||
return __DIR__ . '/stubs/controller.plain.stub';
|
||||
$stubPath = __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR;
|
||||
|
||||
if ($this->input->getOption('api')) {
|
||||
return $stubPath . 'controller.api.stub';
|
||||
}
|
||||
|
||||
return __DIR__ . '/stubs/controller.stub';
|
||||
if ($this->input->getOption('plain')) {
|
||||
return $stubPath . 'controller.plain.stub';
|
||||
}
|
||||
|
||||
return $stubPath . 'controller.stub';
|
||||
}
|
||||
|
||||
protected function getClassName($name)
|
||||
|
||||
@@ -26,7 +26,7 @@ class Model extends Make
|
||||
|
||||
protected function getStub()
|
||||
{
|
||||
return __DIR__ . '/stubs/model.stub';
|
||||
return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'model.stub';
|
||||
}
|
||||
|
||||
protected function getNamespace($appNamespace, $module)
|
||||
|
||||
@@ -12,7 +12,7 @@ class {%className%} extends Controller
|
||||
*
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function index()
|
||||
public function index{%actionSuffix%}()
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -22,7 +22,7 @@ class {%className%} extends Controller
|
||||
*
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function create()
|
||||
public function create{%actionSuffix%}()
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class {%className%} extends Controller
|
||||
* @param \think\Request $request
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function save(Request $request)
|
||||
public function save{%actionSuffix%}(Request $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -44,7 +44,7 @@ class {%className%} extends Controller
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function read($id)
|
||||
public function read{%actionSuffix%}($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -55,7 +55,7 @@ class {%className%} extends Controller
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function edit($id)
|
||||
public function edit{%actionSuffix%}($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -67,7 +67,7 @@ class {%className%} extends Controller
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update{%actionSuffix%}(Request $request, $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -78,7 +78,7 @@ class {%className%} extends Controller
|
||||
* @param int $id
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function delete($id)
|
||||
public function delete{%actionSuffix%}($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
@@ -10,15 +10,13 @@
|
||||
// +----------------------------------------------------------------------
|
||||
namespace think\console\command\optimize;
|
||||
|
||||
use think\App;
|
||||
use think\Config;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
use think\Container;
|
||||
|
||||
class Autoload extends Command
|
||||
{
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('optimize:autoload')
|
||||
@@ -37,20 +35,14 @@ class Autoload extends Command
|
||||
return [
|
||||
|
||||
EOF;
|
||||
|
||||
$app = Container::get('app');
|
||||
$namespacesToScan = [
|
||||
App::$namespace . '\\' => realpath(rtrim(APP_PATH)),
|
||||
'think\\' => LIB_PATH . 'think',
|
||||
'behavior\\' => LIB_PATH . 'behavior',
|
||||
'traits\\' => LIB_PATH . 'traits',
|
||||
'' => realpath(rtrim(EXTEND_PATH)),
|
||||
$app->getNamespace() . '\\' => realpath(rtrim($app->getAppPath())),
|
||||
'think\\' => $app->getThinkPath() . 'library/think',
|
||||
'traits\\' => $app->getThinkPath() . 'library/traits',
|
||||
'' => realpath(rtrim($app->getRootPath() . 'extend')),
|
||||
];
|
||||
|
||||
$root_namespace = Config::get('root_namespace');
|
||||
foreach ($root_namespace as $namespace => $dir) {
|
||||
$namespacesToScan[$namespace . '\\'] = realpath($dir);
|
||||
}
|
||||
|
||||
krsort($namespacesToScan);
|
||||
$classMap = [];
|
||||
foreach ($namespacesToScan as $namespace => $dir) {
|
||||
@@ -59,7 +51,7 @@ EOF;
|
||||
continue;
|
||||
}
|
||||
|
||||
$namespaceFilter = $namespace === '' ? null : $namespace;
|
||||
$namespaceFilter = '' === $namespace ? null : $namespace;
|
||||
$classMap = $this->addClassMapCode($dir, $namespaceFilter, $classMap);
|
||||
}
|
||||
|
||||
@@ -68,12 +60,12 @@ EOF;
|
||||
$classmapFile .= ' ' . var_export($class, true) . ' => ' . $code;
|
||||
}
|
||||
$classmapFile .= "];\n";
|
||||
|
||||
if (!is_dir(RUNTIME_PATH)) {
|
||||
@mkdir(RUNTIME_PATH, 0755, true);
|
||||
$runtimePath = $app->getRuntimePath();
|
||||
if (!is_dir($runtimePath)) {
|
||||
@mkdir($runtimePath, 0755, true);
|
||||
}
|
||||
|
||||
file_put_contents(RUNTIME_PATH . 'classmap' . EXT, $classmapFile);
|
||||
file_put_contents($runtimePath . 'classmap.php', $classmapFile);
|
||||
|
||||
$output->writeln('<info>Succeed!</info>');
|
||||
}
|
||||
@@ -100,40 +92,33 @@ EOF;
|
||||
|
||||
protected function getPathCode($path)
|
||||
{
|
||||
|
||||
$baseDir = '';
|
||||
$libPath = $this->normalizePath(realpath(LIB_PATH));
|
||||
$appPath = $this->normalizePath(realpath(APP_PATH));
|
||||
$extendPath = $this->normalizePath(realpath(EXTEND_PATH));
|
||||
$rootPath = $this->normalizePath(realpath(ROOT_PATH));
|
||||
$app = Container::get('app');
|
||||
$appPath = $this->normalizePath(realpath($app->getAppPath()));
|
||||
$libPath = $this->normalizePath(realpath($app->getThinkPath() . 'library'));
|
||||
$extendPath = $this->normalizePath(realpath($app->getRootPath() . 'extend'));
|
||||
$path = $this->normalizePath($path);
|
||||
|
||||
if ($libPath !== null && strpos($path, $libPath . '/') === 0) {
|
||||
$path = substr($path, strlen(LIB_PATH));
|
||||
$baseDir = 'LIB_PATH';
|
||||
} elseif ($appPath !== null && strpos($path, $appPath . '/') === 0) {
|
||||
if (strpos($path, $libPath . '/') === 0) {
|
||||
$path = substr($path, strlen($app->getThinkPath() . 'library'));
|
||||
$baseDir = "'" . $libPath . "/'";
|
||||
} elseif (strpos($path, $appPath . '/') === 0) {
|
||||
$path = substr($path, strlen($appPath) + 1);
|
||||
$baseDir = 'APP_PATH';
|
||||
} elseif ($extendPath !== null && strpos($path, $extendPath . '/') === 0) {
|
||||
$baseDir = "'" . $appPath . "/'";
|
||||
} elseif (strpos($path, $extendPath . '/') === 0) {
|
||||
$path = substr($path, strlen($extendPath) + 1);
|
||||
$baseDir = 'EXTEND_PATH';
|
||||
} elseif ($rootPath !== null && strpos($path, $rootPath . '/') === 0) {
|
||||
$path = substr($path, strlen($rootPath) + 1);
|
||||
$baseDir = 'ROOT_PATH';
|
||||
$baseDir = "'" . $extendPath . "/'";
|
||||
}
|
||||
|
||||
if ($path !== false) {
|
||||
if (false !== $path) {
|
||||
$baseDir .= " . ";
|
||||
}
|
||||
|
||||
return $baseDir . (($path !== false) ? var_export($path, true) : "");
|
||||
return $baseDir . ((false !== $path) ? var_export($path, true) : "");
|
||||
}
|
||||
|
||||
protected function normalizePath($path)
|
||||
{
|
||||
if ($path === false) {
|
||||
return;
|
||||
}
|
||||
$parts = [];
|
||||
$path = strtr($path, '\\', '/');
|
||||
$prefix = '';
|
||||
@@ -252,7 +237,7 @@ EOF;
|
||||
// strip leading non-php code if needed
|
||||
if (substr($contents, 0, 2) !== '<?') {
|
||||
$contents = preg_replace('{^.+?<\?}s', '<?', $contents, 1, $replacements);
|
||||
if ($replacements === 0) {
|
||||
if (0 === $replacements) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -279,9 +264,9 @@ EOF;
|
||||
$namespace = str_replace([' ', "\t", "\r", "\n"], '', $matches['nsname'][$i]) . '\\';
|
||||
} else {
|
||||
$name = $matches['name'][$i];
|
||||
if ($name[0] === ':') {
|
||||
if (':' === $name[0]) {
|
||||
$name = 'xhp' . substr(str_replace(['-', ':'], ['_', '__'], $name), 1);
|
||||
} elseif ($matches['type'][$i] === 'enum') {
|
||||
} elseif ('enum' === $matches['type'][$i]) {
|
||||
$name = rtrim($name, ':');
|
||||
}
|
||||
$classes[] = ltrim($namespace . $name, '\\');
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// | Copyright (c) 2006-2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
@@ -10,17 +10,15 @@
|
||||
// +----------------------------------------------------------------------
|
||||
namespace think\console\command\optimize;
|
||||
|
||||
use think\Config as ThinkConfig;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument;
|
||||
use think\console\Output;
|
||||
use think\Container;
|
||||
use think\facade\App;
|
||||
|
||||
class Config extends Command
|
||||
{
|
||||
/** @var Output */
|
||||
protected $output;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('optimize:config')
|
||||
@@ -31,63 +29,79 @@ class Config extends Command
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
if ($input->getArgument('module')) {
|
||||
$module = $input->getArgument('module') . DS;
|
||||
$module = $input->getArgument('module') . DIRECTORY_SEPARATOR;
|
||||
} else {
|
||||
$module = '';
|
||||
}
|
||||
|
||||
$content = '<?php ' . PHP_EOL . $this->buildCacheContent($module);
|
||||
|
||||
if (!is_dir(RUNTIME_PATH . $module)) {
|
||||
@mkdir(RUNTIME_PATH . $module, 0755, true);
|
||||
$content = '<?php ' . PHP_EOL . $this->buildCacheContent($module);
|
||||
$runtimePath = App::getRuntimePath();
|
||||
if (!is_dir($runtimePath . $module)) {
|
||||
@mkdir($runtimePath . $module, 0755, true);
|
||||
}
|
||||
|
||||
file_put_contents(RUNTIME_PATH . $module . 'init' . EXT, $content);
|
||||
file_put_contents($runtimePath . $module . 'init.php', $content);
|
||||
|
||||
$output->writeln('<info>Succeed!</info>');
|
||||
}
|
||||
|
||||
protected function buildCacheContent($module)
|
||||
{
|
||||
$content = '';
|
||||
$path = realpath(APP_PATH . $module) . DS;
|
||||
|
||||
$content = '// This cache file is automatically generated at:' . date('Y-m-d H:i:s') . PHP_EOL;
|
||||
$path = realpath(App::getAppPath() . $module) . DIRECTORY_SEPARATOR;
|
||||
if ($module) {
|
||||
// 加载模块配置
|
||||
$config = ThinkConfig::load(CONF_PATH . $module . 'config' . CONF_EXT);
|
||||
$configPath = is_dir($path . 'config') ? $path . 'config' : App::getConfigPath() . $module;
|
||||
} else {
|
||||
$configPath = App::getConfigPath();
|
||||
}
|
||||
$ext = App::getConfigExt();
|
||||
$config = Container::get('config');
|
||||
|
||||
// 读取数据库配置文件
|
||||
$filename = CONF_PATH . $module . 'database' . CONF_EXT;
|
||||
ThinkConfig::load($filename, 'database');
|
||||
$files = is_dir($configPath) ? scandir($configPath) : [];
|
||||
|
||||
// 加载应用状态配置
|
||||
if ($config['app_status']) {
|
||||
$config = ThinkConfig::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT);
|
||||
}
|
||||
// 读取扩展配置文件
|
||||
if (is_dir(CONF_PATH . $module . 'extra')) {
|
||||
$dir = CONF_PATH . $module . 'extra';
|
||||
$files = scandir($dir);
|
||||
foreach ($files as $file) {
|
||||
if (strpos($file, CONF_EXT)) {
|
||||
$filename = $dir . DS . $file;
|
||||
ThinkConfig::load($filename, pathinfo($file, PATHINFO_FILENAME));
|
||||
}
|
||||
}
|
||||
foreach ($files as $file) {
|
||||
if ('.' . pathinfo($file, PATHINFO_EXTENSION) === $ext) {
|
||||
$filename = $configPath . DIRECTORY_SEPARATOR . $file;
|
||||
$config->load($filename, pathinfo($file, PATHINFO_FILENAME));
|
||||
}
|
||||
}
|
||||
|
||||
// 加载行为扩展文件
|
||||
if (is_file(CONF_PATH . $module . 'tags' . EXT)) {
|
||||
$content .= '\think\Hook::import(' . (var_export(include CONF_PATH . $module . 'tags' . EXT, true)) . ');' . PHP_EOL;
|
||||
if (is_file($path . 'tags.php')) {
|
||||
$tags = include $path . 'tags.php';
|
||||
if (is_array($tags)) {
|
||||
$content .= PHP_EOL . '\think\facade\Hook::import(' . (var_export($tags, true)) . ');' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
// 加载公共文件
|
||||
if (is_file($path . 'common' . EXT)) {
|
||||
$content .= substr(php_strip_whitespace($path . 'common' . EXT), 5) . PHP_EOL;
|
||||
if (is_file($path . 'common.php')) {
|
||||
$common = substr(php_strip_whitespace($path . 'common.php'), 6);
|
||||
if ($common) {
|
||||
$content .= PHP_EOL . $common . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
$content .= '\think\Config::set(' . var_export(ThinkConfig::get(), true) . ');';
|
||||
if ('' == $module) {
|
||||
$content .= PHP_EOL . substr(php_strip_whitespace(App::getThinkPath() . 'helper.php'), 6) . PHP_EOL;
|
||||
|
||||
if (is_file($path . 'middleware.php')) {
|
||||
$middleware = include $path . 'middleware.php';
|
||||
if (is_array($middleware)) {
|
||||
$content .= PHP_EOL . '\think\Container::get("middleware")->import(' . var_export($middleware, true) . ');' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_file($path . 'provider.php')) {
|
||||
$provider = include $path . 'provider.php';
|
||||
if (is_array($provider)) {
|
||||
$content .= PHP_EOL . '\think\Container::getInstance()->bindTo(' . var_export($provider, true) . ');' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
$content .= PHP_EOL . '\think\facade\Config::set(' . var_export($config->get(), true) . ');' . PHP_EOL;
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,19 +6,17 @@
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace think\console\command\optimize;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
use think\Container;
|
||||
|
||||
class Route extends Command
|
||||
{
|
||||
/** @var Output */
|
||||
protected $output;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('optimize:route')
|
||||
@@ -27,49 +25,42 @@ class Route extends Command
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
|
||||
if (!is_dir(RUNTIME_PATH)) {
|
||||
@mkdir(RUNTIME_PATH, 0755, true);
|
||||
$filename = Container::get('app')->getRuntimePath() . 'route.php';
|
||||
if (is_file($filename)) {
|
||||
unlink($filename);
|
||||
}
|
||||
|
||||
file_put_contents(RUNTIME_PATH . 'route.php', $this->buildRouteCache());
|
||||
file_put_contents($filename, $this->buildRouteCache());
|
||||
$output->writeln('<info>Succeed!</info>');
|
||||
}
|
||||
|
||||
protected function buildRouteCache()
|
||||
{
|
||||
$files = \think\Config::get('route_config_file');
|
||||
Container::get('route')->setName([]);
|
||||
Container::get('route')->setTestMode(true);
|
||||
// 路由检测
|
||||
$path = Container::get('app')->getRoutePath();
|
||||
|
||||
$files = is_dir($path) ? scandir($path) : [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (is_file(CONF_PATH . $file . CONF_EXT)) {
|
||||
$config = include CONF_PATH . $file . CONF_EXT;
|
||||
if (is_array($config)) {
|
||||
\think\Route::import($config);
|
||||
if (strpos($file, '.php')) {
|
||||
$filename = $path . DIRECTORY_SEPARATOR . $file;
|
||||
// 导入路由配置
|
||||
$rules = include $filename;
|
||||
if (is_array($rules)) {
|
||||
Container::get('route')->import($rules);
|
||||
}
|
||||
}
|
||||
}
|
||||
$rules = \think\Route::rules(true);
|
||||
array_walk_recursive($rules, [$this, 'buildClosure']);
|
||||
|
||||
if (Container::get('config')->get('route_annotation')) {
|
||||
$suffix = Container::get('config')->get('controller_suffix') || Container::get('config')->get('class_suffix');
|
||||
include Container::get('build')->buildRoute($suffix);
|
||||
}
|
||||
|
||||
$content = '<?php ' . PHP_EOL . 'return ';
|
||||
$content .= var_export($rules, true) . ';';
|
||||
$content = str_replace(['\'[__start__', '__end__]\''], '', stripcslashes($content));
|
||||
$content .= var_export(Container::get('route')->getName(), true) . ';';
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function buildClosure(&$value)
|
||||
{
|
||||
if ($value instanceof \Closure) {
|
||||
$reflection = new \ReflectionFunction($value);
|
||||
$startLine = $reflection->getStartLine();
|
||||
$endLine = $reflection->getEndLine();
|
||||
$file = $reflection->getFileName();
|
||||
$item = file($file);
|
||||
$content = '';
|
||||
for ($i = $startLine - 1; $i <= $endLine - 1; $i++) {
|
||||
$content .= $item[$i];
|
||||
}
|
||||
$start = strpos($content, 'function');
|
||||
$end = strrpos($content, '}');
|
||||
$value = '[__start__' . substr($content, $start, $end - $start + 1) . '__end__]';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,22 +10,18 @@
|
||||
// +----------------------------------------------------------------------
|
||||
namespace think\console\command\optimize;
|
||||
|
||||
use think\App;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\Db;
|
||||
use think\facade\App;
|
||||
|
||||
class Schema extends Command
|
||||
{
|
||||
/** @var Output */
|
||||
protected $output;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('optimize:schema')
|
||||
->addOption('config', null, Option::VALUE_REQUIRED, 'db config .')
|
||||
->addOption('db', null, Option::VALUE_REQUIRED, 'db name .')
|
||||
->addOption('table', null, Option::VALUE_REQUIRED, 'table name .')
|
||||
->addOption('module', null, Option::VALUE_REQUIRED, 'module name .')
|
||||
@@ -34,56 +30,58 @@ class Schema extends Command
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
if (!is_dir(RUNTIME_PATH . 'schema')) {
|
||||
@mkdir(RUNTIME_PATH . 'schema', 0755, true);
|
||||
}
|
||||
$config = [];
|
||||
if ($input->hasOption('config')) {
|
||||
$config = $input->getOption('config');
|
||||
if (!is_dir(App::getRuntimePath() . 'schema')) {
|
||||
@mkdir(App::getRuntimePath() . 'schema', 0755, true);
|
||||
}
|
||||
|
||||
if ($input->hasOption('module')) {
|
||||
$module = $input->getOption('module');
|
||||
// 读取模型
|
||||
$path = APP_PATH . $module . DS . 'model';
|
||||
$list = is_dir($path) ? scandir($path) : [];
|
||||
$app = App::$namespace;
|
||||
$path = App::getAppPath() . $module . DIRECTORY_SEPARATOR . 'model';
|
||||
$list = is_dir($path) ? scandir($path) : [];
|
||||
$namespace = App::getNamespace();
|
||||
|
||||
foreach ($list as $file) {
|
||||
if (0 === strpos($file, '.')) {
|
||||
continue;
|
||||
}
|
||||
$class = '\\' . $app . '\\' . $module . '\\model\\' . pathinfo($file, PATHINFO_FILENAME);
|
||||
$class = '\\' . $namespace . '\\' . $module . '\\model\\' . pathinfo($file, PATHINFO_FILENAME);
|
||||
$this->buildModelSchema($class);
|
||||
}
|
||||
|
||||
$output->writeln('<info>Succeed!</info>');
|
||||
return;
|
||||
} elseif ($input->hasOption('table')) {
|
||||
$table = $input->getOption('table');
|
||||
if (!strpos($table, '.')) {
|
||||
$dbName = Db::connect($config)->getConfig('database');
|
||||
if (false === strpos($table, '.')) {
|
||||
$dbName = Db::getConfig('database');
|
||||
}
|
||||
|
||||
$tables[] = $table;
|
||||
} elseif ($input->hasOption('db')) {
|
||||
$dbName = $input->getOption('db');
|
||||
$tables = Db::connect($config)->getTables($dbName);
|
||||
} elseif (!\think\Config::get('app_multi_module')) {
|
||||
$app = App::$namespace;
|
||||
$path = APP_PATH . 'model';
|
||||
$list = is_dir($path) ? scandir($path) : [];
|
||||
$tables = Db::getConnection()->getTables($dbName);
|
||||
} elseif (!\think\facade\Config::get('app_multi_module')) {
|
||||
$namespace = App::getNamespace();
|
||||
$path = App::getAppPath() . 'model';
|
||||
$list = is_dir($path) ? scandir($path) : [];
|
||||
|
||||
foreach ($list as $file) {
|
||||
if (0 === strpos($file, '.')) {
|
||||
continue;
|
||||
}
|
||||
$class = '\\' . $app . '\\model\\' . pathinfo($file, PATHINFO_FILENAME);
|
||||
$class = '\\' . $namespace . '\\model\\' . pathinfo($file, PATHINFO_FILENAME);
|
||||
$this->buildModelSchema($class);
|
||||
}
|
||||
|
||||
$output->writeln('<info>Succeed!</info>');
|
||||
return;
|
||||
} else {
|
||||
$tables = Db::connect($config)->getTables();
|
||||
$tables = Db::getConnection()->getTables();
|
||||
}
|
||||
|
||||
$db = isset($dbName) ? $dbName . '.' : '';
|
||||
$this->buildDataBaseSchema($tables, $db, $config);
|
||||
$this->buildDataBaseSchema($tables, $db);
|
||||
|
||||
$output->writeln('<info>Succeed!</info>');
|
||||
}
|
||||
@@ -97,22 +95,24 @@ class Schema extends Command
|
||||
$content = '<?php ' . PHP_EOL . 'return ';
|
||||
$info = $class::getConnection()->getFields($table);
|
||||
$content .= var_export($info, true) . ';';
|
||||
file_put_contents(RUNTIME_PATH . 'schema' . DS . $dbName . '.' . $table . EXT, $content);
|
||||
|
||||
file_put_contents(App::getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR . $dbName . '.' . $table . '.php', $content);
|
||||
}
|
||||
}
|
||||
|
||||
protected function buildDataBaseSchema($tables, $db, $config)
|
||||
protected function buildDataBaseSchema($tables, $db)
|
||||
{
|
||||
if ('' == $db) {
|
||||
$dbName = Db::connect($config)->getConfig('database') . '.';
|
||||
$dbName = Db::getConfig('database') . '.';
|
||||
} else {
|
||||
$dbName = $db;
|
||||
}
|
||||
|
||||
foreach ($tables as $table) {
|
||||
$content = '<?php ' . PHP_EOL . 'return ';
|
||||
$info = Db::connect($config)->getFields($db . $table);
|
||||
$info = Db::getConnection()->getFields($db . $table);
|
||||
$content .= var_export($info, true) . ';';
|
||||
file_put_contents(RUNTIME_PATH . 'schema' . DS . $dbName . $table . EXT, $content);
|
||||
file_put_contents(App::getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR . $dbName . $table . '.php', $content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class Descriptor
|
||||
* 描述参数
|
||||
* @param InputArgument $argument
|
||||
* @param array $options
|
||||
* @return void
|
||||
* @return string|mixed
|
||||
*/
|
||||
protected function describeInputArgument(InputArgument $argument, array $options = [])
|
||||
{
|
||||
@@ -93,7 +93,7 @@ class Descriptor
|
||||
* 描述选项
|
||||
* @param InputOption $option
|
||||
* @param array $options
|
||||
* @return void
|
||||
* @return string|mixed
|
||||
*/
|
||||
protected function describeInputOption(InputOption $option, array $options = [])
|
||||
{
|
||||
@@ -128,7 +128,7 @@ class Descriptor
|
||||
* 描述输入
|
||||
* @param InputDefinition $definition
|
||||
* @param array $options
|
||||
* @return void
|
||||
* @return string|mixed
|
||||
*/
|
||||
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
|
||||
{
|
||||
@@ -173,7 +173,7 @@ class Descriptor
|
||||
* 描述指令
|
||||
* @param Command $command
|
||||
* @param array $options
|
||||
* @return void
|
||||
* @return string|mixed
|
||||
*/
|
||||
protected function describeCommand(Command $command, array $options = [])
|
||||
{
|
||||
@@ -208,7 +208,7 @@ class Descriptor
|
||||
* 描述控制台
|
||||
* @param Console $console
|
||||
* @param array $options
|
||||
* @return void
|
||||
* @return string|mixed
|
||||
*/
|
||||
protected function describeConsole(Console $console, array $options = [])
|
||||
{
|
||||
|
||||
@@ -104,6 +104,10 @@ class Console
|
||||
|
||||
/** @var Command $command */
|
||||
foreach ($commands as $name => $command) {
|
||||
if (is_string($command)) {
|
||||
$command = new $command();
|
||||
}
|
||||
|
||||
if (!$command->getName()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -37,11 +37,6 @@ class Console
|
||||
$this->formatter->setDecorated($decorated);
|
||||
}
|
||||
|
||||
public function getFormatter()
|
||||
{
|
||||
return $this->formatter;
|
||||
}
|
||||
|
||||
public function setDecorated($decorated)
|
||||
{
|
||||
$this->formatter->setDecorated($decorated);
|
||||
@@ -173,7 +168,7 @@ class Console
|
||||
return $this->terminalDimensions;
|
||||
}
|
||||
|
||||
if ('\\' === DS) {
|
||||
if ('\\' === DIRECTORY_SEPARATOR) {
|
||||
if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
|
||||
return [(int) $matches[1], (int) $matches[2]];
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ class Choice extends Question
|
||||
$result = $value;
|
||||
}
|
||||
|
||||
if (empty($result)) {
|
||||
if (false === $result) {
|
||||
throw new \InvalidArgumentException(sprintf($errorMessage, $value));
|
||||
}
|
||||
array_push($multiselectChoices, $result);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,8 @@
|
||||
namespace think\db\builder;
|
||||
|
||||
use think\db\Builder;
|
||||
use think\db\Expression;
|
||||
use think\db\Query;
|
||||
use think\Exception;
|
||||
|
||||
/**
|
||||
@@ -19,6 +21,20 @@ use think\Exception;
|
||||
*/
|
||||
class Mysql extends Builder
|
||||
{
|
||||
// 查询表达式解析
|
||||
protected $parser = [
|
||||
'parseCompare' => ['=', '<>', '>', '>=', '<', '<='],
|
||||
'parseLike' => ['LIKE', 'NOT LIKE'],
|
||||
'parseBetween' => ['NOT BETWEEN', 'BETWEEN'],
|
||||
'parseIn' => ['NOT IN', 'IN'],
|
||||
'parseExp' => ['EXP'],
|
||||
'parseRegexp' => ['REGEXP', 'NOT REGEXP'],
|
||||
'parseNull' => ['NOT NULL', 'NULL'],
|
||||
'parseBetweenTime' => ['BETWEEN TIME', 'NOT BETWEEN TIME'],
|
||||
'parseTime' => ['< TIME', '> TIME', '<= TIME', '>= TIME'],
|
||||
'parseExists' => ['NOT EXISTS', 'EXISTS'],
|
||||
'parseColumn' => ['COLUMN'],
|
||||
];
|
||||
|
||||
protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES %DATA% %COMMENT%';
|
||||
protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
|
||||
@@ -26,67 +42,80 @@ class Mysql extends Builder
|
||||
/**
|
||||
* 生成insertall SQL
|
||||
* @access public
|
||||
* @param array $dataSet 数据集
|
||||
* @param array $options 表达式
|
||||
* @param bool $replace 是否replace
|
||||
* @param Query $query 查询对象
|
||||
* @param array $dataSet 数据集
|
||||
* @param bool $replace 是否replace
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function insertAll($dataSet, $options = [], $replace = false)
|
||||
public function insertAll(Query $query, $dataSet, $replace = false)
|
||||
{
|
||||
$options = $query->getOptions();
|
||||
|
||||
// 获取合法的字段
|
||||
if ('*' == $options['field']) {
|
||||
$fields = array_keys($this->query->getFieldsType($options['table']));
|
||||
$allowFields = $this->connection->getTableFields($options['table']);
|
||||
} else {
|
||||
$fields = $options['field'];
|
||||
$allowFields = $options['field'];
|
||||
}
|
||||
|
||||
foreach ($dataSet as $data) {
|
||||
foreach ($data as $key => $val) {
|
||||
if (!in_array($key, $fields, true)) {
|
||||
if ($options['strict']) {
|
||||
throw new Exception('fields not exists:[' . $key . ']');
|
||||
}
|
||||
unset($data[$key]);
|
||||
} elseif (is_null($val)) {
|
||||
$data[$key] = 'NULL';
|
||||
} elseif (is_scalar($val)) {
|
||||
$data[$key] = $this->parseValue($val, $key);
|
||||
} elseif (is_object($val) && method_exists($val, '__toString')) {
|
||||
// 对象数据写入
|
||||
$data[$key] = $val->__toString();
|
||||
} else {
|
||||
// 过滤掉非标量数据
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
$value = array_values($data);
|
||||
$values[] = '( ' . implode(',', $value) . ' )';
|
||||
// 获取绑定信息
|
||||
$bind = $this->connection->getFieldsBind($options['table']);
|
||||
|
||||
foreach ($dataSet as $k => $data) {
|
||||
$data = $this->parseData($query, $data, $allowFields, $bind);
|
||||
|
||||
$values[] = '( ' . implode(',', array_values($data)) . ' )';
|
||||
|
||||
if (!isset($insertFields)) {
|
||||
$insertFields = array_map([$this, 'parseKey'], array_keys($data));
|
||||
$insertFields = array_keys($data);
|
||||
}
|
||||
}
|
||||
|
||||
$fields = [];
|
||||
foreach ($insertFields as $field) {
|
||||
$fields[] = $this->parseKey($query, $field);
|
||||
}
|
||||
|
||||
return str_replace(
|
||||
['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
|
||||
[
|
||||
$replace ? 'REPLACE' : 'INSERT',
|
||||
$this->parseTable($options['table'], $options),
|
||||
implode(' , ', $insertFields),
|
||||
$this->parseTable($query, $options['table']),
|
||||
implode(' , ', $fields),
|
||||
implode(' , ', $values),
|
||||
$this->parseComment($options['comment']),
|
||||
], $this->insertAllSql);
|
||||
$this->parseComment($query, $options['comment']),
|
||||
],
|
||||
$this->insertAllSql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 正则查询
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @param string $key
|
||||
* @param string $exp
|
||||
* @param mixed $value
|
||||
* @param string $field
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRegexp(Query $query, $key, $exp, $value, $field)
|
||||
{
|
||||
if ($value instanceof Expression) {
|
||||
$value = $value->getValue();
|
||||
}
|
||||
|
||||
return $key . ' ' . $exp . ' ' . $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段和表名处理
|
||||
* @access protected
|
||||
* @param mixed $key
|
||||
* @param array $options
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $key 字段名
|
||||
* @param bool $strict 严格检测
|
||||
* @return string
|
||||
*/
|
||||
protected function parseKey($key, $options = [], $strict = false)
|
||||
public function parseKey(Query $query, $key, $strict = false)
|
||||
{
|
||||
if (is_numeric($key)) {
|
||||
return $key;
|
||||
@@ -95,41 +124,59 @@ class Mysql extends Builder
|
||||
}
|
||||
|
||||
$key = trim($key);
|
||||
if (strpos($key, '$.') && false === strpos($key, '(')) {
|
||||
|
||||
if(strpos($key, '->>') && false === strpos($key, '(')){
|
||||
// JSON字段支持
|
||||
list($field, $name) = explode('$.', $key);
|
||||
return 'json_extract(' . $field . ', \'$.' . $name . '\')';
|
||||
list($field, $name) = explode('->>', $key, 2);
|
||||
|
||||
return $this->parseKey($query, $field, true) . '->>\'$' . (strpos($name, '[') === 0 ? '' : '.') . str_replace('->>', '.', $name) . '\'';
|
||||
}
|
||||
elseif (strpos($key, '->') && false === strpos($key, '(')) {
|
||||
// JSON字段支持
|
||||
list($field, $name) = explode('->', $key, 2);
|
||||
|
||||
return 'json_extract(' . $this->parseKey($query, $field, true) . ', \'$' . (strpos($name, '[') === 0 ? '' : '.') . str_replace('->', '.', $name) . '\')';
|
||||
} elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) {
|
||||
list($table, $key) = explode('.', $key, 2);
|
||||
|
||||
$alias = $query->getOptions('alias');
|
||||
|
||||
if ('__TABLE__' == $table) {
|
||||
$table = $this->query->getTable();
|
||||
$table = $query->getOptions('table');
|
||||
$table = is_array($table) ? array_shift($table) : $table;
|
||||
}
|
||||
if (isset($options['alias'][$table])) {
|
||||
$table = $options['alias'][$table];
|
||||
|
||||
if (isset($alias[$table])) {
|
||||
$table = $alias[$table];
|
||||
}
|
||||
}
|
||||
|
||||
if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
|
||||
throw new Exception('not support data:' . $key);
|
||||
}
|
||||
if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) {
|
||||
|
||||
if ('*' != $key && !preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
|
||||
$key = '`' . $key . '`';
|
||||
}
|
||||
|
||||
if (isset($table)) {
|
||||
if (strpos($table, '.')) {
|
||||
$table = str_replace('.', '`.`', $table);
|
||||
}
|
||||
|
||||
$key = '`' . $table . '`.' . $key;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机排序
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRand()
|
||||
protected function parseRand(Query $query)
|
||||
{
|
||||
return 'rand()';
|
||||
}
|
||||
|
||||
@@ -12,24 +12,28 @@
|
||||
namespace think\db\builder;
|
||||
|
||||
use think\db\Builder;
|
||||
use think\db\Query;
|
||||
|
||||
/**
|
||||
* Pgsql数据库驱动
|
||||
*/
|
||||
class Pgsql extends Builder
|
||||
{
|
||||
|
||||
protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
|
||||
protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
|
||||
|
||||
/**
|
||||
* limit分析
|
||||
* @access protected
|
||||
* @param mixed $limit
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $limit
|
||||
* @return string
|
||||
*/
|
||||
public function parseLimit($limit)
|
||||
public function parseLimit(Query $query, $limit)
|
||||
{
|
||||
$limitStr = '';
|
||||
|
||||
if (!empty($limit)) {
|
||||
$limit = explode(',', $limit);
|
||||
if (count($limit) > 1) {
|
||||
@@ -38,17 +42,19 @@ class Pgsql extends Builder
|
||||
$limitStr .= ' LIMIT ' . $limit[0] . ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return $limitStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段和表名处理
|
||||
* @access protected
|
||||
* @param mixed $key
|
||||
* @param array $options
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $key 字段名
|
||||
* @param bool $strict 严格检测
|
||||
* @return string
|
||||
*/
|
||||
protected function parseKey($key, $options = [], $strict = false)
|
||||
public function parseKey(Query $query, $key, $strict = false)
|
||||
{
|
||||
if (is_numeric($key)) {
|
||||
return $key;
|
||||
@@ -57,31 +63,40 @@ class Pgsql extends Builder
|
||||
}
|
||||
|
||||
$key = trim($key);
|
||||
if (strpos($key, '$.') && false === strpos($key, '(')) {
|
||||
|
||||
if (strpos($key, '->') && false === strpos($key, '(')) {
|
||||
// JSON字段支持
|
||||
list($field, $name) = explode('$.', $key);
|
||||
list($field, $name) = explode('->', $key);
|
||||
$key = $field . '->>\'' . $name . '\'';
|
||||
} elseif (strpos($key, '.')) {
|
||||
list($table, $key) = explode('.', $key, 2);
|
||||
|
||||
$alias = $query->getOptions('alias');
|
||||
|
||||
if ('__TABLE__' == $table) {
|
||||
$table = $this->query->getTable();
|
||||
$table = $query->getOptions('table');
|
||||
$table = is_array($table) ? array_shift($table) : $table;
|
||||
}
|
||||
if (isset($options['alias'][$table])) {
|
||||
$table = $options['alias'][$table];
|
||||
|
||||
if (isset($alias[$table])) {
|
||||
$table = $alias[$table];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($table)) {
|
||||
$key = $table . '.' . $key;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机排序
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRand()
|
||||
protected function parseRand(Query $query)
|
||||
{
|
||||
return 'RANDOM()';
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
namespace think\db\builder;
|
||||
|
||||
use think\db\Builder;
|
||||
use think\db\Query;
|
||||
|
||||
/**
|
||||
* Sqlite数据库驱动
|
||||
@@ -22,12 +23,14 @@ class Sqlite extends Builder
|
||||
/**
|
||||
* limit
|
||||
* @access public
|
||||
* @param string $limit
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $limit
|
||||
* @return string
|
||||
*/
|
||||
public function parseLimit($limit)
|
||||
public function parseLimit(Query $query, $limit)
|
||||
{
|
||||
$limitStr = '';
|
||||
|
||||
if (!empty($limit)) {
|
||||
$limit = explode(',', $limit);
|
||||
if (count($limit) > 1) {
|
||||
@@ -36,27 +39,30 @@ class Sqlite extends Builder
|
||||
$limitStr .= ' LIMIT ' . $limit[0] . ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return $limitStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机排序
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRand()
|
||||
protected function parseRand(Query $query)
|
||||
{
|
||||
return 'RANDOM()';
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段和表名处理
|
||||
* @access protected
|
||||
* @param mixed $key
|
||||
* @param array $options
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $key 字段名
|
||||
* @param bool $strict 严格检测
|
||||
* @return string
|
||||
*/
|
||||
protected function parseKey($key, $options = [], $strict = false)
|
||||
public function parseKey(Query $query, $key, $strict = false)
|
||||
{
|
||||
if (is_numeric($key)) {
|
||||
return $key;
|
||||
@@ -65,18 +71,26 @@ class Sqlite extends Builder
|
||||
}
|
||||
|
||||
$key = trim($key);
|
||||
|
||||
if (strpos($key, '.')) {
|
||||
list($table, $key) = explode('.', $key, 2);
|
||||
|
||||
$alias = $query->getOptions('alias');
|
||||
|
||||
if ('__TABLE__' == $table) {
|
||||
$table = $this->query->getTable();
|
||||
$table = $query->getOptions('table');
|
||||
$table = is_array($table) ? array_shift($table) : $table;
|
||||
}
|
||||
if (isset($options['alias'][$table])) {
|
||||
$table = $options['alias'][$table];
|
||||
|
||||
if (isset($alias[$table])) {
|
||||
$table = $alias[$table];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($table)) {
|
||||
$key = $table . '.' . $key;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace think\db\builder;
|
||||
|
||||
use think\db\Builder;
|
||||
use think\db\Expression;
|
||||
use think\db\Query;
|
||||
use think\Exception;
|
||||
|
||||
/**
|
||||
* Sqlsrv数据库驱动
|
||||
@@ -22,116 +24,136 @@ class Sqlsrv extends Builder
|
||||
protected $selectSql = 'SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS thinkphp) AS T1 %LIMIT%%COMMENT%';
|
||||
protected $selectInsertSql = 'SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%';
|
||||
protected $updateSql = 'UPDATE %TABLE% SET %SET% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
|
||||
protected $deleteSql = 'DELETE FROM %TABLE% %USING% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
|
||||
protected $deleteSql = 'DELETE FROM %TABLE% %USING% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
|
||||
protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
|
||||
protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
|
||||
|
||||
/**
|
||||
* order分析
|
||||
* @access protected
|
||||
* @param mixed $order
|
||||
* @param array $options
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $order
|
||||
* @return string
|
||||
*/
|
||||
protected function parseOrder($order, $options = [])
|
||||
protected function parseOrder(Query $query, $order)
|
||||
{
|
||||
if (empty($order)) {
|
||||
return ' ORDER BY rand()';
|
||||
}
|
||||
|
||||
$array = [];
|
||||
foreach ($order as $key => $val) {
|
||||
if ($val instanceof Expression) {
|
||||
$array[] = $val->getValue();
|
||||
} elseif (is_numeric($key)) {
|
||||
if (false === strpos($val, '(')) {
|
||||
$array[] = $this->parseKey($val, $options);
|
||||
} elseif ('[rand]' == $val) {
|
||||
$array[] = $this->parseRand();
|
||||
} else {
|
||||
$array[] = $val;
|
||||
}
|
||||
} elseif ('[rand]' == $val) {
|
||||
$array[] = $this->parseRand($query);
|
||||
} else {
|
||||
$sort = in_array(strtolower(trim($val)), ['asc', 'desc'], true) ? ' ' . $val : '';
|
||||
$array[] = $this->parseKey($key, $options, true) . ' ' . $sort;
|
||||
if (is_numeric($key)) {
|
||||
list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' ');
|
||||
} else {
|
||||
$sort = $val;
|
||||
}
|
||||
|
||||
if (preg_match('/^[\w\.]+$/', $key)) {
|
||||
$sort = strtoupper($sort);
|
||||
$sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';
|
||||
$array[] = $this->parseKey($query, $key, true) . $sort;
|
||||
} else {
|
||||
throw new Exception('order express error:' . $key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ' ORDER BY ' . implode(',', $array);
|
||||
return empty($array) ? '' : ' ORDER BY ' . implode(',', $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机排序
|
||||
* @access protected
|
||||
* @param Query $query 查询对象
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRand()
|
||||
protected function parseRand(Query $query)
|
||||
{
|
||||
return 'rand()';
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段和表名处理
|
||||
* @access protected
|
||||
* @param mixed $key
|
||||
* @param array $options
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $key 字段名
|
||||
* @param bool $strict 严格检测
|
||||
* @return string
|
||||
*/
|
||||
protected function parseKey($key, $options = [], $strict = false)
|
||||
public function parseKey(Query $query, $key, $strict = false)
|
||||
{
|
||||
if (is_numeric($key)) {
|
||||
return $key;
|
||||
} elseif ($key instanceof Expression) {
|
||||
return $key->getValue();
|
||||
}
|
||||
|
||||
$key = trim($key);
|
||||
|
||||
if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) {
|
||||
list($table, $key) = explode('.', $key, 2);
|
||||
|
||||
$alias = $query->getOptions('alias');
|
||||
|
||||
if ('__TABLE__' == $table) {
|
||||
$table = $this->query->getTable();
|
||||
$table = $query->getOptions('table');
|
||||
$table = is_array($table) ? array_shift($table) : $table;
|
||||
}
|
||||
if (isset($options['alias'][$table])) {
|
||||
$table = $options['alias'][$table];
|
||||
|
||||
if (isset($alias[$table])) {
|
||||
$table = $alias[$table];
|
||||
}
|
||||
}
|
||||
|
||||
if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
|
||||
throw new Exception('not support data:' . $key);
|
||||
}
|
||||
if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)\[.\s]/', $key))) {
|
||||
|
||||
if ('*' != $key && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) {
|
||||
$key = '[' . $key . ']';
|
||||
}
|
||||
|
||||
if (isset($table)) {
|
||||
$key = '[' . $table . '].' . $key;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* limit
|
||||
* @access protected
|
||||
* @param mixed $limit
|
||||
* @param Query $query 查询对象
|
||||
* @param mixed $limit
|
||||
* @return string
|
||||
*/
|
||||
protected function parseLimit($limit)
|
||||
protected function parseLimit(Query $query, $limit)
|
||||
{
|
||||
if (empty($limit)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$limit = explode(',', $limit);
|
||||
|
||||
if (count($limit) > 1) {
|
||||
$limitStr = '(T1.ROW_NUMBER BETWEEN ' . $limit[0] . ' + 1 AND ' . $limit[0] . ' + ' . $limit[1] . ')';
|
||||
} else {
|
||||
$limitStr = '(T1.ROW_NUMBER BETWEEN 1 AND ' . $limit[0] . ")";
|
||||
}
|
||||
|
||||
return 'WHERE ' . $limitStr;
|
||||
}
|
||||
|
||||
public function selectInsert($fields, $table, $options)
|
||||
public function selectInsert(Query $query, $fields, $table)
|
||||
{
|
||||
$this->selectSql = $this->selectInsertSql;
|
||||
return parent::selectInsert($fields, $table, $options);
|
||||
|
||||
return parent::selectInsert($query, $fields, $table);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace think\db\connector;
|
||||
|
||||
use PDO;
|
||||
use think\db\Connection;
|
||||
use think\Log;
|
||||
use think\db\Query;
|
||||
|
||||
/**
|
||||
* mysql数据库驱动
|
||||
@@ -23,10 +23,32 @@ class Mysql extends Connection
|
||||
|
||||
protected $builder = '\\think\\db\\builder\\Mysql';
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function initialize()
|
||||
{
|
||||
// Point类型支持
|
||||
Query::extend('point', function ($query, $field, $value = null, $fun = 'GeomFromText', $type = 'POINT') {
|
||||
if (!is_null($value)) {
|
||||
$query->data($field, ['point', $value, $fun, $type]);
|
||||
} else {
|
||||
if (is_string($field)) {
|
||||
$field = explode(',', $field);
|
||||
}
|
||||
$query->setOption('point', $field);
|
||||
}
|
||||
|
||||
return $query;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析pdo连接的dsn信息
|
||||
* @access protected
|
||||
* @param array $config 连接信息
|
||||
* @param array $config 连接信息
|
||||
* @return string
|
||||
*/
|
||||
protected function parseDsn($config)
|
||||
@@ -43,48 +65,53 @@ class Mysql extends Connection
|
||||
if (!empty($config['charset'])) {
|
||||
$dsn .= ';charset=' . $config['charset'];
|
||||
}
|
||||
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $tableName
|
||||
* @param string $tableName
|
||||
* @return array
|
||||
*/
|
||||
public function getFields($tableName)
|
||||
{
|
||||
list($tableName) = explode(' ', $tableName);
|
||||
|
||||
if (false === strpos($tableName, '`')) {
|
||||
if (strpos($tableName, '.')) {
|
||||
$tableName = str_replace('.', '`.`', $tableName);
|
||||
}
|
||||
$tableName = '`' . $tableName . '`';
|
||||
}
|
||||
|
||||
$sql = 'SHOW COLUMNS FROM ' . $tableName;
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
if ($result) {
|
||||
foreach ($result as $key => $val) {
|
||||
$val = array_change_key_case($val);
|
||||
$info[$val['field']] = [
|
||||
'name' => $val['field'],
|
||||
'type' => $val['type'],
|
||||
'notnull' => (bool) ('' === $val['null']), // not null is empty, null is yes
|
||||
'notnull' => 'NO' == $val['null'],
|
||||
'default' => $val['default'],
|
||||
'primary' => (strtolower($val['key']) == 'pri'),
|
||||
'autoinc' => (strtolower($val['extra']) == 'auto_increment'),
|
||||
'primary' => strtolower($val['key']) == 'pri',
|
||||
'autoinc' => strtolower($val['extra']) == 'auto_increment',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->fieldCase($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据库的表信息
|
||||
* @access public
|
||||
* @param string $dbName
|
||||
* @param string $dbName
|
||||
* @return array
|
||||
*/
|
||||
public function getTables($dbName = '')
|
||||
@@ -93,28 +120,52 @@ class Mysql extends Connection
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
foreach ($result as $key => $val) {
|
||||
$info[$key] = current($val);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL性能分析
|
||||
* @access protected
|
||||
* @param string $sql
|
||||
* @param string $sql
|
||||
* @return array
|
||||
*/
|
||||
protected function getExplain($sql)
|
||||
{
|
||||
$pdo = $this->linkID->query("EXPLAIN " . $sql);
|
||||
$result = $pdo->fetch(PDO::FETCH_ASSOC);
|
||||
$result = array_change_key_case($result);
|
||||
if (isset($result['extra'])) {
|
||||
if (strpos($result['extra'], 'filesort') || strpos($result['extra'], 'temporary')) {
|
||||
Log::record('SQL:' . $this->queryStr . '[' . $result['extra'] . ']', 'warn');
|
||||
$pdo = $this->linkID->prepare("EXPLAIN " . $this->queryStr);
|
||||
|
||||
foreach ($this->bind as $key => $val) {
|
||||
// 占位符
|
||||
$param = is_int($key) ? $key + 1 : ':' . $key;
|
||||
|
||||
if (is_array($val)) {
|
||||
if (PDO::PARAM_INT == $val[1] && '' === $val[0]) {
|
||||
$val[0] = 0;
|
||||
} elseif (self::PARAM_FLOAT == $val[1]) {
|
||||
$val[0] = is_string($val[0]) ? (float) $val[0] : $val[0];
|
||||
$val[1] = PDO::PARAM_STR;
|
||||
}
|
||||
|
||||
$result = $pdo->bindValue($param, $val[0], $val[1]);
|
||||
} else {
|
||||
$result = $pdo->bindValue($param, $val);
|
||||
}
|
||||
}
|
||||
|
||||
$pdo->execute();
|
||||
$result = $pdo->fetch(PDO::FETCH_ASSOC);
|
||||
$result = array_change_key_case($result);
|
||||
|
||||
if (isset($result['extra'])) {
|
||||
if (strpos($result['extra'], 'filesort') || strpos($result['extra'], 'temporary')) {
|
||||
$this->log('SQL:' . $this->queryStr . '[' . $result['extra'] . ']', 'warn');
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -123,4 +174,56 @@ class Mysql extends Connection
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动XA事务
|
||||
* @access public
|
||||
* @param string $xid XA事务id
|
||||
* @return void
|
||||
*/
|
||||
public function startTransXa($xid)
|
||||
{
|
||||
$this->initConnect(true);
|
||||
if (!$this->linkID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->linkID->exec("XA START '$xid'");
|
||||
}
|
||||
|
||||
/**
|
||||
* 预编译XA事务
|
||||
* @access public
|
||||
* @param string $xid XA事务id
|
||||
* @return void
|
||||
*/
|
||||
public function prepareXa($xid)
|
||||
{
|
||||
$this->initConnect(true);
|
||||
$this->linkID->exec("XA END '$xid'");
|
||||
$this->linkID->exec("XA PREPARE '$xid'");
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交XA事务
|
||||
* @access public
|
||||
* @param string $xid XA事务id
|
||||
* @return void
|
||||
*/
|
||||
public function commitXa($xid)
|
||||
{
|
||||
$this->initConnect(true);
|
||||
$this->linkID->exec("XA COMMIT '$xid'");
|
||||
}
|
||||
|
||||
/**
|
||||
* 回滚XA事务
|
||||
* @access public
|
||||
* @param string $xid XA事务id
|
||||
* @return void
|
||||
*/
|
||||
public function rollbackXa($xid)
|
||||
{
|
||||
$this->initConnect(true);
|
||||
$this->linkID->exec("XA ROLLBACK '$xid'");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,36 +21,46 @@ class Pgsql extends Connection
|
||||
{
|
||||
protected $builder = '\\think\\db\\builder\\Pgsql';
|
||||
|
||||
// PDO连接参数
|
||||
protected $params = [
|
||||
PDO::ATTR_CASE => PDO::CASE_NATURAL,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
|
||||
PDO::ATTR_STRINGIFY_FETCHES => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* 解析pdo连接的dsn信息
|
||||
* @access protected
|
||||
* @param array $config 连接信息
|
||||
* @param array $config 连接信息
|
||||
* @return string
|
||||
*/
|
||||
protected function parseDsn($config)
|
||||
{
|
||||
$dsn = 'pgsql:dbname=' . $config['database'] . ';host=' . $config['hostname'];
|
||||
|
||||
if (!empty($config['hostport'])) {
|
||||
$dsn .= ';port=' . $config['hostport'];
|
||||
}
|
||||
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $tableName
|
||||
* @param string $tableName
|
||||
* @return array
|
||||
*/
|
||||
public function getFields($tableName)
|
||||
{
|
||||
|
||||
list($tableName) = explode(' ', $tableName);
|
||||
$sql = 'select fields_name as "field",fields_type as "type",fields_not_null as "null",fields_key_name as "key",fields_default as "default",fields_default as "extra" from table_msg(\'' . $tableName . '\');';
|
||||
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
if ($result) {
|
||||
foreach ($result as $key => $val) {
|
||||
$val = array_change_key_case($val);
|
||||
@@ -64,13 +74,14 @@ class Pgsql extends Connection
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->fieldCase($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据库的表信息
|
||||
* @access public
|
||||
* @param string $dbName
|
||||
* @param string $dbName
|
||||
* @return array
|
||||
*/
|
||||
public function getTables($dbName = '')
|
||||
@@ -79,16 +90,18 @@ class Pgsql extends Connection
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
foreach ($result as $key => $val) {
|
||||
$info[$key] = current($val);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL性能分析
|
||||
* @access protected
|
||||
* @param string $sql
|
||||
* @param string $sql
|
||||
* @return array
|
||||
*/
|
||||
protected function getExplain($sql)
|
||||
|
||||
@@ -25,19 +25,20 @@ class Sqlite extends Connection
|
||||
/**
|
||||
* 解析pdo连接的dsn信息
|
||||
* @access protected
|
||||
* @param array $config 连接信息
|
||||
* @param array $config 连接信息
|
||||
* @return string
|
||||
*/
|
||||
protected function parseDsn($config)
|
||||
{
|
||||
$dsn = 'sqlite:' . $config['database'];
|
||||
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $tableName
|
||||
* @param string $tableName
|
||||
* @return array
|
||||
*/
|
||||
public function getFields($tableName)
|
||||
@@ -48,6 +49,7 @@ class Sqlite extends Connection
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
if ($result) {
|
||||
foreach ($result as $key => $val) {
|
||||
$val = array_change_key_case($val);
|
||||
@@ -61,18 +63,18 @@ class Sqlite extends Connection
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->fieldCase($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据库的表信息
|
||||
* @access public
|
||||
* @param string $dbName
|
||||
* @param string $dbName
|
||||
* @return array
|
||||
*/
|
||||
public function getTables($dbName = '')
|
||||
{
|
||||
|
||||
$sql = "SELECT name FROM sqlite_master WHERE type='table' "
|
||||
. "UNION ALL SELECT name FROM sqlite_temp_master "
|
||||
. "WHERE type='table' ORDER BY name";
|
||||
@@ -80,16 +82,18 @@ class Sqlite extends Connection
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
foreach ($result as $key => $val) {
|
||||
$info[$key] = current($val);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL性能分析
|
||||
* @access protected
|
||||
* @param string $sql
|
||||
* @param string $sql
|
||||
* @return array
|
||||
*/
|
||||
protected function getExplain($sql)
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace think\db\connector;
|
||||
|
||||
use PDO;
|
||||
use think\db\Connection;
|
||||
use think\db\Query;
|
||||
|
||||
/**
|
||||
* Sqlsrv数据库驱动
|
||||
@@ -23,28 +24,33 @@ class Sqlsrv extends Connection
|
||||
protected $params = [
|
||||
PDO::ATTR_CASE => PDO::CASE_NATURAL,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
|
||||
PDO::ATTR_STRINGIFY_FETCHES => false,
|
||||
];
|
||||
|
||||
protected $builder = '\\think\\db\\builder\\Sqlsrv';
|
||||
|
||||
/**
|
||||
* 解析pdo连接的dsn信息
|
||||
* @access protected
|
||||
* @param array $config 连接信息
|
||||
* @param array $config 连接信息
|
||||
* @return string
|
||||
*/
|
||||
protected function parseDsn($config)
|
||||
{
|
||||
$dsn = 'sqlsrv:Database=' . $config['database'] . ';Server=' . $config['hostname'];
|
||||
|
||||
if (!empty($config['hostport'])) {
|
||||
$dsn .= ',' . $config['hostport'];
|
||||
}
|
||||
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $tableName
|
||||
* @param string $tableName
|
||||
* @return array
|
||||
*/
|
||||
public function getFields($tableName)
|
||||
@@ -64,6 +70,7 @@ class Sqlsrv extends Connection
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
if ($result) {
|
||||
foreach ($result as $key => $val) {
|
||||
$val = array_change_key_case($val);
|
||||
@@ -77,23 +84,30 @@ class Sqlsrv extends Connection
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "SELECT column_name FROM information_schema.key_column_usage WHERE table_name='$tableName'";
|
||||
|
||||
// 调试开始
|
||||
$this->debug(true);
|
||||
|
||||
$pdo = $this->linkID->query($sql);
|
||||
|
||||
// 调试结束
|
||||
$this->debug(false, $sql);
|
||||
|
||||
$result = $pdo->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($result) {
|
||||
$info[$result['column_name']]['primary'] = true;
|
||||
}
|
||||
|
||||
return $this->fieldCase($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $dbName
|
||||
* @param string $dbName
|
||||
* @return array
|
||||
*/
|
||||
public function getTables($dbName = '')
|
||||
@@ -106,16 +120,112 @@ class Sqlsrv extends Connection
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
|
||||
foreach ($result as $key => $val) {
|
||||
$info[$key] = current($val);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到某个列的数组
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param string $field 字段名 多个字段用逗号分隔
|
||||
* @param string $key 索引
|
||||
* @return array
|
||||
*/
|
||||
public function column(Query $query, $field, $key = '')
|
||||
{
|
||||
$options = $query->getOptions();
|
||||
|
||||
if (empty($options['fetch_sql']) && !empty($options['cache'])) {
|
||||
// 判断查询缓存
|
||||
$cache = $options['cache'];
|
||||
|
||||
$guid = is_string($cache['key']) ? $cache['key'] : $this->getCacheKey($query, $field);
|
||||
|
||||
$result = Container::get('cache')->get($guid);
|
||||
|
||||
if (false !== $result) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($options['field'])) {
|
||||
$query->removeOption('field');
|
||||
}
|
||||
|
||||
if (is_null($field)) {
|
||||
$field = '*';
|
||||
} elseif ($key && '*' != $field) {
|
||||
$field = $key . ',' . $field;
|
||||
}
|
||||
|
||||
if (is_string($field)) {
|
||||
$field = array_map('trim', explode(',', $field));
|
||||
}
|
||||
|
||||
$query->setOption('field', $field);
|
||||
|
||||
// 生成查询SQL
|
||||
$sql = $this->builder->select($query);
|
||||
|
||||
$bind = $query->getBind();
|
||||
|
||||
if (!empty($options['fetch_sql'])) {
|
||||
// 获取实际执行的SQL语句
|
||||
return $this->getRealSql($sql, $bind);
|
||||
}
|
||||
|
||||
// 执行查询操作
|
||||
$pdo = $this->query($sql, $bind, $options['master'], true);
|
||||
|
||||
if (1 == $pdo->columnCount()) {
|
||||
$result = $pdo->fetchAll(PDO::FETCH_COLUMN);
|
||||
} else {
|
||||
$resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if ('*' == $field && $key) {
|
||||
$result = array_column($resultSet, null, $key);
|
||||
} elseif ($resultSet) {
|
||||
$fields = array_keys($resultSet[0]);
|
||||
$count = count($fields);
|
||||
$key1 = array_shift($fields);
|
||||
$key2 = $fields ? array_shift($fields) : '';
|
||||
$key = $key ?: $key1;
|
||||
|
||||
if (strpos($key, '.')) {
|
||||
list($alias, $key) = explode('.', $key);
|
||||
}
|
||||
|
||||
if (3 == $count) {
|
||||
$column = $key2;
|
||||
} elseif ($count < 3) {
|
||||
$column = $key1;
|
||||
} else {
|
||||
$column = null;
|
||||
}
|
||||
|
||||
$result = array_column($resultSet, $column, $key);
|
||||
} else {
|
||||
$result = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($cache) && isset($guid)) {
|
||||
// 缓存数据
|
||||
$this->cacheData($guid, $result, $cache);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL性能分析
|
||||
* @access protected
|
||||
* @param string $sql
|
||||
* @param string $sql
|
||||
* @return array
|
||||
*/
|
||||
protected function getExplain($sql)
|
||||
|
||||
@@ -37,6 +37,10 @@ DECLARE
|
||||
v_sql varchar;
|
||||
v_rec RECORD;
|
||||
v_key varchar;
|
||||
v_conkey smallint[];
|
||||
v_pk varchar[];
|
||||
v_len smallint;
|
||||
v_pos smallint := 1;
|
||||
BEGIN
|
||||
SELECT
|
||||
pg_class.oid INTO v_oid
|
||||
@@ -49,6 +53,31 @@ BEGIN
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
SELECT
|
||||
pg_constraint.conkey INTO v_conkey
|
||||
FROM
|
||||
pg_constraint
|
||||
INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid
|
||||
INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid
|
||||
INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid
|
||||
WHERE
|
||||
pg_class.relname = a_table_name
|
||||
AND pg_constraint.contype = 'p';
|
||||
|
||||
v_len := array_length(v_conkey,1) + 1;
|
||||
WHILE v_pos < v_len LOOP
|
||||
SELECT
|
||||
pg_attribute.attname INTO v_key
|
||||
FROM pg_constraint
|
||||
INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid
|
||||
INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid AND pg_attribute.attnum = pg_constraint.conkey [ v_conkey[v_pos] ]
|
||||
INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid
|
||||
WHERE pg_class.relname = a_table_name AND pg_constraint.contype = 'p';
|
||||
v_pk := array_append(v_pk,v_key);
|
||||
|
||||
v_pos := v_pos + 1;
|
||||
END LOOP;
|
||||
|
||||
v_sql='
|
||||
SELECT
|
||||
pg_attribute.attname AS fields_name,
|
||||
@@ -83,12 +112,19 @@ BEGIN
|
||||
v_ret.fields_not_null=v_rec.fields_not_null;
|
||||
v_ret.fields_default=v_rec.fields_default;
|
||||
v_ret.fields_comment=v_rec.fields_comment;
|
||||
SELECT constraint_name INTO v_key FROM information_schema.key_column_usage WHERE table_schema=a_schema_name AND table_name=a_table_name AND column_name=v_rec.fields_name;
|
||||
IF FOUND THEN
|
||||
v_ret.fields_key_name=v_key;
|
||||
ELSE
|
||||
v_ret.fields_key_name='';
|
||||
END IF;
|
||||
|
||||
v_ret.fields_key_name='';
|
||||
|
||||
v_len := array_length(v_pk,1) + 1;
|
||||
v_pos := 1;
|
||||
WHILE v_pos < v_len LOOP
|
||||
IF v_rec.fields_name = v_pk[v_pos] THEN
|
||||
v_ret.fields_key_name=v_pk[v_pos];
|
||||
EXIT;
|
||||
END IF;
|
||||
v_pos := v_pos + 1;
|
||||
END LOOP;
|
||||
|
||||
RETURN NEXT v_ret;
|
||||
END LOOP;
|
||||
RETURN ;
|
||||
|
||||
@@ -21,11 +21,12 @@ class BindParamException extends DbException
|
||||
|
||||
/**
|
||||
* BindParamException constructor.
|
||||
* @param string $message
|
||||
* @param array $config
|
||||
* @param string $sql
|
||||
* @param array $bind
|
||||
* @param int $code
|
||||
* @access public
|
||||
* @param string $message
|
||||
* @param array $config
|
||||
* @param string $sql
|
||||
* @param array $bind
|
||||
* @param int $code
|
||||
*/
|
||||
public function __construct($message, $config, $sql, $bind, $code = 10502)
|
||||
{
|
||||
|
||||
@@ -19,9 +19,10 @@ class DataNotFoundException extends DbException
|
||||
|
||||
/**
|
||||
* DbException constructor.
|
||||
* @param string $message
|
||||
* @param string $table
|
||||
* @param array $config
|
||||
* @access public
|
||||
* @param string $message
|
||||
* @param string $table
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct($message, $table = '', array $config = [])
|
||||
{
|
||||
|
||||
@@ -19,8 +19,10 @@ class ModelNotFoundException extends DbException
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param string $message
|
||||
* @param string $model
|
||||
* @access public
|
||||
* @param string $message
|
||||
* @param string $model
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct($message, $model = '', array $config = [])
|
||||
{
|
||||
|
||||
@@ -11,11 +11,8 @@
|
||||
|
||||
namespace think\debug;
|
||||
|
||||
use think\Cache;
|
||||
use think\Config;
|
||||
use think\Container;
|
||||
use think\Db;
|
||||
use think\Debug;
|
||||
use think\Request;
|
||||
use think\Response;
|
||||
|
||||
/**
|
||||
@@ -24,7 +21,7 @@ use think\Response;
|
||||
class Console
|
||||
{
|
||||
protected $config = [
|
||||
'trace_tabs' => ['base' => '基本', 'file' => '文件', 'info' => '流程', 'notice|error' => '错误', 'sql' => 'SQL', 'debug|log' => '调试'],
|
||||
'tabs' => ['base' => '基本', 'file' => '文件', 'info' => '流程', 'notice|error' => '错误', 'sql' => 'SQL', 'debug|log' => '调试'],
|
||||
];
|
||||
|
||||
// 实例化并传入参数
|
||||
@@ -38,13 +35,13 @@ class Console
|
||||
/**
|
||||
* 调试输出接口
|
||||
* @access public
|
||||
* @param Response $response Response对象
|
||||
* @param array $log 日志信息
|
||||
* @param Response $response Response对象
|
||||
* @param array $log 日志信息
|
||||
* @return bool
|
||||
*/
|
||||
public function output(Response $response, array $log = [])
|
||||
{
|
||||
$request = Request::instance();
|
||||
$request = Container::get('request');
|
||||
$contentType = $response->getHeader('Content-Type');
|
||||
$accept = $request->header('accept');
|
||||
if (strpos($accept, 'application/json') === 0 || $request->isAjax()) {
|
||||
@@ -53,12 +50,12 @@ class Console
|
||||
return false;
|
||||
}
|
||||
// 获取基本信息
|
||||
$runtime = number_format(microtime(true) - THINK_START_TIME, 10);
|
||||
$runtime = number_format(microtime(true) - Container::get('app')->getBeginTime(), 10);
|
||||
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
|
||||
$mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
|
||||
$mem = number_format((memory_get_usage() - Container::get('app')->getBeginMem()) / 1024, 2);
|
||||
|
||||
if (isset($_SERVER['HTTP_HOST'])) {
|
||||
$uri = $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
if ($request->host()) {
|
||||
$uri = $request->protocol() . ' ' . $request->method() . ' : ' . $request->url(true);
|
||||
} else {
|
||||
$uri = 'cmd:' . implode(' ', $_SERVER['argv']);
|
||||
}
|
||||
@@ -68,19 +65,18 @@ class Console
|
||||
'请求信息' => date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']) . ' ' . $uri,
|
||||
'运行时间' => number_format($runtime, 6) . 's [ 吞吐率:' . $reqs . 'req/s ] 内存消耗:' . $mem . 'kb 文件加载:' . count(get_included_files()),
|
||||
'查询信息' => Db::$queryTimes . ' queries ' . Db::$executeTimes . ' writes ',
|
||||
'缓存信息' => Cache::$readTimes . ' reads,' . Cache::$writeTimes . ' writes',
|
||||
'配置加载' => count(Config::get()),
|
||||
'缓存信息' => Container::get('cache')->getReadTimes() . ' reads,' . Container::get('cache')->getWriteTimes() . ' writes',
|
||||
];
|
||||
|
||||
if (session_id()) {
|
||||
$base['会话信息'] = 'SESSION_ID=' . session_id();
|
||||
}
|
||||
|
||||
$info = Debug::getFile(true);
|
||||
$info = Container::get('debug')->getFile(true);
|
||||
|
||||
// 页面Trace信息
|
||||
$trace = [];
|
||||
foreach ($this->config['trace_tabs'] as $name => $title) {
|
||||
foreach ($this->config['tabs'] as $name => $title) {
|
||||
$name = strtolower($name);
|
||||
switch ($name) {
|
||||
case 'base': // 基本信息
|
||||
@@ -121,7 +117,7 @@ JS;
|
||||
protected function console($type, $msg)
|
||||
{
|
||||
$type = strtolower($type);
|
||||
$trace_tabs = array_values($this->config['trace_tabs']);
|
||||
$trace_tabs = array_values($this->config['tabs']);
|
||||
$line[] = ($type == $trace_tabs[0] || '调试' == $type || '错误' == $type)
|
||||
? "console.group('{$type}');"
|
||||
: "console.groupCollapsed('{$type}');";
|
||||
@@ -137,12 +133,12 @@ JS;
|
||||
}
|
||||
break;
|
||||
case '错误':
|
||||
$msg = str_replace("\n", '\n', json_encode($m));
|
||||
$msg = str_replace("\n", '\n', addslashes(is_scalar($m) ? $m : json_encode($m)));
|
||||
$style = 'color:#F4006B;font-size:14px;';
|
||||
$line[] = "console.error(\"%c{$msg}\", \"{$style}\");";
|
||||
break;
|
||||
case 'sql':
|
||||
$msg = str_replace("\n", '\n', $m);
|
||||
$msg = str_replace("\n", '\n', addslashes($m));
|
||||
$style = "color:#009bb4;";
|
||||
$line[] = "console.log(\"%c{$msg}\", \"{$style}\");";
|
||||
break;
|
||||
|
||||
@@ -11,11 +11,8 @@
|
||||
|
||||
namespace think\debug;
|
||||
|
||||
use think\Cache;
|
||||
use think\Config;
|
||||
use think\Container;
|
||||
use think\Db;
|
||||
use think\Debug;
|
||||
use think\Request;
|
||||
use think\Response;
|
||||
|
||||
/**
|
||||
@@ -24,27 +21,26 @@ use think\Response;
|
||||
class Html
|
||||
{
|
||||
protected $config = [
|
||||
'trace_file' => '',
|
||||
'trace_tabs' => ['base' => '基本', 'file' => '文件', 'info' => '流程', 'notice|error' => '错误', 'sql' => 'SQL', 'debug|log' => '调试'],
|
||||
'file' => '',
|
||||
'tabs' => ['base' => '基本', 'file' => '文件', 'info' => '流程', 'notice|error' => '错误', 'sql' => 'SQL', 'debug|log' => '调试'],
|
||||
];
|
||||
|
||||
// 实例化并传入参数
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$this->config['trace_file'] = THINK_PATH . 'tpl/page_trace.tpl';
|
||||
$this->config = array_merge($this->config, $config);
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试输出接口
|
||||
* @access public
|
||||
* @param Response $response Response对象
|
||||
* @param array $log 日志信息
|
||||
* @param Response $response Response对象
|
||||
* @param array $log 日志信息
|
||||
* @return bool
|
||||
*/
|
||||
public function output(Response $response, array $log = [])
|
||||
{
|
||||
$request = Request::instance();
|
||||
$request = Container::get('request');
|
||||
$contentType = $response->getHeader('Content-Type');
|
||||
$accept = $request->header('accept');
|
||||
if (strpos($accept, 'application/json') === 0 || $request->isAjax()) {
|
||||
@@ -53,13 +49,13 @@ class Html
|
||||
return false;
|
||||
}
|
||||
// 获取基本信息
|
||||
$runtime = number_format(microtime(true) - THINK_START_TIME, 10, '.', '');
|
||||
$runtime = number_format(microtime(true) - Container::get('app')->getBeginTime(), 10, '.', '');
|
||||
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
|
||||
$mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
|
||||
$mem = number_format((memory_get_usage() - Container::get('app')->getBeginMem()) / 1024, 2);
|
||||
|
||||
// 页面Trace信息
|
||||
if (isset($_SERVER['HTTP_HOST'])) {
|
||||
$uri = $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
if ($request->host()) {
|
||||
$uri = $request->protocol() . ' ' . $request->method() . ' : ' . $request->url(true);
|
||||
} else {
|
||||
$uri = 'cmd:' . implode(' ', $_SERVER['argv']);
|
||||
}
|
||||
@@ -67,19 +63,18 @@ class Html
|
||||
'请求信息' => date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']) . ' ' . $uri,
|
||||
'运行时间' => number_format($runtime, 6) . 's [ 吞吐率:' . $reqs . 'req/s ] 内存消耗:' . $mem . 'kb 文件加载:' . count(get_included_files()),
|
||||
'查询信息' => Db::$queryTimes . ' queries ' . Db::$executeTimes . ' writes ',
|
||||
'缓存信息' => Cache::$readTimes . ' reads,' . Cache::$writeTimes . ' writes',
|
||||
'配置加载' => count(Config::get()),
|
||||
'缓存信息' => Container::get('cache')->getReadTimes() . ' reads,' . Container::get('cache')->getWriteTimes() . ' writes',
|
||||
];
|
||||
|
||||
if (session_id()) {
|
||||
$base['会话信息'] = 'SESSION_ID=' . session_id();
|
||||
}
|
||||
|
||||
$info = Debug::getFile(true);
|
||||
$info = Container::get('debug')->getFile(true);
|
||||
|
||||
// 页面Trace信息
|
||||
$trace = [];
|
||||
foreach ($this->config['trace_tabs'] as $name => $title) {
|
||||
foreach ($this->config['tabs'] as $name => $title) {
|
||||
$name = strtolower($name);
|
||||
switch ($name) {
|
||||
case 'base': // 基本信息
|
||||
@@ -104,7 +99,7 @@ class Html
|
||||
}
|
||||
// 调用Trace页面模板
|
||||
ob_start();
|
||||
include $this->config['trace_file'];
|
||||
include $this->config['file'];
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,12 +20,13 @@ class DbException extends Exception
|
||||
{
|
||||
/**
|
||||
* DbException constructor.
|
||||
* @param string $message
|
||||
* @param array $config
|
||||
* @param string $sql
|
||||
* @param int $code
|
||||
* @access public
|
||||
* @param string $message
|
||||
* @param array $config
|
||||
* @param string $sql
|
||||
* @param int $code
|
||||
*/
|
||||
public function __construct($message, array $config, $sql, $code = 10500)
|
||||
public function __construct($message, array $config = [], $sql = '', $code = 10500)
|
||||
{
|
||||
$this->message = $message;
|
||||
$this->code = $code;
|
||||
|
||||
@@ -29,25 +29,24 @@ class ErrorException extends Exception
|
||||
|
||||
/**
|
||||
* 错误异常构造函数
|
||||
* @param integer $severity 错误级别
|
||||
* @param string $message 错误详细信息
|
||||
* @param string $file 出错文件路径
|
||||
* @param integer $line 出错行号
|
||||
* @param array $context 错误上下文,会包含错误触发处作用域内所有变量的数组
|
||||
* @access public
|
||||
* @param integer $severity 错误级别
|
||||
* @param string $message 错误详细信息
|
||||
* @param string $file 出错文件路径
|
||||
* @param integer $line 出错行号
|
||||
*/
|
||||
public function __construct($severity, $message, $file, $line, array $context = [])
|
||||
public function __construct($severity, $message, $file, $line)
|
||||
{
|
||||
$this->severity = $severity;
|
||||
$this->message = $message;
|
||||
$this->file = $file;
|
||||
$this->line = $line;
|
||||
$this->code = 0;
|
||||
|
||||
empty($context) || $this->setData('Error Context', $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误级别
|
||||
* @access public
|
||||
* @return integer 错误级别
|
||||
*/
|
||||
final public function getSeverity()
|
||||
|
||||
@@ -12,11 +12,8 @@
|
||||
namespace think\exception;
|
||||
|
||||
use Exception;
|
||||
use think\App;
|
||||
use think\Config;
|
||||
use think\console\Output;
|
||||
use think\Lang;
|
||||
use think\Log;
|
||||
use think\Container;
|
||||
use think\Response;
|
||||
|
||||
class Handle
|
||||
@@ -34,6 +31,7 @@ class Handle
|
||||
/**
|
||||
* Report or log an exception.
|
||||
*
|
||||
* @access public
|
||||
* @param \Exception $exception
|
||||
* @return void
|
||||
*/
|
||||
@@ -41,7 +39,7 @@ class Handle
|
||||
{
|
||||
if (!$this->isIgnoreReport($exception)) {
|
||||
// 收集异常数据
|
||||
if (App::$debug) {
|
||||
if (Container::get('app')->isDebug()) {
|
||||
$data = [
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
@@ -57,11 +55,11 @@ class Handle
|
||||
$log = "[{$data['code']}]{$data['message']}";
|
||||
}
|
||||
|
||||
if (Config::get('record_trace')) {
|
||||
if (Container::get('app')->config('log.record_trace')) {
|
||||
$log .= "\r\n" . $exception->getTraceAsString();
|
||||
}
|
||||
|
||||
Log::record($log, 'error');
|
||||
Container::get('log')->record($log, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,12 +70,14 @@ class Handle
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @access public
|
||||
* @param \Exception $e
|
||||
* @return Response
|
||||
*/
|
||||
@@ -85,6 +85,7 @@ class Handle
|
||||
{
|
||||
if ($this->render && $this->render instanceof \Closure) {
|
||||
$result = call_user_func_array($this->render, [$e]);
|
||||
|
||||
if ($result) {
|
||||
return $result;
|
||||
}
|
||||
@@ -98,26 +99,30 @@ class Handle
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Output $output
|
||||
* @param Exception $e
|
||||
* @access public
|
||||
* @param Output $output
|
||||
* @param Exception $e
|
||||
*/
|
||||
public function renderForConsole(Output $output, Exception $e)
|
||||
{
|
||||
if (App::$debug) {
|
||||
if (Container::get('app')->isDebug()) {
|
||||
$output->setVerbosity(Output::VERBOSITY_DEBUG);
|
||||
}
|
||||
|
||||
$output->renderException($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HttpException $e
|
||||
* @access protected
|
||||
* @param HttpException $e
|
||||
* @return Response
|
||||
*/
|
||||
protected function renderHttpException(HttpException $e)
|
||||
{
|
||||
$status = $e->getStatusCode();
|
||||
$template = Config::get('http_exception_template');
|
||||
if (!App::$debug && !empty($template[$status])) {
|
||||
$template = Container::get('app')->config('http_exception_template');
|
||||
|
||||
if (!Container::get('app')->isDebug() && !empty($template[$status])) {
|
||||
return Response::create($template[$status], 'view', $status)->assign(['e' => $e]);
|
||||
} else {
|
||||
return $this->convertExceptionToResponse($e);
|
||||
@@ -125,13 +130,14 @@ class Handle
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Exception $exception
|
||||
* @access protected
|
||||
* @param Exception $exception
|
||||
* @return Response
|
||||
*/
|
||||
protected function convertExceptionToResponse(Exception $exception)
|
||||
{
|
||||
// 收集异常数据
|
||||
if (App::$debug) {
|
||||
if (Container::get('app')->isDebug()) {
|
||||
// 调试模式,获取详细的错误信息
|
||||
$data = [
|
||||
'name' => get_class($exception),
|
||||
@@ -160,9 +166,9 @@ class Handle
|
||||
'message' => $this->getMessage($exception),
|
||||
];
|
||||
|
||||
if (!Config::get('show_error_msg')) {
|
||||
if (!Container::get('app')->config('show_error_msg')) {
|
||||
// 不显示详细错误信息
|
||||
$data['message'] = Config::get('error_message');
|
||||
$data['message'] = Container::get('app')->config('error_message');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,10 +181,11 @@ class Handle
|
||||
|
||||
ob_start();
|
||||
extract($data);
|
||||
include Config::get('exception_tmpl');
|
||||
include Container::get('app')->config('exception_tmpl');
|
||||
|
||||
// 获取并清空缓存
|
||||
$content = ob_get_clean();
|
||||
$response = new Response($content, 'html');
|
||||
$response = Response::create($content, 'html');
|
||||
|
||||
if ($exception instanceof HttpException) {
|
||||
$statusCode = $exception->getStatusCode();
|
||||
@@ -189,52 +196,62 @@ class Handle
|
||||
$statusCode = 500;
|
||||
}
|
||||
$response->code($statusCode);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误编码
|
||||
* ErrorException则使用错误级别作为错误编码
|
||||
* @access protected
|
||||
* @param \Exception $exception
|
||||
* @return integer 错误编码
|
||||
*/
|
||||
protected function getCode(Exception $exception)
|
||||
{
|
||||
$code = $exception->getCode();
|
||||
|
||||
if (!$code && $exception instanceof ErrorException) {
|
||||
$code = $exception->getSeverity();
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
* ErrorException则使用错误级别作为错误编码
|
||||
* @access protected
|
||||
* @param \Exception $exception
|
||||
* @return string 错误信息
|
||||
*/
|
||||
protected function getMessage(Exception $exception)
|
||||
{
|
||||
$message = $exception->getMessage();
|
||||
if (IS_CLI) {
|
||||
|
||||
if (PHP_SAPI == 'cli') {
|
||||
return $message;
|
||||
}
|
||||
|
||||
$lang = Container::get('lang');
|
||||
|
||||
if (strpos($message, ':')) {
|
||||
$name = strstr($message, ':', true);
|
||||
$message = Lang::has($name) ? Lang::get($name) . strstr($message, ':') : $message;
|
||||
$message = $lang->has($name) ? $lang->get($name) . strstr($message, ':') : $message;
|
||||
} elseif (strpos($message, ',')) {
|
||||
$name = strstr($message, ',', true);
|
||||
$message = Lang::has($name) ? Lang::get($name) . ':' . substr(strstr($message, ','), 1) : $message;
|
||||
} elseif (Lang::has($message)) {
|
||||
$message = Lang::get($message);
|
||||
$message = $lang->has($name) ? $lang->get($name) . ':' . substr(strstr($message, ','), 1) : $message;
|
||||
} elseif ($lang->has($message)) {
|
||||
$message = $lang->get($message);
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取出错文件内容
|
||||
* 获取错误的前9行和后9行
|
||||
* @access protected
|
||||
* @param \Exception $exception
|
||||
* @return array 错误文件内容
|
||||
*/
|
||||
@@ -253,30 +270,37 @@ class Handle
|
||||
} catch (Exception $e) {
|
||||
$source = [];
|
||||
}
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取异常扩展信息
|
||||
* 用于非调试模式html返回类型显示
|
||||
* @access protected
|
||||
* @param \Exception $exception
|
||||
* @return array 异常类定义的扩展数据
|
||||
*/
|
||||
protected function getExtendData(Exception $exception)
|
||||
{
|
||||
$data = [];
|
||||
|
||||
if ($exception instanceof \think\Exception) {
|
||||
$data = $exception->getData();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取常量列表
|
||||
* @access private
|
||||
* @return array 常量列表
|
||||
*/
|
||||
private static function getConst()
|
||||
{
|
||||
return get_defined_constants(true)['user'];
|
||||
$const = get_defined_constants(true);
|
||||
|
||||
return isset($const['user']) ? $const['user'] : [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,11 @@ class PDOException extends DbException
|
||||
{
|
||||
/**
|
||||
* PDOException constructor.
|
||||
* @param \PDOException $exception
|
||||
* @param array $config
|
||||
* @param string $sql
|
||||
* @param int $code
|
||||
* @access public
|
||||
* @param \PDOException $exception
|
||||
* @param array $config
|
||||
* @param string $sql
|
||||
* @param int $code
|
||||
*/
|
||||
public function __construct(\PDOException $exception, array $config, $sql, $code = 10501)
|
||||
{
|
||||
|
||||
@@ -15,10 +15,11 @@ class ValidateException extends \RuntimeException
|
||||
{
|
||||
protected $error;
|
||||
|
||||
public function __construct($error)
|
||||
public function __construct($error, $code = 0)
|
||||
{
|
||||
$this->error = $error;
|
||||
$this->message = is_array($error) ? implode("\n\r", $error) : $error;
|
||||
$this->message = is_array($error) ? implode(PHP_EOL, $error) : $error;
|
||||
$this->code = $code;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
namespace think\log\driver;
|
||||
|
||||
use think\App;
|
||||
use think\Request;
|
||||
|
||||
/**
|
||||
* 本地化调试输出到文件
|
||||
@@ -20,27 +19,37 @@ use think\Request;
|
||||
class File
|
||||
{
|
||||
protected $config = [
|
||||
'time_format' => ' c ',
|
||||
'time_format' => 'c',
|
||||
'single' => false,
|
||||
'file_size' => 2097152,
|
||||
'path' => LOG_PATH,
|
||||
'path' => '',
|
||||
'apart_level' => [],
|
||||
'max_files' => 0,
|
||||
'json' => false,
|
||||
];
|
||||
|
||||
protected $app;
|
||||
|
||||
// 实例化并传入参数
|
||||
public function __construct($config = [])
|
||||
public function __construct(App $app, $config = [])
|
||||
{
|
||||
$this->app = $app;
|
||||
|
||||
if (is_array($config)) {
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
|
||||
if (empty($this->config['path'])) {
|
||||
$this->config['path'] = $this->app->getRuntimePath() . 'log' . DIRECTORY_SEPARATOR;
|
||||
} elseif (substr($this->config['path'], -1) != DIRECTORY_SEPARATOR) {
|
||||
$this->config['path'] .= DIRECTORY_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志写入接口
|
||||
* @access public
|
||||
* @param array $log 日志信息
|
||||
* @param array $log 日志信息
|
||||
* @param bool $append 是否追加请求信息
|
||||
* @return bool
|
||||
*/
|
||||
@@ -52,6 +61,7 @@ class File
|
||||
!is_dir($path) && mkdir($path, 0755, true);
|
||||
|
||||
$info = [];
|
||||
|
||||
foreach ($log as $type => $val) {
|
||||
|
||||
foreach ($val as $msg) {
|
||||
@@ -67,6 +77,7 @@ class File
|
||||
$filename = $this->getApartLevelFile($path, $type);
|
||||
|
||||
$this->write($info[$type], $filename, true, $append);
|
||||
|
||||
unset($info[$type]);
|
||||
}
|
||||
}
|
||||
@@ -78,6 +89,45 @@ class File
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志写入
|
||||
* @access protected
|
||||
* @param array $message 日志信息
|
||||
* @param string $destination 日志文件
|
||||
* @param bool $apart 是否独立文件写入
|
||||
* @param bool $append 是否追加请求信息
|
||||
* @return bool
|
||||
*/
|
||||
protected function write($message, $destination, $apart = false, $append = false)
|
||||
{
|
||||
// 检测日志文件大小,超过配置大小则备份日志文件重新生成
|
||||
$this->checkLogSize($destination);
|
||||
|
||||
// 日志信息封装
|
||||
$info['timestamp'] = date($this->config['time_format']);
|
||||
|
||||
foreach ($message as $type => $msg) {
|
||||
$msg = is_array($msg) ? implode(PHP_EOL, $msg) : $msg;
|
||||
if (PHP_SAPI == 'cli') {
|
||||
$info['msg'] = $msg;
|
||||
$info['type'] = $type;
|
||||
} else {
|
||||
$info[$type] = $msg;
|
||||
}
|
||||
}
|
||||
|
||||
if (PHP_SAPI == 'cli') {
|
||||
$message = $this->parseCliLog($info);
|
||||
} else {
|
||||
// 添加调试日志
|
||||
$this->getDebugLog($info, $append, $apart);
|
||||
|
||||
$message = $this->parseLog($info);
|
||||
}
|
||||
|
||||
return error_log($message, 3, $destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主日志文件名
|
||||
* @access public
|
||||
@@ -85,21 +135,26 @@ class File
|
||||
*/
|
||||
protected function getMasterLogFile()
|
||||
{
|
||||
if ($this->config['max_files']) {
|
||||
$files = glob($this->config['path'] . '*.log');
|
||||
|
||||
try {
|
||||
if (count($files) > $this->config['max_files']) {
|
||||
unlink($files[0]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
$cli = PHP_SAPI == 'cli' ? '_cli' : '';
|
||||
|
||||
if ($this->config['single']) {
|
||||
$name = is_string($this->config['single']) ? $this->config['single'] : 'single';
|
||||
$name = is_string($this->config['single']) ? $this->config['single'] : 'single';
|
||||
|
||||
$destination = $this->config['path'] . $name . $cli . '.log';
|
||||
} else {
|
||||
if ($this->config['max_files']) {
|
||||
$filename = date('Ymd') . $cli . '.log';
|
||||
$files = glob($this->config['path'] . '*.log');
|
||||
|
||||
try {
|
||||
if (count($files) > $this->config['max_files']) {
|
||||
unlink($files[0]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
} else {
|
||||
$filename = date('Ym') . DIRECTORY_SEPARATOR . date('d') . $cli . '.log';
|
||||
}
|
||||
@@ -132,45 +187,6 @@ class File
|
||||
return $path . DIRECTORY_SEPARATOR . $name . '_' . $type . $cli . '.log';
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志写入
|
||||
* @access protected
|
||||
* @param array $message 日志信息
|
||||
* @param string $destination 日志文件
|
||||
* @param bool $apart 是否独立文件写入
|
||||
* @param bool $append 是否追加请求信息
|
||||
* @return bool
|
||||
*/
|
||||
protected function write($message, $destination, $apart = false, $append = false)
|
||||
{
|
||||
// 检测日志文件大小,超过配置大小则备份日志文件重新生成
|
||||
$this->checkLogSize($destination);
|
||||
|
||||
// 日志信息封装
|
||||
$info['timestamp'] = date($this->config['time_format']);
|
||||
|
||||
foreach ($message as $type => $msg) {
|
||||
$msg = is_array($msg) ? implode("\r\n", $msg) : $msg;
|
||||
if (PHP_SAPI == 'cli') {
|
||||
$info['msg'] = $msg;
|
||||
$info['type'] = $type;
|
||||
} else {
|
||||
$info[$type] = $msg;
|
||||
}
|
||||
}
|
||||
|
||||
if (PHP_SAPI == 'cli') {
|
||||
$message = $this->parseCliLog($info);
|
||||
} else {
|
||||
// 添加调试日志
|
||||
$this->getDebugLog($info, $append, $apart);
|
||||
|
||||
$message = $this->parseLog($info);
|
||||
}
|
||||
|
||||
return error_log($message, 3, $destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查日志文件大小并自动生成备份文件
|
||||
* @access protected
|
||||
@@ -196,14 +212,14 @@ class File
|
||||
protected function parseCliLog($info)
|
||||
{
|
||||
if ($this->config['json']) {
|
||||
$message = json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n";
|
||||
$message = json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
|
||||
} else {
|
||||
$now = $info['timestamp'];
|
||||
unset($info['timestamp']);
|
||||
|
||||
$message = implode("\r\n", $info);
|
||||
$message = implode(PHP_EOL, $info);
|
||||
|
||||
$message = "[{$now}]" . $message . "\r\n";
|
||||
$message = "[{$now}]" . $message . PHP_EOL;
|
||||
}
|
||||
|
||||
return $message;
|
||||
@@ -217,35 +233,34 @@ class File
|
||||
*/
|
||||
protected function parseLog($info)
|
||||
{
|
||||
$request = Request::instance();
|
||||
$requestInfo = [
|
||||
'ip' => $request->ip(),
|
||||
'method' => $request->method(),
|
||||
'host' => $request->host(),
|
||||
'uri' => $request->url(),
|
||||
'ip' => $this->app['request']->ip(),
|
||||
'method' => $this->app['request']->method(),
|
||||
'host' => $this->app['request']->host(),
|
||||
'uri' => $this->app['request']->url(),
|
||||
];
|
||||
|
||||
if ($this->config['json']) {
|
||||
$info = $requestInfo + $info;
|
||||
return json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n";
|
||||
return json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
|
||||
}
|
||||
|
||||
array_unshift($info, "---------------------------------------------------------------\r\n[{$info['timestamp']}] {$requestInfo['ip']} {$requestInfo['method']} {$requestInfo['host']}{$requestInfo['uri']}");
|
||||
array_unshift($info, "---------------------------------------------------------------" . PHP_EOL . "\r\n[{$info['timestamp']}] {$requestInfo['ip']} {$requestInfo['method']} {$requestInfo['host']}{$requestInfo['uri']}");
|
||||
unset($info['timestamp']);
|
||||
|
||||
return implode("\r\n", $info) . "\r\n";
|
||||
return implode(PHP_EOL, $info) . PHP_EOL;
|
||||
}
|
||||
|
||||
protected function getDebugLog(&$info, $append, $apart)
|
||||
{
|
||||
if (App::$debug && $append) {
|
||||
if ($this->app->isDebug() && $append) {
|
||||
|
||||
if ($this->config['json']) {
|
||||
// 获取基本信息
|
||||
$runtime = round(microtime(true) - THINK_START_TIME, 10);
|
||||
$runtime = round(microtime(true) - $this->app->getBeginTime(), 10);
|
||||
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
|
||||
|
||||
$memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
|
||||
$memory_use = number_format((memory_get_usage() - $this->app->getBeginMem()) / 1024, 2);
|
||||
|
||||
$info = [
|
||||
'runtime' => number_format($runtime, 6) . 's',
|
||||
@@ -256,10 +271,10 @@ class File
|
||||
|
||||
} elseif (!$apart) {
|
||||
// 增加额外的调试信息
|
||||
$runtime = round(microtime(true) - THINK_START_TIME, 10);
|
||||
$runtime = round(microtime(true) - $this->app->getBeginTime(), 10);
|
||||
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
|
||||
|
||||
$memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
|
||||
$memory_use = number_format((memory_get_usage() - $this->app->getBeginMem()) / 1024, 2);
|
||||
|
||||
$time_str = '[运行时间:' . number_format($runtime, 6) . 's] [吞吐率:' . $reqs . 'req/s]';
|
||||
$memory_str = ' [内存消耗:' . $memory_use . 'kb]';
|
||||
|
||||
@@ -30,6 +30,8 @@ class Socket
|
||||
'force_client_ids' => [],
|
||||
// 限制允许读取日志的client_id
|
||||
'allow_client_ids' => [],
|
||||
//输出到浏览器默认展开的日志级别
|
||||
'expand_level' => ['debug'],
|
||||
];
|
||||
|
||||
protected $css = [
|
||||
@@ -41,14 +43,17 @@ class Socket
|
||||
];
|
||||
|
||||
protected $allowForceClientIds = []; //配置强制推送且被授权的client_id
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $config 缓存参数
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param array $config 缓存参数
|
||||
*/
|
||||
public function __construct(array $config = [])
|
||||
public function __construct(App $app, array $config = [])
|
||||
{
|
||||
$this->app = $app;
|
||||
|
||||
if (!empty($config)) {
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
@@ -57,7 +62,7 @@ class Socket
|
||||
/**
|
||||
* 调试输出接口
|
||||
* @access public
|
||||
* @param array $log 日志信息
|
||||
* @param array $log 日志信息
|
||||
* @return bool
|
||||
*/
|
||||
public function save(array $log = [], $append = false)
|
||||
@@ -65,12 +70,14 @@ class Socket
|
||||
if (!$this->check()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$trace = [];
|
||||
if (App::$debug) {
|
||||
$runtime = round(microtime(true) - THINK_START_TIME, 10);
|
||||
|
||||
if ($this->app->isDebug()) {
|
||||
$runtime = round(microtime(true) - $this->app->getBeginTime(), 10);
|
||||
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
|
||||
$time_str = ' [运行时间:' . number_format($runtime, 6) . 's][吞吐率:' . $reqs . 'req/s]';
|
||||
$memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
|
||||
$memory_use = number_format((memory_get_usage() - $this->app->getBeginMem()) / 1024, 2);
|
||||
$memory_str = ' [内存消耗:' . $memory_use . 'kb]';
|
||||
$file_load = ' [文件加载:' . count(get_included_files()) . ']';
|
||||
|
||||
@@ -79,6 +86,7 @@ class Socket
|
||||
} else {
|
||||
$current_uri = 'cmd:' . implode(' ', $_SERVER['argv']);
|
||||
}
|
||||
|
||||
// 基本信息
|
||||
$trace[] = [
|
||||
'type' => 'group',
|
||||
@@ -89,10 +97,11 @@ class Socket
|
||||
|
||||
foreach ($log as $type => $val) {
|
||||
$trace[] = [
|
||||
'type' => 'groupCollapsed',
|
||||
'type' => in_array($type, $this->config['expand_level']) ? 'group' : 'groupCollapsed',
|
||||
'msg' => '[ ' . $type . ' ]',
|
||||
'css' => isset($this->css[$type]) ? $this->css[$type] : '',
|
||||
];
|
||||
|
||||
foreach ($val as $msg) {
|
||||
if (!is_string($msg)) {
|
||||
$msg = var_export($msg, true);
|
||||
@@ -103,6 +112,7 @@ class Socket
|
||||
'css' => '',
|
||||
];
|
||||
}
|
||||
|
||||
$trace[] = [
|
||||
'type' => 'groupEnd',
|
||||
'msg' => '',
|
||||
@@ -116,11 +126,13 @@ class Socket
|
||||
'msg' => '[ file ]',
|
||||
'css' => '',
|
||||
];
|
||||
|
||||
$trace[] = [
|
||||
'type' => 'log',
|
||||
'msg' => implode("\n", get_included_files()),
|
||||
'css' => '',
|
||||
];
|
||||
|
||||
$trace[] = [
|
||||
'type' => 'groupEnd',
|
||||
'msg' => '',
|
||||
@@ -135,6 +147,7 @@ class Socket
|
||||
];
|
||||
|
||||
$tabid = $this->getClientArg('tabid');
|
||||
|
||||
if (!$client_id = $this->getClientArg('client_id')) {
|
||||
$client_id = '';
|
||||
}
|
||||
@@ -148,16 +161,18 @@ class Socket
|
||||
} else {
|
||||
$this->sendToClient($tabid, $client_id, $trace, '');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送给指定客户端
|
||||
* @access protected
|
||||
* @author Zjmainstay
|
||||
* @param $tabid
|
||||
* @param $client_id
|
||||
* @param $logs
|
||||
* @param $force_client_id
|
||||
* @param $tabid
|
||||
* @param $client_id
|
||||
* @param $logs
|
||||
* @param $force_client_id
|
||||
*/
|
||||
protected function sendToClient($tabid, $client_id, $logs, $force_client_id)
|
||||
{
|
||||
@@ -167,20 +182,25 @@ class Socket
|
||||
'logs' => $logs,
|
||||
'force_client_id' => $force_client_id,
|
||||
];
|
||||
|
||||
$msg = @json_encode($logs);
|
||||
$address = '/' . $client_id; //将client_id作为地址, server端通过地址判断将日志发布给谁
|
||||
|
||||
$this->send($this->config['host'], $msg, $address);
|
||||
}
|
||||
|
||||
protected function check()
|
||||
{
|
||||
$tabid = $this->getClientArg('tabid');
|
||||
|
||||
//是否记录日志的检查
|
||||
if (!$tabid && !$this->config['force_client_ids']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//用户认证
|
||||
$allow_client_ids = $this->config['allow_client_ids'];
|
||||
|
||||
if (!empty($allow_client_ids)) {
|
||||
//通过数组交集得出授权强制推送的client_id
|
||||
$this->allowForceClientIds = array_intersect($allow_client_ids, $this->config['force_client_ids']);
|
||||
@@ -195,6 +215,7 @@ class Socket
|
||||
} else {
|
||||
$this->allowForceClientIds = $this->config['force_client_ids'];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -211,6 +232,7 @@ class Socket
|
||||
if (!isset($_SERVER[$key])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($args)) {
|
||||
if (!preg_match('/SocketLog\((.*?)\)/', $_SERVER[$key], $match)) {
|
||||
$args = ['tabid' => null];
|
||||
@@ -218,32 +240,39 @@ class Socket
|
||||
}
|
||||
parse_str($match[1], $args);
|
||||
}
|
||||
|
||||
if (isset($args[$name])) {
|
||||
return $args[$name];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $host - $host of socket server
|
||||
* @param string $message - 发送的消息
|
||||
* @param string $address - 地址
|
||||
* @access protected
|
||||
* @param string $host - $host of socket server
|
||||
* @param string $message - 发送的消息
|
||||
* @param string $address - 地址
|
||||
* @return bool
|
||||
*/
|
||||
protected function send($host, $message = '', $address = '/')
|
||||
{
|
||||
$url = 'http://' . $host . ':' . $this->port . $address;
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $message);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
|
||||
$headers = [
|
||||
"Content-Type: application/json;charset=UTF-8",
|
||||
];
|
||||
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //设置header
|
||||
|
||||
return curl_exec($ch);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,21 +19,40 @@ class Collection extends BaseCollection
|
||||
/**
|
||||
* 延迟预载入关联查询
|
||||
* @access public
|
||||
* @param mixed $relation 关联
|
||||
* @param mixed $relation 关联
|
||||
* @return $this
|
||||
*/
|
||||
public function load($relation)
|
||||
{
|
||||
$item = current($this->items);
|
||||
$item->eagerlyResultSet($this->items, $relation);
|
||||
if (!$this->isEmpty()) {
|
||||
$item = current($this->items);
|
||||
$item->eagerlyResultSet($this->items, $relation);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定(一对一)关联属性到当前模型
|
||||
* @access protected
|
||||
* @param string $relation 关联名称
|
||||
* @param array $attrs 绑定属性
|
||||
* @return $this
|
||||
*/
|
||||
public function bindAttr($relation, array $attrs = [])
|
||||
{
|
||||
$this->each(function (Model $model) use ($relation, $attrs) {
|
||||
$model->bindAttr($relation, $attrs);
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需要隐藏的输出属性
|
||||
* @access public
|
||||
* @param array $hidden 属性列表
|
||||
* @param bool $override 是否覆盖
|
||||
* @param array $hidden 属性列表
|
||||
* @param bool $override 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function hidden($hidden = [], $override = false)
|
||||
@@ -42,13 +61,15 @@ class Collection extends BaseCollection
|
||||
/** @var Model $model */
|
||||
$model->hidden($hidden, $override);
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需要输出的属性
|
||||
* @param array $visible
|
||||
* @param bool $override 是否覆盖
|
||||
* @access public
|
||||
* @param array $visible
|
||||
* @param bool $override 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function visible($visible = [], $override = false)
|
||||
@@ -57,14 +78,15 @@ class Collection extends BaseCollection
|
||||
/** @var Model $model */
|
||||
$model->visible($visible, $override);
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需要追加的输出属性
|
||||
* @access public
|
||||
* @param array $append 属性列表
|
||||
* @param bool $override 是否覆盖
|
||||
* @param array $append 属性列表
|
||||
* @param bool $override 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function append($append = [], $override = false)
|
||||
@@ -73,7 +95,24 @@ class Collection extends BaseCollection
|
||||
/** @var Model $model */
|
||||
$model && $model->append($append, $override);
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置数据字段获取器
|
||||
* @access public
|
||||
* @param string|array $name 字段名
|
||||
* @param callable $callback 闭包获取器
|
||||
* @return $this
|
||||
*/
|
||||
public function withAttr($name, $callback = null)
|
||||
{
|
||||
$this->each(function ($model) use ($name, $callback) {
|
||||
/** @var Model $model */
|
||||
$model && $model->withAttribute($name, $callback);
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ class Pivot extends Model
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param array|object $data 数据
|
||||
* @param Model $parent 上级模型
|
||||
* @param string $table 中间数据表名
|
||||
* @param array|object $data 数据
|
||||
* @param Model $parent 上级模型
|
||||
* @param string $table 中间数据表名
|
||||
*/
|
||||
public function __construct($data = [], Model $parent = null, $table = '')
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@ abstract class Relation
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的关联模型对象实例
|
||||
* 获取当前的关联模型类的实例
|
||||
* @access public
|
||||
* @return Model
|
||||
*/
|
||||
@@ -59,7 +59,7 @@ abstract class Relation
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取关联的查询对象
|
||||
* 获取当前的关联模型类的实例
|
||||
* @access public
|
||||
* @return Query
|
||||
*/
|
||||
@@ -93,7 +93,7 @@ abstract class Relation
|
||||
/**
|
||||
* 封装关联数据集
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param array $resultSet 数据集
|
||||
* @return mixed
|
||||
*/
|
||||
protected function resultSetBuild($resultSet)
|
||||
@@ -127,6 +127,42 @@ abstract class Relation
|
||||
return $fields;
|
||||
}
|
||||
|
||||
protected function getQueryWhere(&$where, $relation)
|
||||
{
|
||||
foreach ($where as $key => &$val) {
|
||||
if (is_string($key)) {
|
||||
$where[] = [false === strpos($key, '.') ? $relation . '.' . $key : $key, '=', $val];
|
||||
unset($where[$key]);
|
||||
} elseif (isset($val[0]) && false === strpos($val[0], '.')) {
|
||||
$val[0] = $relation . '.' . $val[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @access public
|
||||
* @param array $data 更新数据
|
||||
* @return integer|string
|
||||
*/
|
||||
public function update(array $data = [])
|
||||
{
|
||||
return $this->query->update($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除记录
|
||||
* @access public
|
||||
* @param mixed $data 表达式 true 表示强制删除
|
||||
* @return int
|
||||
* @throws Exception
|
||||
* @throws PDOException
|
||||
*/
|
||||
public function delete($data = null)
|
||||
{
|
||||
return $this->query->delete($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(仅执行一次)
|
||||
* @access protected
|
||||
@@ -141,13 +177,9 @@ abstract class Relation
|
||||
// 执行基础查询
|
||||
$this->baseQuery();
|
||||
|
||||
$result = call_user_func_array([$this->query, $method], $args);
|
||||
if ($result instanceof Query) {
|
||||
return $this;
|
||||
} else {
|
||||
$this->baseQuery = false;
|
||||
return $result;
|
||||
}
|
||||
$result = call_user_func_array([$this->query->getModel(), $method], $args);
|
||||
|
||||
return $result === $this->query && !in_array(strtolower($method), ['fetchsql', 'fetchpdo']) ? $this : $result;
|
||||
} else {
|
||||
throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
|
||||
}
|
||||
|
||||
@@ -11,46 +11,51 @@
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use think\db\Query;
|
||||
use Closure;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
|
||||
class BelongsTo extends OneToOne
|
||||
{
|
||||
/**
|
||||
* 构造函数
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 关联主键
|
||||
* @param string $joinType JOIN类型
|
||||
* @param string $relation 关联名
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 关联主键
|
||||
* @param string $relation 关联名
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER', $relation = null)
|
||||
public function __construct(Model $parent, $model, $foreignKey, $localKey, $relation = null)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->joinType = $joinType;
|
||||
$this->joinType = 'INNER';
|
||||
$this->query = (new $model)->db();
|
||||
$this->relation = $relation;
|
||||
|
||||
if (get_class($parent) == $model) {
|
||||
$this->selfRelation = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @access public
|
||||
* @return array|false|\PDOStatement|string|Model
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return Model
|
||||
*/
|
||||
public function getRelation($subRelation = '', $closure = null)
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
if ($closure instanceof Closure) {
|
||||
$closure($this->query);
|
||||
}
|
||||
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$relationModel = $this->query
|
||||
->removeWhereField($this->localKey)
|
||||
->where($this->localKey, $this->parent->$foreignKey)
|
||||
@@ -65,23 +70,96 @@ class BelongsTo extends OneToOne
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* 创建关联统计子查询
|
||||
* @access public
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @return Query
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $aggregate 聚合查询方法
|
||||
* @param string $field 字段
|
||||
* @param string $aggregateAlias 聚合字段别名
|
||||
* @return string
|
||||
*/
|
||||
public function has($operator = '>=', $count = 1, $id = '*')
|
||||
public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '')
|
||||
{
|
||||
return $this->parent;
|
||||
if ($closure instanceof Closure) {
|
||||
$return = $closure($this->query);
|
||||
|
||||
if ($return && is_string($return)) {
|
||||
$aggregateAlias = $return;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->query
|
||||
->whereExp($this->localKey, '=' . $this->parent->getTable() . '.' . $this->foreignKey)
|
||||
->fetchSql()
|
||||
->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $aggregate 聚合查询方法
|
||||
* @param string $field 字段
|
||||
* @param string $name 统计字段别名
|
||||
* @return integer
|
||||
*/
|
||||
public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '')
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
if (!isset($result->$foreignKey)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($closure instanceof Closure) {
|
||||
$return = $closure($this->query);
|
||||
|
||||
if ($return && is_string($return)) {
|
||||
$name = $return;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->query
|
||||
->where($this->localKey, '=', $result->$foreignKey)
|
||||
->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @param mixed $fields 字段
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @return Query
|
||||
*/
|
||||
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
$model = basename(str_replace('\\', '/', get_class($this->parent)));
|
||||
$relation = basename(str_replace('\\', '/', $this->model));
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
|
||||
return $this->parent->db()
|
||||
->alias($model)
|
||||
->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey) {
|
||||
$query->table([$table => $relation])
|
||||
->field($relation . '.' . $localKey)
|
||||
->whereExp($model . '.' . $foreignKey, '=' . $relation . '.' . $localKey)
|
||||
->when($softDelete, function ($query) use ($softDelete, $relation) {
|
||||
$query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @param mixed $fields 字段
|
||||
* @return Query
|
||||
*/
|
||||
public function hasWhere($where = [], $fields = null)
|
||||
@@ -91,28 +169,29 @@ class BelongsTo extends OneToOne
|
||||
$relation = basename(str_replace('\\', '/', $this->model));
|
||||
|
||||
if (is_array($where)) {
|
||||
foreach ($where as $key => $val) {
|
||||
if (false === strpos($key, '.')) {
|
||||
$where[$relation . '.' . $key] = $val;
|
||||
unset($where[$key]);
|
||||
}
|
||||
}
|
||||
$this->getQueryWhere($where, $relation);
|
||||
}
|
||||
$fields = $this->getRelationQueryFields($fields, $model);
|
||||
|
||||
return $this->parent->db()->alias($model)
|
||||
$fields = $this->getRelationQueryFields($fields, $model);
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
|
||||
return $this->parent->db()
|
||||
->alias($model)
|
||||
->field($fields)
|
||||
->join([$table => $relation], $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $this->joinType)
|
||||
->when($softDelete, function ($query) use ($softDelete, $relation) {
|
||||
$query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
|
||||
})
|
||||
->where($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据集)
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @access protected
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure)
|
||||
@@ -130,14 +209,14 @@ class BelongsTo extends OneToOne
|
||||
|
||||
if (!empty($range)) {
|
||||
$this->query->removeWhereField($localKey);
|
||||
$data = $this->eagerlyWhere($this->query, [
|
||||
$localKey => [
|
||||
'in',
|
||||
$range,
|
||||
],
|
||||
|
||||
$data = $this->eagerlyWhere([
|
||||
[$localKey, 'in', $range],
|
||||
], $localKey, $relation, $subRelation, $closure);
|
||||
|
||||
// 关联属性名
|
||||
$attr = Loader::parseName($relation);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
// 关联模型
|
||||
@@ -151,7 +230,7 @@ class BelongsTo extends OneToOne
|
||||
|
||||
if (!empty($this->bindAttr)) {
|
||||
// 绑定关联属性
|
||||
$this->bindAttr($relationModel, $result, $this->bindAttr);
|
||||
$this->bindAttr($relationModel, $result);
|
||||
} else {
|
||||
// 设置关联属性
|
||||
$result->setRelation($attr, $relationModel);
|
||||
@@ -162,19 +241,24 @@ class BelongsTo extends OneToOne
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据)
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @access protected
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
protected function eagerlyOne(&$result, $relation, $subRelation, $closure)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$this->query->removeWhereField($localKey);
|
||||
$data = $this->eagerlyWhere($this->query, [$localKey => $result->$foreignKey], $localKey, $relation, $subRelation, $closure);
|
||||
|
||||
$data = $this->eagerlyWhere([
|
||||
[$localKey, '=', $result->$foreignKey],
|
||||
], $localKey, $relation, $subRelation, $closure);
|
||||
|
||||
// 关联模型
|
||||
if (!isset($data[$result->$foreignKey])) {
|
||||
$relationModel = null;
|
||||
@@ -183,9 +267,10 @@ class BelongsTo extends OneToOne
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->isUpdate(true);
|
||||
}
|
||||
|
||||
if (!empty($this->bindAttr)) {
|
||||
// 绑定关联属性
|
||||
$this->bindAttr($relationModel, $result, $this->bindAttr);
|
||||
$this->bindAttr($relationModel, $result);
|
||||
} else {
|
||||
// 设置关联属性
|
||||
$result->setRelation(Loader::parseName($relation), $relationModel);
|
||||
@@ -195,15 +280,12 @@ class BelongsTo extends OneToOne
|
||||
/**
|
||||
* 添加关联数据
|
||||
* @access public
|
||||
* @param Model $model 关联模型对象
|
||||
* @param Model $model 关联模型对象
|
||||
* @return Model
|
||||
*/
|
||||
public function associate($model)
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
$pk = $model->getPk();
|
||||
|
||||
$this->parent->setAttr($foreignKey, $model->$pk);
|
||||
$this->parent->setAttr($this->foreignKey, $model->getKey());
|
||||
$this->parent->save();
|
||||
|
||||
return $this->parent->setRelation($this->relation, $model);
|
||||
@@ -216,9 +298,7 @@ class BelongsTo extends OneToOne
|
||||
*/
|
||||
public function dissociate()
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$this->parent->setAttr($foreignKey, null);
|
||||
$this->parent->setAttr($this->foreignKey, null);
|
||||
$this->parent->save();
|
||||
|
||||
return $this->parent->setRelation($this->relation, null);
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use Closure;
|
||||
use think\Collection;
|
||||
use think\Db;
|
||||
use think\db\Query;
|
||||
use think\Exception;
|
||||
use think\Loader;
|
||||
@@ -27,19 +27,19 @@ class BelongsToMany extends Relation
|
||||
protected $middle;
|
||||
// 中间表模型名称
|
||||
protected $pivotName;
|
||||
// 中间表模型对象
|
||||
protected $pivot;
|
||||
// 中间表数据名称
|
||||
protected $pivotDataName = 'pivot';
|
||||
// 中间表模型对象
|
||||
protected $pivot;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $table 中间表名
|
||||
* @param string $foreignKey 关联模型外键
|
||||
* @param string $localKey 当前模型关联键
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $table 中间表名
|
||||
* @param string $foreignKey 关联模型外键
|
||||
* @param string $localKey 当前模型关联键
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $table, $foreignKey, $localKey)
|
||||
{
|
||||
@@ -47,23 +47,22 @@ class BelongsToMany extends Relation
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
|
||||
if (false !== strpos($table, '\\')) {
|
||||
$this->pivotName = $table;
|
||||
$this->middle = basename(str_replace('\\', '/', $table));
|
||||
} else {
|
||||
$this->middle = $table;
|
||||
}
|
||||
|
||||
$this->query = (new $model)->db();
|
||||
$this->pivot = $this->newPivot();
|
||||
|
||||
if ('think\model\Pivot' == get_class($this->pivot)) {
|
||||
$this->pivot->name($this->middle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置中间表模型
|
||||
* @param $pivot
|
||||
* @access public
|
||||
* @param $pivot
|
||||
* @return $this
|
||||
*/
|
||||
public function pivot($pivot)
|
||||
@@ -99,6 +98,7 @@ class BelongsToMany extends Relation
|
||||
|
||||
/**
|
||||
* 实例化中间表模型
|
||||
* @access public
|
||||
* @param array $data
|
||||
* @param bool $isUpdate
|
||||
* @return Pivot
|
||||
@@ -108,94 +108,111 @@ class BelongsToMany extends Relation
|
||||
{
|
||||
$class = $this->pivotName ?: '\\think\\model\\Pivot';
|
||||
$pivot = new $class($data, $this->parent, $this->middle);
|
||||
|
||||
if ($pivot instanceof Pivot) {
|
||||
return $isUpdate ? $pivot->isUpdate(true, $this->getUpdateWhere($data)) : $pivot;
|
||||
} else {
|
||||
throw new Exception('pivot model must extends: \think\model\Pivot');
|
||||
}
|
||||
|
||||
throw new Exception('pivot model must extends: \think\model\Pivot');
|
||||
}
|
||||
|
||||
/**
|
||||
* 合成中间表模型
|
||||
* @param array|Collection|Paginator $models
|
||||
* @access protected
|
||||
* @param array|Collection|Paginator $models
|
||||
*/
|
||||
protected function hydratePivot($models)
|
||||
{
|
||||
foreach ($models as $model) {
|
||||
$pivot = [];
|
||||
|
||||
foreach ($model->getData() as $key => $val) {
|
||||
if (strpos($key, '__')) {
|
||||
list($name, $attr) = explode('__', $key, 2);
|
||||
|
||||
if ('pivot' == $name) {
|
||||
$pivot[$attr] = $val;
|
||||
unset($model->$key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$model->setRelation($this->pivotDataName, $this->newPivot($pivot, true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建关联查询Query对象
|
||||
* @access protected
|
||||
* @return Query
|
||||
*/
|
||||
protected function buildQuery()
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
$localKey = $this->localKey;
|
||||
$pk = $this->parent->getPk();
|
||||
|
||||
// 关联查询
|
||||
$condition['pivot.' . $localKey] = $this->parent->$pk;
|
||||
$pk = $this->parent->getPk();
|
||||
|
||||
$condition[] = ['pivot.' . $localKey, '=', $this->parent->$pk];
|
||||
|
||||
return $this->belongsToManyQuery($foreignKey, $localKey, $condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return false|\PDOStatement|string|\think\Collection
|
||||
* @access public
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return Collection
|
||||
*/
|
||||
public function getRelation($subRelation = '', $closure = null)
|
||||
{
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
if ($closure instanceof Closure) {
|
||||
$closure($this->query);
|
||||
}
|
||||
|
||||
$result = $this->buildQuery()->relation($subRelation)->select();
|
||||
$this->hydratePivot($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载select方法
|
||||
* @param null $data
|
||||
* @return false|\PDOStatement|string|Collection
|
||||
* @access public
|
||||
* @param mixed $data
|
||||
* @return Collection
|
||||
*/
|
||||
public function select($data = null)
|
||||
{
|
||||
$result = $this->buildQuery()->select($data);
|
||||
$this->hydratePivot($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载paginate方法
|
||||
* @param null $listRows
|
||||
* @param bool $simple
|
||||
* @param array $config
|
||||
* @access public
|
||||
* @param null $listRows
|
||||
* @param bool $simple
|
||||
* @param array $config
|
||||
* @return Paginator
|
||||
*/
|
||||
public function paginate($listRows = null, $simple = false, $config = [])
|
||||
{
|
||||
$result = $this->buildQuery()->paginate($listRows, $simple, $config);
|
||||
$this->hydratePivot($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载find方法
|
||||
* @param null $data
|
||||
* @return array|false|\PDOStatement|string|Model
|
||||
* @access public
|
||||
* @param mixed $data
|
||||
* @return Model
|
||||
*/
|
||||
public function find($data = null)
|
||||
{
|
||||
@@ -203,14 +220,15 @@ class BelongsToMany extends Relation
|
||||
if ($result) {
|
||||
$this->hydratePivot([$result]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找多条记录 如果不存在则抛出异常
|
||||
* @access public
|
||||
* @param array|string|Query|\Closure $data
|
||||
* @return array|\PDOStatement|string|Model
|
||||
* @param array|string|Query|\Closure $data
|
||||
* @return Collection
|
||||
*/
|
||||
public function selectOrFail($data = null)
|
||||
{
|
||||
@@ -220,8 +238,8 @@ class BelongsToMany extends Relation
|
||||
/**
|
||||
* 查找单条记录 如果不存在则抛出异常
|
||||
* @access public
|
||||
* @param array|string|Query|\Closure $data
|
||||
* @return array|\PDOStatement|string|Model
|
||||
* @param array|string|Query|\Closure $data
|
||||
* @return Model
|
||||
*/
|
||||
public function findOrFail($data = null)
|
||||
{
|
||||
@@ -231,10 +249,10 @@ class BelongsToMany extends Relation
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @return Query
|
||||
*/
|
||||
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
|
||||
@@ -245,8 +263,8 @@ class BelongsToMany extends Relation
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @param mixed $fields 字段
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @param mixed $fields 字段
|
||||
* @return Query
|
||||
* @throws Exception
|
||||
*/
|
||||
@@ -257,25 +275,25 @@ class BelongsToMany extends Relation
|
||||
|
||||
/**
|
||||
* 设置中间表的查询条件
|
||||
* @param $field
|
||||
* @param null $op
|
||||
* @param null $condition
|
||||
* @access public
|
||||
* @param string $field
|
||||
* @param string $op
|
||||
* @param mixed $condition
|
||||
* @return $this
|
||||
*/
|
||||
public function wherePivot($field, $op = null, $condition = null)
|
||||
{
|
||||
$field = 'pivot.' . $field;
|
||||
$this->query->where($field, $op, $condition);
|
||||
$this->query->where('pivot.' . $field, $op, $condition);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据集)
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
|
||||
@@ -295,13 +313,12 @@ class BelongsToMany extends Relation
|
||||
if (!empty($range)) {
|
||||
// 查询关联数据
|
||||
$data = $this->eagerlyManyToMany([
|
||||
'pivot.' . $localKey => [
|
||||
'in',
|
||||
$range,
|
||||
],
|
||||
], $relation, $subRelation);
|
||||
['pivot.' . $localKey, 'in', $range],
|
||||
], $relation, $subRelation, $closure);
|
||||
|
||||
// 关联属性名
|
||||
$attr = Loader::parseName($relation);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
if (!isset($data[$result->$pk])) {
|
||||
@@ -316,24 +333,28 @@ class BelongsToMany extends Relation
|
||||
/**
|
||||
* 预载入关联查询(单个数据)
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure)
|
||||
{
|
||||
$pk = $result->getPk();
|
||||
|
||||
if (isset($result->$pk)) {
|
||||
$pk = $result->$pk;
|
||||
// 查询管理数据
|
||||
$data = $this->eagerlyManyToMany(['pivot.' . $this->localKey => $pk], $relation, $subRelation);
|
||||
$data = $this->eagerlyManyToMany([
|
||||
['pivot.' . $this->localKey, '=', $pk],
|
||||
], $relation, $subRelation, $closure);
|
||||
|
||||
// 关联数据封装
|
||||
if (!isset($data[$pk])) {
|
||||
$data[$pk] = [];
|
||||
}
|
||||
|
||||
$result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$pk]));
|
||||
}
|
||||
}
|
||||
@@ -341,57 +362,81 @@ class BelongsToMany extends Relation
|
||||
/**
|
||||
* 关联统计
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param \Closure $closure 闭包
|
||||
* @param Model $result 数据对象
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $aggregate 聚合查询方法
|
||||
* @param string $field 字段
|
||||
* @param string $name 统计字段别名
|
||||
* @return integer
|
||||
*/
|
||||
public function relationCount($result, $closure)
|
||||
public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '')
|
||||
{
|
||||
$pk = $result->getPk();
|
||||
$count = 0;
|
||||
if (isset($result->$pk)) {
|
||||
$pk = $result->$pk;
|
||||
$count = $this->belongsToManyQuery($this->foreignKey, $this->localKey, ['pivot.' . $this->localKey => $pk])->count();
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
$pk = $result->getPk();
|
||||
|
||||
if (!isset($result->$pk)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$pk = $result->$pk;
|
||||
|
||||
if ($closure instanceof Closure) {
|
||||
$return = $closure($this->query);
|
||||
|
||||
/**
|
||||
* 获取关联统计子查询
|
||||
* @access public
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $name 统计数据别名
|
||||
* @return string
|
||||
*/
|
||||
public function getRelationCountQuery($closure, &$name = null)
|
||||
{
|
||||
if ($closure) {
|
||||
$return = call_user_func_array($closure, [ & $this->query]);
|
||||
if ($return && is_string($return)) {
|
||||
$name = $return;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
|
||||
'pivot.' . $this->localKey => [
|
||||
'exp',
|
||||
Db::raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()),
|
||||
['pivot.' . $this->localKey, '=', $pk],
|
||||
])->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取关联统计子查询
|
||||
* @access public
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $aggregate 聚合查询方法
|
||||
* @param string $field 字段
|
||||
* @param string $aggregateAlias 聚合字段别名
|
||||
* @return array
|
||||
*/
|
||||
public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '')
|
||||
{
|
||||
if ($closure instanceof Closure) {
|
||||
$return = $closure($this->query);
|
||||
|
||||
if ($return && is_string($return)) {
|
||||
$aggregateAlias = $return;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
|
||||
[
|
||||
'pivot.' . $this->localKey, 'exp', $this->query->raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()),
|
||||
],
|
||||
])->fetchSql()->count();
|
||||
])->fetchSql()->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多对多 关联模型预查询
|
||||
* @access public
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @access protected
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @param \Closure $closure 闭包
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyManyToMany($where, $relation, $subRelation = '')
|
||||
protected function eagerlyManyToMany($where, $relation, $subRelation = '', $closure = null)
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
$list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where)->with($subRelation)->select();
|
||||
if ($closure instanceof Closure) {
|
||||
$closure($this->query);
|
||||
}
|
||||
|
||||
$list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where)
|
||||
->with($subRelation)
|
||||
->select();
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
@@ -406,18 +451,21 @@ class BelongsToMany extends Relation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$set->setRelation($this->pivotDataName, $this->newPivot($pivot, true));
|
||||
|
||||
$data[$pivot[$this->localKey]][] = $set;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* BELONGS TO MANY 关联查询
|
||||
* @access public
|
||||
* @param string $foreignKey 关联模型关联键
|
||||
* @param string $localKey 当前模型关联键
|
||||
* @param array $condition 关联查询条件
|
||||
* @access protected
|
||||
* @param string $foreignKey 关联模型关联键
|
||||
* @param string $localKey 当前模型关联键
|
||||
* @param array $condition 关联查询条件
|
||||
* @return Query
|
||||
*/
|
||||
protected function belongsToManyQuery($foreignKey, $localKey, $condition = [])
|
||||
@@ -427,7 +475,8 @@ class BelongsToMany extends Relation
|
||||
$table = $this->pivot->getTable();
|
||||
$fields = $this->getQueryFields($tableName);
|
||||
|
||||
$query = $this->query->field($fields)
|
||||
$query = $this->query
|
||||
->field($fields)
|
||||
->field(true, false, $table, 'pivot', 'pivot__');
|
||||
|
||||
if (empty($this->baseQuery)) {
|
||||
@@ -435,15 +484,16 @@ class BelongsToMany extends Relation
|
||||
$query->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk)
|
||||
->where($condition);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @return integer
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @return array|Pivot
|
||||
*/
|
||||
public function save($data, array $pivot = [])
|
||||
{
|
||||
@@ -454,30 +504,33 @@ class BelongsToMany extends Relation
|
||||
/**
|
||||
* 批量保存当前关联数据对象
|
||||
* @access public
|
||||
* @param array $dataSet 数据集
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @param bool $samePivot 额外数据是否相同
|
||||
* @return integer
|
||||
* @param array $dataSet 数据集
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @param bool $samePivot 额外数据是否相同
|
||||
* @return array|false
|
||||
*/
|
||||
public function saveAll(array $dataSet, array $pivot = [], $samePivot = false)
|
||||
{
|
||||
$result = false;
|
||||
$result = [];
|
||||
|
||||
foreach ($dataSet as $key => $data) {
|
||||
if (!$samePivot) {
|
||||
$pivotData = isset($pivot[$key]) ? $pivot[$key] : [];
|
||||
} else {
|
||||
$pivotData = $pivot;
|
||||
}
|
||||
$result = $this->attach($data, $pivotData);
|
||||
|
||||
$result[] = $this->attach($data, $pivotData);
|
||||
}
|
||||
return $result;
|
||||
|
||||
return empty($result) ? false : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 附加关联的一个中间表数据
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组、关联模型对象 或者 关联对象的主键
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @param mixed $data 数据 可以使用数组、关联模型对象 或者 关联对象的主键
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @return array|Pivot
|
||||
* @throws Exception
|
||||
*/
|
||||
@@ -489,8 +542,7 @@ class BelongsToMany extends Relation
|
||||
} else {
|
||||
// 保存关联表数据
|
||||
$model = new $this->model;
|
||||
$model->save($data);
|
||||
$id = $model->getLastInsID();
|
||||
$id = $model->insertGetId($data);
|
||||
}
|
||||
} elseif (is_numeric($data) || is_string($data)) {
|
||||
// 根据关联表主键直接写入中间表
|
||||
@@ -506,15 +558,21 @@ class BelongsToMany extends Relation
|
||||
$pk = $this->parent->getPk();
|
||||
$pivot[$this->localKey] = $this->parent->$pk;
|
||||
$ids = (array) $id;
|
||||
|
||||
foreach ($ids as $id) {
|
||||
$pivot[$this->foreignKey] = $id;
|
||||
$this->pivot->insert($pivot, true);
|
||||
$this->pivot->replace()
|
||||
->exists(false)
|
||||
->data([])
|
||||
->save($pivot);
|
||||
$result[] = $this->newPivot($pivot, true);
|
||||
}
|
||||
|
||||
if (count($result) == 1) {
|
||||
// 返回中间表模型对象
|
||||
$result = $result[0];
|
||||
}
|
||||
|
||||
return $result;
|
||||
} else {
|
||||
throw new Exception('miss relation data');
|
||||
@@ -525,21 +583,21 @@ class BelongsToMany extends Relation
|
||||
* 判断是否存在关联数据
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用关联模型对象 或者 关联对象的主键
|
||||
* @return Pivot
|
||||
* @return Pivot|false
|
||||
* @throws Exception
|
||||
*/
|
||||
public function attached($data)
|
||||
{
|
||||
if ($data instanceof Model) {
|
||||
$relationFk = $data->getPk();
|
||||
$id = $data->$relationFk;
|
||||
$id = $data->getKey();
|
||||
} else {
|
||||
$id = $data;
|
||||
}
|
||||
|
||||
$pk = $this->parent->getPk();
|
||||
|
||||
$pivot = $this->pivot->where($this->localKey, $this->parent->$pk)->where($this->foreignKey, $id)->find();
|
||||
$pivot = $this->pivot
|
||||
->where($this->localKey, $this->parent->getKey())
|
||||
->where($this->foreignKey, $id)
|
||||
->find();
|
||||
|
||||
return $pivot ?: false;
|
||||
}
|
||||
@@ -547,8 +605,8 @@ class BelongsToMany extends Relation
|
||||
/**
|
||||
* 解除关联的一个中间表数据
|
||||
* @access public
|
||||
* @param integer|array $data 数据 可以使用关联对象的主键
|
||||
* @param bool $relationDel 是否同时删除关联表数据
|
||||
* @param integer|array $data 数据 可以使用关联对象的主键
|
||||
* @param bool $relationDel 是否同时删除关联表数据
|
||||
* @return integer
|
||||
*/
|
||||
public function detach($data = null, $relationDel = false)
|
||||
@@ -563,24 +621,31 @@ class BelongsToMany extends Relation
|
||||
$relationFk = $data->getPk();
|
||||
$id = $data->$relationFk;
|
||||
}
|
||||
|
||||
// 删除中间表数据
|
||||
$pk = $this->parent->getPk();
|
||||
$pivot[$this->localKey] = $this->parent->$pk;
|
||||
$pk = $this->parent->getPk();
|
||||
$pivot[] = [$this->localKey, '=', $this->parent->$pk];
|
||||
|
||||
if (isset($id)) {
|
||||
$pivot[$this->foreignKey] = is_array($id) ? ['in', $id] : $id;
|
||||
$pivot[] = [$this->foreignKey, is_array($id) ? 'in' : '=', $id];
|
||||
}
|
||||
$this->pivot->where($pivot)->delete();
|
||||
|
||||
$result = $this->pivot->where($pivot)->delete();
|
||||
|
||||
// 删除关联表数据
|
||||
if (isset($id) && $relationDel) {
|
||||
$model = $this->model;
|
||||
$model::destroy($id);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据同步
|
||||
* @param array $ids
|
||||
* @param bool $detaching
|
||||
* @access public
|
||||
* @param array $ids
|
||||
* @param bool $detaching
|
||||
* @return array
|
||||
*/
|
||||
public function sync($ids, $detaching = true)
|
||||
@@ -590,9 +655,13 @@ class BelongsToMany extends Relation
|
||||
'detached' => [],
|
||||
'updated' => [],
|
||||
];
|
||||
$pk = $this->parent->getPk();
|
||||
$current = $this->pivot->where($this->localKey, $this->parent->$pk)
|
||||
|
||||
$pk = $this->parent->getPk();
|
||||
|
||||
$current = $this->pivot
|
||||
->where($this->localKey, $this->parent->$pk)
|
||||
->column($this->foreignKey);
|
||||
|
||||
$records = [];
|
||||
|
||||
foreach ($ids as $key => $value) {
|
||||
@@ -607,7 +676,6 @@ class BelongsToMany extends Relation
|
||||
|
||||
if ($detaching && count($detach) > 0) {
|
||||
$this->detach($detach);
|
||||
|
||||
$changes['detached'] = $detach;
|
||||
}
|
||||
|
||||
@@ -615,19 +683,16 @@ class BelongsToMany extends Relation
|
||||
if (!in_array($id, $current)) {
|
||||
$this->attach($id, $attributes);
|
||||
$changes['attached'][] = $id;
|
||||
} elseif (count($attributes) > 0 &&
|
||||
$this->attach($id, $attributes)
|
||||
) {
|
||||
} elseif (count($attributes) > 0 && $this->attach($id, $attributes)) {
|
||||
$changes['updated'][] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
return $changes;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* 执行基础查询(仅执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
@@ -636,7 +701,10 @@ class BelongsToMany extends Relation
|
||||
if (empty($this->baseQuery) && $this->parent->getData()) {
|
||||
$pk = $this->parent->getPk();
|
||||
$table = $this->pivot->getTable();
|
||||
$this->query->join([$table => 'pivot'], 'pivot.' . $this->foreignKey . '=' . $this->query->getTable() . '.' . $this->query->getPk())->where('pivot.' . $this->localKey, $this->parent->$pk);
|
||||
|
||||
$this->query
|
||||
->join([$table => 'pivot'], 'pivot.' . $this->foreignKey . '=' . $this->query->getTable() . '.' . $this->query->getPk())
|
||||
->where('pivot.' . $this->localKey, $this->parent->$pk);
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use Closure;
|
||||
use think\db\Query;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
@@ -19,12 +20,12 @@ use think\model\Relation;
|
||||
class HasMany extends Relation
|
||||
{
|
||||
/**
|
||||
* 构造函数
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 当前模型主键
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 当前模型主键
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $foreignKey, $localKey)
|
||||
{
|
||||
@@ -33,20 +34,30 @@ class HasMany extends Relation
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->query = (new $model)->db();
|
||||
|
||||
if (get_class($parent) == $model) {
|
||||
$this->selfRelation = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return false|\PDOStatement|string|\think\Collection
|
||||
* @access public
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return \think\Collection
|
||||
*/
|
||||
public function getRelation($subRelation = '', $closure = null)
|
||||
{
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
if ($closure instanceof Closure) {
|
||||
$closure($this->query);
|
||||
}
|
||||
$list = $this->relation($subRelation)->select();
|
||||
|
||||
$list = $this->query
|
||||
->where($this->foreignKey, $this->parent->{$this->localKey})
|
||||
->relation($subRelation)
|
||||
->select();
|
||||
|
||||
$parent = clone $this->parent;
|
||||
|
||||
foreach ($list as &$model) {
|
||||
@@ -58,17 +69,18 @@ class HasMany extends Relation
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$range = [];
|
||||
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$localKey)) {
|
||||
@@ -77,36 +89,37 @@ class HasMany extends Relation
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
$data = $this->eagerlyOneToMany($this->query, [
|
||||
$this->foreignKey => [
|
||||
'in',
|
||||
$range,
|
||||
],
|
||||
], $relation, $subRelation, $closure);
|
||||
$where = [
|
||||
[$this->foreignKey, 'in', $range],
|
||||
];
|
||||
$data = $this->eagerlyOneToMany($where, $relation, $subRelation, $closure);
|
||||
|
||||
// 关联属性名
|
||||
$attr = Loader::parseName($relation);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
if (!isset($data[$result->$localKey])) {
|
||||
$data[$result->$localKey] = [];
|
||||
$pk = $result->$localKey;
|
||||
if (!isset($data[$pk])) {
|
||||
$data[$pk] = [];
|
||||
}
|
||||
|
||||
foreach ($data[$result->$localKey] as &$relationModel) {
|
||||
foreach ($data[$pk] as &$relationModel) {
|
||||
$relationModel->setParent(clone $result);
|
||||
}
|
||||
|
||||
$result->setRelation($attr, $this->resultSetBuild($data[$result->$localKey]));
|
||||
$result->setRelation($attr, $this->resultSetBuild($data[$pk]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure)
|
||||
@@ -114,103 +127,124 @@ class HasMany extends Relation
|
||||
$localKey = $this->localKey;
|
||||
|
||||
if (isset($result->$localKey)) {
|
||||
$data = $this->eagerlyOneToMany($this->query, [$this->foreignKey => $result->$localKey], $relation, $subRelation, $closure);
|
||||
$pk = $result->$localKey;
|
||||
$where = [
|
||||
[$this->foreignKey, '=', $pk],
|
||||
];
|
||||
$data = $this->eagerlyOneToMany($where, $relation, $subRelation, $closure);
|
||||
|
||||
// 关联数据封装
|
||||
if (!isset($data[$result->$localKey])) {
|
||||
$data[$result->$localKey] = [];
|
||||
if (!isset($data[$pk])) {
|
||||
$data[$pk] = [];
|
||||
}
|
||||
|
||||
foreach ($data[$result->$localKey] as &$relationModel) {
|
||||
foreach ($data[$pk] as &$relationModel) {
|
||||
$relationModel->setParent(clone $result);
|
||||
}
|
||||
|
||||
$result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$result->$localKey]));
|
||||
$result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$pk]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param \Closure $closure 闭包
|
||||
* @param Model $result 数据对象
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $aggregate 聚合查询方法
|
||||
* @param string $field 字段
|
||||
* @param string $name 统计字段别名
|
||||
* @return integer
|
||||
*/
|
||||
public function relationCount($result, $closure)
|
||||
public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '')
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$count = 0;
|
||||
if (isset($result->$localKey)) {
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
}
|
||||
$count = $this->query->where($this->foreignKey, $result->$localKey)->count();
|
||||
|
||||
if (!isset($result->$localKey)) {
|
||||
return 0;
|
||||
}
|
||||
return $count;
|
||||
|
||||
if ($closure instanceof Closure) {
|
||||
$return = $closure($this->query);
|
||||
if ($return && is_string($return)) {
|
||||
$name = $return;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->query
|
||||
->where($this->foreignKey, '=', $result->$localKey)
|
||||
->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建关联统计子查询
|
||||
* @access public
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $name 统计数据别名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $aggregate 聚合查询方法
|
||||
* @param string $field 字段
|
||||
* @param string $aggregateAlias 聚合字段别名
|
||||
* @return string
|
||||
*/
|
||||
public function getRelationCountQuery($closure, &$name = null)
|
||||
public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '')
|
||||
{
|
||||
if ($closure) {
|
||||
$return = call_user_func_array($closure, [ & $this->query]);
|
||||
if ($closure instanceof Closure) {
|
||||
$return = $closure($this->query);
|
||||
|
||||
if ($return && is_string($return)) {
|
||||
$name = $return;
|
||||
$aggregateAlias = $return;
|
||||
}
|
||||
}
|
||||
$localKey = $this->localKey ?: $this->parent->getPk();
|
||||
return $this->query->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $localKey)->fetchSql()->count();
|
||||
|
||||
return $this->query->alias($aggregate . '_table')
|
||||
->whereExp($aggregate . '_table.' . $this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey)
|
||||
->fetchSql()
|
||||
->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 一对多 关联模型预查询
|
||||
* @access public
|
||||
* @param object $model 关联模型对象
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @param bool $closure
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @param \Closure $closure
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyOneToMany($model, $where, $relation, $subRelation = '', $closure = false)
|
||||
protected function eagerlyOneToMany($where, $relation, $subRelation = '', $closure = null)
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$this->query->removeWhereField($this->foreignKey);
|
||||
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $model]);
|
||||
if ($closure instanceof Closure) {
|
||||
$closure($this->query);
|
||||
}
|
||||
$list = $model->removeWhereField($foreignKey)->where($where)->with($subRelation)->select();
|
||||
|
||||
$list = $this->query->where($where)->with($subRelation)->select();
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
|
||||
foreach ($list as $set) {
|
||||
$data[$set->$foreignKey][] = $set;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @param boolean $replace 是否自动识别更新和写入
|
||||
* @return Model|false
|
||||
*/
|
||||
public function save($data)
|
||||
public function save($data, $replace = true)
|
||||
{
|
||||
if ($data instanceof Model) {
|
||||
$data = $data->getData();
|
||||
}
|
||||
$model = $this->make();
|
||||
|
||||
// 保存关联表数据
|
||||
$data[$this->foreignKey] = $this->parent->{$this->localKey};
|
||||
|
||||
$model = new $this->model();
|
||||
return $model->save($data) ? $model : false;
|
||||
return $model->replace($replace)->save($data) ? $model : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,37 +267,44 @@ class HasMany extends Relation
|
||||
/**
|
||||
* 批量保存当前关联数据对象
|
||||
* @access public
|
||||
* @param array $dataSet 数据集
|
||||
* @return integer
|
||||
* @param array|\think\Collection $dataSet 数据集
|
||||
* @param boolean $replace 是否自动识别更新和写入
|
||||
* @return array|false
|
||||
*/
|
||||
public function saveAll(array $dataSet)
|
||||
public function saveAll($dataSet, $replace = true)
|
||||
{
|
||||
$result = false;
|
||||
$result = [];
|
||||
|
||||
foreach ($dataSet as $key => $data) {
|
||||
$result = $this->save($data);
|
||||
$result[] = $this->save($data, $replace);
|
||||
}
|
||||
return $result;
|
||||
|
||||
return empty($result) ? false : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @return Query
|
||||
*/
|
||||
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
$model = basename(str_replace('\\', '/', get_class($this->parent)));
|
||||
$relation = basename(str_replace('\\', '/', $this->model));
|
||||
$table = $this->query->getTable();
|
||||
$model = basename(str_replace('\\', '/', get_class($this->parent)));
|
||||
$relation = basename(str_replace('\\', '/', $this->model));
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
|
||||
return $this->parent->db()
|
||||
->alias($model)
|
||||
->field($model . '.*')
|
||||
->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType)
|
||||
->when($softDelete, function ($query) use ($softDelete, $relation) {
|
||||
$query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
|
||||
})
|
||||
->group($relation . '.' . $this->foreignKey)
|
||||
->having('count(' . $id . ')' . $operator . $count);
|
||||
}
|
||||
@@ -271,7 +312,7 @@ class HasMany extends Relation
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @param mixed $fields 字段
|
||||
* @return Query
|
||||
*/
|
||||
@@ -282,25 +323,25 @@ class HasMany extends Relation
|
||||
$relation = basename(str_replace('\\', '/', $this->model));
|
||||
|
||||
if (is_array($where)) {
|
||||
foreach ($where as $key => $val) {
|
||||
if (false === strpos($key, '.')) {
|
||||
$where[$relation . '.' . $key] = $val;
|
||||
unset($where[$key]);
|
||||
}
|
||||
}
|
||||
$this->getQueryWhere($where, $relation);
|
||||
}
|
||||
|
||||
$fields = $this->getRelationQueryFields($fields, $model);
|
||||
$fields = $this->getRelationQueryFields($fields, $model);
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
|
||||
return $this->parent->db()->alias($model)
|
||||
->field($fields)
|
||||
return $this->parent->db()
|
||||
->alias($model)
|
||||
->group($model . '.' . $this->localKey)
|
||||
->field($fields)
|
||||
->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey)
|
||||
->when($softDelete, function ($query) use ($softDelete, $relation) {
|
||||
$query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
|
||||
})
|
||||
->where($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* 执行基础查询(仅执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
@@ -309,8 +350,9 @@ class HasMany extends Relation
|
||||
if (empty($this->baseQuery)) {
|
||||
if (isset($this->parent->{$this->localKey})) {
|
||||
// 关联查询带入关联条件
|
||||
$this->query->where($this->foreignKey, $this->parent->{$this->localKey});
|
||||
$this->query->where($this->foreignKey, '=', $this->parent->{$this->localKey});
|
||||
}
|
||||
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use Closure;
|
||||
use think\db\Query;
|
||||
use think\Exception;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
@@ -25,131 +25,337 @@ class HasManyThrough extends Relation
|
||||
protected $through;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $through 中间模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $throughKey 关联外键
|
||||
* @param string $localKey 关联主键
|
||||
* 中间主键
|
||||
* @var string
|
||||
*/
|
||||
protected $throughPk;
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $through 中间模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $throughKey 关联外键
|
||||
* @param string $localKey 当前主键
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $through, $foreignKey, $throughKey, $localKey)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->through = $through;
|
||||
$this->through = (new $through)->db();
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->throughKey = $throughKey;
|
||||
$this->throughPk = $this->through->getPk();
|
||||
$this->localKey = $localKey;
|
||||
$this->query = (new $model)->db();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return false|\PDOStatement|string|\think\Collection
|
||||
* @access public
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return \think\Collection
|
||||
*/
|
||||
public function getRelation($subRelation = '', $closure = null)
|
||||
{
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
if ($closure instanceof Closure) {
|
||||
$closure($this->query);
|
||||
}
|
||||
|
||||
return $this->relation($subRelation)->select();
|
||||
$this->baseQuery();
|
||||
|
||||
return $this->query->relation($subRelation)->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @return Query
|
||||
*/
|
||||
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
|
||||
{
|
||||
return $this->parent;
|
||||
$model = Loader::parseName(basename(str_replace('\\', '/', get_class($this->parent))));
|
||||
$throughTable = $this->through->getTable();
|
||||
$pk = $this->throughPk;
|
||||
$throughKey = $this->throughKey;
|
||||
$relation = (new $this->model)->db();
|
||||
$relationTable = $relation->getTable();
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
|
||||
if ('*' != $id) {
|
||||
$id = $relationTable . '.' . $relation->getPk();
|
||||
}
|
||||
|
||||
return $this->parent->db()
|
||||
->alias($model)
|
||||
->field($model . '.*')
|
||||
->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey)
|
||||
->join($relationTable, $relationTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk)
|
||||
->when($softDelete, function ($query) use ($softDelete, $relationTable) {
|
||||
$query->where($relationTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
|
||||
})
|
||||
->group($relationTable . '.' . $this->throughKey)
|
||||
->having('count(' . $id . ')' . $operator . $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @param mixed $fields 字段
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @param mixed $fields 字段
|
||||
* @return Query
|
||||
*/
|
||||
public function hasWhere($where = [], $fields = null)
|
||||
{
|
||||
throw new Exception('relation not support: hasWhere');
|
||||
$model = Loader::parseName(basename(str_replace('\\', '/', get_class($this->parent))));
|
||||
$throughTable = $this->through->getTable();
|
||||
$pk = $this->throughPk;
|
||||
$throughKey = $this->throughKey;
|
||||
$modelTable = (new $this->model)->db()->getTable();
|
||||
|
||||
if (is_array($where)) {
|
||||
$this->getQueryWhere($where, $modelTable);
|
||||
}
|
||||
|
||||
$fields = $this->getRelationQueryFields($fields, $model);
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
|
||||
return $this->parent->db()
|
||||
->alias($model)
|
||||
->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey)
|
||||
->join($modelTable, $modelTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk)
|
||||
->when($softDelete, function ($query) use ($softDelete, $modelTable) {
|
||||
$query->where($modelTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
|
||||
})
|
||||
->group($modelTable . '.' . $this->throughKey)
|
||||
->where($where)
|
||||
->field($fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* 预载入关联查询(数据集)
|
||||
* @access protected
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param mixed $subRelation 子关联名
|
||||
* @param Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
|
||||
{}
|
||||
public function eagerlyResultSet(array &$resultSet, $relation, $subRelation = '', $closure = null)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$localKey)) {
|
||||
$range[] = $result->$localKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
$this->query->removeWhereField($foreignKey);
|
||||
|
||||
$data = $this->eagerlyWhere([
|
||||
[$this->foreignKey, 'in', $range],
|
||||
], $foreignKey, $relation, $subRelation, $closure);
|
||||
|
||||
// 关联属性名
|
||||
$attr = Loader::parseName($relation);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
$pk = $result->$localKey;
|
||||
if (!isset($data[$pk])) {
|
||||
$data[$pk] = [];
|
||||
}
|
||||
|
||||
foreach ($data[$pk] as &$relationModel) {
|
||||
$relationModel->setParent(clone $result);
|
||||
}
|
||||
|
||||
// 设置关联属性
|
||||
$result->setRelation($attr, $this->resultSetBuild($data[$pk]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询 返回模型对象
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* 预载入关联查询(数据)
|
||||
* @access protected
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param mixed $subRelation 子关联名
|
||||
* @param Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure)
|
||||
{}
|
||||
public function eagerlyResult($result, $relation, $subRelation = '', $closure = null)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
$pk = $result->$localKey;
|
||||
|
||||
$this->query->removeWhereField($foreignKey);
|
||||
|
||||
$data = $this->eagerlyWhere([
|
||||
[$foreignKey, '=', $pk],
|
||||
], $foreignKey, $relation, $subRelation, $closure);
|
||||
|
||||
// 关联数据封装
|
||||
if (!isset($data[$pk])) {
|
||||
$data[$pk] = [];
|
||||
}
|
||||
|
||||
foreach ($data[$pk] as &$relationModel) {
|
||||
$relationModel->setParent(clone $result);
|
||||
}
|
||||
|
||||
$result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$pk]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联模型预查询
|
||||
* @access public
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $key 关联键名
|
||||
* @param string $relation 关联名
|
||||
* @param mixed $subRelation 子关联
|
||||
* @param Closure $closure
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyWhere(array $where, $key, $relation, $subRelation = '', $closure = null)
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
$throughList = $this->through->where($where)->select();
|
||||
$keys = $throughList->column($this->throughPk, $this->throughPk);
|
||||
|
||||
if ($closure instanceof Closure) {
|
||||
$closure($this->query);
|
||||
}
|
||||
|
||||
$list = $this->query->where($this->throughKey, 'in', $keys)->select();
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
$keys = $throughList->column($this->foreignKey, $this->throughPk);
|
||||
|
||||
foreach ($list as $set) {
|
||||
$data[$keys[$set->{$this->throughKey}]][] = $set;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param \Closure $closure 闭包
|
||||
* @param Model $result 数据对象
|
||||
* @param Closure $closure 闭包
|
||||
* @param string $aggregate 聚合查询方法
|
||||
* @param string $field 字段
|
||||
* @param string $name 统计字段别名
|
||||
* @return integer
|
||||
*/
|
||||
public function relationCount($result, $closure)
|
||||
{}
|
||||
public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = null)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
|
||||
if (!isset($result->$localKey)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($closure instanceof Closure) {
|
||||
$return = $closure($this->query);
|
||||
if ($return && is_string($return)) {
|
||||
$name = $return;
|
||||
}
|
||||
}
|
||||
|
||||
$alias = Loader::parseName(basename(str_replace('\\', '/', $this->model)));
|
||||
$throughTable = $this->through->getTable();
|
||||
$pk = $this->throughPk;
|
||||
$throughKey = $this->throughKey;
|
||||
$modelTable = $this->parent->getTable();
|
||||
|
||||
if (false === strpos($field, '.')) {
|
||||
$field = $alias . '.' . $field;
|
||||
}
|
||||
|
||||
return $this->query
|
||||
->alias($alias)
|
||||
->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
|
||||
->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
|
||||
->where($throughTable . '.' . $this->foreignKey, $result->$localKey)
|
||||
->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建关联统计子查询
|
||||
* @access public
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $name 统计数据别名
|
||||
* @param Closure $closure 闭包
|
||||
* @param string $aggregate 聚合查询方法
|
||||
* @param string $field 字段
|
||||
* @param string $name 统计字段别名
|
||||
* @return string
|
||||
*/
|
||||
public function getRelationCountQuery($closure, &$name = null)
|
||||
public function getRelationCountQuery($closure = null, $aggregate = 'count', $field = '*', &$name = null)
|
||||
{
|
||||
throw new Exception('relation not support: withCount');
|
||||
if ($closure instanceof Closure) {
|
||||
$return = $closure($this->query);
|
||||
if ($return && is_string($return)) {
|
||||
$name = $return;
|
||||
}
|
||||
}
|
||||
|
||||
$alias = Loader::parseName(basename(str_replace('\\', '/', $this->model)));
|
||||
$throughTable = $this->through->getTable();
|
||||
$pk = $this->throughPk;
|
||||
$throughKey = $this->throughKey;
|
||||
$modelTable = $this->parent->getTable();
|
||||
|
||||
if (false === strpos($field, '.')) {
|
||||
$field = $alias . '.' . $field;
|
||||
}
|
||||
|
||||
return $this->query
|
||||
->alias($alias)
|
||||
->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
|
||||
->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
|
||||
->whereExp($throughTable . '.' . $this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey)
|
||||
->fetchSql()
|
||||
->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* 执行基础查询(仅执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery()
|
||||
{
|
||||
if (empty($this->baseQuery) && $this->parent->getData()) {
|
||||
$through = $this->through;
|
||||
$alias = Loader::parseName(basename(str_replace('\\', '/', $this->model)));
|
||||
$throughTable = $through::getTable();
|
||||
$pk = (new $through)->getPk();
|
||||
$throughTable = $this->through->getTable();
|
||||
$pk = $this->throughPk;
|
||||
$throughKey = $this->throughKey;
|
||||
$modelTable = $this->parent->getTable();
|
||||
$this->query->field($alias . '.*')->alias($alias)
|
||||
$fields = $this->getQueryFields($alias);
|
||||
|
||||
$this->query
|
||||
->field($fields)
|
||||
->alias($alias)
|
||||
->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
|
||||
->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
|
||||
->where($throughTable . '.' . $this->foreignKey, $this->parent->{$this->localKey});
|
||||
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use Closure;
|
||||
use think\db\Query;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
@@ -18,37 +19,42 @@ use think\Model;
|
||||
class HasOne extends OneToOne
|
||||
{
|
||||
/**
|
||||
* 构造函数
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 当前模型主键
|
||||
* @param string $joinType JOIN类型
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 当前模型主键
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER')
|
||||
public function __construct(Model $parent, $model, $foreignKey, $localKey)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->joinType = $joinType;
|
||||
$this->joinType = 'INNER';
|
||||
$this->query = (new $model)->db();
|
||||
|
||||
if (get_class($parent) == $model) {
|
||||
$this->selfRelation = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return array|false|\PDOStatement|string|Model
|
||||
* @access public
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return Model
|
||||
*/
|
||||
public function getRelation($subRelation = '', $closure = null)
|
||||
{
|
||||
// 执行关联定义方法
|
||||
$localKey = $this->localKey;
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
|
||||
if ($closure instanceof Closure) {
|
||||
$closure($this->query);
|
||||
}
|
||||
|
||||
// 判断关联类型执行查询
|
||||
$relationModel = $this->query
|
||||
->removeWhereField($this->foreignKey)
|
||||
@@ -63,30 +69,96 @@ class HasOne extends OneToOne
|
||||
return $relationModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建关联统计子查询
|
||||
* @access public
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $aggregate 聚合查询方法
|
||||
* @param string $field 字段
|
||||
* @param string $aggregateAlias 聚合字段别名
|
||||
* @return string
|
||||
*/
|
||||
public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '')
|
||||
{
|
||||
if ($closure instanceof Closure) {
|
||||
$return = $closure($this->query);
|
||||
|
||||
if ($return && is_string($return)) {
|
||||
$aggregateAlias = $return;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->query
|
||||
->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey)
|
||||
->fetchSql()
|
||||
->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $aggregate 聚合查询方法
|
||||
* @param string $field 字段
|
||||
* @param string $name 统计字段别名
|
||||
* @return integer
|
||||
*/
|
||||
public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '')
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
|
||||
if (!isset($result->$localKey)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($closure instanceof Closure) {
|
||||
$return = $closure($this->query);
|
||||
if ($return && is_string($return)) {
|
||||
$name = $return;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->query
|
||||
->where($this->foreignKey, '=', $result->$localKey)
|
||||
->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @return Query
|
||||
*/
|
||||
public function has()
|
||||
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
$model = basename(str_replace('\\', '/', get_class($this->parent)));
|
||||
$relation = basename(str_replace('\\', '/', $this->model));
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
|
||||
return $this->parent->db()
|
||||
->alias($model)
|
||||
->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey) {
|
||||
$query->table([$table => $relation])->field($relation . '.' . $foreignKey)->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey);
|
||||
->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey, $softDelete) {
|
||||
$query->table([$table => $relation])
|
||||
->field($relation . '.' . $foreignKey)
|
||||
->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey)
|
||||
->when($softDelete, function ($query) use ($softDelete, $relation) {
|
||||
$query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @param mixed $fields 字段
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @param mixed $fields 字段
|
||||
* @return Query
|
||||
*/
|
||||
public function hasWhere($where = [], $fields = null)
|
||||
@@ -96,28 +168,29 @@ class HasOne extends OneToOne
|
||||
$relation = basename(str_replace('\\', '/', $this->model));
|
||||
|
||||
if (is_array($where)) {
|
||||
foreach ($where as $key => $val) {
|
||||
if (false === strpos($key, '.')) {
|
||||
$where[$relation . '.' . $key] = $val;
|
||||
unset($where[$key]);
|
||||
}
|
||||
}
|
||||
$this->getQueryWhere($where, $relation);
|
||||
}
|
||||
$fields = $this->getRelationQueryFields($fields, $model);
|
||||
|
||||
return $this->parent->db()->alias($model)
|
||||
$fields = $this->getRelationQueryFields($fields, $model);
|
||||
$softDelete = $this->query->getOptions('soft_delete');
|
||||
|
||||
return $this->parent->db()
|
||||
->alias($model)
|
||||
->field($fields)
|
||||
->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $this->joinType)
|
||||
->when($softDelete, function ($query) use ($softDelete, $relation) {
|
||||
$query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
|
||||
})
|
||||
->where($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据集)
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @access protected
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure)
|
||||
@@ -135,14 +208,14 @@ class HasOne extends OneToOne
|
||||
|
||||
if (!empty($range)) {
|
||||
$this->query->removeWhereField($foreignKey);
|
||||
$data = $this->eagerlyWhere($this->query, [
|
||||
$foreignKey => [
|
||||
'in',
|
||||
$range,
|
||||
],
|
||||
|
||||
$data = $this->eagerlyWhere([
|
||||
[$foreignKey, 'in', $range],
|
||||
], $foreignKey, $relation, $subRelation, $closure);
|
||||
|
||||
// 关联属性名
|
||||
$attr = Loader::parseName($relation);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
// 关联模型
|
||||
@@ -153,6 +226,7 @@ class HasOne extends OneToOne
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->isUpdate(true);
|
||||
}
|
||||
|
||||
if (!empty($this->bindAttr)) {
|
||||
// 绑定关联属性
|
||||
$this->bindAttr($relationModel, $result, $this->bindAttr);
|
||||
@@ -166,19 +240,23 @@ class HasOne extends OneToOne
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据)
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @access protected
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
protected function eagerlyOne(&$result, $relation, $subRelation, $closure)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$this->query->removeWhereField($foreignKey);
|
||||
$data = $this->eagerlyWhere($this->query, [$foreignKey => $result->$localKey], $foreignKey, $relation, $subRelation, $closure);
|
||||
|
||||
$data = $this->eagerlyWhere([
|
||||
[$foreignKey, '=', $result->$localKey],
|
||||
], $foreignKey, $relation, $subRelation, $closure);
|
||||
|
||||
// 关联模型
|
||||
if (!isset($data[$result->$localKey])) {
|
||||
@@ -188,6 +266,7 @@ class HasOne extends OneToOne
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->isUpdate(true);
|
||||
}
|
||||
|
||||
if (!empty($this->bindAttr)) {
|
||||
// 绑定关联属性
|
||||
$this->bindAttr($relationModel, $result, $this->bindAttr);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use think\Db;
|
||||
use Closure;
|
||||
use think\db\Query;
|
||||
use think\Exception;
|
||||
use think\Loader;
|
||||
@@ -27,13 +27,13 @@ class MorphMany extends Relation
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $morphKey 关联外键
|
||||
* @param string $morphType 多态字段名
|
||||
* @param string $type 多态类型
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $morphKey 关联外键
|
||||
* @param string $morphType 多态字段名
|
||||
* @param string $type 多态类型
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $morphKey, $morphType, $type)
|
||||
{
|
||||
@@ -47,16 +47,20 @@ class MorphMany extends Relation
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return false|\PDOStatement|string|\think\Collection
|
||||
* @access public
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return \think\Collection
|
||||
*/
|
||||
public function getRelation($subRelation = '', $closure = null)
|
||||
{
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
if ($closure instanceof Closure) {
|
||||
$closure($this->query);
|
||||
}
|
||||
$list = $this->relation($subRelation)->select();
|
||||
|
||||
$this->baseQuery();
|
||||
|
||||
$list = $this->query->relation($subRelation)->select();
|
||||
$parent = clone $this->parent;
|
||||
|
||||
foreach ($list as &$model) {
|
||||
@@ -69,10 +73,10 @@ class MorphMany extends Relation
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @return Query
|
||||
*/
|
||||
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
|
||||
@@ -83,8 +87,8 @@ class MorphMany extends Relation
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @param mixed $fields 字段
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @param mixed $fields 字段
|
||||
* @return Query
|
||||
*/
|
||||
public function hasWhere($where = [], $fields = null)
|
||||
@@ -95,10 +99,10 @@ class MorphMany extends Relation
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
|
||||
@@ -107,6 +111,7 @@ class MorphMany extends Relation
|
||||
$morphKey = $this->morphKey;
|
||||
$type = $this->type;
|
||||
$range = [];
|
||||
|
||||
foreach ($resultSet as $result) {
|
||||
$pk = $result->getPk();
|
||||
// 获取关联外键列表
|
||||
@@ -116,21 +121,26 @@ class MorphMany extends Relation
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
$data = $this->eagerlyMorphToMany([
|
||||
$morphKey => ['in', $range],
|
||||
$morphType => $type,
|
||||
], $relation, $subRelation, $closure);
|
||||
$where = [
|
||||
[$morphKey, 'in', $range],
|
||||
[$morphType, '=', $type],
|
||||
];
|
||||
$data = $this->eagerlyMorphToMany($where, $relation, $subRelation, $closure);
|
||||
|
||||
// 关联属性名
|
||||
$attr = Loader::parseName($relation);
|
||||
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
if (!isset($data[$result->$pk])) {
|
||||
$data[$result->$pk] = [];
|
||||
}
|
||||
|
||||
foreach ($data[$result->$pk] as &$relationModel) {
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->isUpdate(true);
|
||||
}
|
||||
|
||||
$result->setRelation($attr, $this->resultSetBuild($data[$result->$pk]));
|
||||
}
|
||||
}
|
||||
@@ -139,125 +149,138 @@ class MorphMany extends Relation
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure)
|
||||
{
|
||||
$pk = $result->getPk();
|
||||
if (isset($result->$pk)) {
|
||||
$data = $this->eagerlyMorphToMany([
|
||||
$this->morphKey => $result->$pk,
|
||||
$this->morphType => $this->type,
|
||||
], $relation, $subRelation, $closure);
|
||||
|
||||
if (!isset($data[$result->$pk])) {
|
||||
$data[$result->$pk] = [];
|
||||
if (isset($result->$pk)) {
|
||||
$key = $result->$pk;
|
||||
$where = [
|
||||
[$this->morphKey, '=', $key],
|
||||
[$this->morphType, '=', $this->type],
|
||||
];
|
||||
$data = $this->eagerlyMorphToMany($where, $relation, $subRelation, $closure);
|
||||
|
||||
if (!isset($data[$key])) {
|
||||
$data[$key] = [];
|
||||
}
|
||||
|
||||
foreach ($data[$result->$pk] as &$relationModel) {
|
||||
foreach ($data[$key] as &$relationModel) {
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->isUpdate(true);
|
||||
}
|
||||
|
||||
$result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$result->$pk]));
|
||||
$result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$key]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param \Closure $closure 闭包
|
||||
* @param Model $result 数据对象
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $aggregate 聚合查询方法
|
||||
* @param string $field 字段
|
||||
* @param string $name 统计字段别名
|
||||
* @return integer
|
||||
*/
|
||||
public function relationCount($result, $closure)
|
||||
public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '')
|
||||
{
|
||||
$pk = $result->getPk();
|
||||
$count = 0;
|
||||
if (isset($result->$pk)) {
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
}
|
||||
$count = $this->query->where([$this->morphKey => $result->$pk, $this->morphType => $this->type])->count();
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
$pk = $result->getPk();
|
||||
|
||||
if (!isset($result->$pk)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($closure instanceof Closure) {
|
||||
$return = $closure($this->query);
|
||||
|
||||
/**
|
||||
* 创建关联统计子查询
|
||||
* @access public
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $name 统计数据别名
|
||||
* @return string
|
||||
*/
|
||||
public function getRelationCountQuery($closure, &$name = null)
|
||||
{
|
||||
if ($closure) {
|
||||
$return = call_user_func_array($closure, [ & $this->query]);
|
||||
if ($return && is_string($return)) {
|
||||
$name = $return;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->query->where([
|
||||
$this->morphKey => [
|
||||
'exp',
|
||||
Db::raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()),
|
||||
],
|
||||
$this->morphType => $this->type,
|
||||
])->fetchSql()->count();
|
||||
return $this->query
|
||||
->where([
|
||||
[$this->morphKey, '=', $result->$pk],
|
||||
[$this->morphType, '=', $this->type],
|
||||
])
|
||||
->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取关联统计子查询
|
||||
* @access public
|
||||
* @param \Closure $closure 闭包
|
||||
* @param string $aggregate 聚合查询方法
|
||||
* @param string $field 字段
|
||||
* @param string $aggregateAlias 聚合字段别名
|
||||
* @return string
|
||||
*/
|
||||
public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '')
|
||||
{
|
||||
if ($closure instanceof Closure) {
|
||||
$return = $closure($this->query);
|
||||
|
||||
if ($return && is_string($return)) {
|
||||
$aggregateAlias = $return;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->query
|
||||
->whereExp($this->morphKey, '=' . $this->parent->getTable() . '.' . $this->parent->getPk())
|
||||
->where($this->morphType, '=', $this->type)
|
||||
->fetchSql()
|
||||
->$aggregate($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多态一对多 关联模型预查询
|
||||
* @access public
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @param bool|\Closure $closure 闭包
|
||||
* @access protected
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @param \Closure $closure 闭包
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyMorphToMany($where, $relation, $subRelation = '', $closure = false)
|
||||
protected function eagerlyMorphToMany($where, $relation, $subRelation = '', $closure = null)
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this]);
|
||||
$this->query->removeOption('where');
|
||||
|
||||
if ($closure instanceof Closure) {
|
||||
$closure($this->query);
|
||||
}
|
||||
|
||||
$list = $this->query->where($where)->with($subRelation)->select();
|
||||
$morphKey = $this->morphKey;
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
foreach ($list as $set) {
|
||||
$data[$set->$morphKey][] = $set;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @param mixed $data 数据
|
||||
* @return Model|false
|
||||
*/
|
||||
public function save($data)
|
||||
{
|
||||
if ($data instanceof Model) {
|
||||
$data = $data->getData();
|
||||
}
|
||||
$model = $this->make();
|
||||
|
||||
// 保存关联表数据
|
||||
$pk = $this->parent->getPk();
|
||||
|
||||
$data[$this->morphKey] = $this->parent->$pk;
|
||||
$data[$this->morphType] = $this->type;
|
||||
|
||||
$model = new $this->model();
|
||||
|
||||
return $model->save() ? $model : false;
|
||||
return $model->save($data) ? $model : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,30 +306,35 @@ class MorphMany extends Relation
|
||||
/**
|
||||
* 批量保存当前关联数据对象
|
||||
* @access public
|
||||
* @param array $dataSet 数据集
|
||||
* @return integer
|
||||
* @param array $dataSet 数据集
|
||||
* @return array|false
|
||||
*/
|
||||
public function saveAll(array $dataSet)
|
||||
{
|
||||
$result = false;
|
||||
$result = [];
|
||||
|
||||
foreach ($dataSet as $key => $data) {
|
||||
$result = $this->save($data);
|
||||
$result[] = $this->save($data);
|
||||
}
|
||||
return $result;
|
||||
|
||||
return empty($result) ? false : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* 执行基础查询(仅执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery()
|
||||
{
|
||||
if (empty($this->baseQuery) && $this->parent->getData()) {
|
||||
$pk = $this->parent->getPk();
|
||||
$map[$this->morphKey] = $this->parent->$pk;
|
||||
$map[$this->morphType] = $this->type;
|
||||
$this->query->where($map);
|
||||
$pk = $this->parent->getPk();
|
||||
|
||||
$this->query->where([
|
||||
[$this->morphKey, '=', $this->parent->$pk],
|
||||
[$this->morphType, '=', $this->type],
|
||||
]);
|
||||
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user