fix: 上传TP框架

This commit is contained in:
502647092 2017-10-20 14:00:22 +08:00
parent 3e7cf7c0aa
commit 6168d27257
167 changed files with 38754 additions and 1 deletions

1
.gitignore vendored
View File

@ -1,5 +1,4 @@
.idea
composer.lock
*.log
thinkphp
*.iml

4
thinkphp/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/composer.lock
/vendor
.idea
.DS_Store

1
thinkphp/.htaccess Normal file
View File

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

47
thinkphp/.travis.yml Normal file
View File

@ -0,0 +1,47 @@
sudo: false
language: php
services:
- memcached
- mongodb
- mysql
- postgresql
- redis-server
matrix:
fast_finish: true
include:
- php: 5.4
- php: 5.5
- php: 5.6
- php: 7.0
- php: hhvm
allow_failures:
- php: hhvm
cache:
directories:
- $HOME/.composer/cache
before_install:
- composer self-update
- mysql -e "create database IF NOT EXISTS test;" -uroot
- psql -c 'DROP DATABASE IF EXISTS test;' -U postgres
- psql -c 'create database test;' -U postgres
install:
- ./tests/script/install.sh
script:
## LINT
- find . -path ./vendor -prune -o -type f -name \*.php -exec php -l {} \;
## PHP Copy/Paste Detector
- vendor/bin/phpcpd --verbose --exclude vendor ./ || true
## PHPLOC
- vendor/bin/phploc --exclude vendor ./
## PHPUNIT
- vendor/bin/phpunit --coverage-clover=coverage.xml --configuration=phpunit.xml
after_success:
- bash <(curl -s https://codecov.io/bash)

119
thinkphp/CONTRIBUTING.md Normal file
View File

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

32
thinkphp/LICENSE.txt Normal file
View File

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

114
thinkphp/README.md Normal file
View File

@ -0,0 +1,114 @@
ThinkPHP 5.0
===============
[![StyleCI](https://styleci.io/repos/48530411/shield?style=flat&branch=master)](https://styleci.io/repos/48530411)
[![Build Status](https://travis-ci.org/top-think/framework.svg?branch=master)](https://travis-ci.org/top-think/framework)
[![codecov.io](http://codecov.io/github/top-think/framework/coverage.svg?branch=master)](http://codecov.io/github/github/top-think/framework?branch=master)
[![Total Downloads](https://poser.pugx.org/topthink/framework/downloads)](https://packagist.org/packages/topthink/framework)
[![Latest Stable Version](https://poser.pugx.org/topthink/framework/v/stable)](https://packagist.org/packages/topthink/framework)
[![Latest Unstable Version](https://poser.pugx.org/topthink/framework/v/unstable)](https://packagist.org/packages/topthink/framework)
[![License](https://poser.pugx.org/topthink/framework/license)](https://packagist.org/packages/topthink/framework)
ThinkPHP5在保持快速开发和大道至简的核心理念不变的同时PHP版本要求提升到5.4优化核心减少依赖基于全新的架构思想和命名空间实现是ThinkPHP突破原有框架思路的颠覆之作其主要特性包括
+ 基于命名空间和众多PHP新特性
+ 核心功能组件化
+ 强化路由功能
+ 更灵活的控制器
+ 重构的模型和数据库类
+ 配置文件可分离
+ 重写的自动验证和完成
+ 简化扩展机制
+ API支持完善
+ 改进的Log类
+ 命令行访问支持
+ REST支持
+ 引导文件支持
+ 方便的自动生成定义
+ 真正惰性加载
+ 分布式环境支持
+ 支持Composer
+ 支持MongoDb
> ThinkPHP5的运行环境要求PHP5.4以上。
详细开发文档参考 [ThinkPHP5完全开发手册](http://www.kancloud.cn/manual/thinkphp5) 以及[ThinkPHP5入门系列教程](http://www.kancloud.cn/special/thinkphp5_quickstart)
## 目录结构
初始的目录结构如下:
~~~
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 命令行入口文件
~~~
> router.php用于php自带webserver支持可用于快速测试
> 切换到public目录后启动命令php -S localhost:8888 router.php
> 上面的目录结构和名称是可以改变的,这取决于你的入口文件和配置参数。
## 命名规范
ThinkPHP5的命名规范遵循PSR-2规范以及PSR-4自动加载规范。
## 参与开发
注册并登录 Github 帐号, fork 本项目并进行改动。
更多细节参阅 [CONTRIBUTING.md](CONTRIBUTING.md)
## 版权信息
ThinkPHP遵循Apache2开源协议发布并提供免费使用。
本项目包含的第三方源码和二进制文件之版权信息另行标注。
版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn)
All rights reserved。
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
更多细节参阅 [LICENSE.txt](LICENSE.txt)

63
thinkphp/base.php Normal file
View File

@ -0,0 +1,63 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
define('THINK_VERSION', '5.0.11');
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);
// 载入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");
}
}
}
// 注册自动加载
\think\Loader::register();
// 注册错误和异常处理机制
\think\Error::register();
// 加载惯例配置文件
\think\Config::set(include THINK_PATH . 'convention' . EXT);

12
thinkphp/codecov.yml Normal file
View File

@ -0,0 +1,12 @@
comment:
layout: header, changes, diff
coverage:
ignore:
- base.php
- helper.php
- convention.php
- lang/zh-cn.php
- start.php
- console.php
status:
patch: false

35
thinkphp/composer.json Normal file
View File

@ -0,0 +1,35 @@
{
"name": "topthink/framework",
"description": "the new thinkphp framework",
"type": "think-framework",
"keywords": [
"framework",
"thinkphp",
"ORM"
],
"homepage": "http://thinkphp.cn/",
"license": "Apache-2.0",
"authors": [
{
"name": "liu21st",
"email": "liu21st@gmail.com"
}
],
"require": {
"php": ">=5.4.0",
"topthink/think-installer": "~1.0"
},
"require-dev": {
"phpunit/phpunit": "4.8.*",
"johnkary/phpunit-speedtrap": "^1.0",
"mikey179/vfsStream": "~1.6",
"phploc/phploc": "2.*",
"sebastian/phpcpd": "2.*",
"phpdocumentor/reflection-docblock": "^2.0"
},
"autoload": {
"psr-4": {
"think\\": "library/think"
}
}
}

20
thinkphp/console.php Normal file
View File

@ -0,0 +1,20 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think;
// ThinkPHP 引导文件
// 加载基础文件
require __DIR__ . '/base.php';
// 执行应用
App::initCommon();
Console::init();

289
thinkphp/convention.php Normal file
View File

@ -0,0 +1,289 @@
<?php
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,
// +----------------------------------------------------------------------
// | 模块设置
// +----------------------------------------------------------------------
// 默认模块名
'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,
// +----------------------------------------------------------------------
// | 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' => [],
// +----------------------------------------------------------------------
// | 模板设置
// +----------------------------------------------------------------------
'template' => [
// 模板引擎类型 支持 php think 支持扩展
'type' => 'Think',
// 视图基础目录,配置目录为所有模块的视图起始目录
'view_base' => '',
// 当前模板的视图目录 留空为自动获取
'view_path' => '',
// 模板后缀
'view_suffix' => 'html',
// 模板文件名分隔符
'view_depr' => DS,
// 模板引擎普通标签开始标记
'tpl_begin' => '{',
// 模板引擎普通标签结束标记
'tpl_end' => '}',
// 标签库标签开始标记
'taglib_begin' => '{',
// 标签库标签结束标记
'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' => [
// 日志记录方式,内置 file socket 支持扩展
'type' => 'File',
// 日志保存目录
'path' => LOG_PATH,
// 日志记录级别
'level' => [],
],
// +----------------------------------------------------------------------
// | Trace设置 开启 app_trace 后 有效
// +----------------------------------------------------------------------
'trace' => [
// 内置Html Console 支持扩展
'type' => 'Html',
],
// +----------------------------------------------------------------------
// | 缓存设置
// +----------------------------------------------------------------------
'cache' => [
// 驱动方式
'type' => 'File',
// 缓存保存目录
'path' => CACHE_PATH,
// 缓存前缀
'prefix' => '',
// 缓存有效期 0表示永久缓存
'expire' => 0,
],
// +----------------------------------------------------------------------
// | 会话设置
// +----------------------------------------------------------------------
'session' => [
'id' => '',
// SESSION_ID的提交变量,解决flash上传跨域
'var_session_id' => '',
// SESSION 前缀
'prefix' => 'think',
// 驱动方式 支持redis memcache memcached
'type' => '',
// 是否自动开启 SESSION
'auto_start' => true,
'httponly' => true,
'secure' => false,
],
// +----------------------------------------------------------------------
// | Cookie设置
// +----------------------------------------------------------------------
'cookie' => [
// cookie 名称前缀
'prefix' => '',
// cookie 保存时间
'expire' => 0,
// cookie 保存路径
'path' => '/',
// cookie 有效域名
'domain' => '',
// cookie 启用安全传输
'secure' => false,
// httponly设置
'httponly' => '',
// 是否使用 setcookie
'setcookie' => true,
],
// +----------------------------------------------------------------------
// | 数据库设置
// +----------------------------------------------------------------------
'database' => [
// 数据库类型
'type' => 'mysql',
// 数据库连接DSN配置
'dsn' => '',
// 服务器地址
'hostname' => '127.0.0.1',
// 数据库名
'database' => '',
// 数据库用户名
'username' => 'root',
// 数据库密码
'password' => '',
// 数据库连接端口
'hostport' => '',
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => 'utf8',
// 数据库表前缀
'prefix' => '',
// 数据库调试模式
'debug' => false,
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 数据集返回类型
'resultset_type' => 'array',
// 自动写入时间戳字段
'auto_timestamp' => false,
// 时间字段取出后的默认时间格式
'datetime_format' => 'Y-m-d H:i:s',
// 是否需要进行SQL性能分析
'sql_explain' => false,
],
//分页配置
'paginate' => [
'type' => 'bootstrap',
'var_page' => 'page',
'list_rows' => 15,
],
];

589
thinkphp/helper.php Normal file
View File

@ -0,0 +1,589 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
//------------------------
// ThinkPHP 助手函数
//-------------------------
use think\Cache;
use think\Config;
use think\Cookie;
use think\Db;
use think\Debug;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\Lang;
use think\Loader;
use think\Log;
use think\Model;
use think\Request;
use think\Response;
use think\Session;
use think\Url;
use think\View;
if (!function_exists('load_trait')) {
/**
* 快速导入Traits PHP5.5以上无需调用
* @param string $class trait库
* @param string $ext 类库后缀
* @return boolean
*/
function load_trait($class, $ext = EXT)
{
return Loader::import($class, TRAIT_PATH, $ext);
}
}
if (!function_exists('exception')) {
/**
* 抛出异常处理
*
* @param string $msg 异常消息
* @param integer $code 异常代码 默认为0
* @param string $exception 异常类
*
* @throws Exception
*/
function exception($msg, $code = 0, $exception = '')
{
$e = $exception ?: '\think\Exception';
throw new $e($msg, $code);
}
}
if (!function_exists('debug')) {
/**
* 记录时间(微秒)和内存使用情况
* @param string $start 开始标签
* @param string $end 结束标签
* @param integer|string $dec 小数位 如果是m 表示统计内存占用
* @return mixed
*/
function debug($start, $end = '', $dec = 6)
{
if ('' == $end) {
Debug::remark($start);
} else {
return 'm' == $dec ? Debug::getRangeMem($start, $end) : Debug::getRangeTime($start, $end, $dec);
}
}
}
if (!function_exists('lang')) {
/**
* 获取语言变量值
* @param string $name 语言变量名
* @param array $vars 动态变量值
* @param string $lang 语言
* @return mixed
*/
function lang($name, $vars = [], $lang = '')
{
return Lang::get($name, $vars, $lang);
}
}
if (!function_exists('config')) {
/**
* 获取和设置配置参数
* @param string|array $name 参数名
* @param mixed $value 参数值
* @param string $range 作用域
* @return mixed
*/
function config($name = '', $value = null, $range = '')
{
if (is_null($value) && is_string($name)) {
return 0 === strpos($name, '?') ? Config::has(substr($name, 1), $range) : Config::get($name, $range);
} else {
return Config::set($name, $value, $range);
}
}
}
if (!function_exists('input')) {
/**
* 获取输入数据 支持默认值和过滤
* @param string $key 获取的变量名
* @param mixed $default 默认值
* @param string $filter 过滤方法
* @return mixed
*/
function input($key = '', $default = null, $filter = '')
{
if (0 === strpos($key, '?')) {
$key = substr($key, 1);
$has = true;
}
if ($pos = strpos($key, '.')) {
// 指定参数来源
list($method, $key) = explode('.', $key, 2);
if (!in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'route', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) {
$key = $method . '.' . $key;
$method = 'param';
}
} else {
// 默认为自动判断
$method = 'param';
}
if (isset($has)) {
return request()->has($key, $method, $default);
} else {
return request()->$method($key, $default, $filter);
}
}
}
if (!function_exists('widget')) {
/**
* 渲染输出Widget
* @param string $name Widget名称
* @param array $data 传入的参数
* @return mixed
*/
function widget($name, $data = [])
{
return Loader::action($name, $data, 'widget');
}
}
if (!function_exists('model')) {
/**
* 实例化Model
* @param string $name Model名称
* @param string $layer 业务层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return \think\Model
*/
function model($name = '', $layer = 'model', $appendSuffix = false)
{
return Loader::model($name, $layer, $appendSuffix);
}
}
if (!function_exists('validate')) {
/**
* 实例化验证器
* @param string $name 验证器名称
* @param string $layer 业务层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return \think\Validate
*/
function validate($name = '', $layer = 'validate', $appendSuffix = false)
{
return Loader::validate($name, $layer, $appendSuffix);
}
}
if (!function_exists('db')) {
/**
* 实例化数据库类
* @param string $name 操作的数据表名称(不含前缀)
* @param array|string $config 数据库配置参数
* @param bool $force 是否强制重新连接
* @return \think\db\Query
*/
function db($name = '', $config = [], $force = false)
{
return Db::connect($config, $force)->name($name);
}
}
if (!function_exists('controller')) {
/**
* 实例化控制器 格式:[模块/]控制器
* @param string $name 资源地址
* @param string $layer 控制层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return \think\Controller
*/
function controller($name, $layer = 'controller', $appendSuffix = false)
{
return Loader::controller($name, $layer, $appendSuffix);
}
}
if (!function_exists('action')) {
/**
* 调用模块的操作方法 参数格式 [模块/控制器/]操作
* @param string $url 调用地址
* @param string|array $vars 调用参数 支持字符串和数组
* @param string $layer 要调用的控制层名称
* @param bool $appendSuffix 是否添加类名后缀
* @return mixed
*/
function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
{
return Loader::action($url, $vars, $layer, $appendSuffix);
}
}
if (!function_exists('import')) {
/**
* 导入所需的类库 同java的Import 本函数有缓存功能
* @param string $class 类库命名空间字符串
* @param string $baseUrl 起始路径
* @param string $ext 导入的文件扩展名
* @return boolean
*/
function import($class, $baseUrl = '', $ext = EXT)
{
return Loader::import($class, $baseUrl, $ext);
}
}
if (!function_exists('vendor')) {
/**
* 快速导入第三方框架类库 所有第三方框架的类库文件统一放到 系统的Vendor目录下面
* @param string $class 类库
* @param string $ext 类库后缀
* @return boolean
*/
function vendor($class, $ext = EXT)
{
return Loader::import($class, VENDOR_PATH, $ext);
}
}
if (!function_exists('dump')) {
/**
* 浏览器友好的变量输出
* @param mixed $var 变量
* @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串
* @param string $label 标签 默认为空
* @return void|string
*/
function dump($var, $echo = true, $label = null)
{
return Debug::dump($var, $echo, $label);
}
}
if (!function_exists('url')) {
/**
* Url生成
* @param string $url 路由地址
* @param string|array $vars 变量
* @param bool|string $suffix 生成的URL后缀
* @param bool|string $domain 域名
* @return string
*/
function url($url = '', $vars = '', $suffix = true, $domain = false)
{
return Url::build($url, $vars, $suffix, $domain);
}
}
if (!function_exists('session')) {
/**
* Session管理
* @param string|array $name session名称如果为数组表示进行session设置
* @param mixed $value session值
* @param string $prefix 前缀
* @return mixed
*/
function session($name, $value = '', $prefix = null)
{
if (is_array($name)) {
// 初始化
Session::init($name);
} elseif (is_null($name)) {
// 清除
Session::clear('' === $value ? null : $value);
} elseif ('' === $value) {
// 判断或获取
return 0 === strpos($name, '?') ? Session::has(substr($name, 1), $prefix) : Session::get($name, $prefix);
} elseif (is_null($value)) {
// 删除
return Session::delete($name, $prefix);
} else {
// 设置
return Session::set($name, $value, $prefix);
}
}
}
if (!function_exists('cookie')) {
/**
* Cookie管理
* @param string|array $name cookie名称如果为数组表示进行cookie设置
* @param mixed $value cookie值
* @param mixed $option 参数
* @return mixed
*/
function cookie($name, $value = '', $option = null)
{
if (is_array($name)) {
// 初始化
Cookie::init($name);
} elseif (is_null($name)) {
// 清除
Cookie::clear($value);
} elseif ('' === $value) {
// 获取
return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1), $option) : Cookie::get($name, $option);
} elseif (is_null($value)) {
// 删除
return Cookie::delete($name);
} else {
// 设置
return Cookie::set($name, $value, $option);
}
}
}
if (!function_exists('cache')) {
/**
* 缓存管理
* @param mixed $name 缓存名称,如果为数组表示进行缓存设置
* @param mixed $value 缓存值
* @param mixed $options 缓存参数
* @param string $tag 缓存标签
* @return mixed
*/
function cache($name, $value = '', $options = null, $tag = null)
{
if (is_array($options)) {
// 缓存操作的同时初始化
$cache = Cache::connect($options);
} elseif (is_array($name)) {
// 缓存初始化
return Cache::connect($name);
} else {
$cache = Cache::init();
}
if (is_null($name)) {
return $cache->clear($value);
} elseif ('' === $value) {
// 获取缓存
return 0 === strpos($name, '?') ? $cache->has(substr($name, 1)) : $cache->get($name);
} elseif (is_null($value)) {
// 删除缓存
return $cache->rm($name);
} elseif (0 === strpos($name, '?') && '' !== $value) {
$expire = is_numeric($options) ? $options : null;
return $cache->remember(substr($name, 1), $value, $expire);
} else {
// 缓存数据
if (is_array($options)) {
$expire = isset($options['expire']) ? $options['expire'] : null; //修复查询缓存无法设置过期时间
} else {
$expire = is_numeric($options) ? $options : null; //默认快捷缓存设置过期时间
}
if (is_null($tag)) {
return $cache->set($name, $value, $expire);
} else {
return $cache->tag($tag)->set($name, $value, $expire);
}
}
}
}
if (!function_exists('trace')) {
/**
* 记录日志信息
* @param mixed $log log信息 支持字符串和数组
* @param string $level 日志级别
* @return void|array
*/
function trace($log = '[think]', $level = 'log')
{
if ('[think]' === $log) {
return Log::getLog();
} else {
Log::record($log, $level);
}
}
}
if (!function_exists('request')) {
/**
* 获取当前Request对象实例
* @return Request
*/
function request()
{
return Request::instance();
}
}
if (!function_exists('response')) {
/**
* 创建普通 Response 对象实例
* @param mixed $data 输出数据
* @param int|string $code 状态码
* @param array $header 头信息
* @param string $type
* @return Response
*/
function response($data = [], $code = 200, $header = [], $type = 'html')
{
return Response::create($data, $type, $code, $header);
}
}
if (!function_exists('view')) {
/**
* 渲染模板输出
* @param string $template 模板文件
* @param array $vars 模板变量
* @param array $replace 模板替换
* @param integer $code 状态码
* @return \think\response\View
*/
function view($template = '', $vars = [], $replace = [], $code = 200)
{
return Response::create($template, 'view', $code)->replace($replace)->assign($vars);
}
}
if (!function_exists('json')) {
/**
* 获取\think\response\Json对象实例
* @param mixed $data 返回的数据
* @param integer $code 状态码
* @param array $header 头部
* @param array $options 参数
* @return \think\response\Json
*/
function json($data = [], $code = 200, $header = [], $options = [])
{
return Response::create($data, 'json', $code, $header, $options);
}
}
if (!function_exists('jsonp')) {
/**
* 获取\think\response\Jsonp对象实例
* @param mixed $data 返回的数据
* @param integer $code 状态码
* @param array $header 头部
* @param array $options 参数
* @return \think\response\Jsonp
*/
function jsonp($data = [], $code = 200, $header = [], $options = [])
{
return Response::create($data, 'jsonp', $code, $header, $options);
}
}
if (!function_exists('xml')) {
/**
* 获取\think\response\Xml对象实例
* @param mixed $data 返回的数据
* @param integer $code 状态码
* @param array $header 头部
* @param array $options 参数
* @return \think\response\Xml
*/
function xml($data = [], $code = 200, $header = [], $options = [])
{
return Response::create($data, 'xml', $code, $header, $options);
}
}
if (!function_exists('redirect')) {
/**
* 获取\think\response\Redirect对象实例
* @param mixed $url 重定向地址 支持Url::build方法的地址
* @param array|integer $params 额外参数
* @param integer $code 状态码
* @param array $with 隐式传参
* @return \think\response\Redirect
*/
function redirect($url = [], $params = [], $code = 302, $with = [])
{
if (is_integer($params)) {
$code = $params;
$params = [];
}
return Response::create($url, 'redirect', $code)->params($params)->with($with);
}
}
if (!function_exists('abort')) {
/**
* 抛出HTTP异常
* @param integer|Response $code 状态码 或者 Response对象实例
* @param string $message 错误信息
* @param array $header 参数
*/
function abort($code, $message = null, $header = [])
{
if ($code instanceof Response) {
throw new HttpResponseException($code);
} else {
throw new HttpException($code, $message, null, $header);
}
}
}
if (!function_exists('halt')) {
/**
* 调试变量并且中断输出
* @param mixed $var 调试变量或者信息
*/
function halt($var)
{
dump($var);
throw new HttpResponseException(new Response);
}
}
if (!function_exists('token')) {
/**
* 生成表单令牌
* @param string $name 令牌名称
* @param mixed $type 令牌生成方法
* @return string
*/
function token($name = '__token__', $type = 'md5')
{
$token = Request::instance()->token($name, $type);
return '<input type="hidden" name="' . $name . '" value="' . $token . '" />';
}
}
if (!function_exists('load_relation')) {
/**
* 延迟预载入关联查询
* @param mixed $resultSet 数据集
* @param mixed $relation 关联
* @return array
*/
function load_relation($resultSet, $relation)
{
$item = current($resultSet);
if ($item instanceof Model) {
$item->eagerlyResultSet($resultSet, $relation);
}
return $resultSet;
}
}
if (!function_exists('collection')) {
/**
* 数组转换为数据集对象
* @param array $resultSet 数据集数组
* @return \think\model\Collection|\think\Collection
*/
function collection($resultSet)
{
$item = current($resultSet);
if ($item instanceof Model) {
return \think\model\Collection::make($resultSet);
} else {
return \think\Collection::make($resultSet);
}
}
}

69
thinkphp/lang/zh-cn.php Normal file
View File

@ -0,0 +1,69 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
// 核心中文语言包
return [
// 系统错误提示
'Undefined variable' => '未定义变量',
'Undefined index' => '未定义数组索引',
'Undefined offset' => '未定义数组下标',
'Parse error' => '语法解析错误',
'Type error' => '类型错误',
'Fatal error' => '致命错误',
'syntax error' => '语法错误',
// 框架核心错误提示
'dispatch type not support' => '不支持的调度类型',
'method param miss' => '方法参数错误',
'method not exists' => '方法不存在',
'module not exists' => '模块不存在',
'controller not exists' => '控制器不存在',
'class not exists' => '类不存在',
'property not exists' => '类的属性不存在',
'template not exists' => '模板文件不存在',
'illegal controller name' => '非法的控制器名称',
'illegal action name' => '非法的操作名称',
'url suffix deny' => '禁止的URL后缀访问',
'Route Not Found' => '当前访问路由未定义',
'Undefined db type' => '未定义数据库类型',
'variable type error' => '变量类型错误',
'PSR-4 error' => 'PSR-4 规范错误',
'not support total' => '简洁模式下不能获取数据总数',
'not support last' => '简洁模式下不能获取最后一页',
'error session handler' => '错误的SESSION处理器类',
'not allow php tag' => '模板不允许使用PHP语法',
'not support' => '不支持',
'redisd master' => 'Redisd 主服务器错误',
'redisd slave' => 'Redisd 从服务器错误',
'must run at sae' => '必须在SAE运行',
'memcache init error' => '未开通Memcache服务请在SAE管理平台初始化Memcache服务',
'KVDB init error' => '没有初始化KVDB请在SAE管理平台初始化KVDB服务',
'fields not exists' => '数据表字段不存在',
'where express error' => '查询表达式错误',
'no data to update' => '没有任何数据需要更新',
'miss data to insert' => '缺少需要写入的数据',
'miss complex primary data' => '缺少复合主键数据',
'miss update condition' => '缺少更新条件',
'model data Not Found' => '模型数据不存在',
'table data not Found' => '表数据不存在',
'delete without condition' => '没有条件不会执行删除操作',
'miss relation data' => '缺少关联表数据',
'tag attr must' => '模板标签属性必须',
'tag error' => '模板标签错误',
'cache write error' => '缓存写入失败',
'sae mc write error' => 'SAE mc 写入错误',
'route name not exists' => '路由标识不存在(或参数不够)',
'invalid request' => '非法请求',
'bind attr has exists' => '模型的属性已经存在',
'relation data not exists' => '关联数据不存在',
'relation not support' => '关联不支持',
'chunk not support order' => 'Chunk不支持调用order方法',
];

View File

@ -0,0 +1,591 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
use think\exception\ClassNotFoundException;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\exception\RouteNotFoundException;
/**
* App 应用管理
* @author liu21st <liu21st@gmail.com>
*/
class App
{
/**
* @var bool 是否初始化过
*/
protected static $init = false;
/**
* @var string 当前模块路径
*/
public static $modulePath;
/**
* @var bool 应用调试模式
*/
public static $debug = true;
/**
* @var string 应用类库命名空间
*/
public static $namespace = 'app';
/**
* @var bool 应用类库后缀
*/
public static $suffix = false;
/**
* @var bool 应用路由检测
*/
protected static $routeCheck;
/**
* @var bool 严格路由检测
*/
protected static $routeMust;
protected static $dispatch;
protected static $file = [];
/**
* 执行应用程序
* @access public
* @param Request $request Request对象
* @return Response
* @throws Exception
*/
public static function run(Request $request = null)
{
is_null($request) && $request = Request::instance();
try {
$config = self::initCommon();
if (defined('BIND_MODULE')) {
// 模块/控制器绑定
BIND_MODULE && Route::bind(BIND_MODULE);
} elseif ($config['auto_bind_module']) {
// 入口自动绑定
$name = pathinfo($request->baseFile(), PATHINFO_FILENAME);
if ($name && 'index' != $name && is_dir(APP_PATH . $name)) {
Route::bind($name);
}
}
$request->filter($config['default_filter']);
// 默认语言
Lang::range($config['default_lang']);
if ($config['lang_switch_on']) {
// 开启多语言机制 检测当前语言
Lang::detect();
}
$request->langset(Lang::range());
// 加载系统语言包
Lang::load([
THINK_PATH . 'lang' . DS . $request->langset() . EXT,
APP_PATH . 'lang' . DS . $request->langset() . EXT,
]);
// 获取应用调度信息
$dispatch = self::$dispatch;
if (empty($dispatch)) {
// 进行URL路由检测
$dispatch = self::routeCheck($request, $config);
}
// 记录当前调度信息
$request->dispatch($dispatch);
// 记录路由和请求信息
if (self::$debug) {
Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info');
Log::record('[ HEADER ] ' . var_export($request->header(), true), 'info');
Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info');
}
// 监听app_begin
Hook::listen('app_begin', $dispatch);
// 请求缓存检查
$request->cache($config['request_cache'], $config['request_cache_expire'], $config['request_cache_except']);
$data = self::exec($dispatch, $config);
} catch (HttpResponseException $exception) {
$data = $exception->getResponse();
}
// 清空类的实例化
Loader::clearInstance();
// 输出数据到客户端
if ($data instanceof Response) {
$response = $data;
} elseif (!is_null($data)) {
// 默认自动识别响应输出类型
$isAjax = $request->isAjax();
$type = $isAjax ? Config::get('default_ajax_return') : Config::get('default_return_type');
$response = Response::create($data, $type);
} else {
$response = Response::create();
}
// 监听app_end
Hook::listen('app_end', $response);
return $response;
}
/**
* 设置当前请求的调度信息
* @access public
* @param array|string $dispatch 调度信息
* @param string $type 调度类型
* @return void
*/
public static function dispatch($dispatch, $type = 'module')
{
self::$dispatch = ['type' => $type, $type => $dispatch];
}
/**
* 执行函数或者闭包方法 支持参数调用
* @access public
* @param string|array|\Closure $function 函数或者闭包
* @param array $vars 变量
* @return mixed
*/
public static function invokeFunction($function, $vars = [])
{
$reflect = new \ReflectionFunction($function);
$args = self::bindParams($reflect, $vars);
// 记录执行信息
self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info');
return $reflect->invokeArgs($args);
}
/**
* 调用反射执行类的方法 支持参数绑定
* @access public
* @param string|array $method 方法
* @param array $vars 变量
* @return mixed
*/
public static function invokeMethod($method, $vars = [])
{
if (is_array($method)) {
$class = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]);
$reflect = new \ReflectionMethod($class, $method[1]);
} else {
// 静态方法
$reflect = new \ReflectionMethod($method);
}
$args = self::bindParams($reflect, $vars);
self::$debug && Log::record('[ RUN ] ' . $reflect->class . '->' . $reflect->name . '[ ' . $reflect->getFileName() . ' ]', 'info');
return $reflect->invokeArgs(isset($class) ? $class : null, $args);
}
/**
* 调用反射执行类的实例化 支持依赖注入
* @access public
* @param string $class 类名
* @param array $vars 变量
* @return mixed
*/
public static function invokeClass($class, $vars = [])
{
$reflect = new \ReflectionClass($class);
$constructor = $reflect->getConstructor();
if ($constructor) {
$args = self::bindParams($constructor, $vars);
} else {
$args = [];
}
return $reflect->newInstanceArgs($args);
}
/**
* 绑定参数
* @access private
* @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
* @param array $vars 变量
* @return array
*/
private static function bindParams($reflect, $vars = [])
{
if (empty($vars)) {
// 自动获取请求变量
if (Config::get('url_param_type')) {
$vars = Request::instance()->route();
} else {
$vars = Request::instance()->param();
}
}
$args = [];
if ($reflect->getNumberOfParameters() > 0) {
// 判断数组类型 数字数组时按顺序绑定参数
reset($vars);
$type = key($vars) === 0 ? 1 : 0;
$params = $reflect->getParameters();
foreach ($params as $param) {
$args[] = self::getParamValue($param, $vars, $type);
}
}
return $args;
}
/**
* 获取参数值
* @access private
* @param \ReflectionParameter $param
* @param array $vars 变量
* @param string $type
* @return array
*/
private static function getParamValue($param, &$vars, $type)
{
$name = $param->getName();
$class = $param->getClass();
if ($class) {
$className = $class->getName();
$bind = Request::instance()->$name;
if ($bind instanceof $className) {
$result = $bind;
} else {
if (method_exists($className, 'invoke')) {
$method = new \ReflectionMethod($className, 'invoke');
if ($method->isPublic() && $method->isStatic()) {
return $className::invoke(Request::instance());
}
}
$result = method_exists($className, 'instance') ? $className::instance() : new $className;
}
} elseif (1 == $type && !empty($vars)) {
$result = array_shift($vars);
} elseif (0 == $type && isset($vars[$name])) {
$result = $vars[$name];
} elseif ($param->isDefaultValueAvailable()) {
$result = $param->getDefaultValue();
} else {
throw new \InvalidArgumentException('method param miss:' . $name);
}
return $result;
}
protected static function exec($dispatch, $config)
{
switch ($dispatch['type']) {
case 'redirect':
// 执行重定向跳转
$data = Response::create($dispatch['url'], 'redirect')->code($dispatch['status']);
break;
case 'module':
// 模块/控制器/操作
$data = self::module($dispatch['module'], $config, isset($dispatch['convert']) ? $dispatch['convert'] : null);
break;
case 'controller':
// 执行控制器操作
$vars = array_merge(Request::instance()->param(), $dispatch['var']);
$data = Loader::action($dispatch['controller'], $vars, $config['url_controller_layer'], $config['controller_suffix']);
break;
case 'method':
// 执行回调方法
$vars = array_merge(Request::instance()->param(), $dispatch['var']);
$data = self::invokeMethod($dispatch['method'], $vars);
break;
case 'function':
// 执行闭包
$data = self::invokeFunction($dispatch['function']);
break;
case 'response':
$data = $dispatch['response'];
break;
default:
throw new \InvalidArgumentException('dispatch type not support');
}
return $data;
}
/**
* 执行模块
* @access public
* @param array $result 模块/控制器/操作
* @param array $config 配置参数
* @param bool $convert 是否自动转换控制器和操作名
* @return mixed
*/
public static function module($result, $config, $convert = null)
{
if (is_string($result)) {
$result = explode('/', $result);
}
$request = Request::instance();
if ($config['app_multi_module']) {
// 多模块部署
$module = strip_tags(strtolower($result[0] ?: $config['default_module']));
$bind = Route::getBind('module');
$available = false;
if ($bind) {
// 绑定模块
list($bindModule) = explode('/', $bind);
if (empty($result[0])) {
$module = $bindModule;
$available = true;
} elseif ($module == $bindModule) {
$available = true;
}
} elseif (!in_array($module, $config['deny_module_list']) && is_dir(APP_PATH . $module)) {
$available = true;
}
// 模块初始化
if ($module && $available) {
// 初始化模块
$request->module($module);
$config = self::init($module);
// 模块请求缓存检查
$request->cache($config['request_cache'], $config['request_cache_expire'], $config['request_cache_except']);
} else {
throw new HttpException(404, 'module not exists:' . $module);
}
} else {
// 单一模块部署
$module = '';
$request->module($module);
}
// 当前模块路径
App::$modulePath = APP_PATH . ($module ? $module . DS : '');
// 是否自动转换控制器和操作名
$convert = is_bool($convert) ? $convert : $config['url_convert'];
// 获取控制器名
$controller = strip_tags($result[1] ?: $config['default_controller']);
$controller = $convert ? strtolower($controller) : $controller;
// 获取操作名
$actionName = strip_tags($result[2] ?: $config['default_action']);
$actionName = $convert ? strtolower($actionName) : $actionName;
// 设置当前请求的控制器、操作
$request->controller(Loader::parseName($controller, 1))->action($actionName);
// 监听module_init
Hook::listen('module_init', $request);
try {
$instance = Loader::controller($controller, $config['url_controller_layer'], $config['controller_suffix'], $config['empty_controller']);
} catch (ClassNotFoundException $e) {
throw new HttpException(404, 'controller not exists:' . $e->getClass());
}
// 获取当前操作名
$action = $actionName . $config['action_suffix'];
$vars = [];
if (is_callable([$instance, $action])) {
// 执行操作方法
$call = [$instance, $action];
} elseif (is_callable([$instance, '_empty'])) {
// 空操作
$call = [$instance, '_empty'];
$vars = [$actionName];
} else {
// 操作不存在
throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
}
Hook::listen('action_begin', $call);
return self::invokeMethod($call, $vars);
}
/**
* 初始化应用
*/
public static function initCommon()
{
if (empty(self::$init)) {
if (defined('APP_NAMESPACE')) {
self::$namespace = APP_NAMESPACE;
}
Loader::addNamespace(self::$namespace, APP_PATH);
// 初始化应用
$config = self::init();
self::$suffix = $config['class_suffix'];
// 应用调试模式
self::$debug = Env::get('app_debug', Config::get('app_debug'));
if (!self::$debug) {
ini_set('display_errors', 'Off');
} elseif (!IS_CLI) {
//重新申请一块比较大的buffer
if (ob_get_level() > 0) {
$output = ob_get_clean();
}
ob_start();
if (!empty($output)) {
echo $output;
}
}
if (!empty($config['root_namespace'])) {
Loader::addNamespace($config['root_namespace']);
}
// 加载额外文件
if (!empty($config['extra_file_list'])) {
foreach ($config['extra_file_list'] as $file) {
$file = strpos($file, '.') ? $file : APP_PATH . $file . EXT;
if (is_file($file) && !isset(self::$file[$file])) {
include $file;
self::$file[$file] = true;
}
}
}
// 设置系统时区
date_default_timezone_set($config['default_timezone']);
// 监听app_init
Hook::listen('app_init');
self::$init = true;
}
return Config::get();
}
/**
* 初始化应用或模块
* @access public
* @param string $module 模块名
* @return array
*/
private static function init($module = '')
{
// 定位模块目录
$module = $module ? $module . DS : '';
// 加载初始化文件
if (is_file(APP_PATH . $module . 'init' . EXT)) {
include APP_PATH . $module . 'init' . EXT;
} elseif (is_file(RUNTIME_PATH . $module . 'init' . EXT)) {
include RUNTIME_PATH . $module . 'init' . EXT;
} else {
$path = APP_PATH . $module;
// 加载模块配置
$config = Config::load(CONF_PATH . $module . 'config' . CONF_EXT);
// 读取数据库配置文件
$filename = CONF_PATH . $module . 'database' . CONF_EXT;
Config::load($filename, 'database');
// 读取扩展配置文件
if (is_dir(CONF_PATH . $module . 'extra')) {
$dir = CONF_PATH . $module . 'extra';
$files = scandir($dir);
foreach ($files as $file) {
if ('.' . pathinfo($file, PATHINFO_EXTENSION) === CONF_EXT) {
$filename = $dir . DS . $file;
Config::load($filename, pathinfo($file, PATHINFO_FILENAME));
}
}
}
// 加载应用状态配置
if ($config['app_status']) {
$config = Config::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT);
}
// 加载行为扩展文件
if (is_file(CONF_PATH . $module . 'tags' . EXT)) {
Hook::import(include CONF_PATH . $module . 'tags' . EXT);
}
// 加载公共文件
if (is_file($path . 'common' . EXT)) {
include $path . 'common' . EXT;
}
// 加载当前模块语言包
if ($module) {
Lang::load($path . 'lang' . DS . Request::instance()->langset() . EXT);
}
}
return Config::get();
}
/**
* URL路由检测根据PATH_INFO)
* @access public
* @param \think\Request $request
* @param array $config
* @return array
* @throws \think\Exception
*/
public static function routeCheck($request, array $config)
{
$path = $request->path();
$depr = $config['pathinfo_depr'];
$result = false;
// 路由检测
$check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on'];
if ($check) {
// 开启路由
if (is_file(RUNTIME_PATH . 'route.php')) {
// 读取路由缓存
$rules = include RUNTIME_PATH . 'route.php';
if (is_array($rules)) {
Route::rules($rules);
}
} else {
$files = $config['route_config_file'];
foreach ($files as $file) {
if (is_file(CONF_PATH . $file . CONF_EXT)) {
// 导入路由配置
$rules = include CONF_PATH . $file . CONF_EXT;
if (is_array($rules)) {
Route::import($rules);
}
}
}
}
// 路由检测根据路由定义返回不同的URL调度
$result = Route::check($request, $path, $depr, $config['url_domain_deploy']);
$must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must'];
if ($must && false === $result) {
// 路由无效
throw new RouteNotFoundException();
}
}
if (false === $result) {
// 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索
$result = Route::parseUrl($path, $depr, $config['controller_auto_search']);
}
return $result;
}
/**
* 设置应用的路由检测机制
* @access public
* @param bool $route 是否需要检测路由
* @param bool $must 是否强制检测路由
* @return void
*/
public static function route($route, $must = false)
{
self::$routeCheck = $route;
self::$routeMust = $must;
}
}

View File

@ -0,0 +1,205 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
class Build
{
/**
* 根据传入的build资料创建目录和文件
* @access protected
* @param array $build build列表
* @param string $namespace 应用类库命名空间
* @param bool $suffix 类库后缀
* @return void
*/
public static function run(array $build = [], $namespace = 'app', $suffix = false)
{
// 锁定
$lockfile = APP_PATH . 'build.lock';
if (is_writable($lockfile)) {
return;
} elseif (!touch($lockfile)) {
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($lockfile);
}
/**
* 创建目录
* @access protected
* @param array $list 目录列表
* @return void
*/
protected static function buildDir($list)
{
foreach ($list as $dir) {
if (!is_dir(APP_PATH . $dir)) {
// 创建目录
mkdir(APP_PATH . $dir, 0755, true);
}
}
}
/**
* 创建文件
* @access protected
* @param array $list 文件列表
* @return void
*/
protected static function buildFile($list)
{
foreach ($list as $file) {
if (!is_dir(APP_PATH . dirname($file))) {
// 创建目录
mkdir(APP_PATH . dirname($file), 0755, true);
}
if (!is_file(APP_PATH . $file)) {
file_put_contents(APP_PATH . $file, 'php' == pathinfo($file, PATHINFO_EXTENSION) ? "<?php\n" : '');
}
}
}
/**
* 创建模块
* @access public
* @param string $module 模块名
* @param array $list build列表
* @param string $namespace 应用类库命名空间
* @param bool $suffix 类库后缀
* @return void
*/
public static function module($module = '', $list = [], $namespace = 'app', $suffix = false)
{
$module = $module ? $module : '';
if (!is_dir(APP_PATH . $module)) {
// 创建模块目录
mkdir(APP_PATH . $module);
}
if (basename(RUNTIME_PATH) != $module) {
// 创建配置文件和公共文件
self::buildCommon($module);
// 创建模块的默认页面
self::buildHello($module, $namespace, $suffix);
}
if (empty($list)) {
// 创建默认的模块目录和文件
$list = [
'__file__' => ['config.php', 'common.php'],
'__dir__' => ['controller', 'model', 'view'],
];
}
// 创建子目录和文件
foreach ($list as $path => $file) {
$modulePath = APP_PATH . $module . DS;
if ('__dir__' == $path) {
// 生成子目录
foreach ($file as $dir) {
self::checkDirBuild($modulePath . $dir);
}
} elseif ('__file__' == $path) {
// 生成(空白)文件
foreach ($file as $name) {
if (!is_file($modulePath . $name)) {
file_put_contents($modulePath . $name, 'php' == pathinfo($name, PATHINFO_EXTENSION) ? "<?php\n" : '');
}
}
} else {
// 生成相关MVC文件
foreach ($file as $val) {
$val = trim($val);
$filename = $modulePath . $path . DS . $val . ($suffix ? ucfirst($path) : '') . EXT;
$space = $namespace . '\\' . ($module ? $module . '\\' : '') . $path;
$class = $val . ($suffix ? ucfirst($path) : '');
switch ($path) {
case 'controller': // 控制器
$content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
break;
case 'model': // 模型
$content = "<?php\nnamespace {$space};\n\nuse think\Model;\n\nclass {$class} extends Model\n{\n\n}";
break;
case 'view': // 视图
$filename = $modulePath . $path . DS . $val . '.html';
self::checkDirBuild(dirname($filename));
$content = '';
break;
default:
// 其他文件
$content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
}
if (!is_file($filename)) {
file_put_contents($filename, $content);
}
}
}
}
}
/**
* 创建模块的欢迎页面
* @access public
* @param string $module 模块名
* @param string $namespace 应用类库命名空间
* @param bool $suffix 类库后缀
* @return void
*/
protected static function buildHello($module, $namespace, $suffix = false)
{
$filename = APP_PATH . ($module ? $module . DS : '') . 'controller' . DS . 'Index' . ($suffix ? 'Controller' : '') . EXT;
if (!is_file($filename)) {
$content = file_get_contents(THINK_PATH . 'tpl' . DS . 'default_index.tpl');
$content = str_replace(['{$app}', '{$module}', '{layer}', '{$suffix}'], [$namespace, $module ? $module . '\\' : '', 'controller', $suffix ? 'Controller' : ''], $content);
self::checkDirBuild(dirname($filename));
file_put_contents($filename, $content);
}
}
/**
* 创建模块的公共文件
* @access public
* @param string $module 模块名
* @return void
*/
protected static function buildCommon($module)
{
$filename = CONF_PATH . ($module ? $module . DS : '') . 'config.php';
self::checkDirBuild(dirname($filename));
if (!is_file($filename)) {
file_put_contents($filename, "<?php\n//配置文件\nreturn [\n\n];");
}
$filename = APP_PATH . ($module ? $module . DS : '') . 'common.php';
if (!is_file($filename)) {
file_put_contents($filename, "<?php\n");
}
}
protected static function checkDirBuild($dirname)
{
if (!is_dir($dirname)) {
mkdir($dirname, 0755, true);
}
}
}

View File

@ -0,0 +1,222 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
use think\cache\Driver;
class Cache
{
protected static $instance = [];
public static $readTimes = 0;
public static $writeTimes = 0;
/**
* 操作句柄
* @var object
* @access protected
*/
protected static $handler;
/**
* 连接缓存
* @access public
* @param array $options 配置数组
* @param bool|string $name 缓存连接标识 true 强制重新连接
* @return Driver
*/
public static 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, '\\') ? $type : '\\think\\cache\\driver\\' . ucwords($type);
// 记录初始化信息
App::$debug && Log::record('[ CACHE ] INIT ' . $type, 'info');
if (true === $name) {
return new $class($options);
} else {
self::$instance[$name] = new $class($options);
}
}
return self::$instance[$name];
}
/**
* 自动初始化缓存
* @access public
* @param array $options 配置数组
* @return Driver
*/
public static function init(array $options = [])
{
if (is_null(self::$handler)) {
// 自动初始化缓存
if (!empty($options)) {
$connect = self::connect($options);
} elseif ('complex' == Config::get('cache.type')) {
$connect = self::connect(Config::get('cache.default'));
} else {
$connect = self::connect(Config::get('cache'));
}
self::$handler = $connect;
}
return self::$handler;
}
/**
* 切换缓存类型 需要配置 cache.type complex
* @access public
* @param string $name 缓存标识
* @return Driver
*/
public static function store($name = '')
{
if ('' !== $name && 'complex' == Config::get('cache.type')) {
return self::connect(Config::get('cache.' . $name), strtolower($name));
}
return self::init();
}
/**
* 判断缓存是否存在
* @access public
* @param string $name 缓存变量名
* @return bool
*/
public static function has($name)
{
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);
}
}

View File

@ -0,0 +1,376 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: zhangyajun <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think;
use ArrayAccess;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use JsonSerializable;
class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
{
protected $items = [];
public function __construct($items = [])
{
$this->items = $this->convertToArray($items);
}
public static function make($items = [])
{
return new static($items);
}
/**
* 是否为空
* @return bool
*/
public function isEmpty()
{
return empty($this->items);
}
public function toArray()
{
return array_map(function ($value) {
return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value;
}, $this->items);
}
public function all()
{
return $this->items;
}
/**
* 合并数组
*
* @param mixed $items
* @return static
*/
public function merge($items)
{
return new static(array_merge($this->items, $this->convertToArray($items)));
}
/**
* 比较数组,返回差集
*
* @param mixed $items
* @return static
*/
public function diff($items)
{
return new static(array_diff($this->items, $this->convertToArray($items)));
}
/**
* 交换数组中的键和值
*
* @return static
*/
public function flip()
{
return new static(array_flip($this->items));
}
/**
* 比较数组,返回交集
*
* @param mixed $items
* @return static
*/
public function intersect($items)
{
return new static(array_intersect($this->items, $this->convertToArray($items)));
}
/**
* 返回数组中所有的键名
*
* @return static
*/
public function keys()
{
return new static(array_keys($this->items));
}
/**
* 删除数组的最后一个元素(出栈)
*
* @return mixed
*/
public function pop()
{
return array_pop($this->items);
}
/**
* 通过使用用户自定义函数,以字符串返回数组
*
* @param callable $callback
* @param mixed $initial
* @return mixed
*/
public function reduce(callable $callback, $initial = null)
{
return array_reduce($this->items, $callback, $initial);
}
/**
* 以相反的顺序返回数组。
*
* @return static
*/
public function reverse()
{
return new static(array_reverse($this->items));
}
/**
* 删除数组中首个元素,并返回被删除元素的值
*
* @return mixed
*/
public function shift()
{
return array_shift($this->items);
}
/**
* 把一个数组分割为新的数组块.
*
* @param int $size
* @param bool $preserveKeys
* @return static
*/
public function chunk($size, $preserveKeys = false)
{
$chunks = [];
foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) {
$chunks[] = new static($chunk);
}
return new static($chunks);
}
/**
* 在数组开头插入一个元素
* @param mixed $value
* @param null $key
* @return int
*/
public function unshift($value, $key = null)
{
if (is_null($key)) {
array_unshift($this->items, $value);
} else {
$this->items = [$key => $value] + $this->items;
}
}
/**
* 给每个元素执行个回调
*
* @param callable $callback
* @return $this
*/
public function each(callable $callback)
{
foreach ($this->items as $key => $item) {
$result = $callback($item, $key);
if (false === $result) {
break;
} elseif (!is_object($item)) {
$this->items[$key] = $result;
}
}
return $this;
}
/**
* 用回调函数过滤数组中的元素
* @param callable|null $callback
* @return static
*/
public function filter(callable $callback = null)
{
if ($callback) {
return new static(array_filter($this->items, $callback));
}
return new static(array_filter($this->items));
}
/**
* 返回数组中指定的一列
* @param $column_key
* @param null $index_key
* @return array
*/
public function column($column_key, $index_key = null)
{
if (function_exists('array_column')) {
return array_column($this->items, $column_key, $index_key);
}
$result = [];
foreach ($this->items as $row) {
$key = $value = null;
$keySet = $valueSet = false;
if (null !== $index_key && array_key_exists($index_key, $row)) {
$keySet = true;
$key = (string) $row[$index_key];
}
if (null === $column_key) {
$valueSet = true;
$value = $row;
} elseif (is_array($row) && array_key_exists($column_key, $row)) {
$valueSet = true;
$value = $row[$column_key];
}
if ($valueSet) {
if ($keySet) {
$result[$key] = $value;
} else {
$result[] = $value;
}
}
}
return $result;
}
/**
* 对数组排序
*
* @param callable|null $callback
* @return static
*/
public function sort(callable $callback = null)
{
$items = $this->items;
$callback ? uasort($items, $callback) : uasort($items, function ($a, $b) {
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
});
return new static($items);
}
/**
* 将数组打乱
*
* @return static
*/
public function shuffle()
{
$items = $this->items;
shuffle($items);
return new static($items);
}
/**
* 截取数组
*
* @param int $offset
* @param int $length
* @param bool $preserveKeys
* @return static
*/
public function slice($offset, $length = null, $preserveKeys = false)
{
return new static(array_slice($this->items, $offset, $length, $preserveKeys));
}
// ArrayAccess
public function offsetExists($offset)
{
return array_key_exists($offset, $this->items);
}
public function offsetGet($offset)
{
return $this->items[$offset];
}
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->items[] = $value;
} else {
$this->items[$offset] = $value;
}
}
public function offsetUnset($offset)
{
unset($this->items[$offset]);
}
//Countable
public function count()
{
return count($this->items);
}
//IteratorAggregate
public function getIterator()
{
return new ArrayIterator($this->items);
}
//JsonSerializable
public function jsonSerialize()
{
return $this->toArray();
}
/**
* 转换当前数据集为JSON字符串
* @access public
* @param integer $options json参数
* @return string
*/
public function toJson($options = JSON_UNESCAPED_UNICODE)
{
return json_encode($this->toArray(), $options);
}
public function __toString()
{
return $this->toJson();
}
/**
* 转换成数组
*
* @param mixed $items
* @return array
*/
protected function convertToArray($items)
{
if ($items instanceof self) {
return $items->all();
}
return (array) $items;
}
}

View File

@ -0,0 +1,178 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
class Config
{
// 配置参数
private static $config = [];
// 参数作用域
private static $range = '_sys_';
// 设定配置参数的作用域
public static function range($range)
{
self::$range = $range;
if (!isset(self::$config[$range])) {
self::$config[$range] = [];
}
}
/**
* 解析配置文件或内容
* @param string $config 配置文件路径或内容
* @param string $type 配置解析类型
* @param string $name 配置名(如设置即表示二级配置)
* @param string $range 作用域
* @return mixed
*/
public static function parse($config, $type = '', $name = '', $range = '')
{
$range = $range ?: self::$range;
if (empty($type)) {
$type = pathinfo($config, PATHINFO_EXTENSION);
}
$class = false !== strpos($type, '\\') ? $type : '\\think\\config\\driver\\' . ucwords($type);
return self::set((new $class())->parse($config), $name, $range);
}
/**
* 加载配置文件PHP格式
* @param string $file 配置文件名
* @param string $name 配置名(如设置即表示二级配置)
* @param string $range 作用域
* @return mixed
*/
public static function load($file, $name = '', $range = '')
{
$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);
} elseif ('yaml' == $type && function_exists('yaml_parse_file')) {
return self::set(yaml_parse_file($file), $name, $range);
} else {
return self::parse($file, $type, $name, $range);
}
} else {
return self::$config[$range];
}
}
/**
* 检测配置是否存在
* @param string $name 配置参数名(支持二级配置 .号分割)
* @param string $range 作用域
* @return bool
*/
public static function has($name, $range = '')
{
$range = $range ?: self::$range;
if (!strpos($name, '.')) {
return isset(self::$config[$range][strtolower($name)]);
} else {
// 二维数组设置和获取支持
$name = explode('.', $name, 2);
return isset(self::$config[$range][strtolower($name[0])][$name[1]]);
}
}
/**
* 获取配置参数 为空则获取所有配置
* @param string $name 配置参数名(支持二级配置 .号分割)
* @param string $range 作用域
* @return mixed
*/
public static function get($name = null, $range = '')
{
$range = $range ?: self::$range;
// 无参数时获取所有
if (empty($name) && isset(self::$config[$range])) {
return self::$config[$range];
}
if (!strpos($name, '.')) {
$name = strtolower($name);
return isset(self::$config[$range][$name]) ? self::$config[$range][$name] : null;
} else {
// 二维数组设置和获取支持
$name = explode('.', $name, 2);
$name[0] = strtolower($name[0]);
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]);
}
return isset(self::$config[$range][$name[0]][$name[1]]) ? self::$config[$range][$name[0]][$name[1]] : null;
}
}
/**
* 设置配置参数 name为数组则为批量设置
* @param string|array $name 配置参数名(支持二级配置 .号分割)
* @param mixed $value 配置值
* @param string $range 作用域
* @return mixed
*/
public static function set($name, $value = null, $range = '')
{
$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;
} else {
// 二维数组设置和获取支持
$name = explode('.', $name, 2);
self::$config[$range][strtolower($name[0])][$name[1]] = $value;
}
return;
} elseif (is_array($name)) {
// 批量设置
if (!empty($value)) {
self::$config[$range][$value] = isset(self::$config[$range][$value]) ?
array_merge(self::$config[$range][$value], $name) : $name;
return self::$config[$range][$value];
} else {
return self::$config[$range] = array_merge(self::$config[$range], array_change_key_case($name));
}
} else {
// 为空直接返回 已有配置
return self::$config[$range];
}
}
/**
* 重置配置参数
*/
public static function reset($range = '')
{
$range = $range ?: self::$range;
if (true === $range) {
self::$config = [];
} else {
self::$config[$range] = [];
}
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,156 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
use think\db\Connection;
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 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 插入一条记录
* @method integer insertGetId(array $data, boolean $replace = false, string $sequence = null) static 插入一条记录并返回自增ID
* @method integer insertAll(array $dataSet) static 插入多条记录
* @method integer update(array $data) static 更新记录
* @method integer delete(mixed $data = null) static 删除记录
* @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据
* @method 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 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
*/
class Db
{
// 数据库连接实例
private static $instance = [];
// 查询次数
public static $queryTimes = 0;
// 执行次数
public static $executeTimes = 0;
/**
* 数据库初始化 并取得数据库类实例
* @static
* @access public
* @param mixed $config 连接配置
* @param bool|string $name 连接标识 true 强制重新连接
* @return Connection
* @throws Exception
*/
public static function connect($config = [], $name = false)
{
if (false === $name) {
$name = md5(serialize($config));
}
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];
}
public static function clear() {
self::$instance = null;
}
/**
* 数据库连接参数解析
* @static
* @access private
* @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)) {
return self::parseDsn($config);
} else {
return $config;
}
}
/**
* DSN解析
* 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1&param2=val2#utf8
* @static
* @access private
* @param string $dsnStr
* @return array
*/
private static function parseDsn($dsnStr)
{
$info = parse_url($dsnStr);
if (!$info) {
return [];
}
$dsn = [
'type' => $info['scheme'],
'username' => isset($info['user']) ? $info['user'] : '',
'password' => isset($info['pass']) ? $info['pass'] : '',
'hostname' => isset($info['host']) ? $info['host'] : '',
'hostport' => isset($info['port']) ? $info['port'] : '',
'database' => !empty($info['path']) ? ltrim($info['path'], '/') : '',
'charset' => isset($info['fragment']) ? $info['fragment'] : 'utf8',
];
if (isset($info['query'])) {
parse_str($info['query'], $dsn['params']);
} else {
$dsn['params'] = [];
}
return $dsn;
}
// 调用驱动类的方法
public static function __callStatic($method, $params)
{
// 自动初始化数据库
return call_user_func_array([self::connect(), $method], $params);
}
}

View File

@ -0,0 +1,212 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
use think\exception\ClassNotFoundException;
use think\response\Redirect;
class Debug
{
// 区间时间信息
protected static $info = [];
// 区间内存信息
protected static $mem = [];
/**
* 记录时间(微秒)和内存使用情况
* @param string $name 标记位置
* @param mixed $value 标记值 留空则取当前 time 表示仅记录时间 否则同时记录时间和内存
* @return mixed
*/
public static function remark($name, $value = '')
{
// 记录时间和内存使用
self::$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();
}
}
/**
* 统计某个区间的时间(微秒)使用情况 返回值以秒为单位
* @param string $start 开始标签
* @param string $end 结束标签
* @param integer|string $dec 小数位
* @return integer
*/
public static function getRangeTime($start, $end, $dec = 6)
{
if (!isset(self::$info[$end])) {
self::$info[$end] = microtime(true);
}
return number_format((self::$info[$end] - self::$info[$start]), $dec);
}
/**
* 统计从开始到统计时的时间(微秒)使用情况 返回值以秒为单位
* @param integer|string $dec 小数位
* @return integer
*/
public static function getUseTime($dec = 6)
{
return number_format((microtime(true) - THINK_START_TIME), $dec);
}
/**
* 获取当前访问的吞吐率情况
* @return string
*/
public static function getThroughputRate()
{
return number_format(1 / self::getUseTime(), 2) . 'req/s';
}
/**
* 记录区间的内存使用情况
* @param string $start 开始标签
* @param string $end 结束标签
* @param integer|string $dec 小数位
* @return string
*/
public static function getRangeMem($start, $end, $dec = 2)
{
if (!isset(self::$mem['mem'][$end])) {
self::$mem['mem'][$end] = memory_get_usage();
}
$size = self::$mem['mem'][$end] - self::$mem['mem'][$start];
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
$pos = 0;
while ($size >= 1024) {
$size /= 1024;
$pos++;
}
return round($size, $dec) . " " . $a[$pos];
}
/**
* 统计从开始到统计时的内存使用情况
* @param integer|string $dec 小数位
* @return string
*/
public static function getUseMem($dec = 2)
{
$size = memory_get_usage() - THINK_START_MEM;
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
$pos = 0;
while ($size >= 1024) {
$size /= 1024;
$pos++;
}
return round($size, $dec) . " " . $a[$pos];
}
/**
* 统计区间的内存峰值情况
* @param string $start 开始标签
* @param string $end 结束标签
* @param integer|string $dec 小数位
* @return mixed
*/
public static function getMemPeak($start, $end, $dec = 2)
{
if (!isset(self::$mem['peak'][$end])) {
self::$mem['peak'][$end] = memory_get_peak_usage();
}
$size = self::$mem['peak'][$end] - self::$mem['peak'][$start];
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
$pos = 0;
while ($size >= 1024) {
$size /= 1024;
$pos++;
}
return round($size, $dec) . " " . $a[$pos];
}
/**
* 获取文件加载信息
* @param bool $detail 是否显示详细
* @return integer|array
*/
public static function getFile($detail = false)
{
if ($detail) {
$files = get_included_files();
$info = [];
foreach ($files as $key => $file) {
$info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )';
}
return $info;
}
return count(get_included_files());
}
/**
* 浏览器友好的变量输出
* @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)
{
$label = (null === $label) ? '' : rtrim($label) . ':';
ob_start();
var_dump($var);
$output = ob_get_clean();
$output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output);
if (IS_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;
} else {
return $output;
}
}
public static function inject(Response $response, &$content)
{
$config = Config::get('trace');
$type = isset($config['type']) ? $config['type'] : 'Html';
$request = Request::instance();
$class = false !== strpos($type, '\\') ? $type : '\\think\\debug\\' . ucwords($type);
unset($config['type']);
if (class_exists($class)) {
$trace = new $class($config);
} else {
throw new ClassNotFoundException('class not exists:' . $class, $class);
}
if ($response instanceof Redirect) {
//TODO 记录
} else {
$output = $trace->output($response, Log::getLog());
if (is_string($output)) {
// trace调试信息注入
$pos = strripos($content, '</body>');
if (false !== $pos) {
$content = substr($content, 0, $pos) . $output . substr($content, $pos);
} else {
$content = $content . $output;
}
}
}
}
}

View File

@ -0,0 +1,36 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
class Env
{
/**
* 获取环境变量值
* @param string $name 环境变量名(支持二级 .号分割)
* @param string $default 默认值
* @return mixed
*/
public static function get($name, $default = null)
{
$result = getenv(ENV_PREFIX . strtoupper(str_replace('.', '_', $name)));
if (false !== $result) {
if ('false' === $result) {
$result = false;
} elseif ('true' === $result) {
$result = true;
}
return $result;
} else {
return $default;
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,415 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
use SplFileObject;
class File extends SplFileObject
{
/**
* 错误信息
* @var string
*/
private $error = '';
// 当前完整文件名
protected $filename;
// 上传文件名
protected $saveName;
// 文件上传命名规则
protected $rule = 'date';
// 文件上传验证规则
protected $validate = [];
// 单元测试
protected $isTest;
// 上传文件信息
protected $info;
// 文件hash信息
protected $hash = [];
public function __construct($filename, $mode = 'r')
{
parent::__construct($filename, $mode);
$this->filename = $this->getRealPath() ?: $this->getPathname();
}
/**
* 是否测试
* @param bool $test 是否测试
* @return $this
*/
public function isTest($test = false)
{
$this->isTest = $test;
return $this;
}
/**
* 设置上传信息
* @param array $info 上传文件信息
* @return $this
*/
public function setUploadInfo($info)
{
$this->info = $info;
return $this;
}
/**
* 获取上传文件的信息
* @param string $name
* @return array|string
*/
public function getInfo($name = '')
{
return isset($this->info[$name]) ? $this->info[$name] : $this->info;
}
/**
* 获取上传文件的文件名
* @return string
*/
public function getSaveName()
{
return $this->saveName;
}
/**
* 设置上传文件的保存文件名
* @param string $saveName
* @return $this
*/
public function setSaveName($saveName)
{
$this->saveName = $saveName;
return $this;
}
/**
* 获取文件的哈希散列值
* @param string $type
* @return mixed $string
*/
public function hash($type = 'sha1')
{
if (!isset($this->hash[$type])) {
$this->hash[$type] = hash_file($type, $this->filename);
}
return $this->hash[$type];
}
/**
* 检查目录是否可写
* @param string $path 目录
* @return boolean
*/
protected function checkPath($path)
{
if (is_dir($path)) {
return true;
}
if (mkdir($path, 0755, true)) {
return true;
} else {
$this->error = "目录 {$path} 创建失败!";
return false;
}
}
/**
* 获取文件类型信息
* @return string
*/
public function getMime()
{
$finfo = finfo_open(FILEINFO_MIME_TYPE);
return finfo_file($finfo, $this->filename);
}
/**
* 设置文件的命名规则
* @param string $rule 文件命名规则
* @return $this
*/
public function rule($rule)
{
$this->rule = $rule;
return $this;
}
/**
* 设置上传文件的验证规则
* @param array $rule 验证规则
* @return $this
*/
public function validate($rule = [])
{
$this->validate = $rule;
return $this;
}
/**
* 检测是否合法的上传文件
* @return bool
*/
public function isValid()
{
if ($this->isTest) {
return is_file($this->filename);
}
return is_uploaded_file($this->filename);
}
/**
* 检测上传文件
* @param array $rule 验证规则
* @return bool
*/
public function check($rule = [])
{
$rule = $rule ?: $this->validate;
/* 检查文件大小 */
if (isset($rule['size']) && !$this->checkSize($rule['size'])) {
$this->error = '上传文件大小不符!';
return false;
}
/* 检查文件Mime类型 */
if (isset($rule['type']) && !$this->checkMime($rule['type'])) {
$this->error = '上传文件MIME类型不允许';
return false;
}
/* 检查文件后缀 */
if (isset($rule['ext']) && !$this->checkExt($rule['ext'])) {
$this->error = '上传文件后缀不允许';
return false;
}
/* 检查图像文件 */
if (!$this->checkImg()) {
$this->error = '非法图像文件!';
return false;
}
return true;
}
/**
* 检测上传文件后缀
* @param array|string $ext 允许后缀
* @return bool
*/
public function checkExt($ext)
{
if (is_string($ext)) {
$ext = explode(',', $ext);
}
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
if (!in_array($extension, $ext)) {
return false;
}
return true;
}
/**
* 检测图像文件
* @return bool
*/
public function checkImg()
{
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
/* 对图像文件进行严格检测 */
if (in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) && !in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6, 13])) {
return false;
}
return true;
}
// 判断图像类型
protected function getImageType($image)
{
if (function_exists('exif_imagetype')) {
return exif_imagetype($image);
} else {
try {
$info = getimagesize($image);
return $info ? $info[2] : false;
} catch (\Exception $e) {
return false;
}
}
}
/**
* 检测上传文件大小
* @param integer $size 最大大小
* @return bool
*/
public function checkSize($size)
{
if ($this->getSize() > $size) {
return false;
}
return true;
}
/**
* 检测上传文件类型
* @param array|string $mime 允许类型
* @return bool
*/
public function checkMime($mime)
{
if (is_string($mime)) {
$mime = explode(',', $mime);
}
if (!in_array(strtolower($this->getMime()), $mime)) {
return false;
}
return true;
}
/**
* 移动文件
* @param string $path 保存路径
* @param string|bool $savename 保存的文件名 默认自动生成
* @param boolean $replace 同名文件是否覆盖
* @return false|File false-失败 否则返回File实例
*/
public function move($path, $savename = true, $replace = true)
{
// 文件上传失败,捕获错误代码
if (!empty($this->info['error'])) {
$this->error($this->info['error']);
return false;
}
// 检测合法性
if (!$this->isValid()) {
$this->error = '非法上传文件';
return false;
}
// 验证上传
if (!$this->check()) {
return false;
}
$path = rtrim($path, DS) . DS;
// 文件保存命名规则
$saveName = $this->buildSaveName($savename);
$filename = $path . $saveName;
// 检测目录
if (false === $this->checkPath(dirname($filename))) {
return false;
}
/* 不覆盖同名文件 */
if (!$replace && is_file($filename)) {
$this->error = '存在同名文件' . $filename;
return false;
}
/* 移动文件 */
if ($this->isTest) {
rename($this->filename, $filename);
} elseif (!move_uploaded_file($this->filename, $filename)) {
$this->error = '文件上传保存错误!';
return false;
}
// 返回 File对象实例
$file = new self($filename);
$file->setSaveName($saveName);
$file->setUploadInfo($this->info);
return $file;
}
/**
* 获取保存文件名
* @param string|bool $savename 保存的文件名 默认自动生成
* @return string
*/
protected function buildSaveName($savename)
{
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));
}
}
}
} elseif ('' === $savename) {
$savename = $this->getInfo('name');
}
if (!strpos($savename, '.')) {
$savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION);
}
return $savename;
}
/**
* 获取错误代码信息
* @param int $errorNo 错误号
*/
private function error($errorNo)
{
switch ($errorNo) {
case 1:
case 2:
$this->error = '上传文件大小超过了最大值!';
break;
case 3:
$this->error = '文件只有部分被上传!';
break;
case 4:
$this->error = '没有文件被上传!';
break;
case 6:
$this->error = '找不到临时文件夹!';
break;
case 7:
$this->error = '文件写入失败!';
break;
default:
$this->error = '未知上传错误!';
}
}
/**
* 获取错误信息
* @return mixed
*/
public function getError()
{
return $this->error;
}
public function __call($method, $args)
{
return $this->hash($method);
}
}

View File

@ -0,0 +1,136 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
class Hook
{
private static $tags = [];
/**
* 动态添加行为扩展到某个标签
* @param string $tag 标签名称
* @param mixed $behavior 行为名称
* @param bool $first 是否放到开头执行
* @return void
*/
public static function add($tag, $behavior, $first = false)
{
isset(self::$tags[$tag]) || self::$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);
} else {
unset($behavior['_overlay']);
self::$tags[$tag] = $behavior;
}
} elseif ($first) {
array_unshift(self::$tags[$tag], $behavior);
} else {
self::$tags[$tag][] = $behavior;
}
}
/**
* 批量导入插件
* @param array $tags 插件信息
* @param boolean $recursive 是否递归合并
*/
public static function import(array $tags, $recursive = true)
{
if ($recursive) {
foreach ($tags as $tag => $behavior) {
self::add($tag, $behavior);
}
} else {
self::$tags = $tags + self::$tags;
}
}
/**
* 获取插件信息
* @param string $tag 插件位置 留空获取全部
* @return array
*/
public static function get($tag = '')
{
if (empty($tag)) {
//获取全部的插件信息
return self::$tags;
} else {
return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : [];
}
}
/**
* 监听标签的行为
* @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)
{
$results = [];
$tags = static::get($tag);
foreach ($tags as $key => $name) {
$results[$key] = self::exec($name, $tag, $params, $extra);
if (false === $results[$key]) {
// 如果返回false 则中断行为执行
break;
} elseif (!is_null($results[$key]) && $once) {
break;
}
}
return $once ? end($results) : $results;
}
/**
* 执行某个行为
* @param mixed $class 要执行的行为
* @param string $tag 方法名(标签名)
* @param Mixed $params 传人的参数
* @param mixed $extra 额外参数
* @return mixed
*/
public static function exec($class, $tag = '', &$params = null, $extra = null)
{
App::$debug && Debug::remark('behavior_start', 'time');
$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]);
} else {
$obj = new $class();
$method = ($tag && is_callable([$obj, $method])) ? $method : 'run';
$result = $obj->$method($params, $extra);
}
if (App::$debug) {
Debug::remark('behavior_end', 'time');
Log::record('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info');
}
return $result;
}
}

View File

@ -0,0 +1,223 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
class Lang
{
// 语言数据
private static $lang = [];
// 语言作用域
private static $range = 'zh-cn';
// 语言自动侦测的变量
protected static $langDetectVar = 'lang';
// 语言Cookie变量
protected static $langCookieVar = 'think_var';
// 语言Cookie的过期时间
protected static $langCookieExpire = 3600;
// 允许语言列表
protected static $allowLangList = [];
// Accept-Language转义为对应语言包名称 系统默认配置
protected static $acceptLanguage = [
'zh-hans-cn' => 'zh-cn',
];
// 设定当前的语言
public static function range($range = '')
{
if ('' == $range) {
return self::$range;
} else {
self::$range = $range;
}
return self::$range;
}
/**
* 设置语言定义(不区分大小写)
* @param string|array $name 语言变量
* @param string $value 语言值
* @param string $range 语言作用域
* @return mixed
*/
public static function set($name, $value = null, $range = '')
{
$range = $range ?: self::$range;
// 批量定义
if (!isset(self::$lang[$range])) {
self::$lang[$range] = [];
}
if (is_array($name)) {
return self::$lang[$range] = array_change_key_case($name) + self::$lang[$range];
} else {
return self::$lang[$range][strtolower($name)] = $value;
}
}
/**
* 加载语言定义(不区分大小写)
* @param array|string $file 语言文件
* @param string $range 语言作用域
* @return mixed
*/
public static function load($file, $range = '')
{
$range = $range ?: self::$range;
if (!isset(self::$lang[$range])) {
self::$lang[$range] = [];
}
// 批量定义
if (is_string($file)) {
$file = [$file];
}
$lang = [];
foreach ($file as $_file) {
if (is_file($_file)) {
// 记录加载信息
App::$debug && Log::record('[ LANG ] ' . $_file, 'info');
$_lang = include $_file;
if (is_array($_lang)) {
$lang = array_change_key_case($_lang) + $lang;
}
}
}
if (!empty($lang)) {
self::$lang[$range] = $lang + self::$lang[$range];
}
return self::$lang[$range];
}
/**
* 获取语言定义(不区分大小写)
* @param string|null $name 语言变量
* @param string $range 语言作用域
* @return mixed
*/
public static function has($name, $range = '')
{
$range = $range ?: self::$range;
return isset(self::$lang[$range][strtolower($name)]);
}
/**
* 获取语言定义(不区分大小写)
* @param string|null $name 语言变量
* @param array $vars 变量替换
* @param string $range 语言作用域
* @return mixed
*/
public static function get($name = null, $vars = [], $range = '')
{
$range = $range ?: self::$range;
// 空参数返回所有定义
if (empty($name)) {
return self::$lang[$range];
}
$key = strtolower($name);
$value = isset(self::$lang[$range][$key]) ? self::$lang[$range][$key] : $name;
// 变量解析
if (!empty($vars) && is_array($vars)) {
/**
* Notes:
* 为了检测的方便数字索引的判断仅仅是参数数组的第一个元素的key为数字0
* 数字索引采用的是系统的 sprintf 函数替换,用法请参考 sprintf 函数
*/
if (key($vars) === 0) {
// 数字索引解析
array_unshift($vars, $value);
$value = call_user_func_array('sprintf', $vars);
} else {
// 关联索引解析
$replace = array_keys($vars);
foreach ($replace as &$v) {
$v = "{:{$v}}";
}
$value = str_replace($replace, $vars, $value);
}
}
return $value;
}
/**
* 自动侦测设置获取语言选择
* @return string
*/
public static 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]);
} 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];
}
}
if (empty(self::$allowLangList) || in_array($langSet, self::$allowLangList)) {
// 合法的语言
self::$range = $langSet ?: self::$range;
}
return self::$range;
}
/**
* 设置语言自动侦测的变量
* @param string $var 变量名称
* @return void
*/
public static function setLangDetectVar($var)
{
self::$langDetectVar = $var;
}
/**
* 设置语言的cookie保存变量
* @param string $var 变量名称
* @return void
*/
public static function setLangCookieVar($var)
{
self::$langCookieVar = $var;
}
/**
* 设置语言的cookie的过期时间
* @param string $expire 过期时间
* @return void
*/
public static function setLangCookieExpire($expire)
{
self::$langCookieExpire = $expire;
}
/**
* 设置允许的语言列表
* @param array $list 语言列表
* @return void
*/
public static function setAllowLangList($list)
{
self::$allowLangList = $list;
}
}

View File

@ -0,0 +1,570 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
use think\exception\ClassNotFoundException;
class Loader
{
protected static $instance = [];
// 类名映射
protected static $map = [];
// 命名空间别名
protected static $namespaceAlias = [];
// PSR-4
private static $prefixLengthsPsr4 = [];
private static $prefixDirsPsr4 = [];
private static $fallbackDirsPsr4 = [];
// PSR-0
private static $prefixesPsr0 = [];
private static $fallbackDirsPsr0 = [];
// 自动加载的文件
private static $autoloadFiles = [];
// 自动加载
public static function autoload($class)
{
// 检测命名空间别名
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)) {
return false;
}
__include_file($file);
return true;
}
}
/**
* 查找文件
* @param $class
* @return bool
*/
private static function findFile($class)
{
if (!empty(self::$map[$class])) {
// 类库映射
return self::$map[$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, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DS);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DS) . EXT;
}
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;
}
}
}
}
}
// 查找 PSR-0 fallback dirs
foreach (self::$fallbackDirsPsr0 as $dir) {
if (is_file($file = $dir . DS . $logicalPathPsr0)) {
return $file;
}
}
return self::$map[$class] = false;
}
// 注册classmap
public static function addClassMap($class, $map = '')
{
if (is_array($class)) {
self::$map = array_merge(self::$map, $class);
} else {
self::$map[$class] = $map;
}
}
// 注册命名空间
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);
}
}
// 添加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 addNamespaceAlias($namespace, $original = '')
{
if (is_array($namespace)) {
self::$namespaceAlias = array_merge(self::$namespaceAlias, $namespace);
} else {
self::$namespaceAlias[$namespace] = $original;
}
}
// 注册自动加载机制
public static function register($autoload = '')
{
// 注册系统自动加载
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
// 注册命名空间定义
self::addNamespace([
'think' => LIB_PATH . 'think' . DS,
'behavior' => LIB_PATH . 'behavior' . DS,
'traits' => LIB_PATH . 'traits' . DS,
]);
// 加载类库映射文件
if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {
self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));
}
// Composer自动加载支持
if (is_dir(VENDOR_PATH . 'composer')) {
self::registerComposerLoader();
}
// 自动加载extend目录
self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);
}
// 注册composer自动加载
private static function registerComposerLoader()
{
if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) {
$map = require VENDOR_PATH . 'composer/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';
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 ($classMap) {
self::addClassMap($classMap);
}
}
if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) {
$includeFiles = require VENDOR_PATH . 'composer/autoload_files.php';
foreach ($includeFiles as $fileIdentifier => $file) {
if (empty(self::$autoloadFiles[$fileIdentifier])) {
__require_file($file);
self::$autoloadFiles[$fileIdentifier] = true;
}
}
}
}
/**
* 导入所需的类库 同java的Import 本函数有缓存功能
* @param string $class 类库命名空间字符串
* @param string $baseUrl 起始路径
* @param string $ext 导入的文件扩展名
* @return boolean
*/
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) {
$filename = $path . DS . $class . $ext;
if (is_file($filename)) {
break;
}
}
} else {
$filename = $baseUrl . $class . $ext;
}
if (!empty($filename) && is_file($filename)) {
// 开启调试模式Win环境严格区分大小写
if (IS_WIN && pathinfo($filename, PATHINFO_FILENAME) != pathinfo(realpath($filename), PATHINFO_FILENAME)) {
return false;
}
__include_file($filename);
$_file[$key] = true;
return true;
}
return false;
}
/**
* 实例化(分层)模型
* @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')
{
$guid = $name . $layer;
if (isset(self::$instance[$guid])) {
return self::$instance[$guid];
}
if (false !== strpos($name, '\\')) {
$class = $name;
$module = Request::instance()->module();
} else {
if (strpos($name, '/')) {
list($module, $name) = explode('/', $name, 2);
} else {
$module = Request::instance()->module();
}
$class = self::parseClass($module, $layer, $name, $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);
}
}
self::$instance[$guid] = $model;
return $model;
}
/**
* 实例化(分层)控制器 格式:[模块名/]控制器名
* @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 = '')
{
if (false !== strpos($name, '\\')) {
$class = $name;
$module = Request::instance()->module();
} else {
if (strpos($name, '/')) {
list($module, $name) = explode('/', $name);
} else {
$module = Request::instance()->module();
}
$class = self::parseClass($module, $layer, $name, $appendSuffix);
}
if (class_exists($class)) {
return App::invokeClass($class);
} elseif ($empty && class_exists($emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix))) {
return new $emptyClass(Request::instance());
} else {
throw new ClassNotFoundException('class not exists:' . $class, $class);
}
}
/**
* 实例化验证类 格式:[模块名/]验证器名
* @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;
}
$guid = $name . $layer;
if (isset(self::$instance[$guid])) {
return self::$instance[$guid];
}
if (false !== strpos($name, '\\')) {
$class = $name;
$module = Request::instance()->module();
} else {
if (strpos($name, '/')) {
list($module, $name) = explode('/', $name);
} else {
$module = Request::instance()->module();
}
$class = self::parseClass($module, $layer, $name, $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);
}
}
self::$instance[$guid] = $validate;
return $validate;
}
/**
* 数据库初始化 并取得数据库类实例
* @param mixed $config 数据库配置
* @param bool|string $name 连接标识 true 强制重新连接
* @return \think\db\Connection
*/
public static function db($config = [], $name = false)
{
return Db::connect($config, $name);
}
/**
* 远程调用模块的操作方法 参数格式 [模块/控制器/]操作
* @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);
}
}
/**
* 字符串命名风格转换
* type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
* @param string $name 字符串
* @param integer $type 转换类型
* @param bool $ucfirst 首字母是否大写(驼峰规则)
* @return string
*/
public static function parseName($name, $type = 0, $ucfirst = true)
{
if ($type) {
$name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
return strtoupper($match[1]);
}, $name);
return $ucfirst ? ucfirst($name) : lcfirst($name);
} else {
return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
}
}
/**
* 解析应用类的类名
* @param string $module 模块名
* @param string $layer 层名 controller model ...
* @param string $name 类名
* @param bool $appendSuffix
* @return string
*/
public static function parseClass($module, $layer, $name, $appendSuffix = false)
{
$name = str_replace(['/', '.'], '\\', $name);
$array = explode('\\', $name);
$class = self::parseName(array_pop($array), 1) . (App::$suffix || $appendSuffix ? ucfirst($layer) : '');
$path = $array ? implode('\\', $array) . '\\' : '';
return App::$namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $path . $class;
}
/**
* 初始化类的实例
* @return void
*/
public static function clearInstance()
{
self::$instance = [];
}
}
/**
* 作用范围隔离
*
* @param $file
* @return mixed
*/
function __include_file($file)
{
return include $file;
}
function __require_file($file)
{
return require $file;
}

View File

@ -0,0 +1,213 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
use think\exception\ClassNotFoundException;
/**
* Class Log
* @package think
*
* @method void log($msg) static
* @method void error($msg) static
* @method void info($msg) static
* @method void sql($msg) static
* @method void notice($msg) static
* @method void alert($msg) static
*/
class Log
{
const LOG = 'log';
const ERROR = 'error';
const INFO = 'info';
const SQL = 'sql';
const NOTICE = 'notice';
const ALERT = 'alert';
const DEBUG = 'debug';
// 日志信息
protected static $log = [];
// 配置参数
protected static $config = [];
// 日志类型
protected static $type = ['log', 'error', 'info', 'sql', 'notice', 'alert', 'debug'];
// 日志写入驱动
protected static $driver;
// 当前日志授权key
protected static $key;
/**
* 日志初始化
* @param array $config
*/
public static function init($config = [])
{
$type = isset($config['type']) ? $config['type'] : 'File';
$class = false !== strpos($type, '\\') ? $type : '\\think\\log\\driver\\' . ucwords($type);
self::$config = $config;
unset($config['type']);
if (class_exists($class)) {
self::$driver = new $class($config);
} else {
throw new ClassNotFoundException('class not exists:' . $class, $class);
}
// 记录初始化信息
App::$debug && Log::record('[ LOG ] INIT ' . $type, 'info');
}
/**
* 获取日志信息
* @param string $type 信息类型
* @return array
*/
public static function getLog($type = '')
{
return $type ? self::$log[$type] : self::$log;
}
/**
* 记录调试信息
* @param mixed $msg 调试信息
* @param string $type 信息类型
* @return void
*/
public static function record($msg, $type = 'log')
{
self::$log[$type][] = $msg;
if (IS_CLI) {
// 命令行下面日志写入改进
self::save();
}
}
/**
* 清空日志信息
* @return void
*/
public static function clear()
{
self::$log = [];
}
/**
* 当前日志记录的授权key
* @param string $key 授权key
* @return void
*/
public static function key($key)
{
self::$key = $key;
}
/**
* 检查日志写入权限
* @param array $config 当前日志配置参数
* @return bool
*/
public static function check($config)
{
if (self::$key && !empty($config['allow_key']) && !in_array(self::$key, $config['allow_key'])) {
return false;
}
return true;
}
/**
* 保存调试信息
* @return bool
*/
public static function save()
{
if (!empty(self::$log)) {
if (is_null(self::$driver)) {
self::init(Config::get('log'));
}
if (!self::check(self::$config)) {
// 检测日志写入权限
return false;
}
if (empty(self::$config['level'])) {
// 获取全部日志
$log = self::$log;
if (!App::$debug && isset($log['debug'])) {
unset($log['debug']);
}
} else {
// 记录允许级别
$log = [];
foreach (self::$config['level'] as $level) {
if (isset(self::$log[$level])) {
$log[$level] = self::$log[$level];
}
}
}
$result = self::$driver->save($log);
if ($result) {
self::$log = [];
}
Hook::listen('log_write_done', $log);
return $result;
}
return true;
}
/**
* 实时写入日志信息 并支持行为
* @param mixed $msg 调试信息
* @param string $type 信息类型
* @param bool $force 是否强制写入
* @return bool
*/
public static function write($msg, $type = 'log', $force = false)
{
$log = self::$log;
// 封装日志信息
if (true === $force || empty(self::$config['level'])) {
$log[$type][] = $msg;
} elseif (in_array($type, self::$config['level'])) {
$log[$type][] = $msg;
} else {
return false;
}
// 监听log_write
Hook::listen('log_write', $log);
if (is_null(self::$driver)) {
self::init(Config::get('log'));
}
// 写入日志
$result = self::$driver->save($log);
if ($result) {
self::$log = [];
}
return $result;
}
/**
* 静态调用
* @param $method
* @param $args
* @return mixed
*/
public static function __callStatic($method, $args)
{
if (in_array($method, self::$type)) {
array_push($args, $method);
return call_user_func_array('\\think\\Log::record', $args);
}
}
}

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,334 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
use think\response\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
{
// 原始数据
protected $data;
// 当前的contentType
protected $contentType = 'text/html';
// 字符集
protected $charset = 'utf-8';
//状态
protected $code = 200;
// 输出参数
protected $options = [];
// header参数
protected $header = [];
protected $content = null;
/**
* 构造函数
* @access public
* @param mixed $data 输出数据
* @param int $code
* @param array $header
* @param array $options 输出参数
*/
public function __construct($data = '', $code = 200, array $header = [], $options = [])
{
$this->data($data);
if (!empty($options)) {
$this->options = array_merge($this->options, $options);
}
$this->contentType($this->contentType, $this->charset);
$this->header = array_merge($this->header, $header);
$this->code = $code;
}
/**
* 创建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
*/
public static function create($data = '', $type = '', $code = 200, array $header = [], $options = [])
{
$type = empty($type) ? 'null' : strtolower($type);
$class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst($type);
if (class_exists($class)) {
$response = new $class($data, $code, $header, $options);
} else {
$response = new static($data, $code, $header, $options);
}
return $response;
}
/**
* 发送数据到客户端
* @access public
* @return mixed
* @throws \InvalidArgumentException
*/
public function send()
{
// 监听response_send
Hook::listen('response_send', $this);
// 处理输出数据
$data = $this->getContent();
// Trace调试注入
if (Env::get('app_trace', Config::get('app_trace'))) {
Debug::inject($this, $data);
}
if (200 == $this->code) {
$cache = Request::instance()->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]);
}
}
if (!headers_sent() && !empty($this->header)) {
// 发送状态码
http_response_code($this->code);
// 发送头部信息
foreach ($this->header as $name => $val) {
if (is_null($val)) {
header($name);
} else {
header($name . ':' . $val);
}
}
}
echo $data;
if (function_exists('fastcgi_finish_request')) {
// 提高页面响应
fastcgi_finish_request();
}
// 监听response_end
Hook::listen('response_end', $this);
// 清空当次请求有效的数据
if (!($this instanceof RedirectResponse)) {
Session::flush();
}
}
/**
* 处理数据
* @access protected
* @param mixed $data 要处理的数据
* @return mixed
*/
protected function output($data)
{
return $data;
}
/**
* 输出的参数
* @access public
* @param mixed $options 输出参数
* @return $this
*/
public function options($options = [])
{
$this->options = array_merge($this->options, $options);
return $this;
}
/**
* 输出数据设置
* @access public
* @param mixed $data 输出数据
* @return $this
*/
public function data($data)
{
$this->data = $data;
return $this;
}
/**
* 设置响应头
* @access public
* @param string|array $name 参数名
* @param string $value 参数值
* @return $this
*/
public function header($name, $value = null)
{
if (is_array($name)) {
$this->header = array_merge($this->header, $name);
} else {
$this->header[$name] = $value;
}
return $this;
}
/**
* 设置页面输出内容
* @param $content
* @return $this
*/
public function content($content)
{
if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([
$content,
'__toString',
])
) {
throw new \InvalidArgumentException(sprintf('variable type error %s', gettype($content)));
}
$this->content = (string) $content;
return $this;
}
/**
* 发送HTTP状态
* @param integer $code 状态码
* @return $this
*/
public function code($code)
{
$this->code = $code;
return $this;
}
/**
* LastModified
* @param string $time
* @return $this
*/
public function lastModified($time)
{
$this->header['Last-Modified'] = $time;
return $this;
}
/**
* Expires
* @param string $time
* @return $this
*/
public function expires($time)
{
$this->header['Expires'] = $time;
return $this;
}
/**
* ETag
* @param string $eTag
* @return $this
*/
public function eTag($eTag)
{
$this->header['ETag'] = $eTag;
return $this;
}
/**
* 页面缓存控制
* @param string $cache 状态码
* @return $this
*/
public function cacheControl($cache)
{
$this->header['Cache-control'] = $cache;
return $this;
}
/**
* 页面输出类型
* @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 头部名称
* @return mixed
*/
public function getHeader($name = '')
{
if (!empty($name)) {
return isset($this->header[$name]) ? $this->header[$name] : null;
} else {
return $this->header;
}
}
/**
* 获取原始数据
* @return mixed
*/
public function getData()
{
return $this->data;
}
/**
* 获取输出数据
* @return mixed
*/
public function getContent()
{
if (null == $this->content) {
$content = $this->output($this->data);
if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([
$content,
'__toString',
])
) {
throw new \InvalidArgumentException(sprintf('variable type error %s', gettype($content)));
}
$this->content = (string) $content;
}
return $this->content;
}
/**
* 获取状态码
* @return integer
*/
public function getCode()
{
return $this->code;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,366 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
use think\exception\ClassNotFoundException;
class Session
{
protected static $prefix = '';
protected static $init = null;
/**
* 设置或者获取session作用域前缀
* @param string $prefix
* @return string|void
*/
public static function prefix($prefix = '')
{
empty(self::$init) && self::boot();
if (empty($prefix) && null !== $prefix) {
return self::$prefix;
} else {
self::$prefix = $prefix;
}
}
/**
* session初始化
* @param array $config
* @return void
* @throws \think\Exception
*/
public static function init(array $config = [])
{
if (empty($config)) {
$config = Config::get('session');
}
// 记录初始化信息
App::$debug && Log::record('[ SESSION ] INIT ' . var_export($config, true), 'info');
$isDoStart = false;
if (isset($config['use_trans_sid'])) {
ini_set('session.use_trans_sid', $config['use_trans_sid'] ? 1 : 0);
}
// 启动session
if (!empty($config['auto_start']) && PHP_SESSION_ACTIVE != session_status()) {
ini_set('session.auto_start', 0);
$isDoStart = true;
}
if (isset($config['prefix']) && ('' === self::$prefix || null === self::$prefix)) {
self::$prefix = $config['prefix'];
}
if (isset($config['var_session_id']) && isset($_REQUEST[$config['var_session_id']])) {
session_id($_REQUEST[$config['var_session_id']]);
} elseif (isset($config['id']) && !empty($config['id'])) {
session_id($config['id']);
}
if (isset($config['name'])) {
session_name($config['name']);
}
if (isset($config['path'])) {
session_save_path($config['path']);
}
if (isset($config['domain'])) {
ini_set('session.cookie_domain', $config['domain']);
}
if (isset($config['expire'])) {
ini_set('session.gc_maxlifetime', $config['expire']);
ini_set('session.cookie_lifetime', $config['expire']);
}
if (isset($config['secure'])) {
ini_set('session.cookie_secure', $config['secure']);
}
if (isset($config['httponly'])) {
ini_set('session.cookie_httponly', $config['httponly']);
}
if (isset($config['use_cookies'])) {
ini_set('session.use_cookies', $config['use_cookies'] ? 1 : 0);
}
if (isset($config['cache_limiter'])) {
session_cache_limiter($config['cache_limiter']);
}
if (isset($config['cache_expire'])) {
session_cache_expire($config['cache_expire']);
}
if (!empty($config['type'])) {
// 读取session驱动
$class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\session\\driver\\' . ucwords($config['type']);
// 检查驱动类
if (!class_exists($class) || !session_set_save_handler(new $class($config))) {
throw new ClassNotFoundException('error session handler:' . $class, $class);
}
}
if ($isDoStart) {
session_start();
self::$init = true;
} else {
self::$init = false;
}
}
/**
* session自动启动或者初始化
* @return void
*/
public static function boot()
{
if (is_null(self::$init)) {
self::init();
} elseif (false === self::$init) {
if (PHP_SESSION_ACTIVE != session_status()) {
session_start();
}
self::$init = true;
}
}
/**
* session设置
* @param string $name session名称
* @param mixed $value session值
* @param string|null $prefix 作用域(前缀)
* @return void
*/
public static function set($name, $value = '', $prefix = null)
{
empty(self::$init) && self::boot();
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
if (strpos($name, '.')) {
// 二维数组赋值
list($name1, $name2) = explode('.', $name);
if ($prefix) {
$_SESSION[$prefix][$name1][$name2] = $value;
} else {
$_SESSION[$name1][$name2] = $value;
}
} elseif ($prefix) {
$_SESSION[$prefix][$name] = $value;
} else {
$_SESSION[$name] = $value;
}
}
/**
* session获取
* @param string $name session名称
* @param string|null $prefix 作用域(前缀)
* @return mixed
*/
public static 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;
}
}
return $value;
}
/**
* session获取并删除
* @param string $name session名称
* @param string|null $prefix 作用域(前缀)
* @return mixed
*/
public static function pull($name, $prefix = null)
{
$result = self::get($name, $prefix);
if ($result) {
self::delete($name, $prefix);
return $result;
} else {
return;
}
}
/**
* session设置 下一次请求有效
* @param string $name session名称
* @param mixed $value session值
* @param string|null $prefix 作用域(前缀)
* @return void
*/
public static function flash($name, $value)
{
self::set($name, $value);
if (!self::has('__flash__.__time__')) {
self::set('__flash__.__time__', $_SERVER['REQUEST_TIME_FLOAT']);
}
self::push('__flash__', $name);
}
/**
* 清空当前请求的session数据
* @return void
*/
public static function flush()
{
if (self::$init) {
$item = self::get('__flash__');
if (!empty($item)) {
$time = $item['__time__'];
if ($_SERVER['REQUEST_TIME_FLOAT'] > $time) {
unset($item['__time__']);
self::delete($item);
self::set('__flash__', []);
}
}
}
}
/**
* 删除session数据
* @param string|array $name session名称
* @param string|null $prefix 作用域(前缀)
* @return void
*/
public static function delete($name, $prefix = null)
{
empty(self::$init) && self::boot();
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
if (is_array($name)) {
foreach ($name as $key) {
self::delete($key, $prefix);
}
} elseif (strpos($name, '.')) {
list($name1, $name2) = explode('.', $name);
if ($prefix) {
unset($_SESSION[$prefix][$name1][$name2]);
} else {
unset($_SESSION[$name1][$name2]);
}
} else {
if ($prefix) {
unset($_SESSION[$prefix][$name]);
} else {
unset($_SESSION[$name]);
}
}
}
/**
* 清空session数据
* @param string|null $prefix 作用域(前缀)
* @return void
*/
public static function clear($prefix = null)
{
empty(self::$init) && self::boot();
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
if ($prefix) {
unset($_SESSION[$prefix]);
} else {
$_SESSION = [];
}
}
/**
* 判断session数据
* @param string $name session名称
* @param string|null $prefix
* @return bool
*/
public static 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]);
}
}
/**
* 添加数据到一个session数组
* @param string $key
* @param mixed $value
* @return void
*/
public static function push($key, $value)
{
$array = self::get($key);
if (is_null($array)) {
$array = [];
}
$array[] = $value;
self::set($key, $array);
}
/**
* 启动session
* @return void
*/
public static function start()
{
session_start();
self::$init = true;
}
/**
* 销毁session
* @return void
*/
public static function destroy()
{
if (!empty($_SESSION)) {
$_SESSION = [];
}
session_unset();
session_destroy();
self::$init = null;
}
/**
* 重新生成session_id
* @param bool $delete 是否删除关联会话文件
* @return void
*/
public static function regenerate($delete = false)
{
session_regenerate_id($delete);
}
/**
* 暂停session
* @return void
*/
public static function pause()
{
// 暂停session
session_write_close();
self::$init = false;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,328 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
class Url
{
// 生成URL地址的root
protected static $root;
protected static $bindCheck;
/**
* 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 是否显示域名 或者直接传入域名
* @return string
*/
public static 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);
}
} elseif (strpos($url, '@') && false === strpos($url, '\\')) {
// 解析域名
list($url, $domain) = explode('@', $url, 2);
}
}
// 解析参数
if (is_string($vars)) {
// aaa=1&bbb=2 转换成数组
parse_str($vars, $vars);
}
if ($url) {
$rule = Route::name(isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : ''));
if (is_null($rule) && isset($info['query'])) {
$rule = Route::name($url);
// 解析地址里面参数 合并到vars
parse_str($info['query'], $params);
$vars = array_merge($params, $vars);
unset($info['query']);
}
}
if (!empty($rule) && $match = self::getRuleUrl($rule, $vars)) {
// 匹配路由命名标识
$url = $match[0];
// 替换可选分隔符
$url = preg_replace(['/(\W)\?$/', '/(\W)\?/'], ['', '\1'], $url);
if (!empty($match[1])) {
$domain = $match[1];
}
if (!is_null($match[2])) {
$suffix = $match[2];
}
} elseif (!empty($rule) && isset($name)) {
throw new \InvalidArgumentException('route name not exists:' . $name);
} else {
// 检查别名路由
$alias = Route::rules('alias');
$matchAlias = false;
if ($alias) {
// 别名路由解析
foreach ($alias as $key => $val) {
if (is_array($val)) {
$val = $val[0];
}
if (0 === strpos($url, $val)) {
$url = $key . substr($url, strlen($val));
$matchAlias = true;
break;
}
}
}
if (!$matchAlias) {
// 路由标识不存在 直接解析
$url = self::parseUrl($url, $domain);
}
if (isset($info['query'])) {
// 解析地址里面参数 合并到vars
parse_str($info['query'], $params);
$vars = array_merge($params, $vars);
}
}
// 检测URL绑定
if (!self::$bindCheck) {
$type = Route::getBind('type');
if ($type) {
$bind = Route::getBind($type);
if (0 === strpos($url, $bind)) {
$url = substr($url, strlen($bind) + 1);
}
}
}
// 还原URL分隔符
$depr = Config::get('pathinfo_depr');
$url = str_replace('/', $depr, $url);
// URL后缀
$suffix = in_array($url, ['/', '']) ? '' : self::parseSuffix($suffix);
// 锚点
$anchor = !empty($anchor) ? '#' . $anchor : '';
// 参数组装
if (!empty($vars)) {
// 添加参数
if (Config::get('url_common_param')) {
$vars = urldecode(http_build_query($vars));
$url .= $suffix . '?' . $vars . $anchor;
} else {
$paramType = Config::get('url_param_type');
foreach ($vars as $var => $val) {
if ('' !== trim($val)) {
if ($paramType) {
$url .= $depr . urlencode($val);
} else {
$url .= $depr . $var . $depr . urlencode($val);
}
}
}
$url .= $suffix . $anchor;
}
} else {
$url .= $suffix . $anchor;
}
// 检测域名
$domain = self::parseDomain($url, $domain);
// URL组装
$url = $domain . rtrim(self::$root ?: Request::instance()->root(), '/') . '/' . ltrim($url, '/');
self::$bindCheck = false;
return $url;
}
// 直接解析URL地址
protected static function parseUrl($url, &$domain)
{
$request = Request::instance();
if (0 === strpos($url, '/')) {
// 直接作为路由地址解析
$url = substr($url, 1);
} elseif (false !== strpos($url, '\\')) {
// 解析到类
$url = ltrim(str_replace('\\', '/', $url), '/');
} elseif (0 === strpos($url, '@')) {
// 解析到控制器
$url = substr($url, 1);
} else {
// 解析到 模块/控制器/操作
$module = $request->module();
$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 . '/' : '';
$controller = Loader::parseName($request->controller());
if ('' == $url) {
// 空字符串输出当前的 模块/控制器/操作
$url = $module . $controller . '/' . $request->action();
} else {
$path = explode('/', $url);
$action = Config::get('url_convert') ? strtolower(array_pop($path)) : array_pop($path);
$controller = empty($path) ? $controller : (Config::get('url_convert') ? Loader::parseName(array_pop($path)) : array_pop($path));
$module = empty($path) ? $module : array_pop($path) . '/';
$url = $module . $controller . '/' . $action;
}
}
return $url;
}
// 检测域名
protected static function parseDomain(&$url, $domain)
{
if (!$domain) {
return '';
}
$request = Request::instance();
$rootDomain = Config::get('url_domain_root');
if (true === $domain) {
// 自动判断域名
$domain = Config::get('app_host') ?: $request->host();
$domains = Route::rules('domain');
if ($domains) {
$route_domain = array_keys($domains);
foreach ($route_domain as $domain_prefix) {
if (0 === strpos($domain_prefix, '*.') && strpos($domain, ltrim($domain_prefix, '*.')) !== false) {
foreach ($domains as $key => $rule) {
$rule = is_array($rule) ? $rule[0] : $rule;
if (is_string($rule) && false === strpos($key, '*') && 0 === strpos($url, $rule)) {
$url = ltrim($url, $rule);
$domain = $key;
// 生成对应子域名
if (!empty($rootDomain)) {
$domain .= $rootDomain;
}
break;
} elseif (false !== strpos($key, '*')) {
if (!empty($rootDomain)) {
$domain .= $rootDomain;
}
break;
}
}
}
}
}
} 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;
}
}
if (false !== strpos($domain, '://')) {
$scheme = '';
} else {
$scheme = $request->isSsl() || Config::get('is_https') ? 'https://' : 'http://';
}
return $scheme . $domain;
}
// 解析URL后缀
protected static function parseSuffix($suffix)
{
if ($suffix) {
$suffix = true === $suffix ? Config::get('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 = [])
{
foreach ($rule as $item) {
list($url, $pattern, $domain, $suffix) = $item;
if (empty($pattern)) {
return [$url, $domain, $suffix];
}
foreach ($pattern as $key => $val) {
if (isset($vars[$key])) {
$url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key . '', '<' . $key . '>'], urlencode($vars[$key]), $url);
unset($vars[$key]);
$result = [$url, $domain, $suffix];
} elseif (2 == $val) {
$url = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url);
$result = [$url, $domain, $suffix];
} else {
break;
}
}
if (isset($result)) {
return $result;
}
}
return false;
}
// 指定当前生成URL地址的root
public static function root($root)
{
self::$root = $root;
Request::instance()->root($root);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,236 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think;
class View
{
// 视图实例
protected static $instance;
// 模板引擎实例
public $engine;
// 模板变量
protected $data = [];
// 用于静态赋值的模板变量
protected static $var = [];
// 视图输出替换
protected $replace = [];
/**
* 构造函数
* @access public
* @param array $engine 模板引擎参数
* @param array $replace 字符串替换参数
*/
public function __construct($engine = [], $replace = [])
{
// 初始化模板引擎
$this->engine((array) $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);
}
/**
* 初始化视图
* @access public
* @param array $engine 模板引擎参数
* @param array $replace 字符串替换参数
* @return object
*/
public static function instance($engine = [], $replace = [])
{
if (is_null(self::$instance)) {
self::$instance = new self($engine, $replace);
}
return self::$instance;
}
/**
* 模板变量静态赋值
* @access public
* @param mixed $name 变量名
* @param mixed $value 变量值
* @return void
*/
public static function share($name, $value = '')
{
if (is_array($name)) {
self::$var = array_merge(self::$var, $name);
} else {
self::$var[$name] = $value;
}
}
/**
* 模板变量赋值
* @access public
* @param mixed $name 变量名
* @param mixed $value 变量值
* @return $this
*/
public function assign($name, $value = '')
{
if (is_array($name)) {
$this->data = array_merge($this->data, $name);
} else {
$this->data[$name] = $value;
}
return $this;
}
/**
* 设置当前模板解析的引擎
* @access public
* @param array|string $options 引擎参数
* @return $this
*/
public function engine($options = [])
{
if (is_string($options)) {
$type = $options;
$options = [];
} else {
$type = !empty($options['type']) ? $options['type'] : 'Think';
}
$class = false !== strpos($type, '\\') ? $type : '\\think\\view\\driver\\' . ucfirst($type);
if (isset($options['type'])) {
unset($options['type']);
}
$this->engine = new $class($options);
return $this;
}
/**
* 配置模板引擎
* @access private
* @param string|array $name 参数名
* @param mixed $value 参数值
* @return $this
*/
public function config($name, $value = null)
{
$this->engine->config($name, $value);
return $this;
}
/**
* 解析和获取模板内容 用于输出
* @param string $template 模板文件名或者内容
* @param array $vars 模板输出变量
* @param array $replace 替换内容
* @param array $config 模板参数
* @param bool $renderContent 是否渲染内容
* @return string
* @throws Exception
*/
public function fetch($template = '', $vars = [], $replace = [], $config = [], $renderContent = false)
{
// 模板变量
$vars = array_merge(self::$var, $this->data, $vars);
// 页面缓存
ob_start();
ob_implicit_flush(0);
// 渲染输出
$method = $renderContent ? 'display' : 'fetch';
$this->engine->$method($template, $vars, $config);
// 获取并清空缓存
$content = ob_get_clean();
// 内容过滤标签
Hook::listen('view_filter', $content);
// 允许用户自定义模板的字符串替换
$replace = array_merge($this->replace, $replace);
if (!empty($replace)) {
$content = strtr($content, $replace);
}
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;
}
return $this;
}
/**
* 渲染内容输出
* @access public
* @param string $content 内容
* @param array $vars 模板输出变量
* @param array $replace 替换内容
* @param array $config 模板参数
* @return mixed
*/
public function display($content, $vars = [], $replace = [], $config = [])
{
return $this->fetch($content, $vars, $replace, $config, true);
}
/**
* 模板变量赋值
* @access public
* @param string $name 变量名
* @param mixed $value 变量值
*/
public function __set($name, $value)
{
$this->data[$name] = $value;
}
/**
* 取得模板显示变量的值
* @access protected
* @param string $name 模板变量
* @return mixed
*/
public function __get($name)
{
return $this->data[$name];
}
/**
* 检测模板变量是否设置
* @access public
* @param string $name 模板变量名
* @return bool
*/
public function __isset($name)
{
return isset($this->data[$name]);
}
}

225
thinkphp/library/think/cache/Driver.php vendored Normal file
View File

@ -0,0 +1,225 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\cache;
/**
* 缓存基础类
*/
abstract class Driver
{
protected $handler = null;
protected $options = [];
protected $tag;
/**
* 判断缓存是否存在
* @access public
* @param string $name 缓存变量名
* @return bool
*/
abstract public function has($name);
/**
* 读取缓存
* @access public
* @param string $name 缓存变量名
* @param mixed $default 默认值
* @return mixed
*/
abstract public function get($name, $default = false);
/**
* 写入缓存
* @access public
* @param string $name 缓存变量名
* @param mixed $value 存储数据
* @param int $expire 有效时间 0为永久
* @return boolean
*/
abstract public function set($name, $value, $expire = null);
/**
* 自增缓存(针对数值缓存)
* @access public
* @param string $name 缓存变量名
* @param int $step 步长
* @return false|int
*/
abstract public function inc($name, $step = 1);
/**
* 自减缓存(针对数值缓存)
* @access public
* @param string $name 缓存变量名
* @param int $step 步长
* @return false|int
*/
abstract public function dec($name, $step = 1);
/**
* 删除缓存
* @access public
* @param string $name 缓存变量名
* @return boolean
*/
abstract public function rm($name);
/**
* 清除缓存
* @access public
* @param string $tag 标签名
* @return boolean
*/
abstract public function clear($tag = null);
/**
* 获取实际的缓存标识
* @access public
* @param string $name 缓存名
* @return string
*/
protected function getCacheKey($name)
{
return $this->options['prefix'] . $name;
}
/**
* 读取缓存并删除
* @access public
* @param string $name 缓存变量名
* @return mixed
*/
public function pull($name)
{
$result = $this->get($name, false);
if ($result) {
$this->rm($name);
return $result;
} else {
return;
}
}
/**
* 如果不存在则写入缓存
* @access public
* @param string $name 缓存变量名
* @param mixed $value 存储数据
* @param int $expire 有效时间 0为永久
* @return mixed
*/
public function remember($name, $value, $expire = null)
{
if (!$this->has($name)) {
while ($this->has($name . '_lock')) {
// 存在锁定则等待
}
try {
// 锁定
$this->set($name . '_lock', true);
if ($value instanceof \Closure) {
$value = call_user_func($value);
}
$this->set($name, $value, $expire);
// 解锁
$this->rm($name . '_lock');
} catch (\Exception $e) {
// 解锁
$this->rm($name . '_lock');
}
} else {
$value = $this->get($name);
}
return $value;
}
/**
* 缓存标签
* @access public
* @param string $name 标签名
* @param string|array $keys 缓存标识
* @param bool $overlay 是否覆盖
* @return $this
*/
public function tag($name, $keys = null, $overlay = false)
{
if (is_null($name)) {
} elseif (is_null($keys)) {
$this->tag = $name;
} else {
$key = 'tag_' . md5($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 缓存标识
* @return void
*/
protected function setTagItem($name)
{
if ($this->tag) {
$key = 'tag_' . md5($this->tag);
$this->tag = null;
if ($this->has($key)) {
$value = explode(',', $this->get($key));
$value[] = $name;
$value = implode(',', array_unique($value));
} else {
$value = $name;
}
$this->set($key, $value, 0);
}
}
/**
* 获取标签包含的缓存标识
* @access public
* @param string $tag 缓存标签
* @return array
*/
protected function getTagItem($tag)
{
$key = 'tag_' . md5($tag);
$value = $this->get($key);
if ($value) {
return array_filter(explode(',', $value));
} else {
return [];
}
}
/**
* 返回句柄对象,可执行其它高级方法
*
* @access public
* @return object
*/
public function handler()
{
return $this->handler;
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,187 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\cache\driver;
use think\cache\Driver;
class Memcached extends Driver
{
protected $options = [
'host' => '127.0.0.1',
'port' => 11211,
'expire' => 0,
'timeout' => 0, // 超时时间(单位:毫秒)
'prefix' => '',
'username' => '', //账号
'password' => '', //密码
'option' => [],
];
/**
* 构造函数
* @param array $options 缓存参数
* @access public
*/
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);
if ('' != $this->options['username']) {
$this->handler->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
$this->handler->setSaslAuthData($this->options['username'], $this->options['password']);
}
}
/**
* 判断缓存
* @access public
* @param string $name 缓存变量名
* @return bool
*/
public function has($name)
{
$key = $this->getCacheKey($name);
return $this->handler->get($key) ? true : false;
}
/**
* 读取缓存
* @access public
* @param string $name 缓存变量名
* @param mixed $default 默认值
* @return mixed
*/
public function get($name, $default = false)
{
$result = $this->handler->get($this->getCacheKey($name));
return false !== $result ? $result : $default;
}
/**
* 写入缓存
* @access public
* @param string $name 缓存变量名
* @param mixed $value 存储数据
* @param integer|\DateTime $expire 有效时间(秒)
* @return bool
*/
public function set($name, $value, $expire = null)
{
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;
if ($this->handler->set($key, $value, $expire)) {
isset($first) && $this->setTagItem($key);
return true;
}
return false;
}
/**
* 自增缓存(针对数值缓存)
* @access public
* @param string $name 缓存变量名
* @param int $step 步长
* @return false|int
*/
public function inc($name, $step = 1)
{
$key = $this->getCacheKey($name);
if ($this->handler->get($key)) {
return $this->handler->increment($key, $step);
}
return $this->handler->set($key, $step);
}
/**
* 自减缓存(针对数值缓存)
* @access public
* @param string $name 缓存变量名
* @param int $step 步长
* @return false|int
*/
public function dec($name, $step = 1)
{
$key = $this->getCacheKey($name);
$value = $this->handler->get($key) - $step;
$res = $this->handler->set($key, $value);
if (!$res) {
return false;
} else {
return $value;
}
}
/**
* 删除缓存
* @param string $name 缓存变量名
* @param bool|false $ttl
* @return bool
*/
public function rm($name, $ttl = false)
{
$key = $this->getCacheKey($name);
return false === $ttl ?
$this->handler->delete($key) :
$this->handler->delete($key, $ttl);
}
/**
* 清除缓存
* @access public
* @param string $tag 标签名
* @return bool
*/
public function clear($tag = null)
{
if ($tag) {
// 指定标签清除
$keys = $this->getTagItem($tag);
$this->handler->deleteMulti($keys);
$this->rm('tag_' . md5($tag));
return true;
}
return $this->handler->flush();
}
}

View File

@ -0,0 +1,179 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\cache\driver;
use think\cache\Driver;
/**
* Redis缓存驱动适合单机部署、有前端代理实现高可用的场景性能最好
* 有需要在业务层实现读写分离、或者使用RedisCluster的需求请使用Redisd驱动
*
* 要求安装phpredis扩展https://github.com/nicolasff/phpredis
* @author 尘缘 <130775@qq.com>
*/
class Redis extends Driver
{
protected $options = [
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'select' => 0,
'timeout' => 0,
'expire' => 0,
'persistent' => false,
'prefix' => '',
];
/**
* 构造函数
* @param array $options 缓存参数
* @access public
*/
public function __construct($options = [])
{
if (!extension_loaded('redis')) {
throw new \BadFunctionCallException('not support: redis');
}
if (!empty($options)) {
$this->options = array_merge($this->options, $options);
}
$func = $this->options['persistent'] ? 'pconnect' : 'connect';
$this->handler = new \Redis;
$this->handler->$func($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']);
}
}
/**
* 判断缓存
* @access public
* @param string $name 缓存变量名
* @return bool
*/
public function has($name)
{
return $this->handler->get($this->getCacheKey($name)) ? true : false;
}
/**
* 读取缓存
* @access public
* @param string $name 缓存变量名
* @param mixed $default 默认值
* @return mixed
*/
public function get($name, $default = false)
{
$value = $this->handler->get($this->getCacheKey($name));
if (is_null($value)) {
return $default;
}
$jsonData = json_decode($value, true);
// 检测是否为JSON数据 true 返回JSON解析数组, false返回源数据 byron sampson<xiaobo.sun@qq.com>
return (null === $jsonData) ? $value : $jsonData;
}
/**
* 写入缓存
* @access public
* @param string $name 缓存变量名
* @param mixed $value 存储数据
* @param integer|\DateTime $expire 有效时间(秒)
* @return boolean
*/
public function set($name, $value, $expire = null)
{
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);
//对数组/对象数据进行缓存处理,保证数据完整性 byron sampson<xiaobo.sun@qq.com>
$value = (is_object($value) || is_array($value)) ? json_encode($value) : $value;
if (is_int($expire) && $expire) {
$result = $this->handler->setex($key, $expire, $value);
} else {
$result = $this->handler->set($key, $value);
}
isset($first) && $this->setTagItem($key);
return $result;
}
/**
* 自增缓存(针对数值缓存)
* @access public
* @param string $name 缓存变量名
* @param int $step 步长
* @return false|int
*/
public function inc($name, $step = 1)
{
$key = $this->getCacheKey($name);
return $this->handler->incrby($key, $step);
}
/**
* 自减缓存(针对数值缓存)
* @access public
* @param string $name 缓存变量名
* @param int $step 步长
* @return false|int
*/
public function dec($name, $step = 1)
{
$key = $this->getCacheKey($name);
return $this->handler->decrby($key, $step);
}
/**
* 删除缓存
* @access public
* @param string $name 缓存变量名
* @return boolean
*/
public function rm($name)
{
return $this->handler->delete($this->getCacheKey($name));
}
/**
* 清除缓存
* @access public
* @param string $tag 标签名
* @return boolean
*/
public function clear($tag = null)
{
if ($tag) {
// 指定标签清除
$keys = $this->getTagItem($tag);
foreach ($keys as $key) {
$this->handler->delete($key);
}
$this->rm('tag_' . md5($tag));
return true;
}
return $this->handler->flushDB();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

View File

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

View File

@ -0,0 +1,54 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\console\command;
use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
class Clear extends Command
{
protected function configure()
{
// 指令配置
$this
->setName('clear')
->addOption('path', 'd', Option::VALUE_OPTIONAL, 'path to clear', null)
->setDescription('Clear runtime file');
}
protected function execute(Input $input, Output $output)
{
$path = $input->getOption('path') ?: RUNTIME_PATH;
if (is_dir($path)) {
$this->clearPath($path);
}
$output->writeln("<info>Clear Successed</info>");
}
protected function clearPath($path)
{
$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);
}
}
}
}
}

View File

@ -0,0 +1,69 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\command;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument as InputArgument;
use think\console\input\Option as InputOption;
use think\console\Output;
class Help extends Command
{
private $command;
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->ignoreValidationErrors();
$this->setName('help')->setDefinition([
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
])->setDescription('Displays help for a command')->setHelp(<<<EOF
The <info>%command.name%</info> command displays help for a given command:
<info>php %command.full_name% list</info>
To display the list of available commands, please use the <info>list</info> command.
EOF
);
}
/**
* Sets the command.
* @param Command $command The command to set
*/
public function setCommand(Command $command)
{
$this->command = $command;
}
/**
* {@inheritdoc}
*/
protected function execute(Input $input, Output $output)
{
if (null === $this->command) {
$this->command = $this->getConsole()->find($input->getArgument('command_name'));
}
$output->describe($this->command, [
'raw_text' => $input->getOption('raw'),
]);
$this->command = null;
}
}

View File

@ -0,0 +1,74 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\command;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\console\input\Argument as InputArgument;
use think\console\input\Option as InputOption;
use think\console\input\Definition as InputDefinition;
class Lists extends Command
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->setName('list')->setDefinition($this->createDefinition())->setDescription('Lists commands')->setHelp(<<<EOF
The <info>%command.name%</info> command lists all commands:
<info>php %command.full_name%</info>
You can also display the commands for a specific namespace:
<info>php %command.full_name% test</info>
It's also possible to get raw list of commands (useful for embedding command runner):
<info>php %command.full_name% --raw</info>
EOF
);
}
/**
* {@inheritdoc}
*/
public function getNativeDefinition()
{
return $this->createDefinition();
}
/**
* {@inheritdoc}
*/
protected function execute(Input $input, Output $output)
{
$output->describe($this->getConsole(), [
'raw_text' => $input->getOption('raw'),
'namespace' => $input->getArgument('namespace'),
]);
}
/**
* {@inheritdoc}
*/
private function createDefinition()
{
return new InputDefinition([
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list')
]);
}
}

View File

@ -0,0 +1,110 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 刘志淳 <chun@engineer.com>
// +----------------------------------------------------------------------
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;
abstract class Make extends Command
{
protected $type;
abstract protected function getStub();
protected function configure()
{
$this->addArgument('name', Argument::REQUIRED, "The name of the class");
}
protected function execute(Input $input, Output $output)
{
$name = trim($input->getArgument('name'));
$classname = $this->getClassName($name);
$pathname = $this->getPathName($classname);
if (is_file($pathname)) {
$output->writeln('<error>' . $this->type . ' already exists!</error>');
return false;
}
if (!is_dir(dirname($pathname))) {
mkdir(strtolower(dirname($pathname)), 0755, true);
}
file_put_contents($pathname, $this->buildClass($classname));
$output->writeln('<info>' . $this->type . ' created successfully.</info>');
}
protected function buildClass($name)
{
$stub = file_get_contents($this->getStub());
$namespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
$class = str_replace($namespace . '\\', '', $name);
return str_replace(['{%className%}', '{%namespace%}', '{%app_namespace%}'], [
$class,
$namespace,
App::$namespace,
], $stub);
}
protected function getPathName($name)
{
$name = str_replace(App::$namespace . '\\', '', $name);
return APP_PATH . str_replace('\\', '/', $name) . '.php';
}
protected function getClassName($name)
{
$appNamespace = App::$namespace;
if (strpos($name, $appNamespace . '\\') === 0) {
return $name;
}
if (Config::get('app_multi_module')) {
if (strpos($name, '/')) {
list($module, $name) = explode('/', $name, 2);
} else {
$module = 'common';
}
} else {
$module = null;
}
if (strpos($name, '/') !== false) {
$name = str_replace('/', '\\', $name);
}
return $this->getNamespace($appNamespace, $module) . '\\' . $name;
}
protected function getNamespace($appNamespace, $module)
{
return $module ? ($appNamespace . '\\' . $module) : $appNamespace;
}
}

View File

@ -0,0 +1,50 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 刘志淳 <chun@engineer.com>
// +----------------------------------------------------------------------
namespace think\console\command\make;
use think\Config;
use think\console\command\Make;
use think\console\input\Option;
class Controller extends Make
{
protected $type = "Controller";
protected function configure()
{
parent::configure();
$this->setName('make:controller')
->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';
}
return __DIR__ . '/stubs/controller.stub';
}
protected function getClassName($name)
{
return parent::getClassName($name) . (Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : '');
}
protected function getNamespace($appNamespace, $module)
{
return parent::getNamespace($appNamespace, $module) . '\controller';
}
}

View File

@ -0,0 +1,36 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 刘志淳 <chun@engineer.com>
// +----------------------------------------------------------------------
namespace think\console\command\make;
use think\console\command\Make;
class Model extends Make
{
protected $type = "Model";
protected function configure()
{
parent::configure();
$this->setName('make:model')
->setDescription('Create a new model class');
}
protected function getStub()
{
return __DIR__ . '/stubs/model.stub';
}
protected function getNamespace($appNamespace, $module)
{
return parent::getNamespace($appNamespace, $module) . '\model';
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace {%namespace%};
use think\Controller;
class {%className%} extends Controller
{
//
}

View File

@ -0,0 +1,85 @@
<?php
namespace {%namespace%};
use think\Controller;
use think\Request;
class {%className%} extends Controller
{
/**
* 显示资源列表
*
* @return \think\Response
*/
public function index()
{
//
}
/**
* 显示创建资源表单页.
*
* @return \think\Response
*/
public function create()
{
//
}
/**
* 保存新建的资源
*
* @param \think\Request $request
* @return \think\Response
*/
public function save(Request $request)
{
//
}
/**
* 显示指定的资源
*
* @param int $id
* @return \think\Response
*/
public function read($id)
{
//
}
/**
* 显示编辑资源表单页.
*
* @param int $id
* @return \think\Response
*/
public function edit($id)
{
//
}
/**
* 保存更新的资源
*
* @param \think\Request $request
* @param int $id
* @return \think\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* 删除指定资源
*
* @param int $id
* @return \think\Response
*/
public function delete($id)
{
//
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace {%namespace%};
use think\Model;
class {%className%} extends Model
{
//
}

View File

@ -0,0 +1,294 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\command\optimize;
use think\App;
use think\Config;
use think\console\Command;
use think\console\Input;
use think\console\Output;
class Autoload extends Command
{
protected function configure()
{
$this->setName('optimize:autoload')
->setDescription('Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.');
}
protected function execute(Input $input, Output $output)
{
$classmapFile = <<<EOF
<?php
/**
* 类库映射
*/
return [
EOF;
$namespacesToScan = [
App::$namespace . '\\' => realpath(rtrim(APP_PATH)),
'think\\' => LIB_PATH . 'think',
'behavior\\' => LIB_PATH . 'behavior',
'traits\\' => LIB_PATH . 'traits',
'' => realpath(rtrim(EXTEND_PATH)),
];
$root_namespace = Config::get('root_namespace');
foreach ($root_namespace as $namespace => $dir) {
$namespacesToScan[$namespace . '\\'] = realpath($dir);
}
krsort($namespacesToScan);
$classMap = [];
foreach ($namespacesToScan as $namespace => $dir) {
if (!is_dir($dir)) {
continue;
}
$namespaceFilter = $namespace === '' ? null : $namespace;
$classMap = $this->addClassMapCode($dir, $namespaceFilter, $classMap);
}
ksort($classMap);
foreach ($classMap as $class => $code) {
$classmapFile .= ' ' . var_export($class, true) . ' => ' . $code;
}
$classmapFile .= "];\n";
if (!is_dir(RUNTIME_PATH)) {
@mkdir(RUNTIME_PATH, 0755, true);
}
file_put_contents(RUNTIME_PATH . 'classmap' . EXT, $classmapFile);
$output->writeln('<info>Succeed!</info>');
}
protected function addClassMapCode($dir, $namespace, $classMap)
{
foreach ($this->createMap($dir, $namespace) as $class => $path) {
$pathCode = $this->getPathCode($path) . ",\n";
if (!isset($classMap[$class])) {
$classMap[$class] = $pathCode;
} elseif ($classMap[$class] !== $pathCode && !preg_match('{/(test|fixture|example|stub)s?/}i', strtr($classMap[$class] . ' ' . $path, '\\', '/'))) {
$this->output->writeln(
'<warning>Warning: Ambiguous class resolution, "' . $class . '"' .
' was found in both "' . str_replace(["',\n"], [
'',
], $classMap[$class]) . '" and "' . $path . '", the first will be used.</warning>'
);
}
}
return $classMap;
}
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));
$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) {
$path = substr($path, strlen($appPath) + 1);
$baseDir = 'APP_PATH';
} elseif ($extendPath !== null && 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';
}
if ($path !== false) {
$baseDir .= " . ";
}
return $baseDir . (($path !== false) ? var_export($path, true) : "");
}
protected function normalizePath($path)
{
if ($path === false) {
return;
}
$parts = [];
$path = strtr($path, '\\', '/');
$prefix = '';
$absolute = false;
if (preg_match('{^([0-9a-z]+:(?://(?:[a-z]:)?)?)}i', $path, $match)) {
$prefix = $match[1];
$path = substr($path, strlen($prefix));
}
if (substr($path, 0, 1) === '/') {
$absolute = true;
$path = substr($path, 1);
}
$up = false;
foreach (explode('/', $path) as $chunk) {
if ('..' === $chunk && ($absolute || $up)) {
array_pop($parts);
$up = !(empty($parts) || '..' === end($parts));
} elseif ('.' !== $chunk && '' !== $chunk) {
$parts[] = $chunk;
$up = '..' !== $chunk;
}
}
return $prefix . ($absolute ? '/' : '') . implode('/', $parts);
}
protected function createMap($path, $namespace = null)
{
if (is_string($path)) {
if (is_file($path)) {
$path = [new \SplFileInfo($path)];
} elseif (is_dir($path)) {
$objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path), \RecursiveIteratorIterator::SELF_FIRST);
$path = [];
/** @var \SplFileInfo $object */
foreach ($objects as $object) {
if ($object->isFile() && $object->getExtension() == 'php') {
$path[] = $object;
}
}
} else {
throw new \RuntimeException(
'Could not scan for classes inside "' . $path .
'" which does not appear to be a file nor a folder'
);
}
}
$map = [];
/** @var \SplFileInfo $file */
foreach ($path as $file) {
$filePath = $file->getRealPath();
if (pathinfo($filePath, PATHINFO_EXTENSION) != 'php') {
continue;
}
$classes = $this->findClasses($filePath);
foreach ($classes as $class) {
if (null !== $namespace && 0 !== strpos($class, $namespace)) {
continue;
}
if (!isset($map[$class])) {
$map[$class] = $filePath;
} elseif ($map[$class] !== $filePath && !preg_match('{/(test|fixture|example|stub)s?/}i', strtr($map[$class] . ' ' . $filePath, '\\', '/'))) {
$this->output->writeln(
'<warning>Warning: Ambiguous class resolution, "' . $class . '"' .
' was found in both "' . $map[$class] . '" and "' . $filePath . '", the first will be used.</warning>'
);
}
}
}
return $map;
}
protected function findClasses($path)
{
$extraTypes = '|trait';
$contents = @php_strip_whitespace($path);
if (!$contents) {
if (!file_exists($path)) {
$message = 'File at "%s" does not exist, check your classmap definitions';
} elseif (!is_readable($path)) {
$message = 'File at "%s" is not readable, check its permissions';
} elseif ('' === trim(file_get_contents($path))) {
return [];
} else {
$message = 'File at "%s" could not be parsed as PHP, it may be binary or corrupted';
}
$error = error_get_last();
if (isset($error['message'])) {
$message .= PHP_EOL . 'The following message may be helpful:' . PHP_EOL . $error['message'];
}
throw new \RuntimeException(sprintf($message, $path));
}
if (!preg_match('{\b(?:class|interface' . $extraTypes . ')\s}i', $contents)) {
return [];
}
// strip heredocs/nowdocs
$contents = preg_replace('{<<<\s*(\'?)(\w+)\\1(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)\\2(?=\r\n|\n|\r|;)}s', 'null', $contents);
// strip strings
$contents = preg_replace('{"[^"\\\\]*+(\\\\.[^"\\\\]*+)*+"|\'[^\'\\\\]*+(\\\\.[^\'\\\\]*+)*+\'}s', 'null', $contents);
// strip leading non-php code if needed
if (substr($contents, 0, 2) !== '<?') {
$contents = preg_replace('{^.+?<\?}s', '<?', $contents, 1, $replacements);
if ($replacements === 0) {
return [];
}
}
// strip non-php blocks in the file
$contents = preg_replace('{\?>.+<\?}s', '?><?', $contents);
// strip trailing non-php code if needed
$pos = strrpos($contents, '?>');
if (false !== $pos && false === strpos(substr($contents, $pos), '<?')) {
$contents = substr($contents, 0, $pos);
}
preg_match_all('{
(?:
\b(?<![\$:>])(?P<type>class|interface' . $extraTypes . ') \s++ (?P<name>[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:\-]*+)
| \b(?<![\$:>])(?P<ns>namespace) (?P<nsname>\s++[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\s*+\\\\\s*+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+)? \s*+ [\{;]
)
}ix', $contents, $matches);
$classes = [];
$namespace = '';
for ($i = 0, $len = count($matches['type']); $i < $len; $i++) {
if (!empty($matches['ns'][$i])) {
$namespace = str_replace([' ', "\t", "\r", "\n"], '', $matches['nsname'][$i]) . '\\';
} else {
$name = $matches['name'][$i];
if ($name[0] === ':') {
$name = 'xhp' . substr(str_replace(['-', ':'], ['_', '__'], $name), 1);
} elseif ($matches['type'][$i] === 'enum') {
$name = rtrim($name, ':');
}
$classes[] = ltrim($namespace . $name, '\\');
}
}
return $classes;
}
}

View File

@ -0,0 +1,93 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
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;
class Config extends Command
{
/** @var Output */
protected $output;
protected function configure()
{
$this->setName('optimize:config')
->addArgument('module', Argument::OPTIONAL, 'Build module config cache .')
->setDescription('Build config and common file cache.');
}
protected function execute(Input $input, Output $output)
{
if ($input->hasArgument('module')) {
$module = $input->getArgument('module') . DS;
} else {
$module = '';
}
$content = '<?php ' . PHP_EOL . $this->buildCacheContent($module);
if (!is_dir(RUNTIME_PATH . $module)) {
@mkdir(RUNTIME_PATH . $module, 0755, true);
}
file_put_contents(RUNTIME_PATH . $module . 'init' . EXT, $content);
$output->writeln('<info>Succeed!</info>');
}
protected function buildCacheContent($module)
{
$content = '';
$path = realpath(APP_PATH . $module) . DS;
if ($module) {
// 加载模块配置
$config = ThinkConfig::load(CONF_PATH . $module . 'config' . CONF_EXT);
// 读取数据库配置文件
$filename = CONF_PATH . $module . 'database' . CONF_EXT;
ThinkConfig::load($filename, 'database');
// 加载应用状态配置
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));
}
}
}
}
// 加载行为扩展文件
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 . 'common' . EXT)) {
$content .= substr(php_strip_whitespace($path . 'common' . EXT), 5) . PHP_EOL;
}
$content .= '\think\Config::set(' . var_export(ThinkConfig::get(), true) . ');';
return $content;
}
}

View File

@ -0,0 +1,70 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\console\command\optimize;
use think\console\Command;
use think\console\Input;
use think\console\Output;
class Route extends Command
{
/** @var Output */
protected $output;
protected function configure()
{
$this->setName('optimize:route')
->setDescription('Build route cache.');
}
protected function execute(Input $input, Output $output)
{
file_put_contents(RUNTIME_PATH . 'route.php', $this->buildRouteCache());
$output->writeln('<info>Succeed!</info>');
}
protected function buildRouteCache()
{
$files = \think\Config::get('route_config_file');
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);
}
}
}
$rules = \think\Route::rules(true);
array_walk_recursive($rules, [$this, 'buildClosure']);
$content = '<?php ' . PHP_EOL . 'return ';
$content .= var_export($rules, true) . ';';
$content = str_replace(['\'[__start__', '__end__]\''], '', stripcslashes($content));
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__]';
}
}
}

View File

@ -0,0 +1,116 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
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;
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 .')
->setDescription('Build database schema cache.');
}
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 ($input->hasOption('module')) {
$module = $input->getOption('module');
// 读取模型
$list = scandir(APP_PATH . $module . DS . 'model');
$app = App::$namespace;
foreach ($list as $file) {
if (0 === strpos($file, '.')) {
continue;
}
$class = '\\' . $app . '\\' . $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');
}
$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;
$list = scandir(APP_PATH . 'model');
foreach ($list as $file) {
if (0 === strpos($file, '.')) {
continue;
}
$class = '\\' . $app . '\\model\\' . pathinfo($file, PATHINFO_FILENAME);
$this->buildModelSchema($class);
}
$output->writeln('<info>Succeed!</info>');
return;
} else {
$tables = Db::connect($config)->getTables();
}
$db = isset($dbName) ? $dbName . '.' : '';
$this->buildDataBaseSchema($tables, $db, $config);
$output->writeln('<info>Succeed!</info>');
}
protected function buildModelSchema($class)
{
$reflect = new \ReflectionClass($class);
if (!$reflect->isAbstract() && $reflect->isSubclassOf('\think\Model')) {
$table = $class::getTable();
$dbName = $class::getConfig('database');
$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);
}
}
protected function buildDataBaseSchema($tables, $db, $config)
{
if ('' == $db) {
$dbName = Db::connect($config)->getConfig('database') . '.';
} else {
$dbName = $db;
}
foreach ($tables as $table) {
$content = '<?php ' . PHP_EOL . 'return ';
$info = Db::connect($config)->getFields($db . $table);
$content .= var_export($info, true) . ';';
file_put_contents(RUNTIME_PATH . 'schema' . DS . $dbName . $table . EXT, $content);
}
}
}

View File

@ -0,0 +1,115 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\input;
class Argument
{
const REQUIRED = 1;
const OPTIONAL = 2;
const IS_ARRAY = 4;
private $name;
private $mode;
private $default;
private $description;
/**
* 构造方法
* @param string $name 参数名
* @param int $mode 参数类型: self::REQUIRED 或者 self::OPTIONAL
* @param string $description 描述
* @param mixed $default 默认值 ( self::OPTIONAL 类型有效)
* @throws \InvalidArgumentException
*/
public function __construct($name, $mode = null, $description = '', $default = null)
{
if (null === $mode) {
$mode = self::OPTIONAL;
} elseif (!is_int($mode) || $mode > 7 || $mode < 1) {
throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
}
$this->name = $name;
$this->mode = $mode;
$this->description = $description;
$this->setDefault($default);
}
/**
* 获取参数名
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* 是否必须
* @return bool
*/
public function isRequired()
{
return self::REQUIRED === (self::REQUIRED & $this->mode);
}
/**
* 该参数是否接受数组
* @return bool
*/
public function isArray()
{
return self::IS_ARRAY === (self::IS_ARRAY & $this->mode);
}
/**
* 设置默认值
* @param mixed $default 默认值
* @throws \LogicException
*/
public function setDefault($default = null)
{
if (self::REQUIRED === $this->mode && null !== $default) {
throw new \LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
}
if ($this->isArray()) {
if (null === $default) {
$default = [];
} elseif (!is_array($default)) {
throw new \LogicException('A default value for an array argument must be an array.');
}
}
$this->default = $default;
}
/**
* 获取默认值
* @return mixed
*/
public function getDefault()
{
return $this->default;
}
/**
* 获取描述
* @return string
*/
public function getDescription()
{
return $this->description;
}
}

View File

@ -0,0 +1,375 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\input;
class Definition
{
/**
* @var Argument[]
*/
private $arguments;
private $requiredCount;
private $hasAnArrayArgument = false;
private $hasOptional;
/**
* @var Option[]
*/
private $options;
private $shortcuts;
/**
* 构造方法
* @param array $definition
* @api
*/
public function __construct(array $definition = [])
{
$this->setDefinition($definition);
}
/**
* 设置指令的定义
* @param array $definition 定义的数组
*/
public function setDefinition(array $definition)
{
$arguments = [];
$options = [];
foreach ($definition as $item) {
if ($item instanceof Option) {
$options[] = $item;
} else {
$arguments[] = $item;
}
}
$this->setArguments($arguments);
$this->setOptions($options);
}
/**
* 设置参数
* @param Argument[] $arguments 参数数组
*/
public function setArguments($arguments = [])
{
$this->arguments = [];
$this->requiredCount = 0;
$this->hasOptional = false;
$this->hasAnArrayArgument = false;
$this->addArguments($arguments);
}
/**
* 添加参数
* @param Argument[] $arguments 参数数组
* @api
*/
public function addArguments($arguments = [])
{
if (null !== $arguments) {
foreach ($arguments as $argument) {
$this->addArgument($argument);
}
}
}
/**
* 添加一个参数
* @param Argument $argument 参数
* @throws \LogicException
*/
public function addArgument(Argument $argument)
{
if (isset($this->arguments[$argument->getName()])) {
throw new \LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName()));
}
if ($this->hasAnArrayArgument) {
throw new \LogicException('Cannot add an argument after an array argument.');
}
if ($argument->isRequired() && $this->hasOptional) {
throw new \LogicException('Cannot add a required argument after an optional one.');
}
if ($argument->isArray()) {
$this->hasAnArrayArgument = true;
}
if ($argument->isRequired()) {
++$this->requiredCount;
} else {
$this->hasOptional = true;
}
$this->arguments[$argument->getName()] = $argument;
}
/**
* 根据名称或者位置获取参数
* @param string|int $name 参数名或者位置
* @return Argument 参数
* @throws \InvalidArgumentException
*/
public function getArgument($name)
{
if (!$this->hasArgument($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
return $arguments[$name];
}
/**
* 根据名称或位置检查是否具有某个参数
* @param string|int $name 参数名或者位置
* @return bool
* @api
*/
public function hasArgument($name)
{
$arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
return isset($arguments[$name]);
}
/**
* 获取所有的参数
* @return Argument[] 参数数组
*/
public function getArguments()
{
return $this->arguments;
}
/**
* 获取参数数量
* @return int
*/
public function getArgumentCount()
{
return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments);
}
/**
* 获取必填的参数的数量
* @return int
*/
public function getArgumentRequiredCount()
{
return $this->requiredCount;
}
/**
* 获取参数默认值
* @return array
*/
public function getArgumentDefaults()
{
$values = [];
foreach ($this->arguments as $argument) {
$values[$argument->getName()] = $argument->getDefault();
}
return $values;
}
/**
* 设置选项
* @param Option[] $options 选项数组
*/
public function setOptions($options = [])
{
$this->options = [];
$this->shortcuts = [];
$this->addOptions($options);
}
/**
* 添加选项
* @param Option[] $options 选项数组
* @api
*/
public function addOptions($options = [])
{
foreach ($options as $option) {
$this->addOption($option);
}
}
/**
* 添加一个选项
* @param Option $option 选项
* @throws \LogicException
* @api
*/
public function addOption(Option $option)
{
if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) {
throw new \LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
}
if ($option->getShortcut()) {
foreach (explode('|', $option->getShortcut()) as $shortcut) {
if (isset($this->shortcuts[$shortcut])
&& !$option->equals($this->options[$this->shortcuts[$shortcut]])
) {
throw new \LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut));
}
}
}
$this->options[$option->getName()] = $option;
if ($option->getShortcut()) {
foreach (explode('|', $option->getShortcut()) as $shortcut) {
$this->shortcuts[$shortcut] = $option->getName();
}
}
}
/**
* 根据名称获取选项
* @param string $name 选项名
* @return Option
* @throws \InvalidArgumentException
* @api
*/
public function getOption($name)
{
if (!$this->hasOption($name)) {
throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
}
return $this->options[$name];
}
/**
* 根据名称检查是否有这个选项
* @param string $name 选项名
* @return bool
* @api
*/
public function hasOption($name)
{
return isset($this->options[$name]);
}
/**
* 获取所有选项
* @return Option[]
* @api
*/
public function getOptions()
{
return $this->options;
}
/**
* 根据名称检查某个选项是否有短名称
* @param string $name 短名称
* @return bool
*/
public function hasShortcut($name)
{
return isset($this->shortcuts[$name]);
}
/**
* 根据短名称获取选项
* @param string $shortcut 短名称
* @return Option
*/
public function getOptionForShortcut($shortcut)
{
return $this->getOption($this->shortcutToName($shortcut));
}
/**
* 获取所有选项的默认值
* @return array
*/
public function getOptionDefaults()
{
$values = [];
foreach ($this->options as $option) {
$values[$option->getName()] = $option->getDefault();
}
return $values;
}
/**
* 根据短名称获取选项名
* @param string $shortcut 短名称
* @return string
* @throws \InvalidArgumentException
*/
private function shortcutToName($shortcut)
{
if (!isset($this->shortcuts[$shortcut])) {
throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
}
return $this->shortcuts[$shortcut];
}
/**
* 获取该指令的介绍
* @param bool $short 是否简洁介绍
* @return string
*/
public function getSynopsis($short = false)
{
$elements = [];
if ($short && $this->getOptions()) {
$elements[] = '[options]';
} elseif (!$short) {
foreach ($this->getOptions() as $option) {
$value = '';
if ($option->acceptValue()) {
$value = sprintf(' %s%s%s', $option->isValueOptional() ? '[' : '', strtoupper($option->getName()), $option->isValueOptional() ? ']' : '');
}
$shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : '';
$elements[] = sprintf('[%s--%s%s]', $shortcut, $option->getName(), $value);
}
}
if (count($elements) && $this->getArguments()) {
$elements[] = '[--]';
}
foreach ($this->getArguments() as $argument) {
$element = '<' . $argument->getName() . '>';
if (!$argument->isRequired()) {
$element = '[' . $element . ']';
} elseif ($argument->isArray()) {
$element .= ' (' . $element . ')';
}
if ($argument->isArray()) {
$element .= '...';
}
$elements[] = $element;
}
return implode(' ', $elements);
}
}

View File

@ -0,0 +1,190 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\input;
class Option
{
const VALUE_NONE = 1;
const VALUE_REQUIRED = 2;
const VALUE_OPTIONAL = 4;
const VALUE_IS_ARRAY = 8;
private $name;
private $shortcut;
private $mode;
private $default;
private $description;
/**
* 构造方法
* @param string $name 选项名
* @param string|array $shortcut 短名称,多个用|隔开或者使用数组
* @param int $mode 选项类型(可选类型为 self::VALUE_*)
* @param string $description 描述
* @param mixed $default 默认值 (类型为 self::VALUE_REQUIRED 或者 self::VALUE_NONE 的时候必须为null)
* @throws \InvalidArgumentException
*/
public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null)
{
if (0 === strpos($name, '--')) {
$name = substr($name, 2);
}
if (empty($name)) {
throw new \InvalidArgumentException('An option name cannot be empty.');
}
if (empty($shortcut)) {
$shortcut = null;
}
if (null !== $shortcut) {
if (is_array($shortcut)) {
$shortcut = implode('|', $shortcut);
}
$shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-'));
$shortcuts = array_filter($shortcuts);
$shortcut = implode('|', $shortcuts);
if (empty($shortcut)) {
throw new \InvalidArgumentException('An option shortcut cannot be empty.');
}
}
if (null === $mode) {
$mode = self::VALUE_NONE;
} elseif (!is_int($mode) || $mode > 15 || $mode < 1) {
throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
}
$this->name = $name;
$this->shortcut = $shortcut;
$this->mode = $mode;
$this->description = $description;
if ($this->isArray() && !$this->acceptValue()) {
throw new \InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
}
$this->setDefault($default);
}
/**
* 获取短名称
* @return string
*/
public function getShortcut()
{
return $this->shortcut;
}
/**
* 获取选项名
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* 是否可以设置值
* @return bool 类型不是 self::VALUE_NONE 的时候返回true,其他均返回false
*/
public function acceptValue()
{
return $this->isValueRequired() || $this->isValueOptional();
}
/**
* 是否必须
* @return bool 类型是 self::VALUE_REQUIRED 的时候返回true,其他均返回false
*/
public function isValueRequired()
{
return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode);
}
/**
* 是否可选
* @return bool 类型是 self::VALUE_OPTIONAL 的时候返回true,其他均返回false
*/
public function isValueOptional()
{
return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode);
}
/**
* 选项值是否接受数组
* @return bool 类型是 self::VALUE_IS_ARRAY 的时候返回true,其他均返回false
*/
public function isArray()
{
return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
}
/**
* 设置默认值
* @param mixed $default 默认值
* @throws \LogicException
*/
public function setDefault($default = null)
{
if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) {
throw new \LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.');
}
if ($this->isArray()) {
if (null === $default) {
$default = [];
} elseif (!is_array($default)) {
throw new \LogicException('A default value for an array option must be an array.');
}
}
$this->default = $this->acceptValue() ? $default : false;
}
/**
* 获取默认值
* @return mixed
*/
public function getDefault()
{
return $this->default;
}
/**
* 获取描述文字
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* 检查所给选项是否是当前这个
* @param Option $option
* @return bool
*/
public function equals(Option $option)
{
return $option->getName() === $this->getName()
&& $option->getShortcut() === $this->getShortcut()
&& $option->getDefault() === $this->getDefault()
&& $option->isArray() === $this->isArray()
&& $option->isValueRequired() === $this->isValueRequired()
&& $option->isValueOptional() === $this->isValueOptional();
}
}

View File

@ -0,0 +1,340 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\output;
use think\console\Input;
use think\console\Output;
use think\console\output\question\Choice;
use think\console\output\question\Confirmation;
class Ask
{
private static $stty;
private static $shell;
/** @var Input */
protected $input;
/** @var Output */
protected $output;
/** @var Question */
protected $question;
public function __construct(Input $input, Output $output, Question $question)
{
$this->input = $input;
$this->output = $output;
$this->question = $question;
}
public function run()
{
if (!$this->input->isInteractive()) {
return $this->question->getDefault();
}
if (!$this->question->getValidator()) {
return $this->doAsk();
}
$that = $this;
$interviewer = function () use ($that) {
return $that->doAsk();
};
return $this->validateAttempts($interviewer);
}
protected function doAsk()
{
$this->writePrompt();
$inputStream = STDIN;
$autocomplete = $this->question->getAutocompleterValues();
if (null === $autocomplete || !$this->hasSttyAvailable()) {
$ret = false;
if ($this->question->isHidden()) {
try {
$ret = trim($this->getHiddenResponse($inputStream));
} catch (\RuntimeException $e) {
if (!$this->question->isHiddenFallback()) {
throw $e;
}
}
}
if (false === $ret) {
$ret = fgets($inputStream, 4096);
if (false === $ret) {
throw new \RuntimeException('Aborted');
}
$ret = trim($ret);
}
} else {
$ret = trim($this->autocomplete($inputStream));
}
$ret = strlen($ret) > 0 ? $ret : $this->question->getDefault();
if ($normalizer = $this->question->getNormalizer()) {
return $normalizer($ret);
}
return $ret;
}
private function autocomplete($inputStream)
{
$autocomplete = $this->question->getAutocompleterValues();
$ret = '';
$i = 0;
$ofs = -1;
$matches = $autocomplete;
$numMatches = count($matches);
$sttyMode = shell_exec('stty -g');
shell_exec('stty -icanon -echo');
while (!feof($inputStream)) {
$c = fread($inputStream, 1);
if ("\177" === $c) {
if (0 === $numMatches && 0 !== $i) {
--$i;
$this->output->write("\033[1D");
}
if ($i === 0) {
$ofs = -1;
$matches = $autocomplete;
$numMatches = count($matches);
} else {
$numMatches = 0;
}
$ret = substr($ret, 0, $i);
} elseif ("\033" === $c) {
$c .= fread($inputStream, 2);
if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
if ('A' === $c[2] && -1 === $ofs) {
$ofs = 0;
}
if (0 === $numMatches) {
continue;
}
$ofs += ('A' === $c[2]) ? -1 : 1;
$ofs = ($numMatches + $ofs) % $numMatches;
}
} elseif (ord($c) < 32) {
if ("\t" === $c || "\n" === $c) {
if ($numMatches > 0 && -1 !== $ofs) {
$ret = $matches[$ofs];
$this->output->write(substr($ret, $i));
$i = strlen($ret);
}
if ("\n" === $c) {
$this->output->write($c);
break;
}
$numMatches = 0;
}
continue;
} else {
$this->output->write($c);
$ret .= $c;
++$i;
$numMatches = 0;
$ofs = 0;
foreach ($autocomplete as $value) {
if (0 === strpos($value, $ret) && $i !== strlen($value)) {
$matches[$numMatches++] = $value;
}
}
}
$this->output->write("\033[K");
if ($numMatches > 0 && -1 !== $ofs) {
$this->output->write("\0337");
$this->output->highlight(substr($matches[$ofs], $i));
$this->output->write("\0338");
}
}
shell_exec(sprintf('stty %s', $sttyMode));
return $ret;
}
protected function getHiddenResponse($inputStream)
{
if ('\\' === DIRECTORY_SEPARATOR) {
$exe = __DIR__ . '/../bin/hiddeninput.exe';
$value = rtrim(shell_exec($exe));
$this->output->writeln('');
if (isset($tmpExe)) {
unlink($tmpExe);
}
return $value;
}
if ($this->hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');
shell_exec('stty -echo');
$value = fgets($inputStream, 4096);
shell_exec(sprintf('stty %s', $sttyMode));
if (false === $value) {
throw new \RuntimeException('Aborted');
}
$value = trim($value);
$this->output->writeln('');
return $value;
}
if (false !== $shell = $this->getShell()) {
$readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword';
$command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
$value = rtrim(shell_exec($command));
$this->output->writeln('');
return $value;
}
throw new \RuntimeException('Unable to hide the response.');
}
protected function validateAttempts($interviewer)
{
/** @var \Exception $error */
$error = null;
$attempts = $this->question->getMaxAttempts();
while (null === $attempts || $attempts--) {
if (null !== $error) {
$this->output->error($error->getMessage());
}
try {
return call_user_func($this->question->getValidator(), $interviewer());
} catch (\Exception $error) {
}
}
throw $error;
}
/**
* 显示问题的提示信息
*/
protected function writePrompt()
{
$text = $this->question->getQuestion();
$default = $this->question->getDefault();
switch (true) {
case null === $default:
$text = sprintf(' <info>%s</info>:', $text);
break;
case $this->question instanceof Confirmation:
$text = sprintf(' <info>%s (yes/no)</info> [<comment>%s</comment>]:', $text, $default ? 'yes' : 'no');
break;
case $this->question instanceof Choice && $this->question->isMultiselect():
$choices = $this->question->getChoices();
$default = explode(',', $default);
foreach ($default as $key => $value) {
$default[$key] = $choices[trim($value)];
}
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, implode(', ', $default));
break;
case $this->question instanceof Choice:
$choices = $this->question->getChoices();
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, $choices[$default]);
break;
default:
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, $default);
}
$this->output->writeln($text);
if ($this->question instanceof Choice) {
$width = max(array_map('strlen', array_keys($this->question->getChoices())));
foreach ($this->question->getChoices() as $key => $value) {
$this->output->writeln(sprintf(" [<comment>%-${width}s</comment>] %s", $key, $value));
}
}
$this->output->write(' > ');
}
private function getShell()
{
if (null !== self::$shell) {
return self::$shell;
}
self::$shell = false;
if (file_exists('/usr/bin/env')) {
$test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
foreach (['bash', 'zsh', 'ksh', 'csh'] as $sh) {
if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
self::$shell = $sh;
break;
}
}
}
return self::$shell;
}
private function hasSttyAvailable()
{
if (null !== self::$stty) {
return self::$stty;
}
exec('stty 2>&1', $output, $exitcode);
return self::$stty = $exitcode === 0;
}
}

View File

@ -0,0 +1,319 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\output;
use think\Console;
use think\console\Command;
use think\console\input\Argument as InputArgument;
use think\console\input\Definition as InputDefinition;
use think\console\input\Option as InputOption;
use think\console\Output;
use think\console\output\descriptor\Console as ConsoleDescription;
class Descriptor
{
/**
* @var Output
*/
protected $output;
/**
* {@inheritdoc}
*/
public function describe(Output $output, $object, array $options = [])
{
$this->output = $output;
switch (true) {
case $object instanceof InputArgument:
$this->describeInputArgument($object, $options);
break;
case $object instanceof InputOption:
$this->describeInputOption($object, $options);
break;
case $object instanceof InputDefinition:
$this->describeInputDefinition($object, $options);
break;
case $object instanceof Command:
$this->describeCommand($object, $options);
break;
case $object instanceof Console:
$this->describeConsole($object, $options);
break;
default:
throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object)));
}
}
/**
* 输出内容
* @param string $content
* @param bool $decorated
*/
protected function write($content, $decorated = false)
{
$this->output->write($content, false, $decorated ? Output::OUTPUT_NORMAL : Output::OUTPUT_RAW);
}
/**
* 描述参数
* @param InputArgument $argument
* @param array $options
* @return void
*/
protected function describeInputArgument(InputArgument $argument, array $options = [])
{
if (null !== $argument->getDefault()
&& (!is_array($argument->getDefault())
|| count($argument->getDefault()))
) {
$default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($argument->getDefault()));
} else {
$default = '';
}
$totalWidth = isset($options['total_width']) ? $options['total_width'] : strlen($argument->getName());
$spacingWidth = $totalWidth - strlen($argument->getName()) + 2;
$this->writeText(sprintf(" <info>%s</info>%s%s%s", $argument->getName(), str_repeat(' ', $spacingWidth), // + 17 = 2 spaces + <info> + </info> + 2 spaces
preg_replace('/\s*\R\s*/', PHP_EOL . str_repeat(' ', $totalWidth + 17), $argument->getDescription()), $default), $options);
}
/**
* 描述选项
* @param InputOption $option
* @param array $options
* @return void
*/
protected function describeInputOption(InputOption $option, array $options = [])
{
if ($option->acceptValue() && null !== $option->getDefault()
&& (!is_array($option->getDefault())
|| count($option->getDefault()))
) {
$default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($option->getDefault()));
} else {
$default = '';
}
$value = '';
if ($option->acceptValue()) {
$value = '=' . strtoupper($option->getName());
if ($option->isValueOptional()) {
$value = '[' . $value . ']';
}
}
$totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions([$option]);
$synopsis = sprintf('%s%s', $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ', sprintf('--%s%s', $option->getName(), $value));
$spacingWidth = $totalWidth - strlen($synopsis) + 2;
$this->writeText(sprintf(" <info>%s</info>%s%s%s%s", $synopsis, str_repeat(' ', $spacingWidth), // + 17 = 2 spaces + <info> + </info> + 2 spaces
preg_replace('/\s*\R\s*/', "\n" . str_repeat(' ', $totalWidth + 17), $option->getDescription()), $default, $option->isArray() ? '<comment> (multiple values allowed)</comment>' : ''), $options);
}
/**
* 描述输入
* @param InputDefinition $definition
* @param array $options
* @return void
*/
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
{
$totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions());
foreach ($definition->getArguments() as $argument) {
$totalWidth = max($totalWidth, strlen($argument->getName()));
}
if ($definition->getArguments()) {
$this->writeText('<comment>Arguments:</comment>', $options);
$this->writeText("\n");
foreach ($definition->getArguments() as $argument) {
$this->describeInputArgument($argument, array_merge($options, ['total_width' => $totalWidth]));
$this->writeText("\n");
}
}
if ($definition->getArguments() && $definition->getOptions()) {
$this->writeText("\n");
}
if ($definition->getOptions()) {
$laterOptions = [];
$this->writeText('<comment>Options:</comment>', $options);
foreach ($definition->getOptions() as $option) {
if (strlen($option->getShortcut()) > 1) {
$laterOptions[] = $option;
continue;
}
$this->writeText("\n");
$this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth]));
}
foreach ($laterOptions as $option) {
$this->writeText("\n");
$this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth]));
}
}
}
/**
* 描述指令
* @param Command $command
* @param array $options
* @return void
*/
protected function describeCommand(Command $command, array $options = [])
{
$command->getSynopsis(true);
$command->getSynopsis(false);
$command->mergeConsoleDefinition(false);
$this->writeText('<comment>Usage:</comment>', $options);
foreach (array_merge([$command->getSynopsis(true)], $command->getAliases(), $command->getUsages()) as $usage) {
$this->writeText("\n");
$this->writeText(' ' . $usage, $options);
}
$this->writeText("\n");
$definition = $command->getNativeDefinition();
if ($definition->getOptions() || $definition->getArguments()) {
$this->writeText("\n");
$this->describeInputDefinition($definition, $options);
$this->writeText("\n");
}
if ($help = $command->getProcessedHelp()) {
$this->writeText("\n");
$this->writeText('<comment>Help:</comment>', $options);
$this->writeText("\n");
$this->writeText(' ' . str_replace("\n", "\n ", $help), $options);
$this->writeText("\n");
}
}
/**
* 描述控制台
* @param Console $console
* @param array $options
* @return void
*/
protected function describeConsole(Console $console, array $options = [])
{
$describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
$description = new ConsoleDescription($console, $describedNamespace);
if (isset($options['raw_text']) && $options['raw_text']) {
$width = $this->getColumnWidth($description->getCommands());
foreach ($description->getCommands() as $command) {
$this->writeText(sprintf("%-${width}s %s", $command->getName(), $command->getDescription()), $options);
$this->writeText("\n");
}
} else {
if ('' != $help = $console->getHelp()) {
$this->writeText("$help\n\n", $options);
}
$this->writeText("<comment>Usage:</comment>\n", $options);
$this->writeText(" command [options] [arguments]\n\n", $options);
$this->describeInputDefinition(new InputDefinition($console->getDefinition()->getOptions()), $options);
$this->writeText("\n");
$this->writeText("\n");
$width = $this->getColumnWidth($description->getCommands());
if ($describedNamespace) {
$this->writeText(sprintf('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options);
} else {
$this->writeText('<comment>Available commands:</comment>', $options);
}
// add commands by namespace
foreach ($description->getNamespaces() as $namespace) {
if (!$describedNamespace && ConsoleDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
$this->writeText("\n");
$this->writeText(' <comment>' . $namespace['id'] . '</comment>', $options);
}
foreach ($namespace['commands'] as $name) {
$this->writeText("\n");
$spacingWidth = $width - strlen($name);
$this->writeText(sprintf(" <info>%s</info>%s%s", $name, str_repeat(' ', $spacingWidth), $description->getCommand($name)
->getDescription()), $options);
}
}
$this->writeText("\n");
}
}
/**
* {@inheritdoc}
*/
private function writeText($content, array $options = [])
{
$this->write(isset($options['raw_text'])
&& $options['raw_text'] ? strip_tags($content) : $content, isset($options['raw_output']) ? !$options['raw_output'] : true);
}
/**
* 格式化
* @param mixed $default
* @return string
*/
private function formatDefaultValue($default)
{
return json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
/**
* @param Command[] $commands
* @return int
*/
private function getColumnWidth(array $commands)
{
$width = 0;
foreach ($commands as $command) {
$width = strlen($command->getName()) > $width ? strlen($command->getName()) : $width;
}
return $width + 2;
}
/**
* @param InputOption[] $options
* @return int
*/
private function calculateTotalWidthForOptions($options)
{
$totalWidth = 0;
foreach ($options as $option) {
$nameLength = 4 + strlen($option->getName()) + 2; // - + shortcut + , + whitespace + name + --
if ($option->acceptValue()) {
$valueLength = 1 + strlen($option->getName()); // = + value
$valueLength += $option->isValueOptional() ? 2 : 0; // [ + ]
$nameLength += $valueLength;
}
$totalWidth = max($totalWidth, $nameLength);
}
return $totalWidth;
}
}

View File

@ -0,0 +1,198 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\output;
use think\console\output\formatter\Stack as StyleStack;
use think\console\output\formatter\Style;
class Formatter
{
private $decorated = false;
private $styles = [];
private $styleStack;
/**
* 转义
* @param string $text
* @return string
*/
public static function escape($text)
{
return preg_replace('/([^\\\\]?)</is', '$1\\<', $text);
}
/**
* 初始化命令行输出格式
*/
public function __construct()
{
$this->setStyle('error', new Style('white', 'red'));
$this->setStyle('info', new Style('green'));
$this->setStyle('comment', new Style('yellow'));
$this->setStyle('question', new Style('black', 'cyan'));
$this->setStyle('highlight', new Style('red'));
$this->setStyle('warning', new Style('black', 'yellow'));
$this->styleStack = new StyleStack();
}
/**
* 设置外观标识
* @param bool $decorated 是否美化文字
*/
public function setDecorated($decorated)
{
$this->decorated = (bool) $decorated;
}
/**
* 获取外观标识
* @return bool
*/
public function isDecorated()
{
return $this->decorated;
}
/**
* 添加一个新样式
* @param string $name 样式名
* @param Style $style 样式实例
*/
public function setStyle($name, Style $style)
{
$this->styles[strtolower($name)] = $style;
}
/**
* 是否有这个样式
* @param string $name
* @return bool
*/
public function hasStyle($name)
{
return isset($this->styles[strtolower($name)]);
}
/**
* 获取样式
* @param string $name
* @return Style
* @throws \InvalidArgumentException
*/
public function getStyle($name)
{
if (!$this->hasStyle($name)) {
throw new \InvalidArgumentException(sprintf('Undefined style: %s', $name));
}
return $this->styles[strtolower($name)];
}
/**
* 使用所给的样式格式化文字
* @param string $message 文字
* @return string
*/
public function format($message)
{
$offset = 0;
$output = '';
$tagRegex = '[a-z][a-z0-9_=;-]*';
preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#isx", $message, $matches, PREG_OFFSET_CAPTURE);
foreach ($matches[0] as $i => $match) {
$pos = $match[1];
$text = $match[0];
if (0 != $pos && '\\' == $message[$pos - 1]) {
continue;
}
$output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset));
$offset = $pos + strlen($text);
if ($open = '/' != $text[1]) {
$tag = $matches[1][$i][0];
} else {
$tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : '';
}
if (!$open && !$tag) {
// </>
$this->styleStack->pop();
} elseif (false === $style = $this->createStyleFromString(strtolower($tag))) {
$output .= $this->applyCurrentStyle($text);
} elseif ($open) {
$this->styleStack->push($style);
} else {
$this->styleStack->pop($style);
}
}
$output .= $this->applyCurrentStyle(substr($message, $offset));
return str_replace('\\<', '<', $output);
}
/**
* @return StyleStack
*/
public function getStyleStack()
{
return $this->styleStack;
}
/**
* 根据字符串创建新的样式实例
* @param string $string
* @return Style|bool
*/
private function createStyleFromString($string)
{
if (isset($this->styles[$string])) {
return $this->styles[$string];
}
if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) {
return false;
}
$style = new Style();
foreach ($matches as $match) {
array_shift($match);
if ('fg' == $match[0]) {
$style->setForeground($match[1]);
} elseif ('bg' == $match[0]) {
$style->setBackground($match[1]);
} else {
try {
$style->setOption($match[1]);
} catch (\InvalidArgumentException $e) {
return false;
}
}
}
return $style;
}
/**
* 从堆栈应用样式到文字
* @param string $text 文字
* @return string
*/
private function applyCurrentStyle($text)
{
return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text;
}
}

View File

@ -0,0 +1,211 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\output;
class Question
{
private $question;
private $attempts;
private $hidden = false;
private $hiddenFallback = true;
private $autocompleterValues;
private $validator;
private $default;
private $normalizer;
/**
* 构造方法
* @param string $question 问题
* @param mixed $default 默认答案
*/
public function __construct($question, $default = null)
{
$this->question = $question;
$this->default = $default;
}
/**
* 获取问题
* @return string
*/
public function getQuestion()
{
return $this->question;
}
/**
* 获取默认答案
* @return mixed
*/
public function getDefault()
{
return $this->default;
}
/**
* 是否隐藏答案
* @return bool
*/
public function isHidden()
{
return $this->hidden;
}
/**
* 隐藏答案
* @param bool $hidden
* @return Question
*/
public function setHidden($hidden)
{
if ($this->autocompleterValues) {
throw new \LogicException('A hidden question cannot use the autocompleter.');
}
$this->hidden = (bool) $hidden;
return $this;
}
/**
* 不能被隐藏是否撤销
* @return bool
*/
public function isHiddenFallback()
{
return $this->hiddenFallback;
}
/**
* 设置不能被隐藏的时候的操作
* @param bool $fallback
* @return Question
*/
public function setHiddenFallback($fallback)
{
$this->hiddenFallback = (bool) $fallback;
return $this;
}
/**
* 获取自动完成
* @return null|array|\Traversable
*/
public function getAutocompleterValues()
{
return $this->autocompleterValues;
}
/**
* 设置自动完成的值
* @param null|array|\Traversable $values
* @return Question
* @throws \InvalidArgumentException
* @throws \LogicException
*/
public function setAutocompleterValues($values)
{
if (is_array($values) && $this->isAssoc($values)) {
$values = array_merge(array_keys($values), array_values($values));
}
if (null !== $values && !is_array($values)) {
if (!$values instanceof \Traversable || $values instanceof \Countable) {
throw new \InvalidArgumentException('Autocompleter values can be either an array, `null` or an object implementing both `Countable` and `Traversable` interfaces.');
}
}
if ($this->hidden) {
throw new \LogicException('A hidden question cannot use the autocompleter.');
}
$this->autocompleterValues = $values;
return $this;
}
/**
* 设置答案的验证器
* @param null|callable $validator
* @return Question The current instance
*/
public function setValidator($validator)
{
$this->validator = $validator;
return $this;
}
/**
* 获取验证器
* @return null|callable
*/
public function getValidator()
{
return $this->validator;
}
/**
* 设置最大重试次数
* @param null|int $attempts
* @return Question
* @throws \InvalidArgumentException
*/
public function setMaxAttempts($attempts)
{
if (null !== $attempts && $attempts < 1) {
throw new \InvalidArgumentException('Maximum number of attempts must be a positive value.');
}
$this->attempts = $attempts;
return $this;
}
/**
* 获取最大重试次数
* @return null|int
*/
public function getMaxAttempts()
{
return $this->attempts;
}
/**
* 设置响应的回调
* @param string|\Closure $normalizer
* @return Question
*/
public function setNormalizer($normalizer)
{
$this->normalizer = $normalizer;
return $this;
}
/**
* 获取响应回调
* The normalizer can ba a callable (a string), a closure or a class implementing __invoke.
* @return string|\Closure
*/
public function getNormalizer()
{
return $this->normalizer;
}
protected function isAssoc($array)
{
return (bool) count(array_filter(array_keys($array), 'is_string'));
}
}

View File

@ -0,0 +1,149 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\output\descriptor;
use think\Console as ThinkConsole;
use think\console\Command;
class Console
{
const GLOBAL_NAMESPACE = '_global';
/**
* @var ThinkConsole
*/
private $console;
/**
* @var null|string
*/
private $namespace;
/**
* @var array
*/
private $namespaces;
/**
* @var Command[]
*/
private $commands;
/**
* @var Command[]
*/
private $aliases;
/**
* 构造方法
* @param ThinkConsole $console
* @param string|null $namespace
*/
public function __construct(ThinkConsole $console, $namespace = null)
{
$this->console = $console;
$this->namespace = $namespace;
}
/**
* @return array
*/
public function getNamespaces()
{
if (null === $this->namespaces) {
$this->inspectConsole();
}
return $this->namespaces;
}
/**
* @return Command[]
*/
public function getCommands()
{
if (null === $this->commands) {
$this->inspectConsole();
}
return $this->commands;
}
/**
* @param string $name
* @return Command
* @throws \InvalidArgumentException
*/
public function getCommand($name)
{
if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) {
throw new \InvalidArgumentException(sprintf('Command %s does not exist.', $name));
}
return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name];
}
private function inspectConsole()
{
$this->commands = [];
$this->namespaces = [];
$all = $this->console->all($this->namespace ? $this->console->findNamespace($this->namespace) : null);
foreach ($this->sortCommands($all) as $namespace => $commands) {
$names = [];
/** @var Command $command */
foreach ($commands as $name => $command) {
if (!$command->getName()) {
continue;
}
if ($command->getName() === $name) {
$this->commands[$name] = $command;
} else {
$this->aliases[$name] = $command;
}
$names[] = $name;
}
$this->namespaces[$namespace] = ['id' => $namespace, 'commands' => $names];
}
}
/**
* @param array $commands
* @return array
*/
private function sortCommands(array $commands)
{
$namespacedCommands = [];
foreach ($commands as $name => $command) {
$key = $this->console->extractNamespace($name, 1);
if (!$key) {
$key = self::GLOBAL_NAMESPACE;
}
$namespacedCommands[$key][$name] = $command;
}
ksort($namespacedCommands);
foreach ($namespacedCommands as &$commandsSet) {
ksort($commandsSet);
}
// unset reference to keep scope clear
unset($commandsSet);
return $namespacedCommands;
}
}

View File

@ -0,0 +1,52 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\output\driver;
use think\console\Output;
class Buffer
{
/**
* @var string
*/
private $buffer = '';
public function __construct(Output $output)
{
// do nothing
}
public function fetch()
{
$content = $this->buffer;
$this->buffer = '';
return $content;
}
public function write($messages, $newline = false, $options = Output::OUTPUT_NORMAL)
{
$messages = (array) $messages;
foreach ($messages as $message) {
$this->buffer .= $message;
}
if ($newline) {
$this->buffer .= "\n";
}
}
public function renderException(\Exception $e)
{
// do nothing
}
}

View File

@ -0,0 +1,373 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\output\driver;
use think\console\Output;
use think\console\output\Formatter;
class Console
{
/** @var Resource */
private $stdout;
/** @var Formatter */
private $formatter;
private $terminalDimensions;
/** @var Output */
private $output;
public function __construct(Output $output)
{
$this->output = $output;
$this->formatter = new Formatter();
$this->stdout = $this->openOutputStream();
$decorated = $this->hasColorSupport($this->stdout);
$this->formatter->setDecorated($decorated);
}
public function getFormatter()
{
return $this->formatter;
}
public function setDecorated($decorated)
{
$this->formatter->setDecorated($decorated);
}
public function write($messages, $newline = false, $type = Output::OUTPUT_NORMAL, $stream = null)
{
if (Output::VERBOSITY_QUIET === $this->output->getVerbosity()) {
return;
}
$messages = (array) $messages;
foreach ($messages as $message) {
switch ($type) {
case Output::OUTPUT_NORMAL:
$message = $this->formatter->format($message);
break;
case Output::OUTPUT_RAW:
break;
case Output::OUTPUT_PLAIN:
$message = strip_tags($this->formatter->format($message));
break;
default:
throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type));
}
$this->doWrite($message, $newline, $stream);
}
}
public function renderException(\Exception $e)
{
$stderr = $this->openErrorStream();
$decorated = $this->hasColorSupport($stderr);
$this->formatter->setDecorated($decorated);
do {
$title = sprintf(' [%s] ', get_class($e));
$len = $this->stringWidth($title);
$width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX;
if (defined('HHVM_VERSION') && $width > 1 << 31) {
$width = 1 << 31;
}
$lines = [];
foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) {
foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
$lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $line)) + 4;
$lines[] = [$line, $lineLength];
$len = max($lineLength, $len);
}
}
$messages = ['', ''];
$messages[] = $emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $len));
$messages[] = sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title))));
foreach ($lines as $line) {
$messages[] = sprintf('<error> %s %s</error>', $line[0], str_repeat(' ', $len - $line[1]));
}
$messages[] = $emptyLine;
$messages[] = '';
$messages[] = '';
$this->write($messages, true, Output::OUTPUT_NORMAL, $stderr);
if (Output::VERBOSITY_VERBOSE <= $this->output->getVerbosity()) {
$this->write('<comment>Exception trace:</comment>', true, Output::OUTPUT_NORMAL, $stderr);
// exception related properties
$trace = $e->getTrace();
array_unshift($trace, [
'function' => '',
'file' => $e->getFile() !== null ? $e->getFile() : 'n/a',
'line' => $e->getLine() !== null ? $e->getLine() : 'n/a',
'args' => [],
]);
for ($i = 0, $count = count($trace); $i < $count; ++$i) {
$class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
$type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
$function = $trace[$i]['function'];
$file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
$line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
$this->write(sprintf(' %s%s%s() at <info>%s:%s</info>', $class, $type, $function, $file, $line), true, Output::OUTPUT_NORMAL, $stderr);
}
$this->write('', true, Output::OUTPUT_NORMAL, $stderr);
$this->write('', true, Output::OUTPUT_NORMAL, $stderr);
}
} while ($e = $e->getPrevious());
}
/**
* 获取终端宽度
* @return int|null
*/
protected function getTerminalWidth()
{
$dimensions = $this->getTerminalDimensions();
return $dimensions[0];
}
/**
* 获取终端高度
* @return int|null
*/
protected function getTerminalHeight()
{
$dimensions = $this->getTerminalDimensions();
return $dimensions[1];
}
/**
* 获取当前终端的尺寸
* @return array
*/
public function getTerminalDimensions()
{
if ($this->terminalDimensions) {
return $this->terminalDimensions;
}
if ('\\' === DS) {
if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
return [(int) $matches[1], (int) $matches[2]];
}
if (preg_match('/^(\d+)x(\d+)$/', $this->getMode(), $matches)) {
return [(int) $matches[1], (int) $matches[2]];
}
}
if ($sttyString = $this->getSttyColumns()) {
if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
return [(int) $matches[2], (int) $matches[1]];
}
if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {
return [(int) $matches[2], (int) $matches[1]];
}
}
return [null, null];
}
/**
* 获取stty列数
* @return string
*/
private function getSttyColumns()
{
if (!function_exists('proc_open')) {
return;
}
$descriptorspec = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
$process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
if (is_resource($process)) {
$info = stream_get_contents($pipes[1]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
return $info;
}
return;
}
/**
* 获取终端模式
* @return string <width>x<height> null
*/
private function getMode()
{
if (!function_exists('proc_open')) {
return;
}
$descriptorspec = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
$process = proc_open('mode CON', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
if (is_resource($process)) {
$info = stream_get_contents($pipes[1]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
return $matches[2] . 'x' . $matches[1];
}
}
return;
}
private function stringWidth($string)
{
if (!function_exists('mb_strwidth')) {
return strlen($string);
}
if (false === $encoding = mb_detect_encoding($string)) {
return strlen($string);
}
return mb_strwidth($string, $encoding);
}
private function splitStringByWidth($string, $width)
{
if (!function_exists('mb_strwidth')) {
return str_split($string, $width);
}
if (false === $encoding = mb_detect_encoding($string)) {
return str_split($string, $width);
}
$utf8String = mb_convert_encoding($string, 'utf8', $encoding);
$lines = [];
$line = '';
foreach (preg_split('//u', $utf8String) as $char) {
if (mb_strwidth($line . $char, 'utf8') <= $width) {
$line .= $char;
continue;
}
$lines[] = str_pad($line, $width);
$line = $char;
}
if (strlen($line)) {
$lines[] = count($lines) ? str_pad($line, $width) : $line;
}
mb_convert_variables($encoding, 'utf8', $lines);
return $lines;
}
private function isRunningOS400()
{
$checks = [
function_exists('php_uname') ? php_uname('s') : '',
getenv('OSTYPE'),
PHP_OS,
];
return false !== stripos(implode(';', $checks), 'OS400');
}
/**
* 当前环境是否支持写入控制台输出到stdout.
*
* @return bool
*/
protected function hasStdoutSupport()
{
return false === $this->isRunningOS400();
}
/**
* 当前环境是否支持写入控制台输出到stderr.
*
* @return bool
*/
protected function hasStderrSupport()
{
return false === $this->isRunningOS400();
}
/**
* @return resource
*/
private function openOutputStream()
{
if (!$this->hasStdoutSupport()) {
return fopen('php://output', 'w');
}
return @fopen('php://stdout', 'w') ?: fopen('php://output', 'w');
}
/**
* @return resource
*/
private function openErrorStream()
{
return fopen($this->hasStderrSupport() ? 'php://stderr' : 'php://output', 'w');
}
/**
* 将消息写入到输出。
* @param string $message 消息
* @param bool $newline 是否另起一行
* @param null $stream
*/
protected function doWrite($message, $newline, $stream = null)
{
if (null === $stream) {
$stream = $this->stdout;
}
if (false === @fwrite($stream, $message . ($newline ? PHP_EOL : ''))) {
throw new \RuntimeException('Unable to write output.');
}
fflush($stream);
}
/**
* 是否支持着色
* @param $stream
* @return bool
*/
protected function hasColorSupport($stream)
{
if (DIRECTORY_SEPARATOR === '\\') {
return
'10.0.10586' === PHP_WINDOWS_VERSION_MAJOR . '.' . PHP_WINDOWS_VERSION_MINOR . '.' . PHP_WINDOWS_VERSION_BUILD
|| false !== getenv('ANSICON')
|| 'ON' === getenv('ConEmuANSI')
|| 'xterm' === getenv('TERM');
}
return function_exists('posix_isatty') && @posix_isatty($stream);
}
}

View File

@ -0,0 +1,33 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\output\driver;
use think\console\Output;
class Nothing
{
public function __construct(Output $output)
{
// do nothing
}
public function write($messages, $newline = false, $options = Output::OUTPUT_NORMAL)
{
// do nothing
}
public function renderException(\Exception $e)
{
// do nothing
}
}

View File

@ -0,0 +1,116 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\output\formatter;
class Stack
{
/**
* @var Style[]
*/
private $styles;
/**
* @var Style
*/
private $emptyStyle;
/**
* 构造方法
* @param Style|null $emptyStyle
*/
public function __construct(Style $emptyStyle = null)
{
$this->emptyStyle = $emptyStyle ?: new Style();
$this->reset();
}
/**
* 重置堆栈
*/
public function reset()
{
$this->styles = [];
}
/**
* 推一个样式进入堆栈
* @param Style $style
*/
public function push(Style $style)
{
$this->styles[] = $style;
}
/**
* 从堆栈中弹出一个样式
* @param Style|null $style
* @return Style
* @throws \InvalidArgumentException
*/
public function pop(Style $style = null)
{
if (empty($this->styles)) {
return $this->emptyStyle;
}
if (null === $style) {
return array_pop($this->styles);
}
/**
* @var int $index
* @var Style $stackedStyle
*/
foreach (array_reverse($this->styles, true) as $index => $stackedStyle) {
if ($style->apply('') === $stackedStyle->apply('')) {
$this->styles = array_slice($this->styles, 0, $index);
return $stackedStyle;
}
}
throw new \InvalidArgumentException('Incorrectly nested style tag found.');
}
/**
* 计算堆栈的当前样式。
* @return Style
*/
public function getCurrent()
{
if (empty($this->styles)) {
return $this->emptyStyle;
}
return $this->styles[count($this->styles) - 1];
}
/**
* @param Style $emptyStyle
* @return Stack
*/
public function setEmptyStyle(Style $emptyStyle)
{
$this->emptyStyle = $emptyStyle;
return $this;
}
/**
* @return Style
*/
public function getEmptyStyle()
{
return $this->emptyStyle;
}
}

View File

@ -0,0 +1,189 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\output\formatter;
class Style
{
private static $availableForegroundColors = [
'black' => ['set' => 30, 'unset' => 39],
'red' => ['set' => 31, 'unset' => 39],
'green' => ['set' => 32, 'unset' => 39],
'yellow' => ['set' => 33, 'unset' => 39],
'blue' => ['set' => 34, 'unset' => 39],
'magenta' => ['set' => 35, 'unset' => 39],
'cyan' => ['set' => 36, 'unset' => 39],
'white' => ['set' => 37, 'unset' => 39],
];
private static $availableBackgroundColors = [
'black' => ['set' => 40, 'unset' => 49],
'red' => ['set' => 41, 'unset' => 49],
'green' => ['set' => 42, 'unset' => 49],
'yellow' => ['set' => 43, 'unset' => 49],
'blue' => ['set' => 44, 'unset' => 49],
'magenta' => ['set' => 45, 'unset' => 49],
'cyan' => ['set' => 46, 'unset' => 49],
'white' => ['set' => 47, 'unset' => 49],
];
private static $availableOptions = [
'bold' => ['set' => 1, 'unset' => 22],
'underscore' => ['set' => 4, 'unset' => 24],
'blink' => ['set' => 5, 'unset' => 25],
'reverse' => ['set' => 7, 'unset' => 27],
'conceal' => ['set' => 8, 'unset' => 28],
];
private $foreground;
private $background;
private $options = [];
/**
* 初始化输出的样式
* @param string|null $foreground 字体颜色
* @param string|null $background 背景色
* @param array $options 格式
* @api
*/
public function __construct($foreground = null, $background = null, array $options = [])
{
if (null !== $foreground) {
$this->setForeground($foreground);
}
if (null !== $background) {
$this->setBackground($background);
}
if (count($options)) {
$this->setOptions($options);
}
}
/**
* 设置字体颜色
* @param string|null $color 颜色名
* @throws \InvalidArgumentException
* @api
*/
public function setForeground($color = null)
{
if (null === $color) {
$this->foreground = null;
return;
}
if (!isset(static::$availableForegroundColors[$color])) {
throw new \InvalidArgumentException(sprintf('Invalid foreground color specified: "%s". Expected one of (%s)', $color, implode(', ', array_keys(static::$availableForegroundColors))));
}
$this->foreground = static::$availableForegroundColors[$color];
}
/**
* 设置背景色
* @param string|null $color 颜色名
* @throws \InvalidArgumentException
* @api
*/
public function setBackground($color = null)
{
if (null === $color) {
$this->background = null;
return;
}
if (!isset(static::$availableBackgroundColors[$color])) {
throw new \InvalidArgumentException(sprintf('Invalid background color specified: "%s". Expected one of (%s)', $color, implode(', ', array_keys(static::$availableBackgroundColors))));
}
$this->background = static::$availableBackgroundColors[$color];
}
/**
* 设置字体格式
* @param string $option 格式名
* @throws \InvalidArgumentException When the option name isn't defined
* @api
*/
public function setOption($option)
{
if (!isset(static::$availableOptions[$option])) {
throw new \InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s)', $option, implode(', ', array_keys(static::$availableOptions))));
}
if (!in_array(static::$availableOptions[$option], $this->options)) {
$this->options[] = static::$availableOptions[$option];
}
}
/**
* 重置字体格式
* @param string $option 格式名
* @throws \InvalidArgumentException
*/
public function unsetOption($option)
{
if (!isset(static::$availableOptions[$option])) {
throw new \InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s)', $option, implode(', ', array_keys(static::$availableOptions))));
}
$pos = array_search(static::$availableOptions[$option], $this->options);
if (false !== $pos) {
unset($this->options[$pos]);
}
}
/**
* 批量设置字体格式
* @param array $options
*/
public function setOptions(array $options)
{
$this->options = [];
foreach ($options as $option) {
$this->setOption($option);
}
}
/**
* 应用样式到文字
* @param string $text 文字
* @return string
*/
public function apply($text)
{
$setCodes = [];
$unsetCodes = [];
if (null !== $this->foreground) {
$setCodes[] = $this->foreground['set'];
$unsetCodes[] = $this->foreground['unset'];
}
if (null !== $this->background) {
$setCodes[] = $this->background['set'];
$unsetCodes[] = $this->background['unset'];
}
if (count($this->options)) {
foreach ($this->options as $option) {
$setCodes[] = $option['set'];
$unsetCodes[] = $option['unset'];
}
}
if (0 === count($setCodes)) {
return $text;
}
return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes));
}
}

View File

@ -0,0 +1,163 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\output\question;
use think\console\output\Question;
class Choice extends Question
{
private $choices;
private $multiselect = false;
private $prompt = ' > ';
private $errorMessage = 'Value "%s" is invalid';
/**
* 构造方法
* @param string $question 问题
* @param array $choices 选项
* @param mixed $default 默认答案
*/
public function __construct($question, array $choices, $default = null)
{
parent::__construct($question, $default);
$this->choices = $choices;
$this->setValidator($this->getDefaultValidator());
$this->setAutocompleterValues($choices);
}
/**
* 可选项
* @return array
*/
public function getChoices()
{
return $this->choices;
}
/**
* 设置可否多选
* @param bool $multiselect
* @return self
*/
public function setMultiselect($multiselect)
{
$this->multiselect = $multiselect;
$this->setValidator($this->getDefaultValidator());
return $this;
}
public function isMultiselect()
{
return $this->multiselect;
}
/**
* 获取提示
* @return string
*/
public function getPrompt()
{
return $this->prompt;
}
/**
* 设置提示
* @param string $prompt
* @return self
*/
public function setPrompt($prompt)
{
$this->prompt = $prompt;
return $this;
}
/**
* 设置错误提示信息
* @param string $errorMessage
* @return self
*/
public function setErrorMessage($errorMessage)
{
$this->errorMessage = $errorMessage;
$this->setValidator($this->getDefaultValidator());
return $this;
}
/**
* 获取默认的验证方法
* @return callable
*/
private function getDefaultValidator()
{
$choices = $this->choices;
$errorMessage = $this->errorMessage;
$multiselect = $this->multiselect;
$isAssoc = $this->isAssoc($choices);
return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) {
// Collapse all spaces.
$selectedChoices = str_replace(' ', '', $selected);
if ($multiselect) {
// Check for a separated comma values
if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) {
throw new \InvalidArgumentException(sprintf($errorMessage, $selected));
}
$selectedChoices = explode(',', $selectedChoices);
} else {
$selectedChoices = [$selected];
}
$multiselectChoices = [];
foreach ($selectedChoices as $value) {
$results = [];
foreach ($choices as $key => $choice) {
if ($choice === $value) {
$results[] = $key;
}
}
if (count($results) > 1) {
throw new \InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', implode(' or ', $results)));
}
$result = array_search($value, $choices);
if (!$isAssoc) {
if (!empty($result)) {
$result = $choices[$result];
} elseif (isset($choices[$value])) {
$result = $choices[$value];
}
} elseif (empty($result) && array_key_exists($value, $choices)) {
$result = $value;
}
if (empty($result)) {
throw new \InvalidArgumentException(sprintf($errorMessage, $value));
}
array_push($multiselectChoices, $result);
}
if ($multiselect) {
return $multiselectChoices;
}
return current($multiselectChoices);
};
}
}

View File

@ -0,0 +1,57 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\output\question;
use think\console\output\Question;
class Confirmation extends Question
{
private $trueAnswerRegex;
/**
* 构造方法
* @param string $question 问题
* @param bool $default 默认答案
* @param string $trueAnswerRegex 验证正则
*/
public function __construct($question, $default = true, $trueAnswerRegex = '/^y/i')
{
parent::__construct($question, (bool) $default);
$this->trueAnswerRegex = $trueAnswerRegex;
$this->setNormalizer($this->getDefaultNormalizer());
}
/**
* 获取默认的答案回调
* @return callable
*/
private function getDefaultNormalizer()
{
$default = $this->getDefault();
$regex = $this->trueAnswerRegex;
return function ($answer) use ($default, $regex) {
if (is_bool($answer)) {
return $answer;
}
$answerIsTrue = (bool) preg_match($regex, $answer);
if (false === $default) {
return $answer && $answerIsTrue;
}
return !$answer || $answerIsTrue;
};
}
}

View File

@ -0,0 +1,99 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\controller;
use think\App;
use think\Request;
use think\Response;
abstract class Rest
{
protected $method; // 当前请求类型
protected $type; // 当前资源类型
// 输出类型
protected $restMethodList = 'get|post|put|delete';
protected $restDefaultMethod = 'get';
protected $restTypeList = 'html|xml|json|rss';
protected $restDefaultType = 'html';
protected $restOutputType = [ // REST允许输出的资源类型列表
'xml' => 'application/xml',
'json' => 'application/json',
'html' => 'text/html',
];
/**
* 构造函数 取得模板对象实例
* @access public
*/
public function __construct()
{
// 资源类型检测
$request = Request::instance();
$ext = $request->ext();
if ('' == $ext) {
// 自动检测资源类型
$this->type = $request->type();
} elseif (!preg_match('/\(' . $this->restTypeList . '\)$/i', $ext)) {
// 资源类型非法 则用默认资源类型访问
$this->type = $this->restDefaultType;
} else {
$this->type = $ext;
}
// 请求方式检测
$method = strtolower($request->method());
if (false === stripos($this->restMethodList, $method)) {
// 请求方式非法 则用默认请求方法
$method = $this->restDefaultMethod;
}
$this->method = $method;
}
/**
* REST 调用
* @access public
* @param string $method 方法名
* @return mixed
* @throws \Exception
*/
public function _empty($method)
{
if (method_exists($this, $method . '_' . $this->method . '_' . $this->type)) {
// RESTFul方法支持
$fun = $method . '_' . $this->method . '_' . $this->type;
} elseif ($this->method == $this->restDefaultMethod && method_exists($this, $method . '_' . $this->type)) {
$fun = $method . '_' . $this->type;
} elseif ($this->type == $this->restDefaultType && method_exists($this, $method . '_' . $this->method)) {
$fun = $method . '_' . $this->method;
}
if (isset($fun)) {
return App::invokeMethod([$this, $fun]);
} else {
// 抛出异常
throw new \Exception('error action :' . $method);
}
}
/**
* 输出返回数据
* @access protected
* @param mixed $data 要返回的数据
* @param String $type 返回类型 JSON XML
* @param integer $code HTTP状态码
* @return Response
*/
protected function response($data, $type = 'json', $code = 200)
{
return Response::create($data, $type)->code($code);
}
}

View File

@ -0,0 +1,51 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\controller;
/**
* ThinkPHP Yar控制器类
*/
abstract class Yar
{
/**
* 构造函数
* @access public
*/
public function __construct()
{
//控制器初始化
if (method_exists($this, '_initialize')) {
$this->_initialize();
}
//判断扩展是否存在
if (!extension_loaded('yar')) {
throw new \Exception('not support yar');
}
//实例化Yar_Server
$server = new \Yar_Server($this);
// 启动server
$server->handle();
}
/**
* 魔术方法 有不存在的操作的时候执行
* @access public
* @param string $method 方法名
* @param array $args 参数
* @return mixed
*/
public function __call($method, $args)
{}
}

View File

@ -0,0 +1,864 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db;
use BadMethodCallException;
use PDO;
use think\Exception;
abstract class Builder
{
// connection对象实例
protected $connection;
// 查询对象实例
protected $query;
// 数据库表达式
protected $exp = ['eq' => '=', 'neq' => '<>', 'gt' => '>', 'egt' => '>=', 'lt' => '<', 'elt' => '<=', 'notlike' => 'NOT LIKE', 'not like' => 'NOT LIKE', 'like' => 'LIKE', 'in' => 'IN', 'exp' => 'EXP', 'notin' => 'NOT IN', 'not in' => 'NOT IN', 'between' => 'BETWEEN', 'not between' => 'NOT BETWEEN', 'notbetween' => 'NOT BETWEEN', 'exists' => 'EXISTS', 'notexists' => 'NOT EXISTS', 'not exists' => 'NOT EXISTS', 'null' => 'NULL', 'notnull' => 'NOT NULL', 'not null' => 'NOT NULL', '> time' => '> TIME', '< time' => '< TIME', '>= time' => '>= TIME', '<= time' => '<= TIME', 'between time' => 'BETWEEN TIME', 'not between time' => 'NOT BETWEEN TIME', 'notbetween time' => 'NOT BETWEEN TIME'];
// SQL表达式
protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%';
protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
/**
* 构造函数
* @access public
* @param Connection $connection 数据库连接对象实例
* @param Query $query 数据库查询对象实例
*/
public function __construct(Connection $connection, Query $query)
{
$this->connection = $connection;
$this->query = $query;
}
/**
* 获取当前的连接对象实例
* @access public
* @return Connection
*/
public function getConnection()
{
return $this->connection;
}
/**
* 获取当前的Query对象实例
* @access public
* @return Query
*/
public function getQuery()
{
return $this->query;
}
/**
* 将SQL语句中的__TABLE_NAME__字符串替换成带前缀的表名小写
* @access protected
* @param string $sql sql语句
* @return string
*/
protected function parseSqlTable($sql)
{
return $this->query->parseSqlTable($sql);
}
/**
* 数据分析
* @access protected
* @param array $data 数据
* @param array $options 查询参数
* @return array
* @throws Exception
*/
protected function parseData($data, $options)
{
if (empty($data)) {
return [];
}
// 获取绑定信息
$bind = $this->query->getFieldsBind($options['table']);
if ('*' == $options['field']) {
$fields = array_keys($bind);
} else {
$fields = $options['field'];
}
$result = [];
foreach ($data as $key => $val) {
$item = $this->parseKey($key, $options);
if (is_object($val) && method_exists($val, '__toString')) {
// 对象数据写入
$val = $val->__toString();
}
if (false === strpos($key, '.') && !in_array($key, $fields, true)) {
if ($options['strict']) {
throw new Exception('fields not exists:[' . $key . ']');
}
} elseif (is_null($val)) {
$result[$item] = 'NULL';
} elseif (isset($val[0]) && 'exp' == $val[0]) {
$result[$item] = $val[1];
} elseif (is_scalar($val)) {
// 过滤非标量数据
if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) {
$result[$item] = $val;
} else {
$key = str_replace('.', '_', $key);
$this->query->bind('data__' . $key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
$result[$item] = ':data__' . $key;
}
}
}
return $result;
}
/**
* 字段名分析
* @access protected
* @param string $key
* @param array $options
* @return string
*/
protected function parseKey($key, $options = [])
{
return $key;
}
/**
* value分析
* @access protected
* @param mixed $value
* @param string $field
* @return string|array
*/
protected function parseValue($value, $field = '')
{
if (is_string($value)) {
$value = strpos($value, ':') === 0 && $this->query->isBind(substr($value, 1)) ? $value : $this->connection->quote($value);
} elseif (is_array($value)) {
$value = array_map([$this, 'parseValue'], $value);
} elseif (is_bool($value)) {
$value = $value ? '1' : '0';
} elseif (is_null($value)) {
$value = 'null';
}
return $value;
}
/**
* field分析
* @access protected
* @param mixed $fields
* @param array $options
* @return string
*/
protected function parseField($fields, $options = [])
{
if ('*' == $fields || empty($fields)) {
$fieldsStr = '*';
} elseif (is_array($fields)) {
// 支持 'field1'=>'field2' 这样的字段别名定义
$array = [];
foreach ($fields as $key => $field) {
if (!is_numeric($key)) {
$array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options);
} else {
$array[] = $this->parseKey($field, $options);
}
}
$fieldsStr = implode(',', $array);
}
return $fieldsStr;
}
/**
* table分析
* @access protected
* @param mixed $tables
* @param array $options
* @return string
*/
protected function parseTable($tables, $options = [])
{
$item = [];
foreach ((array) $tables as $key => $table) {
if (!is_numeric($key)) {
if (strpos($key, '@think')) {
$key = strstr($key, '@think', true);
}
$key = $this->parseSqlTable($key);
$item[] = $this->parseKey($key) . ' ' . (isset($options['alias'][$table]) ? $this->parseKey($options['alias'][$table]) : $this->parseKey($table));
} else {
$table = $this->parseSqlTable($table);
if (isset($options['alias'][$table])) {
$item[] = $this->parseKey($table) . ' ' . $this->parseKey($options['alias'][$table]);
} else {
$item[] = $this->parseKey($table);
}
}
}
return implode(',', $item);
}
/**
* where分析
* @access protected
* @param mixed $where 查询条件
* @param array $options 查询参数
* @return string
*/
protected function parseWhere($where, $options)
{
$whereStr = $this->buildWhere($where, $options);
if (!empty($options['soft_delete'])) {
// 附加软删除条件
list($field, $condition) = $options['soft_delete'];
$binds = $this->query->getFieldsBind($options['table']);
$whereStr = $whereStr ? '( ' . $whereStr . ' ) AND ' : '';
$whereStr = $whereStr . $this->parseWhereItem($field, $condition, '', $options, $binds);
}
return empty($whereStr) ? '' : ' WHERE ' . $whereStr;
}
/**
* 生成查询条件SQL
* @access public
* @param mixed $where
* @param array $options
* @return string
*/
public function buildWhere($where, $options)
{
if (empty($where)) {
$where = [];
}
if ($where instanceof Query) {
return $this->buildWhere($where->getOptions('where'), $options);
}
$whereStr = '';
$binds = $this->query->getFieldsBind($options['table']);
foreach ($where as $key => $val) {
$str = [];
foreach ($val as $field => $value) {
if ($value instanceof \Closure) {
// 使用闭包查询
$query = new Query($this->connection);
call_user_func_array($value, [ & $query]);
$whereClause = $this->buildWhere($query->getOptions('where'), $options);
if (!empty($whereClause)) {
$str[] = ' ' . $key . ' ( ' . $whereClause . ' )';
}
} elseif (strpos($field, '|')) {
// 不同字段使用相同查询条件OR
$array = explode('|', $field);
$item = [];
foreach ($array as $k) {
$item[] = $this->parseWhereItem($k, $value, '', $options, $binds);
}
$str[] = ' ' . $key . ' ( ' . implode(' OR ', $item) . ' )';
} elseif (strpos($field, '&')) {
// 不同字段使用相同查询条件AND
$array = explode('&', $field);
$item = [];
foreach ($array as $k) {
$item[] = $this->parseWhereItem($k, $value, '', $options, $binds);
}
$str[] = ' ' . $key . ' ( ' . implode(' AND ', $item) . ' )';
} else {
// 对字段使用表达式查询
$field = is_string($field) ? $field : '';
$str[] = ' ' . $key . ' ' . $this->parseWhereItem($field, $value, $key, $options, $binds);
}
}
$whereStr .= empty($whereStr) ? substr(implode(' ', $str), strlen($key) + 1) : implode(' ', $str);
}
return $whereStr;
}
// where子单元分析
protected function parseWhereItem($field, $val, $rule = '', $options = [], $binds = [], $bindName = null)
{
// 字段分析
$key = $field ? $this->parseKey($field, $options) : '';
// 查询规则和条件
if (!is_array($val)) {
$val = is_null($val) ? ['null', ''] : ['=', $val];
}
list($exp, $value) = $val;
// 对一个字段使用多个查询条件
if (is_array($exp)) {
$item = array_pop($val);
// 传入 or 或者 and
if (is_string($item) && in_array($item, ['AND', 'and', 'OR', 'or'])) {
$rule = $item;
} else {
array_push($val, $item);
}
foreach ($val as $k => $item) {
$bindName = 'where_' . str_replace('.', '_', $field) . '_' . $k;
$str[] = $this->parseWhereItem($field, $item, $rule, $options, $binds, $bindName);
}
return '( ' . implode(' ' . $rule . ' ', $str) . ' )';
}
// 检测操作符
if (!in_array($exp, $this->exp)) {
$exp = strtolower($exp);
if (isset($this->exp[$exp])) {
$exp = $this->exp[$exp];
} else {
throw new Exception('where express error:' . $exp);
}
}
$bindName = $bindName ?: 'where_' . str_replace(['.', '-'], '_', $field);
if (preg_match('/\W/', $bindName)) {
// 处理带非单词字符的字段名
$bindName = md5($bindName);
}
if (is_object($value) && method_exists($value, '__toString')) {
// 对象数据写入
$value = $value->__toString();
}
$bindType = isset($binds[$field]) ? $binds[$field] : PDO::PARAM_STR;
if (is_scalar($value) && array_key_exists($field, $binds) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) {
if (strpos($value, ':') !== 0 || !$this->query->isBind(substr($value, 1))) {
if ($this->query->isBind($bindName)) {
$bindName .= '_' . str_replace('.', '_', uniqid('', true));
}
$this->query->bind($bindName, $value, $bindType);
$value = ':' . $bindName;
}
}
$whereStr = '';
if (in_array($exp, ['=', '<>', '>', '>=', '<', '<='])) {
// 比较运算
if ($value instanceof \Closure) {
$whereStr .= $key . ' ' . $exp . ' ' . $this->parseClosure($value);
} else {
$whereStr .= $key . ' ' . $exp . ' ' . $this->parseValue($value, $field);
}
} elseif ('LIKE' == $exp || 'NOT LIKE' == $exp) {
// 模糊匹配
if (is_array($value)) {
foreach ($value as $item) {
$array[] = $key . ' ' . $exp . ' ' . $this->parseValue($item, $field);
}
$logic = isset($val[2]) ? $val[2] : 'AND';
$whereStr .= '(' . implode($array, ' ' . strtoupper($logic) . ' ') . ')';
} else {
$whereStr .= $key . ' ' . $exp . ' ' . $this->parseValue($value, $field);
}
} elseif ('EXP' == $exp) {
// 表达式查询
$whereStr .= '( ' . $key . ' ' . $value . ' )';
} elseif (in_array($exp, ['NOT NULL', 'NULL'])) {
// NULL 查询
$whereStr .= $key . ' IS ' . $exp;
} elseif (in_array($exp, ['NOT IN', 'IN'])) {
// IN 查询
if ($value instanceof \Closure) {
$whereStr .= $key . ' ' . $exp . ' ' . $this->parseClosure($value);
} else {
$value = array_unique(is_array($value) ? $value : explode(',', $value));
if (array_key_exists($field, $binds)) {
$bind = [];
$array = [];
$i = 0;
foreach ($value as $v) {
$i++;
if ($this->query->isBind($bindName . '_in_' . $i)) {
$bindKey = $bindName . '_in_' . uniqid() . '_' . $i;
} else {
$bindKey = $bindName . '_in_' . $i;
}
$bind[$bindKey] = [$v, $bindType];
$array[] = ':' . $bindKey;
}
$this->query->bind($bind);
$zone = implode(',', $array);
} else {
$zone = implode(',', $this->parseValue($value, $field));
}
$whereStr .= $key . ' ' . $exp . ' (' . (empty($zone) ? "''" : $zone) . ')';
}
} elseif (in_array($exp, ['NOT BETWEEN', 'BETWEEN'])) {
// BETWEEN 查询
$data = is_array($value) ? $value : explode(',', $value);
if (array_key_exists($field, $binds)) {
if ($this->query->isBind($bindName . '_between_1')) {
$bindKey1 = $bindName . '_between_1' . uniqid();
$bindKey2 = $bindName . '_between_2' . uniqid();
} else {
$bindKey1 = $bindName . '_between_1';
$bindKey2 = $bindName . '_between_2';
}
$bind = [
$bindKey1 => [$data[0], $bindType],
$bindKey2 => [$data[1], $bindType],
];
$this->query->bind($bind);
$between = ':' . $bindKey1 . ' AND :' . $bindKey2;
} else {
$between = $this->parseValue($data[0], $field) . ' AND ' . $this->parseValue($data[1], $field);
}
$whereStr .= $key . ' ' . $exp . ' ' . $between;
} elseif (in_array($exp, ['NOT EXISTS', 'EXISTS'])) {
// EXISTS 查询
if ($value instanceof \Closure) {
$whereStr .= $exp . ' ' . $this->parseClosure($value);
} else {
$whereStr .= $exp . ' (' . $value . ')';
}
} elseif (in_array($exp, ['< TIME', '> TIME', '<= TIME', '>= TIME'])) {
$whereStr .= $key . ' ' . substr($exp, 0, 2) . ' ' . $this->parseDateTime($value, $field, $options, $bindName, $bindType);
} elseif (in_array($exp, ['BETWEEN TIME', 'NOT BETWEEN TIME'])) {
if (is_string($value)) {
$value = explode(',', $value);
}
$whereStr .= $key . ' ' . substr($exp, 0, -4) . $this->parseDateTime($value[0], $field, $options, $bindName . '_between_1', $bindType) . ' AND ' . $this->parseDateTime($value[1], $field, $options, $bindName . '_between_2', $bindType);
}
return $whereStr;
}
// 执行闭包子查询
protected function parseClosure($call, $show = true)
{
$query = new Query($this->connection);
call_user_func_array($call, [ & $query]);
return $query->buildSql($show);
}
/**
* 日期时间条件解析
* @access protected
* @param string $value
* @param string $key
* @param array $options
* @param string $bindName
* @param integer $bindType
* @return string
*/
protected function parseDateTime($value, $key, $options = [], $bindName = null, $bindType = null)
{
// 获取时间字段类型
if (strpos($key, '.')) {
list($table, $key) = explode('.', $key);
if (isset($options['alias']) && $pos = array_search($table, $options['alias'])) {
$table = $pos;
}
} else {
$table = $options['table'];
}
$type = $this->query->getTableInfo($table, 'type');
if (isset($type[$key])) {
$info = $type[$key];
}
if (isset($info)) {
if (is_string($value)) {
$value = strtotime($value) ?: $value;
}
if (preg_match('/(datetime|timestamp)/is', $info)) {
// 日期及时间戳类型
$value = date('Y-m-d H:i:s', $value);
} elseif (preg_match('/(date)/is', $info)) {
// 日期及时间戳类型
$value = date('Y-m-d', $value);
}
}
$bindName = $bindName ?: $key;
$this->query->bind($bindName, $value, $bindType);
return ':' . $bindName;
}
/**
* limit分析
* @access protected
* @param mixed $limit
* @return string
*/
protected function parseLimit($limit)
{
return (!empty($limit) && false === strpos($limit, '(')) ? ' LIMIT ' . $limit . ' ' : '';
}
/**
* join分析
* @access protected
* @param array $join
* @param array $options 查询条件
* @return string
*/
protected function parseJoin($join, $options = [])
{
$joinStr = '';
if (!empty($join)) {
foreach ($join as $item) {
list($table, $type, $on) = $item;
$condition = [];
foreach ((array) $on as $val) {
if (strpos($val, '=')) {
list($val1, $val2) = explode('=', $val, 2);
$condition[] = $this->parseKey($val1, $options) . '=' . $this->parseKey($val2, $options);
} else {
$condition[] = $val;
}
}
$table = $this->parseTable($table, $options);
$joinStr .= ' ' . $type . ' JOIN ' . $table . ' ON ' . implode(' AND ', $condition);
}
}
return $joinStr;
}
/**
* order分析
* @access protected
* @param mixed $order
* @param array $options 查询条件
* @return string
*/
protected function parseOrder($order, $options = [])
{
if (is_array($order)) {
$array = [];
foreach ($order as $key => $val) {
if (is_numeric($key)) {
if ('[rand]' == $val) {
if (method_exists($this, 'parseRand')) {
$array[] = $this->parseRand();
} else {
throw new BadMethodCallException('method not exists:' . get_class($this) . '-> parseRand');
}
} elseif (false === strpos($val, '(')) {
$array[] = $this->parseKey($val, $options);
} else {
$array[] = $val;
}
} else {
$sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : '';
$array[] = $this->parseKey($key, $options) . ' ' . $sort;
}
}
$order = implode(',', $array);
}
return !empty($order) ? ' ORDER BY ' . $order : '';
}
/**
* group分析
* @access protected
* @param mixed $group
* @return string
*/
protected function parseGroup($group)
{
return !empty($group) ? ' GROUP BY ' . $this->parseKey($group) : '';
}
/**
* having分析
* @access protected
* @param string $having
* @return string
*/
protected function parseHaving($having)
{
return !empty($having) ? ' HAVING ' . $having : '';
}
/**
* comment分析
* @access protected
* @param string $comment
* @return string
*/
protected function parseComment($comment)
{
return !empty($comment) ? ' /* ' . $comment . ' */' : '';
}
/**
* distinct分析
* @access protected
* @param mixed $distinct
* @return string
*/
protected function parseDistinct($distinct)
{
return !empty($distinct) ? ' DISTINCT ' : '';
}
/**
* union分析
* @access protected
* @param mixed $union
* @return string
*/
protected function parseUnion($union)
{
if (empty($union)) {
return '';
}
$type = $union['type'];
unset($union['type']);
foreach ($union as $u) {
if ($u instanceof \Closure) {
$sql[] = $type . ' ' . $this->parseClosure($u, false);
} elseif (is_string($u)) {
$sql[] = $type . ' ' . $this->parseSqlTable($u);
}
}
return implode(' ', $sql);
}
/**
* index分析可在操作链中指定需要强制使用的索引
* @access protected
* @param mixed $index
* @return string
*/
protected function parseForce($index)
{
if (empty($index)) {
return '';
}
if (is_array($index)) {
$index = join(",", $index);
}
return sprintf(" FORCE INDEX ( %s ) ", $index);
}
/**
* 设置锁机制
* @access protected
* @param bool|string $lock
* @return string
*/
protected function parseLock($lock = false)
{
if (is_bool($lock)) {
return $lock ? ' FOR UPDATE ' : '';
} elseif (is_string($lock)) {
return ' ' . trim($lock) . ' ';
}
}
/**
* 生成查询SQL
* @access public
* @param array $options 表达式
* @return string
*/
public function select($options = [])
{
$sql = str_replace(
['%TABLE%', '%DISTINCT%', '%FIELD%', '%JOIN%', '%WHERE%', '%GROUP%', '%HAVING%', '%ORDER%', '%LIMIT%', '%UNION%', '%LOCK%', '%COMMENT%', '%FORCE%'],
[
$this->parseTable($options['table'], $options),
$this->parseDistinct($options['distinct']),
$this->parseField($options['field'], $options),
$this->parseJoin($options['join'], $options),
$this->parseWhere($options['where'], $options),
$this->parseGroup($options['group']),
$this->parseHaving($options['having']),
$this->parseOrder($options['order'], $options),
$this->parseLimit($options['limit']),
$this->parseUnion($options['union']),
$this->parseLock($options['lock']),
$this->parseComment($options['comment']),
$this->parseForce($options['force']),
], $this->selectSql);
return $sql;
}
/**
* 生成insert SQL
* @access public
* @param array $data 数据
* @param array $options 表达式
* @param bool $replace 是否replace
* @return string
*/
public function insert(array $data, $options = [], $replace = false)
{
// 分析并处理数据
$data = $this->parseData($data, $options);
if (empty($data)) {
return 0;
}
$fields = array_keys($data);
$values = array_values($data);
$sql = str_replace(
['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
[
$replace ? 'REPLACE' : 'INSERT',
$this->parseTable($options['table'], $options),
implode(' , ', $fields),
implode(' , ', $values),
$this->parseComment($options['comment']),
], $this->insertSql);
return $sql;
}
/**
* 生成insertall SQL
* @access public
* @param array $dataSet 数据集
* @param array $options 表达式
* @param bool $replace 是否replace
* @return string
* @throws Exception
*/
public function insertAll($dataSet, $options = [], $replace = false)
{
// 获取合法的字段
if ('*' == $options['field']) {
$fields = array_keys($this->query->getFieldsType($options['table']));
} else {
$fields = $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[] = 'SELECT ' . implode(',', $value);
}
$fields = array_map([$this, 'parseKey'], array_keys(reset($dataSet)));
$sql = str_replace(
['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
[
$replace ? 'REPLACE' : 'INSERT',
$this->parseTable($options['table'], $options),
implode(' , ', $fields),
implode(' UNION ALL ', $values),
$this->parseComment($options['comment']),
], $this->insertAllSql);
return $sql;
}
/**
* 生成select insert SQL
* @access public
* @param array $fields 数据
* @param string $table 数据表
* @param array $options 表达式
* @return string
*/
public function selectInsert($fields, $table, $options)
{
if (is_string($fields)) {
$fields = explode(',', $fields);
}
$fields = array_map([$this, 'parseKey'], $fields);
$sql = 'INSERT INTO ' . $this->parseTable($table, $options) . ' (' . implode(',', $fields) . ') ' . $this->select($options);
return $sql;
}
/**
* 生成update SQL
* @access public
* @param array $data 数据
* @param array $options 表达式
* @return string
*/
public function update($data, $options)
{
$table = $this->parseTable($options['table'], $options);
$data = $this->parseData($data, $options);
if (empty($data)) {
return '';
}
foreach ($data as $key => $val) {
$set[] = $key . '=' . $val;
}
$sql = str_replace(
['%TABLE%', '%SET%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'],
[
$this->parseTable($options['table'], $options),
implode(',', $set),
$this->parseJoin($options['join'], $options),
$this->parseWhere($options['where'], $options),
$this->parseOrder($options['order'], $options),
$this->parseLimit($options['limit']),
$this->parseLock($options['lock']),
$this->parseComment($options['comment']),
], $this->updateSql);
return $sql;
}
/**
* 生成delete SQL
* @access public
* @param array $options 表达式
* @return string
*/
public function delete($options)
{
$sql = str_replace(
['%TABLE%', '%USING%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'],
[
$this->parseTable($options['table'], $options),
!empty($options['using']) ? ' USING ' . $this->parseTable($options['using'], $options) . ' ' : '',
$this->parseJoin($options['join'], $options),
$this->parseWhere($options['where'], $options),
$this->parseOrder($options['order'], $options),
$this->parseLimit($options['limit']),
$this->parseLock($options['lock']),
$this->parseComment($options['comment']),
], $this->deleteSql);
return $sql;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,68 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\builder;
use think\db\Builder;
/**
* mysql数据库驱动
*/
class Mysql extends Builder
{
protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
/**
* 字段和表名处理
* @access protected
* @param string $key
* @param array $options
* @return string
*/
protected function parseKey($key, $options = [])
{
$key = trim($key);
if (strpos($key, '$.') && false === strpos($key, '(')) {
// JSON字段支持
list($field, $name) = explode('$.', $key);
$key = 'json_extract(' . $field . ', \'$.' . $name . '\')';
} elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) {
list($table, $key) = explode('.', $key, 2);
if ('__TABLE__' == $table) {
$table = $this->query->getTable();
}
if (isset($options['alias'][$table])) {
$table = $options['alias'][$table];
}
}
if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
$key = '`' . $key . '`';
}
if (isset($table)) {
if (strpos($table, '.')) {
$table = str_replace('.', '`.`', $table);
}
$key = '`' . $table . '`.' . $key;
}
return $key;
}
/**
* 随机排序
* @access protected
* @return string
*/
protected function parseRand()
{
return 'rand()';
}
}

View File

@ -0,0 +1,83 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\builder;
use think\db\Builder;
/**
* 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
* @return string
*/
public function parseLimit($limit)
{
$limitStr = '';
if (!empty($limit)) {
$limit = explode(',', $limit);
if (count($limit) > 1) {
$limitStr .= ' LIMIT ' . $limit[1] . ' OFFSET ' . $limit[0] . ' ';
} else {
$limitStr .= ' LIMIT ' . $limit[0] . ' ';
}
}
return $limitStr;
}
/**
* 字段和表名处理
* @access protected
* @param string $key
* @param array $options
* @return string
*/
protected function parseKey($key, $options = [])
{
$key = trim($key);
if (strpos($key, '$.') && false === strpos($key, '(')) {
// JSON字段支持
list($field, $name) = explode('$.', $key);
$key = $field . '->>\'' . $name . '\'';
} elseif (strpos($key, '.')) {
list($table, $key) = explode('.', $key, 2);
if ('__TABLE__' == $table) {
$table = $this->query->getTable();
}
if (isset($options['alias'][$table])) {
$table = $options['alias'][$table];
}
}
if (isset($table)) {
$key = $table . '.' . $key;
}
return $key;
}
/**
* 随机排序
* @access protected
* @return string
*/
protected function parseRand()
{
return 'RANDOM()';
}
}

View File

@ -0,0 +1,76 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\builder;
use think\db\Builder;
/**
* Sqlite数据库驱动
*/
class Sqlite extends Builder
{
/**
* limit
* @access public
* @param string $limit
* @return string
*/
public function parseLimit($limit)
{
$limitStr = '';
if (!empty($limit)) {
$limit = explode(',', $limit);
if (count($limit) > 1) {
$limitStr .= ' LIMIT ' . $limit[1] . ' OFFSET ' . $limit[0] . ' ';
} else {
$limitStr .= ' LIMIT ' . $limit[0] . ' ';
}
}
return $limitStr;
}
/**
* 随机排序
* @access protected
* @return string
*/
protected function parseRand()
{
return 'RANDOM()';
}
/**
* 字段和表名处理
* @access protected
* @param string $key
* @param array $options
* @return string
*/
protected function parseKey($key, $options = [])
{
$key = trim($key);
if (strpos($key, '.')) {
list($table, $key) = explode('.', $key, 2);
if ('__TABLE__' == $table) {
$table = $this->query->getTable();
}
if (isset($options['alias'][$table])) {
$table = $options['alias'][$table];
}
}
if (isset($table)) {
$key = $table . '.' . $key;
}
return $key;
}
}

View File

@ -0,0 +1,123 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\builder;
use think\db\Builder;
/**
* Sqlsrv数据库驱动
*/
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 $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
* @return string
*/
protected function parseOrder($order, $options = [])
{
if (is_array($order)) {
$array = [];
foreach ($order as $key => $val) {
if (is_numeric($key)) {
if (false === strpos($val, '(')) {
$array[] = $this->parseKey($val, $options);
} elseif ('[rand]' == $val) {
$array[] = $this->parseRand();
} else {
$array[] = $val;
}
} else {
$sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : '';
$array[] = $this->parseKey($key, $options) . ' ' . $sort;
}
}
$order = implode(',', $array);
}
return !empty($order) ? ' ORDER BY ' . $order : ' ORDER BY rand()';
}
/**
* 随机排序
* @access protected
* @return string
*/
protected function parseRand()
{
return 'rand()';
}
/**
* 字段和表名处理
* @access protected
* @param string $key
* @param array $options
* @return string
*/
protected function parseKey($key, $options = [])
{
$key = trim($key);
if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) {
list($table, $key) = explode('.', $key, 2);
if ('__TABLE__' == $table) {
$table = $this->query->getTable();
}
if (isset($options['alias'][$table])) {
$table = $options['alias'][$table];
}
}
if (!is_numeric($key) && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) {
$key = '[' . $key . ']';
}
if (isset($table)) {
$key = '[' . $table . '].' . $key;
}
return $key;
}
/**
* limit
* @access protected
* @param mixed $limit
* @return string
*/
protected function parseLimit($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)
{
$this->selectSql = $this->selectInsertSql;
return parent::selectInsert($fields, $table, $options);
}
}

View File

@ -0,0 +1,126 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\db\connector;
use PDO;
use think\db\Connection;
use think\Log;
/**
* mysql数据库驱动
*/
class Mysql extends Connection
{
protected $builder = '\\think\\db\\builder\\Mysql';
/**
* 解析pdo连接的dsn信息
* @access protected
* @param array $config 连接信息
* @return string
*/
protected function parseDsn($config)
{
if (!empty($config['socket'])) {
$dsn = 'mysql:unix_socket=' . $config['socket'];
} elseif (!empty($config['hostport'])) {
$dsn = 'mysql:host=' . $config['hostname'] . ';port=' . $config['hostport'];
} else {
$dsn = 'mysql:host=' . $config['hostname'];
}
$dsn .= ';dbname=' . $config['database'];
if (!empty($config['charset'])) {
$dsn .= ';charset=' . $config['charset'];
}
return $dsn;
}
/**
* 取得数据表的字段信息
* @access public
* @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
'default' => $val['default'],
'primary' => (strtolower($val['key']) == 'pri'),
'autoinc' => (strtolower($val['extra']) == 'auto_increment'),
];
}
}
return $this->fieldCase($info);
}
/**
* 取得数据库的表信息
* @access public
* @param string $dbName
* @return array
*/
public function getTables($dbName = '')
{
$sql = !empty($dbName) ? 'SHOW TABLES FROM ' . $dbName : 'SHOW TABLES ';
$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
* @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');
}
}
return $result;
}
protected function supportSavepoint()
{
return true;
}
}

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