Upload 167 files
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- thinkphp/.gitignore +4 -0
- thinkphp/.htaccess +1 -0
- thinkphp/.travis.yml +47 -0
- thinkphp/CONTRIBUTING.md +119 -0
- thinkphp/LICENSE.txt +32 -0
- thinkphp/README.md +114 -0
- thinkphp/base.php +65 -0
- thinkphp/codecov.yml +12 -0
- thinkphp/composer.json +35 -0
- thinkphp/console.php +20 -0
- thinkphp/convention.php +298 -0
- thinkphp/helper.php +589 -0
- thinkphp/lang/zh-cn.php +137 -0
- thinkphp/library/think/App.php +677 -0
- thinkphp/library/think/Build.php +235 -0
- thinkphp/library/think/Cache.php +251 -0
- thinkphp/library/think/Collection.php +467 -0
- thinkphp/library/think/Config.php +214 -0
- thinkphp/library/think/Console.php +863 -0
- thinkphp/library/think/Controller.php +239 -0
- thinkphp/library/think/Cookie.php +268 -0
- thinkphp/library/think/Db.php +180 -0
- thinkphp/library/think/Debug.php +252 -0
- thinkphp/library/think/Env.php +39 -0
- thinkphp/library/think/Error.php +136 -0
- thinkphp/library/think/Exception.php +55 -0
- thinkphp/library/think/File.php +478 -0
- thinkphp/library/think/Hook.php +148 -0
- thinkphp/library/think/Lang.php +265 -0
- thinkphp/library/think/Loader.php +677 -0
- thinkphp/library/think/Log.php +237 -0
- thinkphp/library/think/Model.php +2350 -0
- thinkphp/library/think/Paginator.php +409 -0
- thinkphp/library/think/Process.php +1205 -0
- thinkphp/library/think/Request.php +1690 -0
- thinkphp/library/think/Response.php +332 -0
- thinkphp/library/think/Route.php +1645 -0
- thinkphp/library/think/Session.php +366 -0
- thinkphp/library/think/Template.php +1139 -0
- thinkphp/library/think/Url.php +333 -0
- thinkphp/library/think/Validate.php +1371 -0
- thinkphp/library/think/View.php +253 -0
- thinkphp/library/think/cache/Driver.php +231 -0
- thinkphp/library/think/cache/driver/File.php +268 -0
- thinkphp/library/think/cache/driver/Lite.php +187 -0
- thinkphp/library/think/cache/driver/Memcache.php +177 -0
- thinkphp/library/think/cache/driver/Memcached.php +187 -0
- thinkphp/library/think/cache/driver/Redis.php +188 -0
- thinkphp/library/think/cache/driver/Sqlite.php +199 -0
- thinkphp/library/think/cache/driver/Wincache.php +152 -0
thinkphp/.gitignore
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/composer.lock
|
| 2 |
+
/vendor
|
| 3 |
+
.idea
|
| 4 |
+
.DS_Store
|
thinkphp/.htaccess
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
deny from all
|
thinkphp/.travis.yml
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
sudo: false
|
| 2 |
+
|
| 3 |
+
language: php
|
| 4 |
+
|
| 5 |
+
services:
|
| 6 |
+
- memcached
|
| 7 |
+
- mongodb
|
| 8 |
+
- mysql
|
| 9 |
+
- postgresql
|
| 10 |
+
- redis-server
|
| 11 |
+
|
| 12 |
+
matrix:
|
| 13 |
+
fast_finish: true
|
| 14 |
+
include:
|
| 15 |
+
- php: 5.4
|
| 16 |
+
- php: 5.5
|
| 17 |
+
- php: 5.6
|
| 18 |
+
- php: 7.0
|
| 19 |
+
- php: hhvm
|
| 20 |
+
allow_failures:
|
| 21 |
+
- php: hhvm
|
| 22 |
+
|
| 23 |
+
cache:
|
| 24 |
+
directories:
|
| 25 |
+
- $HOME/.composer/cache
|
| 26 |
+
|
| 27 |
+
before_install:
|
| 28 |
+
- composer self-update
|
| 29 |
+
- mysql -e "create database IF NOT EXISTS test;" -uroot
|
| 30 |
+
- psql -c 'DROP DATABASE IF EXISTS test;' -U postgres
|
| 31 |
+
- psql -c 'create database test;' -U postgres
|
| 32 |
+
|
| 33 |
+
install:
|
| 34 |
+
- ./tests/script/install.sh
|
| 35 |
+
|
| 36 |
+
script:
|
| 37 |
+
## LINT
|
| 38 |
+
- find . -path ./vendor -prune -o -type f -name \*.php -exec php -l {} \;
|
| 39 |
+
## PHP Copy/Paste Detector
|
| 40 |
+
- vendor/bin/phpcpd --verbose --exclude vendor ./ || true
|
| 41 |
+
## PHPLOC
|
| 42 |
+
- vendor/bin/phploc --exclude vendor ./
|
| 43 |
+
## PHPUNIT
|
| 44 |
+
- vendor/bin/phpunit --coverage-clover=coverage.xml --configuration=phpunit.xml
|
| 45 |
+
|
| 46 |
+
after_success:
|
| 47 |
+
- bash <(curl -s https://codecov.io/bash)
|
thinkphp/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
如何贡献我的源代码
|
| 2 |
+
===
|
| 3 |
+
|
| 4 |
+
此文档介绍了 ThinkPHP 团队的组成以及运转机制,您提交的代码将给 ThinkPHP 项目带来什么好处,以及如何才能加入我们的行列。
|
| 5 |
+
|
| 6 |
+
## 通过 Github 贡献代码
|
| 7 |
+
|
| 8 |
+
ThinkPHP 目前使用 Git 来控制程序版本,如果你想为 ThinkPHP 贡献源代码,请先大致了解 Git 的使用方法。我们目前把项目托管在 GitHub 上,任何 GitHub 用户都可以向我们贡献代码。
|
| 9 |
+
|
| 10 |
+
参与的方式很简单,`fork`一份 ThinkPHP 的代码到你的仓库中,修改后提交,并向我们发起`pull request`申请,我们会及时对代码进行审查并处理你的申请。审查通过后,你的代码将被`merge`进我们的仓库中,这样你就会自动出现在贡献者名单里了,非常方便。
|
| 11 |
+
|
| 12 |
+
我们希望你贡献的代码符合:
|
| 13 |
+
|
| 14 |
+
* ThinkPHP 的编码规范
|
| 15 |
+
* 适当的注释,能让其他人读懂
|
| 16 |
+
* 遵循 Apache2 开源协议
|
| 17 |
+
|
| 18 |
+
**如果想要了解更多细节或有任何疑问,请继续阅读下面的内容**
|
| 19 |
+
|
| 20 |
+
### 注意事项
|
| 21 |
+
|
| 22 |
+
* 本项目代码格式化标准选用 [**PSR-2**](http://www.kancloud.cn/thinkphp/php-fig-psr/3141);
|
| 23 |
+
* 类名和类文件名遵循 [**PSR-4**](http://www.kancloud.cn/thinkphp/php-fig-psr/3144);
|
| 24 |
+
* 对于 Issues 的处理,请使用诸如 `fix #xxx(Issue ID)` 的 commit title 直接关闭 issue。
|
| 25 |
+
* 系统会自动在 PHP 5.4 5.5 5.6 7.0 和 HHVM 上测试修改,其中 HHVM 下的测试容许报错,请确保你的修改符合 PHP 5.4 ~ 5.6 和 PHP 7.0 的语法规范;
|
| 26 |
+
* 管理员不会合并造成 CI faild 的修改,若出现 CI faild 请检查自己的源代码或修改相应的[单元测试文件](tests);
|
| 27 |
+
|
| 28 |
+
## GitHub Issue
|
| 29 |
+
|
| 30 |
+
GitHub 提供了 Issue 功能,该功能可以用于:
|
| 31 |
+
|
| 32 |
+
* 提出 bug
|
| 33 |
+
* 提出功能改进
|
| 34 |
+
* 反馈使用体验
|
| 35 |
+
|
| 36 |
+
该功能不应该用于:
|
| 37 |
+
|
| 38 |
+
* 提出修改意见(涉及代码署名和修订追溯问题)
|
| 39 |
+
* 不友善的言论
|
| 40 |
+
|
| 41 |
+
## 快速修改
|
| 42 |
+
|
| 43 |
+
**GitHub 提供了快速编辑文件的功能**
|
| 44 |
+
|
| 45 |
+
1. 登录 GitHub 帐号;
|
| 46 |
+
2. 浏览项目文件,找到要进行修改的文件;
|
| 47 |
+
3. 点击右上角铅笔图标进行修改;
|
| 48 |
+
4. 填写 `Commit changes` 相关内容(Title 必填);
|
| 49 |
+
5. 提交修改,等待 CI 验证和管理员合并。
|
| 50 |
+
|
| 51 |
+
**若您需要一次提交大量修改,请继续阅读下面的内容**
|
| 52 |
+
|
| 53 |
+
## 完整流程
|
| 54 |
+
|
| 55 |
+
1. `fork`本项目;
|
| 56 |
+
2. 克隆(`clone`)你 `fork` 的项目到本地;
|
| 57 |
+
3. 新建分支(`branch`)并检出(`checkout`)新分支;
|
| 58 |
+
4. 添加本项目到你的本地 git 仓库作为上游(`upstream`);
|
| 59 |
+
5. 进行修改,若你的修改包含方法或函数的增减,请记得修改[单元测试文件](tests);
|
| 60 |
+
6. 变基(衍合 `rebase`)你的分支到上游 master 分支;
|
| 61 |
+
7. `push` 你的本地仓库到 GitHub;
|
| 62 |
+
8. 提交 `pull request`;
|
| 63 |
+
9. 等待 CI 验证(若不通过则重复 5~7,不需要重新提交 `pull request`,GitHub 会自动更新你的 `pull request`);
|
| 64 |
+
10. 等待管理员处理,并及时 `rebase` 你的分支到上游 master 分支(若上游 master 分支有修改)。
|
| 65 |
+
|
| 66 |
+
*若有必要,可以 `git push -f` 强行推送 rebase 后的分支到自己的 `fork`*
|
| 67 |
+
|
| 68 |
+
*绝对不可以使用 `git push -f` 强行推送修改到上游*
|
| 69 |
+
|
| 70 |
+
### 注意事项
|
| 71 |
+
|
| 72 |
+
* 若对上述流程有任何不清楚的地方,请查阅 GIT 教程,如 [这个](http://backlogtool.com/git-guide/cn/);
|
| 73 |
+
* 对于代码**不同方面**的修改,请在自己 `fork` 的项目中**创建不同的分支**(原因参见`完整流程`第9条备注部分);
|
| 74 |
+
* 变基及交互式变基操作参见 [Git 交互式变基](http://pakchoi.me/2015/03/17/git-interactive-rebase/)
|
| 75 |
+
|
| 76 |
+
## 推荐资源
|
| 77 |
+
|
| 78 |
+
### 开发环境
|
| 79 |
+
|
| 80 |
+
* XAMPP for Windows 5.5.x
|
| 81 |
+
* WampServer (for Windows)
|
| 82 |
+
* upupw Apache PHP5.4 ( for Windows)
|
| 83 |
+
|
| 84 |
+
或自行安装
|
| 85 |
+
|
| 86 |
+
- Apache / Nginx
|
| 87 |
+
- PHP 5.4 ~ 5.6
|
| 88 |
+
- MySQL / MariaDB
|
| 89 |
+
|
| 90 |
+
*Windows 用户推荐添加 PHP bin 目录到 PATH,方便使用 composer*
|
| 91 |
+
|
| 92 |
+
*Linux 用户自行配置环境, Mac 用户推荐使用内置 Apache 配合 Homebrew 安装 PHP 和 MariaDB*
|
| 93 |
+
|
| 94 |
+
### 编辑器
|
| 95 |
+
|
| 96 |
+
Sublime Text 3 + phpfmt 插件
|
| 97 |
+
|
| 98 |
+
phpfmt 插件参数
|
| 99 |
+
|
| 100 |
+
```json
|
| 101 |
+
{
|
| 102 |
+
"autocomplete": true,
|
| 103 |
+
"enable_auto_align": true,
|
| 104 |
+
"format_on_save": true,
|
| 105 |
+
"indent_with_space": true,
|
| 106 |
+
"psr1_naming": false,
|
| 107 |
+
"psr2": true,
|
| 108 |
+
"version": 4
|
| 109 |
+
}
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
或其他 编辑器 / IDE 配合 PSR2 自动格式化工具
|
| 113 |
+
|
| 114 |
+
### Git GUI
|
| 115 |
+
|
| 116 |
+
* SourceTree
|
| 117 |
+
* GitHub Desktop
|
| 118 |
+
|
| 119 |
+
或其他 Git 图形界面客户端
|
thinkphp/LICENSE.txt
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
|
| 3 |
+
版权所有Copyright © 2006-2017 by ThinkPHP (http://thinkphp.cn)
|
| 4 |
+
All rights reserved。
|
| 5 |
+
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
|
| 6 |
+
|
| 7 |
+
Apache Licence是著名的非盈利开源组织Apache采用的协议。
|
| 8 |
+
该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
|
| 9 |
+
允许代码修改,再作为开源或商业软件发布。需要满足
|
| 10 |
+
的条件:
|
| 11 |
+
1. 需要给代码的用户一份Apache Licence ;
|
| 12 |
+
2. 如果你修改了代码,需要在被修改的文件中说明;
|
| 13 |
+
3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
|
| 14 |
+
带有原来代码中的协议,商标,专利声明和其他原来作者规
|
| 15 |
+
定需要包含的说明;
|
| 16 |
+
4. 如果再发布的产品中包含一个Notice文件,则在Notice文
|
| 17 |
+
件中需要带有本协议内容。你可以在Notice中增加自己的
|
| 18 |
+
许可,但不可以表现为对Apache Licence构成更改。
|
| 19 |
+
具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
|
| 20 |
+
|
| 21 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 22 |
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 23 |
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
| 24 |
+
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
| 25 |
+
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
| 26 |
+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
| 27 |
+
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 28 |
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
| 29 |
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
| 30 |
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
| 31 |
+
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
| 32 |
+
POSSIBILITY OF SUCH DAMAGE.
|
thinkphp/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ThinkPHP 5.0
|
| 2 |
+
===============
|
| 3 |
+
|
| 4 |
+
[](https://styleci.io/repos/48530411)
|
| 5 |
+
[](https://travis-ci.org/top-think/framework)
|
| 6 |
+
[](http://codecov.io/github/github/top-think/framework?branch=master)
|
| 7 |
+
[](https://packagist.org/packages/topthink/framework)
|
| 8 |
+
[](https://packagist.org/packages/topthink/framework)
|
| 9 |
+
[](https://packagist.org/packages/topthink/framework)
|
| 10 |
+
[](https://packagist.org/packages/topthink/framework)
|
| 11 |
+
|
| 12 |
+
ThinkPHP5在保持快速开发和大道至简的核心理念不变的同时,PHP版本要求提升到5.4,优化核心,减少依赖,基于全新的架构思想和命名空间实现,是ThinkPHP突破原有框架思路的颠覆之作,其主要特性包括:
|
| 13 |
+
|
| 14 |
+
+ 基于命名空间和众多PHP新特性
|
| 15 |
+
+ 核心功能组件化
|
| 16 |
+
+ 强化路由功能
|
| 17 |
+
+ 更灵活的控制器
|
| 18 |
+
+ 重构的模型和数据库类
|
| 19 |
+
+ 配置文件可分离
|
| 20 |
+
+ 重写的自动验证和完成
|
| 21 |
+
+ 简化扩展机制
|
| 22 |
+
+ API支持完善
|
| 23 |
+
+ 改进的Log类
|
| 24 |
+
+ 命令行访问支持
|
| 25 |
+
+ REST支持
|
| 26 |
+
+ 引导文件支持
|
| 27 |
+
+ 方便的自动生成定义
|
| 28 |
+
+ 真正惰性加载
|
| 29 |
+
+ 分布式环境支持
|
| 30 |
+
+ 支持Composer
|
| 31 |
+
+ 支持MongoDb
|
| 32 |
+
|
| 33 |
+
> ThinkPHP5的运行环境要求PHP5.4以上。
|
| 34 |
+
|
| 35 |
+
详细开发文档参考 [ThinkPHP5完全开发手册](http://www.kancloud.cn/manual/thinkphp5) 以及[ThinkPHP5入门系列教程](http://www.kancloud.cn/special/thinkphp5_quickstart)
|
| 36 |
+
|
| 37 |
+
## 目录结构
|
| 38 |
+
|
| 39 |
+
初始的目录结构如下:
|
| 40 |
+
|
| 41 |
+
~~~
|
| 42 |
+
www WEB部署目录(或者子目录)
|
| 43 |
+
├─application 应用目录
|
| 44 |
+
│ ├─common 公共模块目录(可以更改)
|
| 45 |
+
│ ├─module_name 模块目录
|
| 46 |
+
│ │ ├─config.php 模块配置文件
|
| 47 |
+
│ │ ├─common.php 模块函数文件
|
| 48 |
+
│ │ ├─controller 控制器目录
|
| 49 |
+
│ │ ├─model 模型目录
|
| 50 |
+
│ │ ├─view 视图目录
|
| 51 |
+
│ │ └─ ... 更多类库目录
|
| 52 |
+
│ │
|
| 53 |
+
│ ├─command.php 命令行工具配置文件
|
| 54 |
+
│ ├─common.php 公共函数文件
|
| 55 |
+
│ ├─config.php 公共配置文件
|
| 56 |
+
│ ├─route.php 路由配置文件
|
| 57 |
+
│ ├─tags.php 应用行为扩展定义文件
|
| 58 |
+
│ └─database.php 数据库配置文件
|
| 59 |
+
│
|
| 60 |
+
├─public WEB目录(对外访问目录)
|
| 61 |
+
│ ├─index.php 入口文件
|
| 62 |
+
│ ├─router.php 快速测试文件
|
| 63 |
+
│ └─.htaccess 用于apache的重写
|
| 64 |
+
│
|
| 65 |
+
├─thinkphp 框架系统目录
|
| 66 |
+
│ ├─lang 语言文件目录
|
| 67 |
+
│ ├─library 框架类库目录
|
| 68 |
+
│ │ ├─think Think类库包目录
|
| 69 |
+
│ │ └─traits 系统Trait目录
|
| 70 |
+
│ │
|
| 71 |
+
│ ├─tpl 系统模板目录
|
| 72 |
+
│ ├─base.php 基础定义文件
|
| 73 |
+
│ ├─console.php 控制台入口文件
|
| 74 |
+
│ ├─convention.php 框架惯例配置文件
|
| 75 |
+
│ ├─helper.php 助手函数文件
|
| 76 |
+
│ ├─phpunit.xml phpunit配置文件
|
| 77 |
+
│ └─start.php 框架入口文件
|
| 78 |
+
│
|
| 79 |
+
├─extend 扩展类库目录
|
| 80 |
+
├─runtime 应用的运行时目录(可写,可定制)
|
| 81 |
+
├─vendor 第三方类库目录(Composer依赖库)
|
| 82 |
+
├─build.php 自动生成定义文件(参考)
|
| 83 |
+
├─composer.json composer 定义文件
|
| 84 |
+
├─LICENSE.txt 授权说明文件
|
| 85 |
+
├─README.md README 文件
|
| 86 |
+
├─think 命令行入口文件
|
| 87 |
+
~~~
|
| 88 |
+
|
| 89 |
+
> router.php用于php自带webserver支持,可用于快速测试
|
| 90 |
+
> 切换到public目录后,启动命令:php -S localhost:8888 router.php
|
| 91 |
+
> 上面的目录结构和名称是可以改变的,这取决于你的入口文件和配置参数。
|
| 92 |
+
|
| 93 |
+
## 命名规范
|
| 94 |
+
|
| 95 |
+
ThinkPHP5的命名规范遵循`PSR-2`规范以及`PSR-4`自动加载规范。
|
| 96 |
+
|
| 97 |
+
## 参与开发
|
| 98 |
+
注册并登录 Github 帐号, fork 本项目并进行改动。
|
| 99 |
+
|
| 100 |
+
更多细节参阅 [CONTRIBUTING.md](CONTRIBUTING.md)
|
| 101 |
+
|
| 102 |
+
## 版权信息
|
| 103 |
+
|
| 104 |
+
ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
|
| 105 |
+
|
| 106 |
+
本项目包含的第三方源码和二进制文件之版权信息另行标注。
|
| 107 |
+
|
| 108 |
+
版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn)
|
| 109 |
+
|
| 110 |
+
All rights reserved。
|
| 111 |
+
|
| 112 |
+
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
|
| 113 |
+
|
| 114 |
+
更多细节参阅 [LICENSE.txt](LICENSE.txt)
|
thinkphp/base.php
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
define('THINK_VERSION', '5.0.24');
|
| 13 |
+
define('THINK_START_TIME', microtime(true));
|
| 14 |
+
define('THINK_START_MEM', memory_get_usage());
|
| 15 |
+
define('EXT', '.php');
|
| 16 |
+
define('DS', DIRECTORY_SEPARATOR);
|
| 17 |
+
defined('THINK_PATH') or define('THINK_PATH', __DIR__ . DS);
|
| 18 |
+
define('LIB_PATH', THINK_PATH . 'library' . DS);
|
| 19 |
+
define('CORE_PATH', LIB_PATH . 'think' . DS);
|
| 20 |
+
define('TRAIT_PATH', LIB_PATH . 'traits' . DS);
|
| 21 |
+
defined('APP_PATH') or define('APP_PATH', dirname($_SERVER['SCRIPT_FILENAME']) . DS);
|
| 22 |
+
defined('ROOT_PATH') or define('ROOT_PATH', dirname(realpath(APP_PATH)) . DS);
|
| 23 |
+
defined('EXTEND_PATH') or define('EXTEND_PATH', ROOT_PATH . 'extend' . DS);
|
| 24 |
+
defined('VENDOR_PATH') or define('VENDOR_PATH', ROOT_PATH . 'vendor' . DS);
|
| 25 |
+
defined('RUNTIME_PATH') or define('RUNTIME_PATH', ROOT_PATH . 'runtime' . DS);
|
| 26 |
+
defined('LOG_PATH') or define('LOG_PATH', RUNTIME_PATH . 'log' . DS);
|
| 27 |
+
defined('CACHE_PATH') or define('CACHE_PATH', RUNTIME_PATH . 'cache' . DS);
|
| 28 |
+
defined('TEMP_PATH') or define('TEMP_PATH', RUNTIME_PATH . 'temp' . DS);
|
| 29 |
+
defined('CONF_PATH') or define('CONF_PATH', APP_PATH); // 配置文件目录
|
| 30 |
+
defined('CONF_EXT') or define('CONF_EXT', EXT); // 配置文件后缀
|
| 31 |
+
defined('ENV_PREFIX') or define('ENV_PREFIX', 'PHP_'); // 环境变量的配置前缀
|
| 32 |
+
|
| 33 |
+
// 环境常量
|
| 34 |
+
define('IS_CLI', PHP_SAPI == 'cli' ? true : false);
|
| 35 |
+
define('IS_WIN', strpos(PHP_OS, 'WIN') !== false);
|
| 36 |
+
|
| 37 |
+
// 载入Loader类
|
| 38 |
+
require CORE_PATH . 'Loader.php';
|
| 39 |
+
|
| 40 |
+
// 加载环境变量配置文件
|
| 41 |
+
if (is_file(ROOT_PATH . '.env')) {
|
| 42 |
+
$env = parse_ini_file(ROOT_PATH . '.env', true);
|
| 43 |
+
|
| 44 |
+
foreach ($env as $key => $val) {
|
| 45 |
+
$name = ENV_PREFIX . strtoupper($key);
|
| 46 |
+
|
| 47 |
+
if (is_array($val)) {
|
| 48 |
+
foreach ($val as $k => $v) {
|
| 49 |
+
$item = $name . '_' . strtoupper($k);
|
| 50 |
+
putenv("$item=$v");
|
| 51 |
+
}
|
| 52 |
+
} else {
|
| 53 |
+
putenv("$name=$val");
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
// 注册自动加载
|
| 59 |
+
\think\Loader::register();
|
| 60 |
+
|
| 61 |
+
// 注册错误和异常处理机制
|
| 62 |
+
\think\Error::register();
|
| 63 |
+
|
| 64 |
+
// 加载惯例配置文件
|
| 65 |
+
\think\Config::set(include THINK_PATH . 'convention' . EXT);
|
thinkphp/codecov.yml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
comment:
|
| 2 |
+
layout: header, changes, diff
|
| 3 |
+
coverage:
|
| 4 |
+
ignore:
|
| 5 |
+
- base.php
|
| 6 |
+
- helper.php
|
| 7 |
+
- convention.php
|
| 8 |
+
- lang/zh-cn.php
|
| 9 |
+
- start.php
|
| 10 |
+
- console.php
|
| 11 |
+
status:
|
| 12 |
+
patch: false
|
thinkphp/composer.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "topthink/framework",
|
| 3 |
+
"description": "the new thinkphp framework",
|
| 4 |
+
"type": "think-framework",
|
| 5 |
+
"keywords": [
|
| 6 |
+
"framework",
|
| 7 |
+
"thinkphp",
|
| 8 |
+
"ORM"
|
| 9 |
+
],
|
| 10 |
+
"homepage": "http://thinkphp.cn/",
|
| 11 |
+
"license": "Apache-2.0",
|
| 12 |
+
"authors": [
|
| 13 |
+
{
|
| 14 |
+
"name": "liu21st",
|
| 15 |
+
"email": "liu21st@gmail.com"
|
| 16 |
+
}
|
| 17 |
+
],
|
| 18 |
+
"require": {
|
| 19 |
+
"php": ">=5.4.0",
|
| 20 |
+
"topthink/think-installer": "~1.0"
|
| 21 |
+
},
|
| 22 |
+
"require-dev": {
|
| 23 |
+
"phpunit/phpunit": "4.8.*",
|
| 24 |
+
"johnkary/phpunit-speedtrap": "^1.0",
|
| 25 |
+
"mikey179/vfsStream": "~1.6",
|
| 26 |
+
"phploc/phploc": "2.*",
|
| 27 |
+
"sebastian/phpcpd": "2.*",
|
| 28 |
+
"phpdocumentor/reflection-docblock": "^2.0"
|
| 29 |
+
},
|
| 30 |
+
"autoload": {
|
| 31 |
+
"psr-4": {
|
| 32 |
+
"think\\": "library/think"
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
}
|
thinkphp/console.php
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006-2017 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: yunwuxin <448901948@qq.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
// ThinkPHP 引导文件
|
| 15 |
+
// 加载基础文件
|
| 16 |
+
require __DIR__ . '/base.php';
|
| 17 |
+
|
| 18 |
+
// 执行应用
|
| 19 |
+
App::initCommon();
|
| 20 |
+
Console::init();
|
thinkphp/convention.php
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
return [
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | 应用设置
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// 默认Host地址
|
| 8 |
+
'app_host' => '',
|
| 9 |
+
// 应用调试模式
|
| 10 |
+
'app_debug' => false,
|
| 11 |
+
// 应用Trace
|
| 12 |
+
'app_trace' => false,
|
| 13 |
+
// 应用模式状态
|
| 14 |
+
'app_status' => '',
|
| 15 |
+
// 是否支持多模块
|
| 16 |
+
'app_multi_module' => true,
|
| 17 |
+
// 入口自动绑定模块
|
| 18 |
+
'auto_bind_module' => false,
|
| 19 |
+
// 注册的根命名空间
|
| 20 |
+
'root_namespace' => [],
|
| 21 |
+
// 扩展函数文件
|
| 22 |
+
'extra_file_list' => [THINK_PATH . 'helper' . EXT],
|
| 23 |
+
// 默认输出类型
|
| 24 |
+
'default_return_type' => 'html',
|
| 25 |
+
// 默认AJAX 数据返回格式,可选json xml ...
|
| 26 |
+
'default_ajax_return' => 'json',
|
| 27 |
+
// 默认JSONP格式返回的处理方法
|
| 28 |
+
'default_jsonp_handler' => 'jsonpReturn',
|
| 29 |
+
// 默认JSONP处理方法
|
| 30 |
+
'var_jsonp_handler' => 'callback',
|
| 31 |
+
// 默认时区
|
| 32 |
+
'default_timezone' => 'PRC',
|
| 33 |
+
// 是否开启多语言
|
| 34 |
+
'lang_switch_on' => false,
|
| 35 |
+
// 默认全局过滤方法 用逗号分隔多个
|
| 36 |
+
'default_filter' => '',
|
| 37 |
+
// 默认语言
|
| 38 |
+
'default_lang' => 'zh-cn',
|
| 39 |
+
// 应用类库后缀
|
| 40 |
+
'class_suffix' => false,
|
| 41 |
+
// 控制器类后缀
|
| 42 |
+
'controller_suffix' => false,
|
| 43 |
+
|
| 44 |
+
// +----------------------------------------------------------------------
|
| 45 |
+
// | 模块设置
|
| 46 |
+
// +----------------------------------------------------------------------
|
| 47 |
+
|
| 48 |
+
// 默认模块名
|
| 49 |
+
'default_module' => 'index',
|
| 50 |
+
// 禁止访问模块
|
| 51 |
+
'deny_module_list' => ['common'],
|
| 52 |
+
// 默认控制器名
|
| 53 |
+
'default_controller' => 'Index',
|
| 54 |
+
// 默认操作名
|
| 55 |
+
'default_action' => 'index',
|
| 56 |
+
// 默认验证器
|
| 57 |
+
'default_validate' => '',
|
| 58 |
+
// 默认的空控制器名
|
| 59 |
+
'empty_controller' => 'Error',
|
| 60 |
+
// 操作方法前缀
|
| 61 |
+
'use_action_prefix' => false,
|
| 62 |
+
// 操作方法后缀
|
| 63 |
+
'action_suffix' => '',
|
| 64 |
+
// 自动搜索控制器
|
| 65 |
+
'controller_auto_search' => false,
|
| 66 |
+
|
| 67 |
+
// +----------------------------------------------------------------------
|
| 68 |
+
// | URL设置
|
| 69 |
+
// +----------------------------------------------------------------------
|
| 70 |
+
|
| 71 |
+
// PATHINFO变量名 用于兼容模式
|
| 72 |
+
'var_pathinfo' => 's',
|
| 73 |
+
// 兼容PATH_INFO获取
|
| 74 |
+
'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
|
| 75 |
+
// pathinfo分隔符
|
| 76 |
+
'pathinfo_depr' => '/',
|
| 77 |
+
// HTTPS代理标识
|
| 78 |
+
'https_agent_name' => '',
|
| 79 |
+
// URL伪静态后缀
|
| 80 |
+
'url_html_suffix' => 'html',
|
| 81 |
+
// URL普通方式参数 用于自动生成
|
| 82 |
+
'url_common_param' => false,
|
| 83 |
+
// URL参数方式 0 按名称成对解析 1 按顺序解析
|
| 84 |
+
'url_param_type' => 0,
|
| 85 |
+
// 是否开启路由
|
| 86 |
+
'url_route_on' => true,
|
| 87 |
+
// 路由配置文件(支持配置多个)
|
| 88 |
+
'route_config_file' => ['route'],
|
| 89 |
+
// 路由使用完整匹配
|
| 90 |
+
'route_complete_match' => false,
|
| 91 |
+
// 是否强制使用路由
|
| 92 |
+
'url_route_must' => false,
|
| 93 |
+
// 域名部署
|
| 94 |
+
'url_domain_deploy' => false,
|
| 95 |
+
// 域名根,如thinkphp.cn
|
| 96 |
+
'url_domain_root' => '',
|
| 97 |
+
// 是否自动转换URL中的控制器和操作名
|
| 98 |
+
'url_convert' => true,
|
| 99 |
+
// 默认的访问控制器层
|
| 100 |
+
'url_controller_layer' => 'controller',
|
| 101 |
+
// 表单请求类型伪装变量
|
| 102 |
+
'var_method' => '_method',
|
| 103 |
+
// 表单ajax伪装变量
|
| 104 |
+
'var_ajax' => '_ajax',
|
| 105 |
+
// 表单pjax伪装变量
|
| 106 |
+
'var_pjax' => '_pjax',
|
| 107 |
+
// 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
|
| 108 |
+
'request_cache' => false,
|
| 109 |
+
// 请求缓存有效期
|
| 110 |
+
'request_cache_expire' => null,
|
| 111 |
+
// 全局请求缓存排除规则
|
| 112 |
+
'request_cache_except' => [],
|
| 113 |
+
|
| 114 |
+
// +----------------------------------------------------------------------
|
| 115 |
+
// | 模板设置
|
| 116 |
+
// +----------------------------------------------------------------------
|
| 117 |
+
|
| 118 |
+
'template' => [
|
| 119 |
+
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写
|
| 120 |
+
'auto_rule' => 1,
|
| 121 |
+
// 模板引擎类型 支持 php think 支持扩展
|
| 122 |
+
'type' => 'Think',
|
| 123 |
+
// 视图基础目录,配置目录为所有模块的视图起始目录
|
| 124 |
+
'view_base' => '',
|
| 125 |
+
// 当前模板的视图目录 留空为自动获取
|
| 126 |
+
'view_path' => '',
|
| 127 |
+
// 模板后缀
|
| 128 |
+
'view_suffix' => 'html',
|
| 129 |
+
// 模板文件名分隔符
|
| 130 |
+
'view_depr' => DS,
|
| 131 |
+
// 模板引擎普通标签开始标记
|
| 132 |
+
'tpl_begin' => '{',
|
| 133 |
+
// 模板引擎普通标签结束标记
|
| 134 |
+
'tpl_end' => '}',
|
| 135 |
+
// 标签库标签开始标记
|
| 136 |
+
'taglib_begin' => '{',
|
| 137 |
+
// 标签库标签结束标记
|
| 138 |
+
'taglib_end' => '}',
|
| 139 |
+
],
|
| 140 |
+
|
| 141 |
+
// 视图输出字符串内容替换
|
| 142 |
+
'view_replace_str' => [],
|
| 143 |
+
// 默认跳转页面对应的模板文件
|
| 144 |
+
'dispatch_success_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl',
|
| 145 |
+
'dispatch_error_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl',
|
| 146 |
+
|
| 147 |
+
// +----------------------------------------------------------------------
|
| 148 |
+
// | 异常及错误设置
|
| 149 |
+
// +----------------------------------------------------------------------
|
| 150 |
+
|
| 151 |
+
// 异常页面的模板文件
|
| 152 |
+
'exception_tmpl' => THINK_PATH . 'tpl' . DS . 'think_exception.tpl',
|
| 153 |
+
|
| 154 |
+
// 错误显示信息,非调试模式有效
|
| 155 |
+
'error_message' => '页面错误!请稍后再试~',
|
| 156 |
+
// 显示错误信息
|
| 157 |
+
'show_error_msg' => false,
|
| 158 |
+
// 异常处理handle类 留空使用 \think\exception\Handle
|
| 159 |
+
'exception_handle' => '',
|
| 160 |
+
// 是否记录trace信息到日志
|
| 161 |
+
'record_trace' => false,
|
| 162 |
+
|
| 163 |
+
// +----------------------------------------------------------------------
|
| 164 |
+
// | 日志设置
|
| 165 |
+
// +----------------------------------------------------------------------
|
| 166 |
+
|
| 167 |
+
'log' => [
|
| 168 |
+
// 日志记录方式,内置 file socket 支持扩展
|
| 169 |
+
'type' => 'File',
|
| 170 |
+
// 日志保存目录
|
| 171 |
+
'path' => LOG_PATH,
|
| 172 |
+
// 日志记录级别
|
| 173 |
+
'level' => [],
|
| 174 |
+
],
|
| 175 |
+
|
| 176 |
+
// +----------------------------------------------------------------------
|
| 177 |
+
// | Trace设置 开启 app_trace 后 有效
|
| 178 |
+
// +----------------------------------------------------------------------
|
| 179 |
+
'trace' => [
|
| 180 |
+
// 内置Html Console 支持扩展
|
| 181 |
+
'type' => 'Html',
|
| 182 |
+
],
|
| 183 |
+
|
| 184 |
+
// +----------------------------------------------------------------------
|
| 185 |
+
// | 缓存设置
|
| 186 |
+
// +----------------------------------------------------------------------
|
| 187 |
+
|
| 188 |
+
'cache' => [
|
| 189 |
+
// 驱动方式
|
| 190 |
+
'type' => 'File',
|
| 191 |
+
// 缓存保存目录
|
| 192 |
+
'path' => CACHE_PATH,
|
| 193 |
+
// 缓存前缀
|
| 194 |
+
'prefix' => '',
|
| 195 |
+
// 缓存有效期 0表示永久缓存
|
| 196 |
+
'expire' => 0,
|
| 197 |
+
],
|
| 198 |
+
|
| 199 |
+
// +----------------------------------------------------------------------
|
| 200 |
+
// | 会话设置
|
| 201 |
+
// +----------------------------------------------------------------------
|
| 202 |
+
|
| 203 |
+
'session' => [
|
| 204 |
+
'id' => '',
|
| 205 |
+
// SESSION_ID的提交变量,解决flash上传跨域
|
| 206 |
+
'var_session_id' => '',
|
| 207 |
+
// SESSION 前缀
|
| 208 |
+
'prefix' => 'think',
|
| 209 |
+
// 驱动方式 支持redis memcache memcached
|
| 210 |
+
'type' => '',
|
| 211 |
+
// 是否自动开启 SESSION
|
| 212 |
+
'auto_start' => true,
|
| 213 |
+
'httponly' => true,
|
| 214 |
+
'secure' => false,
|
| 215 |
+
],
|
| 216 |
+
|
| 217 |
+
// +----------------------------------------------------------------------
|
| 218 |
+
// | Cookie设置
|
| 219 |
+
// +----------------------------------------------------------------------
|
| 220 |
+
'cookie' => [
|
| 221 |
+
// cookie 名称前缀
|
| 222 |
+
'prefix' => '',
|
| 223 |
+
// cookie 保存时间
|
| 224 |
+
'expire' => 0,
|
| 225 |
+
// cookie 保存路径
|
| 226 |
+
'path' => '/',
|
| 227 |
+
// cookie 有效域名
|
| 228 |
+
'domain' => '',
|
| 229 |
+
// cookie 启用安全传输
|
| 230 |
+
'secure' => false,
|
| 231 |
+
// httponly设置
|
| 232 |
+
'httponly' => '',
|
| 233 |
+
// 是否使用 setcookie
|
| 234 |
+
'setcookie' => true,
|
| 235 |
+
],
|
| 236 |
+
|
| 237 |
+
// +----------------------------------------------------------------------
|
| 238 |
+
// | 数据库设置
|
| 239 |
+
// +----------------------------------------------------------------------
|
| 240 |
+
|
| 241 |
+
'database' => [
|
| 242 |
+
// 数据库类型
|
| 243 |
+
'type' => 'mysql',
|
| 244 |
+
// 数据库连接DSN配置
|
| 245 |
+
'dsn' => '',
|
| 246 |
+
// 服务器地址
|
| 247 |
+
'hostname' => '127.0.0.1',
|
| 248 |
+
// 数据库名
|
| 249 |
+
'database' => '',
|
| 250 |
+
// 数据库用户名
|
| 251 |
+
'username' => 'root',
|
| 252 |
+
// 数据库密码
|
| 253 |
+
'password' => '',
|
| 254 |
+
// 数据库连接端口
|
| 255 |
+
'hostport' => '',
|
| 256 |
+
// 数据库连接参数
|
| 257 |
+
'params' => [],
|
| 258 |
+
// 数据库编码默认采用utf8
|
| 259 |
+
'charset' => 'utf8',
|
| 260 |
+
// 数据库表前缀
|
| 261 |
+
'prefix' => '',
|
| 262 |
+
// 数据库调试模式
|
| 263 |
+
'debug' => false,
|
| 264 |
+
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
|
| 265 |
+
'deploy' => 0,
|
| 266 |
+
// 数据库读写是否分离 主从式有效
|
| 267 |
+
'rw_separate' => false,
|
| 268 |
+
// 读写分离后 主服务器数量
|
| 269 |
+
'master_num' => 1,
|
| 270 |
+
// 指定从服务器序号
|
| 271 |
+
'slave_no' => '',
|
| 272 |
+
// 是否严格检查字段是否存在
|
| 273 |
+
'fields_strict' => true,
|
| 274 |
+
// 数据集返回类型
|
| 275 |
+
'resultset_type' => 'array',
|
| 276 |
+
// 自动写入时间戳字段
|
| 277 |
+
'auto_timestamp' => false,
|
| 278 |
+
// 时间字段取出后的默认时间格式
|
| 279 |
+
'datetime_format' => 'Y-m-d H:i:s',
|
| 280 |
+
// 是否需要进行SQL性能分析
|
| 281 |
+
'sql_explain' => false,
|
| 282 |
+
],
|
| 283 |
+
|
| 284 |
+
//分页配置
|
| 285 |
+
'paginate' => [
|
| 286 |
+
'type' => 'bootstrap',
|
| 287 |
+
'var_page' => 'page',
|
| 288 |
+
'list_rows' => 15,
|
| 289 |
+
],
|
| 290 |
+
|
| 291 |
+
//控制台配置
|
| 292 |
+
'console' => [
|
| 293 |
+
'name' => 'Think Console',
|
| 294 |
+
'version' => '0.1',
|
| 295 |
+
'user' => null,
|
| 296 |
+
],
|
| 297 |
+
|
| 298 |
+
];
|
thinkphp/helper.php
ADDED
|
@@ -0,0 +1,589 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
//------------------------
|
| 13 |
+
// ThinkPHP 助手函数
|
| 14 |
+
//-------------------------
|
| 15 |
+
|
| 16 |
+
use think\Cache;
|
| 17 |
+
use think\Config;
|
| 18 |
+
use think\Cookie;
|
| 19 |
+
use think\Db;
|
| 20 |
+
use think\Debug;
|
| 21 |
+
use think\exception\HttpException;
|
| 22 |
+
use think\exception\HttpResponseException;
|
| 23 |
+
use think\Lang;
|
| 24 |
+
use think\Loader;
|
| 25 |
+
use think\Log;
|
| 26 |
+
use think\Model;
|
| 27 |
+
use think\Request;
|
| 28 |
+
use think\Response;
|
| 29 |
+
use think\Session;
|
| 30 |
+
use think\Url;
|
| 31 |
+
use think\View;
|
| 32 |
+
|
| 33 |
+
if (!function_exists('load_trait')) {
|
| 34 |
+
/**
|
| 35 |
+
* 快速导入Traits PHP5.5以上无需调用
|
| 36 |
+
* @param string $class trait库
|
| 37 |
+
* @param string $ext 类库后缀
|
| 38 |
+
* @return boolean
|
| 39 |
+
*/
|
| 40 |
+
function load_trait($class, $ext = EXT)
|
| 41 |
+
{
|
| 42 |
+
return Loader::import($class, TRAIT_PATH, $ext);
|
| 43 |
+
}
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
if (!function_exists('exception')) {
|
| 47 |
+
/**
|
| 48 |
+
* 抛出异常处理
|
| 49 |
+
*
|
| 50 |
+
* @param string $msg 异常消息
|
| 51 |
+
* @param integer $code 异常代码 默认为0
|
| 52 |
+
* @param string $exception 异常类
|
| 53 |
+
*
|
| 54 |
+
* @throws Exception
|
| 55 |
+
*/
|
| 56 |
+
function exception($msg, $code = 0, $exception = '')
|
| 57 |
+
{
|
| 58 |
+
$e = $exception ?: '\think\Exception';
|
| 59 |
+
throw new $e($msg, $code);
|
| 60 |
+
}
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
if (!function_exists('debug')) {
|
| 64 |
+
/**
|
| 65 |
+
* 记录时间(微秒)和内存使用情况
|
| 66 |
+
* @param string $start 开始标签
|
| 67 |
+
* @param string $end 结束标签
|
| 68 |
+
* @param integer|string $dec 小数位 如果是m 表示统计内存占用
|
| 69 |
+
* @return mixed
|
| 70 |
+
*/
|
| 71 |
+
function debug($start, $end = '', $dec = 6)
|
| 72 |
+
{
|
| 73 |
+
if ('' == $end) {
|
| 74 |
+
Debug::remark($start);
|
| 75 |
+
} else {
|
| 76 |
+
return 'm' == $dec ? Debug::getRangeMem($start, $end) : Debug::getRangeTime($start, $end, $dec);
|
| 77 |
+
}
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
if (!function_exists('lang')) {
|
| 82 |
+
/**
|
| 83 |
+
* 获取语言变量值
|
| 84 |
+
* @param string $name 语言变量名
|
| 85 |
+
* @param array $vars 动态变量值
|
| 86 |
+
* @param string $lang 语言
|
| 87 |
+
* @return mixed
|
| 88 |
+
*/
|
| 89 |
+
function lang($name, $vars = [], $lang = '')
|
| 90 |
+
{
|
| 91 |
+
return Lang::get($name, $vars, $lang);
|
| 92 |
+
}
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
if (!function_exists('config')) {
|
| 96 |
+
/**
|
| 97 |
+
* 获取和设置配置参数
|
| 98 |
+
* @param string|array $name 参数名
|
| 99 |
+
* @param mixed $value 参数值
|
| 100 |
+
* @param string $range 作用域
|
| 101 |
+
* @return mixed
|
| 102 |
+
*/
|
| 103 |
+
function config($name = '', $value = null, $range = '')
|
| 104 |
+
{
|
| 105 |
+
if (is_null($value) && is_string($name)) {
|
| 106 |
+
return 0 === strpos($name, '?') ? Config::has(substr($name, 1), $range) : Config::get($name, $range);
|
| 107 |
+
} else {
|
| 108 |
+
return Config::set($name, $value, $range);
|
| 109 |
+
}
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
if (!function_exists('input')) {
|
| 114 |
+
/**
|
| 115 |
+
* 获取输入数据 支持默认值和过滤
|
| 116 |
+
* @param string $key 获取的变量名
|
| 117 |
+
* @param mixed $default 默认值
|
| 118 |
+
* @param string $filter 过滤方法
|
| 119 |
+
* @return mixed
|
| 120 |
+
*/
|
| 121 |
+
function input($key = '', $default = null, $filter = '')
|
| 122 |
+
{
|
| 123 |
+
if (0 === strpos($key, '?')) {
|
| 124 |
+
$key = substr($key, 1);
|
| 125 |
+
$has = true;
|
| 126 |
+
}
|
| 127 |
+
if ($pos = strpos($key, '.')) {
|
| 128 |
+
// 指定参数来源
|
| 129 |
+
list($method, $key) = explode('.', $key, 2);
|
| 130 |
+
if (!in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'route', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) {
|
| 131 |
+
$key = $method . '.' . $key;
|
| 132 |
+
$method = 'param';
|
| 133 |
+
}
|
| 134 |
+
} else {
|
| 135 |
+
// 默认为自动判断
|
| 136 |
+
$method = 'param';
|
| 137 |
+
}
|
| 138 |
+
if (isset($has)) {
|
| 139 |
+
return request()->has($key, $method, $default);
|
| 140 |
+
} else {
|
| 141 |
+
return request()->$method($key, $default, $filter);
|
| 142 |
+
}
|
| 143 |
+
}
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
if (!function_exists('widget')) {
|
| 147 |
+
/**
|
| 148 |
+
* 渲染输出Widget
|
| 149 |
+
* @param string $name Widget名称
|
| 150 |
+
* @param array $data 传入的参数
|
| 151 |
+
* @return mixed
|
| 152 |
+
*/
|
| 153 |
+
function widget($name, $data = [])
|
| 154 |
+
{
|
| 155 |
+
return Loader::action($name, $data, 'widget');
|
| 156 |
+
}
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
if (!function_exists('model')) {
|
| 160 |
+
/**
|
| 161 |
+
* 实例化Model
|
| 162 |
+
* @param string $name Model名称
|
| 163 |
+
* @param string $layer 业务层名称
|
| 164 |
+
* @param bool $appendSuffix 是否添加类名后缀
|
| 165 |
+
* @return \think\Model
|
| 166 |
+
*/
|
| 167 |
+
function model($name = '', $layer = 'model', $appendSuffix = false)
|
| 168 |
+
{
|
| 169 |
+
return Loader::model($name, $layer, $appendSuffix);
|
| 170 |
+
}
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
if (!function_exists('validate')) {
|
| 174 |
+
/**
|
| 175 |
+
* 实例化验证器
|
| 176 |
+
* @param string $name 验证器名称
|
| 177 |
+
* @param string $layer 业务层名称
|
| 178 |
+
* @param bool $appendSuffix 是否添加类名后缀
|
| 179 |
+
* @return \think\Validate
|
| 180 |
+
*/
|
| 181 |
+
function validate($name = '', $layer = 'validate', $appendSuffix = false)
|
| 182 |
+
{
|
| 183 |
+
return Loader::validate($name, $layer, $appendSuffix);
|
| 184 |
+
}
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
if (!function_exists('db')) {
|
| 188 |
+
/**
|
| 189 |
+
* 实例化数据库类
|
| 190 |
+
* @param string $name 操作的数据表名称(不含前缀)
|
| 191 |
+
* @param array|string $config 数据库配置参数
|
| 192 |
+
* @param bool $force 是否强制重新连接
|
| 193 |
+
* @return \think\db\Query
|
| 194 |
+
*/
|
| 195 |
+
function db($name = '', $config = [], $force = false)
|
| 196 |
+
{
|
| 197 |
+
return Db::connect($config, $force)->name($name);
|
| 198 |
+
}
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
if (!function_exists('controller')) {
|
| 202 |
+
/**
|
| 203 |
+
* 实例化控制器 格式:[模块/]控制器
|
| 204 |
+
* @param string $name 资源地址
|
| 205 |
+
* @param string $layer 控制层名称
|
| 206 |
+
* @param bool $appendSuffix 是否添加类名后缀
|
| 207 |
+
* @return \think\Controller
|
| 208 |
+
*/
|
| 209 |
+
function controller($name, $layer = 'controller', $appendSuffix = false)
|
| 210 |
+
{
|
| 211 |
+
return Loader::controller($name, $layer, $appendSuffix);
|
| 212 |
+
}
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
if (!function_exists('action')) {
|
| 216 |
+
/**
|
| 217 |
+
* 调用模块的操作方法 参数格式 [模块/控制器/]操作
|
| 218 |
+
* @param string $url 调用地址
|
| 219 |
+
* @param string|array $vars 调用参数 支持字符串和数组
|
| 220 |
+
* @param string $layer 要调用的控制层名称
|
| 221 |
+
* @param bool $appendSuffix 是否添加类名后缀
|
| 222 |
+
* @return mixed
|
| 223 |
+
*/
|
| 224 |
+
function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
|
| 225 |
+
{
|
| 226 |
+
return Loader::action($url, $vars, $layer, $appendSuffix);
|
| 227 |
+
}
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
if (!function_exists('import')) {
|
| 231 |
+
/**
|
| 232 |
+
* 导入所需的类库 同java的Import 本函数有缓存功能
|
| 233 |
+
* @param string $class 类库命名空间字符串
|
| 234 |
+
* @param string $baseUrl 起始路径
|
| 235 |
+
* @param string $ext 导入的文件扩展名
|
| 236 |
+
* @return boolean
|
| 237 |
+
*/
|
| 238 |
+
function import($class, $baseUrl = '', $ext = EXT)
|
| 239 |
+
{
|
| 240 |
+
return Loader::import($class, $baseUrl, $ext);
|
| 241 |
+
}
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
if (!function_exists('vendor')) {
|
| 245 |
+
/**
|
| 246 |
+
* 快速导入第三方框架类库 所有第三方框架的类库文件统一放到 系统的Vendor目录下面
|
| 247 |
+
* @param string $class 类库
|
| 248 |
+
* @param string $ext 类库后缀
|
| 249 |
+
* @return boolean
|
| 250 |
+
*/
|
| 251 |
+
function vendor($class, $ext = EXT)
|
| 252 |
+
{
|
| 253 |
+
return Loader::import($class, VENDOR_PATH, $ext);
|
| 254 |
+
}
|
| 255 |
+
}
|
| 256 |
+
|
| 257 |
+
if (!function_exists('dump')) {
|
| 258 |
+
/**
|
| 259 |
+
* 浏览器友好的变量输出
|
| 260 |
+
* @param mixed $var 变量
|
| 261 |
+
* @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串
|
| 262 |
+
* @param string $label 标签 默认为空
|
| 263 |
+
* @return void|string
|
| 264 |
+
*/
|
| 265 |
+
function dump($var, $echo = true, $label = null)
|
| 266 |
+
{
|
| 267 |
+
return Debug::dump($var, $echo, $label);
|
| 268 |
+
}
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
if (!function_exists('url')) {
|
| 272 |
+
/**
|
| 273 |
+
* Url生成
|
| 274 |
+
* @param string $url 路由地址
|
| 275 |
+
* @param string|array $vars 变量
|
| 276 |
+
* @param bool|string $suffix 生成的URL后缀
|
| 277 |
+
* @param bool|string $domain 域名
|
| 278 |
+
* @return string
|
| 279 |
+
*/
|
| 280 |
+
function url($url = '', $vars = '', $suffix = true, $domain = false)
|
| 281 |
+
{
|
| 282 |
+
return Url::build($url, $vars, $suffix, $domain);
|
| 283 |
+
}
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
if (!function_exists('session')) {
|
| 287 |
+
/**
|
| 288 |
+
* Session管理
|
| 289 |
+
* @param string|array $name session名称,如果为数组表示进行session设置
|
| 290 |
+
* @param mixed $value session值
|
| 291 |
+
* @param string $prefix 前缀
|
| 292 |
+
* @return mixed
|
| 293 |
+
*/
|
| 294 |
+
function session($name, $value = '', $prefix = null)
|
| 295 |
+
{
|
| 296 |
+
if (is_array($name)) {
|
| 297 |
+
// 初始化
|
| 298 |
+
Session::init($name);
|
| 299 |
+
} elseif (is_null($name)) {
|
| 300 |
+
// 清除
|
| 301 |
+
Session::clear('' === $value ? null : $value);
|
| 302 |
+
} elseif ('' === $value) {
|
| 303 |
+
// 判断或获取
|
| 304 |
+
return 0 === strpos($name, '?') ? Session::has(substr($name, 1), $prefix) : Session::get($name, $prefix);
|
| 305 |
+
} elseif (is_null($value)) {
|
| 306 |
+
// 删除
|
| 307 |
+
return Session::delete($name, $prefix);
|
| 308 |
+
} else {
|
| 309 |
+
// 设置
|
| 310 |
+
return Session::set($name, $value, $prefix);
|
| 311 |
+
}
|
| 312 |
+
}
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
if (!function_exists('cookie')) {
|
| 316 |
+
/**
|
| 317 |
+
* Cookie管理
|
| 318 |
+
* @param string|array $name cookie名称,如果为数组表示进行cookie设置
|
| 319 |
+
* @param mixed $value cookie值
|
| 320 |
+
* @param mixed $option 参数
|
| 321 |
+
* @return mixed
|
| 322 |
+
*/
|
| 323 |
+
function cookie($name, $value = '', $option = null)
|
| 324 |
+
{
|
| 325 |
+
if (is_array($name)) {
|
| 326 |
+
// 初始化
|
| 327 |
+
Cookie::init($name);
|
| 328 |
+
} elseif (is_null($name)) {
|
| 329 |
+
// 清除
|
| 330 |
+
Cookie::clear($value);
|
| 331 |
+
} elseif ('' === $value) {
|
| 332 |
+
// 获取
|
| 333 |
+
return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1), $option) : Cookie::get($name, $option);
|
| 334 |
+
} elseif (is_null($value)) {
|
| 335 |
+
// 删除
|
| 336 |
+
return Cookie::delete($name);
|
| 337 |
+
} else {
|
| 338 |
+
// 设置
|
| 339 |
+
return Cookie::set($name, $value, $option);
|
| 340 |
+
}
|
| 341 |
+
}
|
| 342 |
+
}
|
| 343 |
+
|
| 344 |
+
if (!function_exists('cache')) {
|
| 345 |
+
/**
|
| 346 |
+
* 缓存管理
|
| 347 |
+
* @param mixed $name 缓存名称,如果为数组表示进行缓存设置
|
| 348 |
+
* @param mixed $value 缓存值
|
| 349 |
+
* @param mixed $options 缓存参数
|
| 350 |
+
* @param string $tag 缓存标签
|
| 351 |
+
* @return mixed
|
| 352 |
+
*/
|
| 353 |
+
function cache($name, $value = '', $options = null, $tag = null)
|
| 354 |
+
{
|
| 355 |
+
if (is_array($options)) {
|
| 356 |
+
// 缓存操作的同时初始化
|
| 357 |
+
$cache = Cache::connect($options);
|
| 358 |
+
} elseif (is_array($name)) {
|
| 359 |
+
// 缓存初始化
|
| 360 |
+
return Cache::connect($name);
|
| 361 |
+
} else {
|
| 362 |
+
$cache = Cache::init();
|
| 363 |
+
}
|
| 364 |
+
|
| 365 |
+
if (is_null($name)) {
|
| 366 |
+
return $cache->clear($value);
|
| 367 |
+
} elseif ('' === $value) {
|
| 368 |
+
// 获取缓存
|
| 369 |
+
return 0 === strpos($name, '?') ? $cache->has(substr($name, 1)) : $cache->get($name);
|
| 370 |
+
} elseif (is_null($value)) {
|
| 371 |
+
// 删除缓存
|
| 372 |
+
return $cache->rm($name);
|
| 373 |
+
} elseif (0 === strpos($name, '?') && '' !== $value) {
|
| 374 |
+
$expire = is_numeric($options) ? $options : null;
|
| 375 |
+
return $cache->remember(substr($name, 1), $value, $expire);
|
| 376 |
+
} else {
|
| 377 |
+
// 缓存数据
|
| 378 |
+
if (is_array($options)) {
|
| 379 |
+
$expire = isset($options['expire']) ? $options['expire'] : null; //修复查询缓存无法设置过期时间
|
| 380 |
+
} else {
|
| 381 |
+
$expire = is_numeric($options) ? $options : null; //默认快捷缓存设置过期时间
|
| 382 |
+
}
|
| 383 |
+
if (is_null($tag)) {
|
| 384 |
+
return $cache->set($name, $value, $expire);
|
| 385 |
+
} else {
|
| 386 |
+
return $cache->tag($tag)->set($name, $value, $expire);
|
| 387 |
+
}
|
| 388 |
+
}
|
| 389 |
+
}
|
| 390 |
+
}
|
| 391 |
+
|
| 392 |
+
if (!function_exists('trace')) {
|
| 393 |
+
/**
|
| 394 |
+
* 记录日志信息
|
| 395 |
+
* @param mixed $log log信息 支持字符串和数组
|
| 396 |
+
* @param string $level 日志级别
|
| 397 |
+
* @return void|array
|
| 398 |
+
*/
|
| 399 |
+
function trace($log = '[think]', $level = 'log')
|
| 400 |
+
{
|
| 401 |
+
if ('[think]' === $log) {
|
| 402 |
+
return Log::getLog();
|
| 403 |
+
} else {
|
| 404 |
+
Log::record($log, $level);
|
| 405 |
+
}
|
| 406 |
+
}
|
| 407 |
+
}
|
| 408 |
+
|
| 409 |
+
if (!function_exists('request')) {
|
| 410 |
+
/**
|
| 411 |
+
* 获取当前Request对象实例
|
| 412 |
+
* @return Request
|
| 413 |
+
*/
|
| 414 |
+
function request()
|
| 415 |
+
{
|
| 416 |
+
return Request::instance();
|
| 417 |
+
}
|
| 418 |
+
}
|
| 419 |
+
|
| 420 |
+
if (!function_exists('response')) {
|
| 421 |
+
/**
|
| 422 |
+
* 创建普通 Response 对象实例
|
| 423 |
+
* @param mixed $data 输出数据
|
| 424 |
+
* @param int|string $code 状态码
|
| 425 |
+
* @param array $header 头信息
|
| 426 |
+
* @param string $type
|
| 427 |
+
* @return Response
|
| 428 |
+
*/
|
| 429 |
+
function response($data = [], $code = 200, $header = [], $type = 'html')
|
| 430 |
+
{
|
| 431 |
+
return Response::create($data, $type, $code, $header);
|
| 432 |
+
}
|
| 433 |
+
}
|
| 434 |
+
|
| 435 |
+
if (!function_exists('view')) {
|
| 436 |
+
/**
|
| 437 |
+
* 渲染模板输出
|
| 438 |
+
* @param string $template 模板文件
|
| 439 |
+
* @param array $vars 模板变量
|
| 440 |
+
* @param array $replace 模板替换
|
| 441 |
+
* @param integer $code 状态码
|
| 442 |
+
* @return \think\response\View
|
| 443 |
+
*/
|
| 444 |
+
function view($template = '', $vars = [], $replace = [], $code = 200)
|
| 445 |
+
{
|
| 446 |
+
return Response::create($template, 'view', $code)->replace($replace)->assign($vars);
|
| 447 |
+
}
|
| 448 |
+
}
|
| 449 |
+
|
| 450 |
+
if (!function_exists('json')) {
|
| 451 |
+
/**
|
| 452 |
+
* 获取\think\response\Json对象实例
|
| 453 |
+
* @param mixed $data 返回的数据
|
| 454 |
+
* @param integer $code 状态码
|
| 455 |
+
* @param array $header 头部
|
| 456 |
+
* @param array $options 参数
|
| 457 |
+
* @return \think\response\Json
|
| 458 |
+
*/
|
| 459 |
+
function json($data = [], $code = 200, $header = [], $options = [])
|
| 460 |
+
{
|
| 461 |
+
return Response::create($data, 'json', $code, $header, $options);
|
| 462 |
+
}
|
| 463 |
+
}
|
| 464 |
+
|
| 465 |
+
if (!function_exists('jsonp')) {
|
| 466 |
+
/**
|
| 467 |
+
* 获取\think\response\Jsonp对象实例
|
| 468 |
+
* @param mixed $data 返回的数据
|
| 469 |
+
* @param integer $code 状态码
|
| 470 |
+
* @param array $header 头部
|
| 471 |
+
* @param array $options 参数
|
| 472 |
+
* @return \think\response\Jsonp
|
| 473 |
+
*/
|
| 474 |
+
function jsonp($data = [], $code = 200, $header = [], $options = [])
|
| 475 |
+
{
|
| 476 |
+
return Response::create($data, 'jsonp', $code, $header, $options);
|
| 477 |
+
}
|
| 478 |
+
}
|
| 479 |
+
|
| 480 |
+
if (!function_exists('xml')) {
|
| 481 |
+
/**
|
| 482 |
+
* 获取\think\response\Xml对象实例
|
| 483 |
+
* @param mixed $data 返回的数据
|
| 484 |
+
* @param integer $code 状态码
|
| 485 |
+
* @param array $header 头部
|
| 486 |
+
* @param array $options 参数
|
| 487 |
+
* @return \think\response\Xml
|
| 488 |
+
*/
|
| 489 |
+
function xml($data = [], $code = 200, $header = [], $options = [])
|
| 490 |
+
{
|
| 491 |
+
return Response::create($data, 'xml', $code, $header, $options);
|
| 492 |
+
}
|
| 493 |
+
}
|
| 494 |
+
|
| 495 |
+
if (!function_exists('redirect')) {
|
| 496 |
+
/**
|
| 497 |
+
* 获取\think\response\Redirect对象实例
|
| 498 |
+
* @param mixed $url 重定向地址 支持Url::build方法的地址
|
| 499 |
+
* @param array|integer $params 额外参数
|
| 500 |
+
* @param integer $code 状态码
|
| 501 |
+
* @param array $with 隐式传参
|
| 502 |
+
* @return \think\response\Redirect
|
| 503 |
+
*/
|
| 504 |
+
function redirect($url = [], $params = [], $code = 302, $with = [])
|
| 505 |
+
{
|
| 506 |
+
if (is_integer($params)) {
|
| 507 |
+
$code = $params;
|
| 508 |
+
$params = [];
|
| 509 |
+
}
|
| 510 |
+
return Response::create($url, 'redirect', $code)->params($params)->with($with);
|
| 511 |
+
}
|
| 512 |
+
}
|
| 513 |
+
|
| 514 |
+
if (!function_exists('abort')) {
|
| 515 |
+
/**
|
| 516 |
+
* 抛出HTTP异常
|
| 517 |
+
* @param integer|Response $code 状态码 或者 Response对象实例
|
| 518 |
+
* @param string $message 错误信息
|
| 519 |
+
* @param array $header 参数
|
| 520 |
+
*/
|
| 521 |
+
function abort($code, $message = null, $header = [])
|
| 522 |
+
{
|
| 523 |
+
if ($code instanceof Response) {
|
| 524 |
+
throw new HttpResponseException($code);
|
| 525 |
+
} else {
|
| 526 |
+
throw new HttpException($code, $message, null, $header);
|
| 527 |
+
}
|
| 528 |
+
}
|
| 529 |
+
}
|
| 530 |
+
|
| 531 |
+
if (!function_exists('halt')) {
|
| 532 |
+
/**
|
| 533 |
+
* 调试变量并且中断输出
|
| 534 |
+
* @param mixed $var 调试变量或者信息
|
| 535 |
+
*/
|
| 536 |
+
function halt($var)
|
| 537 |
+
{
|
| 538 |
+
dump($var);
|
| 539 |
+
throw new HttpResponseException(new Response);
|
| 540 |
+
}
|
| 541 |
+
}
|
| 542 |
+
|
| 543 |
+
if (!function_exists('token')) {
|
| 544 |
+
/**
|
| 545 |
+
* 生成表单令牌
|
| 546 |
+
* @param string $name 令牌名称
|
| 547 |
+
* @param mixed $type 令牌生成方法
|
| 548 |
+
* @return string
|
| 549 |
+
*/
|
| 550 |
+
function token($name = '__token__', $type = 'md5')
|
| 551 |
+
{
|
| 552 |
+
$token = Request::instance()->token($name, $type);
|
| 553 |
+
return '<input type="hidden" name="' . $name . '" value="' . $token . '" />';
|
| 554 |
+
}
|
| 555 |
+
}
|
| 556 |
+
|
| 557 |
+
if (!function_exists('load_relation')) {
|
| 558 |
+
/**
|
| 559 |
+
* 延迟预载入关联查询
|
| 560 |
+
* @param mixed $resultSet 数据集
|
| 561 |
+
* @param mixed $relation 关联
|
| 562 |
+
* @return array
|
| 563 |
+
*/
|
| 564 |
+
function load_relation($resultSet, $relation)
|
| 565 |
+
{
|
| 566 |
+
$item = current($resultSet);
|
| 567 |
+
if ($item instanceof Model) {
|
| 568 |
+
$item->eagerlyResultSet($resultSet, $relation);
|
| 569 |
+
}
|
| 570 |
+
return $resultSet;
|
| 571 |
+
}
|
| 572 |
+
}
|
| 573 |
+
|
| 574 |
+
if (!function_exists('collection')) {
|
| 575 |
+
/**
|
| 576 |
+
* 数组转换为数据集对象
|
| 577 |
+
* @param array $resultSet 数据集数组
|
| 578 |
+
* @return \think\model\Collection|\think\Collection
|
| 579 |
+
*/
|
| 580 |
+
function collection($resultSet)
|
| 581 |
+
{
|
| 582 |
+
$item = current($resultSet);
|
| 583 |
+
if ($item instanceof Model) {
|
| 584 |
+
return \think\model\Collection::make($resultSet);
|
| 585 |
+
} else {
|
| 586 |
+
return \think\Collection::make($resultSet);
|
| 587 |
+
}
|
| 588 |
+
}
|
| 589 |
+
}
|
thinkphp/lang/zh-cn.php
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
// 核心中文语言包
|
| 13 |
+
return [
|
| 14 |
+
// 系统错误提示
|
| 15 |
+
'Undefined variable' => '未定义变量',
|
| 16 |
+
'Undefined index' => '未定义数组索引',
|
| 17 |
+
'Undefined offset' => '未定义数组下标',
|
| 18 |
+
'Parse error' => '语法解析错误',
|
| 19 |
+
'Type error' => '类型错误',
|
| 20 |
+
'Fatal error' => '致命错误',
|
| 21 |
+
'syntax error' => '语法错误',
|
| 22 |
+
|
| 23 |
+
// 框架核心错误提示
|
| 24 |
+
'dispatch type not support' => '不支持的调度类型',
|
| 25 |
+
'method param miss' => '方法参数错误',
|
| 26 |
+
'method not exists' => '方法不存在',
|
| 27 |
+
'module not exists' => '模块不存在',
|
| 28 |
+
'controller not exists' => '控制器不存在',
|
| 29 |
+
'class not exists' => '类不存在',
|
| 30 |
+
'property not exists' => '类的属性不存在',
|
| 31 |
+
'template not exists' => '模板文件不存在',
|
| 32 |
+
'illegal controller name' => '非法的控制器名称',
|
| 33 |
+
'illegal action name' => '非法的操作名称',
|
| 34 |
+
'url suffix deny' => '禁止的URL后缀访问',
|
| 35 |
+
'Route Not Found' => '当前访问路由未定义',
|
| 36 |
+
'Undefined db type' => '未定义数据库类型',
|
| 37 |
+
'variable type error' => '变量类型错误',
|
| 38 |
+
'PSR-4 error' => 'PSR-4 规范错误',
|
| 39 |
+
'not support total' => '简洁模式下不能获取数据总数',
|
| 40 |
+
'not support last' => '简洁模式下不能获取最后一页',
|
| 41 |
+
'error session handler' => '错误的SESSION处理器类',
|
| 42 |
+
'not allow php tag' => '模板不允许使用PHP语法',
|
| 43 |
+
'not support' => '不支持',
|
| 44 |
+
'redisd master' => 'Redisd 主服务器错误',
|
| 45 |
+
'redisd slave' => 'Redisd 从服务器错误',
|
| 46 |
+
'must run at sae' => '必须在SAE运行',
|
| 47 |
+
'memcache init error' => '未开通Memcache服务,请在SAE管理平台初始化Memcache服务',
|
| 48 |
+
'KVDB init error' => '没有初始化KVDB,请在SAE管理平台初始化KVDB服务',
|
| 49 |
+
'fields not exists' => '数据表字段不存在',
|
| 50 |
+
'where express error' => '查询表达式错误',
|
| 51 |
+
'not support data' => '不支持的数据表达式',
|
| 52 |
+
'no data to update' => '没有任何数据需要更新',
|
| 53 |
+
'miss data to insert' => '缺少需要写入的数据',
|
| 54 |
+
'miss complex primary data' => '缺少复合主键数据',
|
| 55 |
+
'miss update condition' => '缺少更新条件',
|
| 56 |
+
'model data Not Found' => '模型数据不存在',
|
| 57 |
+
'table data not Found' => '表数据不存在',
|
| 58 |
+
'delete without condition' => '没有条件不会执行删除操作',
|
| 59 |
+
'miss relation data' => '缺少关联表数据',
|
| 60 |
+
'tag attr must' => '模板标签属性必须',
|
| 61 |
+
'tag error' => '模板标签错误',
|
| 62 |
+
'cache write error' => '缓存写入失败',
|
| 63 |
+
'sae mc write error' => 'SAE mc 写入错误',
|
| 64 |
+
'route name not exists' => '路由标识不存在(或参数不够)',
|
| 65 |
+
'invalid request' => '非法请求',
|
| 66 |
+
'bind attr has exists' => '模型的属性已经存在',
|
| 67 |
+
'relation data not exists' => '关联数据不存在',
|
| 68 |
+
'relation not support' => '关联不支持',
|
| 69 |
+
'chunk not support order' => 'Chunk不支持调用order方法',
|
| 70 |
+
'closure not support cache(true)' => '使用闭包查询不支持cache(true),请指定缓存Key',
|
| 71 |
+
|
| 72 |
+
// 上传错误信息
|
| 73 |
+
'unknown upload error' => '未知上传错误!',
|
| 74 |
+
'file write error' => '文件写入失败!',
|
| 75 |
+
'upload temp dir not found' => '找不到临时文件夹!',
|
| 76 |
+
'no file to uploaded' => '没有文件被上传!',
|
| 77 |
+
'only the portion of file is uploaded' => '文件只有部分被上传!',
|
| 78 |
+
'upload File size exceeds the maximum value' => '上传文件大小超过了最大值!',
|
| 79 |
+
'upload write error' => '文件上传保存错误!',
|
| 80 |
+
'has the same filename: {:filename}' => '存在同名文件:{:filename}',
|
| 81 |
+
'upload illegal files' => '非法上传文件',
|
| 82 |
+
'illegal image files' => '非法图片文件',
|
| 83 |
+
'extensions to upload is not allowed' => '上传文件后缀不允许',
|
| 84 |
+
'mimetype to upload is not allowed' => '上传文件MIME类型不允许!',
|
| 85 |
+
'filesize not match' => '上传文件大小不符!',
|
| 86 |
+
'directory {:path} creation failed' => '目录 {:path} 创建失败!',
|
| 87 |
+
|
| 88 |
+
// Validate Error Message
|
| 89 |
+
':attribute require' => ':attribute不能为空',
|
| 90 |
+
':attribute must be numeric' => ':attribute必须是数字',
|
| 91 |
+
':attribute must be integer' => ':attribute必须是整数',
|
| 92 |
+
':attribute must be float' => ':attribute必须是浮点数',
|
| 93 |
+
':attribute must be bool' => ':attribute必须是布尔值',
|
| 94 |
+
':attribute not a valid email address' => ':attribute格式不符',
|
| 95 |
+
':attribute not a valid mobile' => ':attribute格式不符',
|
| 96 |
+
':attribute must be a array' => ':attribute必须是数组',
|
| 97 |
+
':attribute must be yes,on or 1' => ':attribute必须是yes、on或者1',
|
| 98 |
+
':attribute not a valid datetime' => ':attribute不是一个有效的日期或时间格式',
|
| 99 |
+
':attribute not a valid file' => ':attribute不是有效的上传文件',
|
| 100 |
+
':attribute not a valid image' => ':attribute不是有效的图像文件',
|
| 101 |
+
':attribute must be alpha' => ':attribute只能是字母',
|
| 102 |
+
':attribute must be alpha-numeric' => ':attribute只能是字母和数字',
|
| 103 |
+
':attribute must be alpha-numeric, dash, underscore' => ':attribute只能是字母、数字和下划线_及破折号-',
|
| 104 |
+
':attribute not a valid domain or ip' => ':attribute不是有效的域名或者IP',
|
| 105 |
+
':attribute must be chinese' => ':attribute只能是汉字',
|
| 106 |
+
':attribute must be chinese or alpha' => ':attribute只能是汉字、字母',
|
| 107 |
+
':attribute must be chinese,alpha-numeric' => ':attribute只能是汉字、字母和数字',
|
| 108 |
+
':attribute must be chinese,alpha-numeric,underscore, dash' => ':attribute只能是汉字、字母、数字和下划线_及破折号-',
|
| 109 |
+
':attribute not a valid url' => ':attribute不是有效的URL地址',
|
| 110 |
+
':attribute not a valid ip' => ':attribute不是有效的IP地址',
|
| 111 |
+
':attribute must be dateFormat of :rule' => ':attribute必须使用日期格式 :rule',
|
| 112 |
+
':attribute must be in :rule' => ':attribute必须在 :rule 范围内',
|
| 113 |
+
':attribute be notin :rule' => ':attribute不能在 :rule 范围内',
|
| 114 |
+
':attribute must between :1 - :2' => ':attribute只能在 :1 - :2 之间',
|
| 115 |
+
':attribute not between :1 - :2' => ':attribute不能在 :1 - :2 之间',
|
| 116 |
+
'size of :attribute must be :rule' => ':attribute长度不符合要求 :rule',
|
| 117 |
+
'max size of :attribute must be :rule' => ':attribute长度不能超过 :rule',
|
| 118 |
+
'min size of :attribute must be :rule' => ':attribute长度不能小于 :rule',
|
| 119 |
+
':attribute cannot be less than :rule' => ':attribute日期不能小于 :rule',
|
| 120 |
+
':attribute cannot exceed :rule' => ':attribute日期不能超过 :rule',
|
| 121 |
+
':attribute not within :rule' => '不在有效期内 :rule',
|
| 122 |
+
'access IP is not allowed' => '不允许的IP访问',
|
| 123 |
+
'access IP denied' => '禁止的IP访问',
|
| 124 |
+
':attribute out of accord with :2' => ':attribute和确认字段:2不一致',
|
| 125 |
+
':attribute cannot be same with :2' => ':attribute和比较字段:2不能相同',
|
| 126 |
+
':attribute must greater than or equal :rule' => ':attribute必须大于等于 :rule',
|
| 127 |
+
':attribute must greater than :rule' => ':attribute必须大于 :rule',
|
| 128 |
+
':attribute must less than or equal :rule' => ':attribute必须小于等于 :rule',
|
| 129 |
+
':attribute must less than :rule' => ':attribute必须小于 :rule',
|
| 130 |
+
':attribute must equal :rule' => ':attribute必须等于 :rule',
|
| 131 |
+
':attribute has exists' => ':attribute已存在',
|
| 132 |
+
':attribute not conform to the rules' => ':attribute不符合指定规则',
|
| 133 |
+
'invalid Request method' => '无效的请求类型',
|
| 134 |
+
'invalid token' => '令牌数据无效',
|
| 135 |
+
'not conform to the rules' => '规则错误',
|
| 136 |
+
'Please copy file' => '请复制',
|
| 137 |
+
];
|
thinkphp/library/think/App.php
ADDED
|
@@ -0,0 +1,677 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use think\exception\ClassNotFoundException;
|
| 15 |
+
use think\exception\HttpException;
|
| 16 |
+
use think\exception\HttpResponseException;
|
| 17 |
+
use think\exception\RouteNotFoundException;
|
| 18 |
+
|
| 19 |
+
/**
|
| 20 |
+
* App 应用管理
|
| 21 |
+
* @author liu21st <liu21st@gmail.com>
|
| 22 |
+
*/
|
| 23 |
+
class App
|
| 24 |
+
{
|
| 25 |
+
/**
|
| 26 |
+
* @var bool 是否初始化过
|
| 27 |
+
*/
|
| 28 |
+
protected static $init = false;
|
| 29 |
+
|
| 30 |
+
/**
|
| 31 |
+
* @var string 当前模块路径
|
| 32 |
+
*/
|
| 33 |
+
public static $modulePath;
|
| 34 |
+
|
| 35 |
+
/**
|
| 36 |
+
* @var bool 应用调试模式
|
| 37 |
+
*/
|
| 38 |
+
public static $debug = true;
|
| 39 |
+
|
| 40 |
+
/**
|
| 41 |
+
* @var string 应用类库命名空间
|
| 42 |
+
*/
|
| 43 |
+
public static $namespace = 'app';
|
| 44 |
+
|
| 45 |
+
/**
|
| 46 |
+
* @var bool 应用类库后缀
|
| 47 |
+
*/
|
| 48 |
+
public static $suffix = false;
|
| 49 |
+
|
| 50 |
+
/**
|
| 51 |
+
* @var bool 应用路由检测
|
| 52 |
+
*/
|
| 53 |
+
protected static $routeCheck;
|
| 54 |
+
|
| 55 |
+
/**
|
| 56 |
+
* @var bool 严格路由检测
|
| 57 |
+
*/
|
| 58 |
+
protected static $routeMust;
|
| 59 |
+
|
| 60 |
+
/**
|
| 61 |
+
* @var array 请求调度分发
|
| 62 |
+
*/
|
| 63 |
+
protected static $dispatch;
|
| 64 |
+
|
| 65 |
+
/**
|
| 66 |
+
* @var array 额外加载文件
|
| 67 |
+
*/
|
| 68 |
+
protected static $file = [];
|
| 69 |
+
|
| 70 |
+
/**
|
| 71 |
+
* 执行应用程序
|
| 72 |
+
* @access public
|
| 73 |
+
* @param Request $request 请求对象
|
| 74 |
+
* @return Response
|
| 75 |
+
* @throws Exception
|
| 76 |
+
*/
|
| 77 |
+
public static function run(Request $request = null)
|
| 78 |
+
{
|
| 79 |
+
$request = is_null($request) ? Request::instance() : $request;
|
| 80 |
+
|
| 81 |
+
try {
|
| 82 |
+
$config = self::initCommon();
|
| 83 |
+
|
| 84 |
+
// 模块/控制器绑定
|
| 85 |
+
if (defined('BIND_MODULE')) {
|
| 86 |
+
BIND_MODULE && Route::bind(BIND_MODULE);
|
| 87 |
+
} elseif ($config['auto_bind_module']) {
|
| 88 |
+
// 入口自动绑定
|
| 89 |
+
$name = pathinfo($request->baseFile(), PATHINFO_FILENAME);
|
| 90 |
+
if ($name && 'index' != $name && is_dir(APP_PATH . $name)) {
|
| 91 |
+
Route::bind($name);
|
| 92 |
+
}
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
$request->filter($config['default_filter']);
|
| 96 |
+
|
| 97 |
+
// 默认语言
|
| 98 |
+
Lang::range($config['default_lang']);
|
| 99 |
+
// 开启多语言机制 检测当前语言
|
| 100 |
+
$config['lang_switch_on'] && Lang::detect();
|
| 101 |
+
$request->langset(Lang::range());
|
| 102 |
+
|
| 103 |
+
// 加载系统语言包
|
| 104 |
+
Lang::load([
|
| 105 |
+
THINK_PATH . 'lang' . DS . $request->langset() . EXT,
|
| 106 |
+
APP_PATH . 'lang' . DS . $request->langset() . EXT,
|
| 107 |
+
]);
|
| 108 |
+
|
| 109 |
+
// 监听 app_dispatch
|
| 110 |
+
Hook::listen('app_dispatch', self::$dispatch);
|
| 111 |
+
// 获取应用调度信息
|
| 112 |
+
$dispatch = self::$dispatch;
|
| 113 |
+
|
| 114 |
+
// 未设置调度信息则进行 URL 路由检测
|
| 115 |
+
if (empty($dispatch)) {
|
| 116 |
+
$dispatch = self::routeCheck($request, $config);
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
// 记录当前调度信息
|
| 120 |
+
$request->dispatch($dispatch);
|
| 121 |
+
|
| 122 |
+
// 记录路由和请求信息
|
| 123 |
+
if (self::$debug) {
|
| 124 |
+
Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info');
|
| 125 |
+
Log::record('[ HEADER ] ' . var_export($request->header(), true), 'info');
|
| 126 |
+
Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info');
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
// 监听 app_begin
|
| 130 |
+
Hook::listen('app_begin', $dispatch);
|
| 131 |
+
|
| 132 |
+
// 请求缓存检查
|
| 133 |
+
$request->cache(
|
| 134 |
+
$config['request_cache'],
|
| 135 |
+
$config['request_cache_expire'],
|
| 136 |
+
$config['request_cache_except']
|
| 137 |
+
);
|
| 138 |
+
|
| 139 |
+
$data = self::exec($dispatch, $config);
|
| 140 |
+
} catch (HttpResponseException $exception) {
|
| 141 |
+
$data = $exception->getResponse();
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
// 清空类的实例化
|
| 145 |
+
Loader::clearInstance();
|
| 146 |
+
|
| 147 |
+
// 输出数据到客户端
|
| 148 |
+
if ($data instanceof Response) {
|
| 149 |
+
$response = $data;
|
| 150 |
+
} elseif (!is_null($data)) {
|
| 151 |
+
// 默认自动识别响应输出类型
|
| 152 |
+
$type = $request->isAjax() ?
|
| 153 |
+
Config::get('default_ajax_return') :
|
| 154 |
+
Config::get('default_return_type');
|
| 155 |
+
|
| 156 |
+
$response = Response::create($data, $type);
|
| 157 |
+
} else {
|
| 158 |
+
$response = Response::create();
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
// 监听 app_end
|
| 162 |
+
Hook::listen('app_end', $response);
|
| 163 |
+
|
| 164 |
+
return $response;
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
/**
|
| 168 |
+
* 初始化应用,并返回配置信息
|
| 169 |
+
* @access public
|
| 170 |
+
* @return array
|
| 171 |
+
*/
|
| 172 |
+
public static function initCommon()
|
| 173 |
+
{
|
| 174 |
+
if (empty(self::$init)) {
|
| 175 |
+
if (defined('APP_NAMESPACE')) {
|
| 176 |
+
self::$namespace = APP_NAMESPACE;
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
Loader::addNamespace(self::$namespace, APP_PATH);
|
| 180 |
+
|
| 181 |
+
// 初始化应用
|
| 182 |
+
$config = self::init();
|
| 183 |
+
self::$suffix = $config['class_suffix'];
|
| 184 |
+
|
| 185 |
+
// 应用调试模式
|
| 186 |
+
self::$debug = Env::get('app_debug', Config::get('app_debug'));
|
| 187 |
+
|
| 188 |
+
if (!self::$debug) {
|
| 189 |
+
ini_set('display_errors', 'Off');
|
| 190 |
+
} elseif (!IS_CLI) {
|
| 191 |
+
// 重新申请一块比较大的 buffer
|
| 192 |
+
if (ob_get_level() > 0) {
|
| 193 |
+
$output = ob_get_clean();
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
ob_start();
|
| 197 |
+
|
| 198 |
+
if (!empty($output)) {
|
| 199 |
+
echo $output;
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
if (!empty($config['root_namespace'])) {
|
| 205 |
+
Loader::addNamespace($config['root_namespace']);
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
// 加载额外文件
|
| 209 |
+
if (!empty($config['extra_file_list'])) {
|
| 210 |
+
foreach ($config['extra_file_list'] as $file) {
|
| 211 |
+
$file = strpos($file, '.') ? $file : APP_PATH . $file . EXT;
|
| 212 |
+
if (is_file($file) && !isset(self::$file[$file])) {
|
| 213 |
+
include $file;
|
| 214 |
+
self::$file[$file] = true;
|
| 215 |
+
}
|
| 216 |
+
}
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
// 设置系统时区
|
| 220 |
+
date_default_timezone_set($config['default_timezone']);
|
| 221 |
+
|
| 222 |
+
// 监听 app_init
|
| 223 |
+
Hook::listen('app_init');
|
| 224 |
+
|
| 225 |
+
self::$init = true;
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
return Config::get();
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
/**
|
| 232 |
+
* 初始化应用或模块
|
| 233 |
+
* @access public
|
| 234 |
+
* @param string $module 模块名
|
| 235 |
+
* @return array
|
| 236 |
+
*/
|
| 237 |
+
private static function init($module = '')
|
| 238 |
+
{
|
| 239 |
+
// 定位模块目录
|
| 240 |
+
$module = $module ? $module . DS : '';
|
| 241 |
+
|
| 242 |
+
// 加载初始化文件
|
| 243 |
+
if (is_file(APP_PATH . $module . 'init' . EXT)) {
|
| 244 |
+
include APP_PATH . $module . 'init' . EXT;
|
| 245 |
+
} elseif (is_file(RUNTIME_PATH . $module . 'init' . EXT)) {
|
| 246 |
+
include RUNTIME_PATH . $module . 'init' . EXT;
|
| 247 |
+
} else {
|
| 248 |
+
// 加载模块配置
|
| 249 |
+
$config = Config::load(CONF_PATH . $module . 'config' . CONF_EXT);
|
| 250 |
+
|
| 251 |
+
// 读取数据库配置文件
|
| 252 |
+
$filename = CONF_PATH . $module . 'database' . CONF_EXT;
|
| 253 |
+
Config::load($filename, 'database');
|
| 254 |
+
|
| 255 |
+
// 读取扩展配置文件
|
| 256 |
+
if (is_dir(CONF_PATH . $module . 'extra')) {
|
| 257 |
+
$dir = CONF_PATH . $module . 'extra';
|
| 258 |
+
$files = scandir($dir);
|
| 259 |
+
foreach ($files as $file) {
|
| 260 |
+
if ('.' . pathinfo($file, PATHINFO_EXTENSION) === CONF_EXT) {
|
| 261 |
+
$filename = $dir . DS . $file;
|
| 262 |
+
Config::load($filename, pathinfo($file, PATHINFO_FILENAME));
|
| 263 |
+
}
|
| 264 |
+
}
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
// 加载应用状态配置
|
| 268 |
+
if ($config['app_status']) {
|
| 269 |
+
Config::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT);
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
// 加载行为扩展文件
|
| 273 |
+
if (is_file(CONF_PATH . $module . 'tags' . EXT)) {
|
| 274 |
+
Hook::import(include CONF_PATH . $module . 'tags' . EXT);
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
// 加载公共文件
|
| 278 |
+
$path = APP_PATH . $module;
|
| 279 |
+
if (is_file($path . 'common' . EXT)) {
|
| 280 |
+
include $path . 'common' . EXT;
|
| 281 |
+
}
|
| 282 |
+
|
| 283 |
+
// 加载当前模块语言包
|
| 284 |
+
if ($module) {
|
| 285 |
+
Lang::load($path . 'lang' . DS . Request::instance()->langset() . EXT);
|
| 286 |
+
}
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
return Config::get();
|
| 290 |
+
}
|
| 291 |
+
|
| 292 |
+
/**
|
| 293 |
+
* 设置当前请求的调度信息
|
| 294 |
+
* @access public
|
| 295 |
+
* @param array|string $dispatch 调度信息
|
| 296 |
+
* @param string $type 调度类型
|
| 297 |
+
* @return void
|
| 298 |
+
*/
|
| 299 |
+
public static function dispatch($dispatch, $type = 'module')
|
| 300 |
+
{
|
| 301 |
+
self::$dispatch = ['type' => $type, $type => $dispatch];
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
/**
|
| 305 |
+
* 执行函数或者闭包方法 支持参数调用
|
| 306 |
+
* @access public
|
| 307 |
+
* @param string|array|\Closure $function 函数或者闭包
|
| 308 |
+
* @param array $vars 变量
|
| 309 |
+
* @return mixed
|
| 310 |
+
*/
|
| 311 |
+
public static function invokeFunction($function, $vars = [])
|
| 312 |
+
{
|
| 313 |
+
$reflect = new \ReflectionFunction($function);
|
| 314 |
+
$args = self::bindParams($reflect, $vars);
|
| 315 |
+
|
| 316 |
+
// 记录执行信息
|
| 317 |
+
self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info');
|
| 318 |
+
|
| 319 |
+
return $reflect->invokeArgs($args);
|
| 320 |
+
}
|
| 321 |
+
|
| 322 |
+
/**
|
| 323 |
+
* 调用反射执行类的方法 支持参数绑定
|
| 324 |
+
* @access public
|
| 325 |
+
* @param string|array $method 方法
|
| 326 |
+
* @param array $vars 变量
|
| 327 |
+
* @return mixed
|
| 328 |
+
*/
|
| 329 |
+
public static function invokeMethod($method, $vars = [])
|
| 330 |
+
{
|
| 331 |
+
if (is_array($method)) {
|
| 332 |
+
$class = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]);
|
| 333 |
+
$reflect = new \ReflectionMethod($class, $method[1]);
|
| 334 |
+
} else {
|
| 335 |
+
// 静态方法
|
| 336 |
+
$reflect = new \ReflectionMethod($method);
|
| 337 |
+
}
|
| 338 |
+
|
| 339 |
+
$args = self::bindParams($reflect, $vars);
|
| 340 |
+
|
| 341 |
+
self::$debug && Log::record('[ RUN ] ' . $reflect->class . '->' . $reflect->name . '[ ' . $reflect->getFileName() . ' ]', 'info');
|
| 342 |
+
|
| 343 |
+
return $reflect->invokeArgs(isset($class) ? $class : null, $args);
|
| 344 |
+
}
|
| 345 |
+
|
| 346 |
+
/**
|
| 347 |
+
* 调用反射执行类的实例化 支持依赖注入
|
| 348 |
+
* @access public
|
| 349 |
+
* @param string $class 类名
|
| 350 |
+
* @param array $vars 变量
|
| 351 |
+
* @return mixed
|
| 352 |
+
*/
|
| 353 |
+
public static function invokeClass($class, $vars = [])
|
| 354 |
+
{
|
| 355 |
+
$reflect = new \ReflectionClass($class);
|
| 356 |
+
$constructor = $reflect->getConstructor();
|
| 357 |
+
$args = $constructor ? self::bindParams($constructor, $vars) : [];
|
| 358 |
+
|
| 359 |
+
return $reflect->newInstanceArgs($args);
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
/**
|
| 363 |
+
* 绑定参数
|
| 364 |
+
* @access private
|
| 365 |
+
* @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
|
| 366 |
+
* @param array $vars 变量
|
| 367 |
+
* @return array
|
| 368 |
+
*/
|
| 369 |
+
private static function bindParams($reflect, $vars = [])
|
| 370 |
+
{
|
| 371 |
+
// 自动获取请求变量
|
| 372 |
+
if (empty($vars)) {
|
| 373 |
+
$vars = Config::get('url_param_type') ?
|
| 374 |
+
Request::instance()->route() :
|
| 375 |
+
Request::instance()->param();
|
| 376 |
+
}
|
| 377 |
+
|
| 378 |
+
$args = [];
|
| 379 |
+
if ($reflect->getNumberOfParameters() > 0) {
|
| 380 |
+
// 判断数组类型 数字数组时按顺序绑定参数
|
| 381 |
+
reset($vars);
|
| 382 |
+
$type = key($vars) === 0 ? 1 : 0;
|
| 383 |
+
|
| 384 |
+
foreach ($reflect->getParameters() as $param) {
|
| 385 |
+
$args[] = self::getParamValue($param, $vars, $type);
|
| 386 |
+
}
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
return $args;
|
| 390 |
+
}
|
| 391 |
+
|
| 392 |
+
/**
|
| 393 |
+
* 获取参数值
|
| 394 |
+
* @access private
|
| 395 |
+
* @param \ReflectionParameter $param 参数
|
| 396 |
+
* @param array $vars 变量
|
| 397 |
+
* @param string $type 类别
|
| 398 |
+
* @return array
|
| 399 |
+
*/
|
| 400 |
+
private static function getParamValue($param, &$vars, $type)
|
| 401 |
+
{
|
| 402 |
+
$name = $param->getName();
|
| 403 |
+
$class = $param->getClass();
|
| 404 |
+
|
| 405 |
+
if ($class) {
|
| 406 |
+
$className = $class->getName();
|
| 407 |
+
$bind = Request::instance()->$name;
|
| 408 |
+
|
| 409 |
+
if ($bind instanceof $className) {
|
| 410 |
+
$result = $bind;
|
| 411 |
+
} else {
|
| 412 |
+
if (method_exists($className, 'invoke')) {
|
| 413 |
+
$method = new \ReflectionMethod($className, 'invoke');
|
| 414 |
+
|
| 415 |
+
if ($method->isPublic() && $method->isStatic()) {
|
| 416 |
+
return $className::invoke(Request::instance());
|
| 417 |
+
}
|
| 418 |
+
}
|
| 419 |
+
|
| 420 |
+
$result = method_exists($className, 'instance') ?
|
| 421 |
+
$className::instance() :
|
| 422 |
+
new $className;
|
| 423 |
+
}
|
| 424 |
+
} elseif (1 == $type && !empty($vars)) {
|
| 425 |
+
$result = array_shift($vars);
|
| 426 |
+
} elseif (0 == $type && isset($vars[$name])) {
|
| 427 |
+
$result = $vars[$name];
|
| 428 |
+
} elseif ($param->isDefaultValueAvailable()) {
|
| 429 |
+
$result = $param->getDefaultValue();
|
| 430 |
+
} else {
|
| 431 |
+
throw new \InvalidArgumentException('method param miss:' . $name);
|
| 432 |
+
}
|
| 433 |
+
|
| 434 |
+
return $result;
|
| 435 |
+
}
|
| 436 |
+
|
| 437 |
+
/**
|
| 438 |
+
* 执行调用分发
|
| 439 |
+
* @access protected
|
| 440 |
+
* @param array $dispatch 调用信息
|
| 441 |
+
* @param array $config 配置信息
|
| 442 |
+
* @return Response|mixed
|
| 443 |
+
* @throws \InvalidArgumentException
|
| 444 |
+
*/
|
| 445 |
+
protected static function exec($dispatch, $config)
|
| 446 |
+
{
|
| 447 |
+
switch ($dispatch['type']) {
|
| 448 |
+
case 'redirect': // 重定向跳转
|
| 449 |
+
$data = Response::create($dispatch['url'], 'redirect')
|
| 450 |
+
->code($dispatch['status']);
|
| 451 |
+
break;
|
| 452 |
+
case 'module': // 模块/控制器/操作
|
| 453 |
+
$data = self::module(
|
| 454 |
+
$dispatch['module'],
|
| 455 |
+
$config,
|
| 456 |
+
isset($dispatch['convert']) ? $dispatch['convert'] : null
|
| 457 |
+
);
|
| 458 |
+
break;
|
| 459 |
+
case 'controller': // 执行控制器操作
|
| 460 |
+
$vars = array_merge(Request::instance()->param(), $dispatch['var']);
|
| 461 |
+
$data = Loader::action(
|
| 462 |
+
$dispatch['controller'],
|
| 463 |
+
$vars,
|
| 464 |
+
$config['url_controller_layer'],
|
| 465 |
+
$config['controller_suffix']
|
| 466 |
+
);
|
| 467 |
+
break;
|
| 468 |
+
case 'method': // 回调方法
|
| 469 |
+
$vars = array_merge(Request::instance()->param(), $dispatch['var']);
|
| 470 |
+
$data = self::invokeMethod($dispatch['method'], $vars);
|
| 471 |
+
break;
|
| 472 |
+
case 'function': // 闭包
|
| 473 |
+
$data = self::invokeFunction($dispatch['function']);
|
| 474 |
+
break;
|
| 475 |
+
case 'response': // Response 实例
|
| 476 |
+
$data = $dispatch['response'];
|
| 477 |
+
break;
|
| 478 |
+
default:
|
| 479 |
+
throw new \InvalidArgumentException('dispatch type not support');
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
return $data;
|
| 483 |
+
}
|
| 484 |
+
|
| 485 |
+
/**
|
| 486 |
+
* 执行模块
|
| 487 |
+
* @access public
|
| 488 |
+
* @param array $result 模块/控制器/操作
|
| 489 |
+
* @param array $config 配置参数
|
| 490 |
+
* @param bool $convert 是否自动转换控制器和操作名
|
| 491 |
+
* @return mixed
|
| 492 |
+
* @throws HttpException
|
| 493 |
+
*/
|
| 494 |
+
public static function module($result, $config, $convert = null)
|
| 495 |
+
{
|
| 496 |
+
if (is_string($result)) {
|
| 497 |
+
$result = explode('/', $result);
|
| 498 |
+
}
|
| 499 |
+
|
| 500 |
+
$request = Request::instance();
|
| 501 |
+
|
| 502 |
+
if ($config['app_multi_module']) {
|
| 503 |
+
// 多模块部署
|
| 504 |
+
$module = strip_tags(strtolower($result[0] ?: $config['default_module']));
|
| 505 |
+
$bind = Route::getBind('module');
|
| 506 |
+
$available = false;
|
| 507 |
+
|
| 508 |
+
if ($bind) {
|
| 509 |
+
// 绑定模块
|
| 510 |
+
list($bindModule) = explode('/', $bind);
|
| 511 |
+
|
| 512 |
+
if (empty($result[0])) {
|
| 513 |
+
$module = $bindModule;
|
| 514 |
+
$available = true;
|
| 515 |
+
} elseif ($module == $bindModule) {
|
| 516 |
+
$available = true;
|
| 517 |
+
}
|
| 518 |
+
} elseif (!in_array($module, $config['deny_module_list']) && is_dir(APP_PATH . $module)) {
|
| 519 |
+
$available = true;
|
| 520 |
+
}
|
| 521 |
+
|
| 522 |
+
// 模块初始化
|
| 523 |
+
if ($module && $available) {
|
| 524 |
+
// 初始化模块
|
| 525 |
+
$request->module($module);
|
| 526 |
+
$config = self::init($module);
|
| 527 |
+
|
| 528 |
+
// 模块请求缓存检查
|
| 529 |
+
$request->cache(
|
| 530 |
+
$config['request_cache'],
|
| 531 |
+
$config['request_cache_expire'],
|
| 532 |
+
$config['request_cache_except']
|
| 533 |
+
);
|
| 534 |
+
} else {
|
| 535 |
+
throw new HttpException(404, 'module not exists:' . $module);
|
| 536 |
+
}
|
| 537 |
+
} else {
|
| 538 |
+
// 单一模块部署
|
| 539 |
+
$module = '';
|
| 540 |
+
$request->module($module);
|
| 541 |
+
}
|
| 542 |
+
|
| 543 |
+
// 设置默认过滤机制
|
| 544 |
+
$request->filter($config['default_filter']);
|
| 545 |
+
|
| 546 |
+
// 当前模块路径
|
| 547 |
+
App::$modulePath = APP_PATH . ($module ? $module . DS : '');
|
| 548 |
+
|
| 549 |
+
// 是否自动转换控制器和操作名
|
| 550 |
+
$convert = is_bool($convert) ? $convert : $config['url_convert'];
|
| 551 |
+
|
| 552 |
+
// 获取控制器名
|
| 553 |
+
$controller = strip_tags($result[1] ?: $config['default_controller']);
|
| 554 |
+
|
| 555 |
+
if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) {
|
| 556 |
+
throw new HttpException(404, 'controller not exists:' . $controller);
|
| 557 |
+
}
|
| 558 |
+
|
| 559 |
+
$controller = $convert ? strtolower($controller) : $controller;
|
| 560 |
+
|
| 561 |
+
// 获取操作名
|
| 562 |
+
$actionName = strip_tags($result[2] ?: $config['default_action']);
|
| 563 |
+
if (!empty($config['action_convert'])) {
|
| 564 |
+
$actionName = Loader::parseName($actionName, 1);
|
| 565 |
+
} else {
|
| 566 |
+
$actionName = $convert ? strtolower($actionName) : $actionName;
|
| 567 |
+
}
|
| 568 |
+
|
| 569 |
+
// 设置当前请求的控制器、操作
|
| 570 |
+
$request->controller(Loader::parseName($controller, 1))->action($actionName);
|
| 571 |
+
|
| 572 |
+
// 监听module_init
|
| 573 |
+
Hook::listen('module_init', $request);
|
| 574 |
+
|
| 575 |
+
try {
|
| 576 |
+
$instance = Loader::controller(
|
| 577 |
+
$controller,
|
| 578 |
+
$config['url_controller_layer'],
|
| 579 |
+
$config['controller_suffix'],
|
| 580 |
+
$config['empty_controller']
|
| 581 |
+
);
|
| 582 |
+
} catch (ClassNotFoundException $e) {
|
| 583 |
+
throw new HttpException(404, 'controller not exists:' . $e->getClass());
|
| 584 |
+
}
|
| 585 |
+
|
| 586 |
+
// 获取当前操作名
|
| 587 |
+
$action = $actionName . $config['action_suffix'];
|
| 588 |
+
|
| 589 |
+
$vars = [];
|
| 590 |
+
if (is_callable([$instance, $action])) {
|
| 591 |
+
// 执行操作方法
|
| 592 |
+
$call = [$instance, $action];
|
| 593 |
+
// 严格获取当前操作方法名
|
| 594 |
+
$reflect = new \ReflectionMethod($instance, $action);
|
| 595 |
+
$methodName = $reflect->getName();
|
| 596 |
+
$suffix = $config['action_suffix'];
|
| 597 |
+
$actionName = $suffix ? substr($methodName, 0, -strlen($suffix)) : $methodName;
|
| 598 |
+
$request->action($actionName);
|
| 599 |
+
|
| 600 |
+
} elseif (is_callable([$instance, '_empty'])) {
|
| 601 |
+
// 空操作
|
| 602 |
+
$call = [$instance, '_empty'];
|
| 603 |
+
$vars = [$actionName];
|
| 604 |
+
} else {
|
| 605 |
+
// 操作不存在
|
| 606 |
+
throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
|
| 607 |
+
}
|
| 608 |
+
|
| 609 |
+
Hook::listen('action_begin', $call);
|
| 610 |
+
|
| 611 |
+
return self::invokeMethod($call, $vars);
|
| 612 |
+
}
|
| 613 |
+
|
| 614 |
+
/**
|
| 615 |
+
* URL路由检测(根据PATH_INFO)
|
| 616 |
+
* @access public
|
| 617 |
+
* @param \think\Request $request 请求实例
|
| 618 |
+
* @param array $config 配置信息
|
| 619 |
+
* @return array
|
| 620 |
+
* @throws \think\Exception
|
| 621 |
+
*/
|
| 622 |
+
public static function routeCheck($request, array $config)
|
| 623 |
+
{
|
| 624 |
+
$path = $request->path();
|
| 625 |
+
$depr = $config['pathinfo_depr'];
|
| 626 |
+
$result = false;
|
| 627 |
+
|
| 628 |
+
// 路由检测
|
| 629 |
+
$check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on'];
|
| 630 |
+
if ($check) {
|
| 631 |
+
// 开启路由
|
| 632 |
+
if (is_file(RUNTIME_PATH . 'route.php')) {
|
| 633 |
+
// 读取路由缓存
|
| 634 |
+
$rules = include RUNTIME_PATH . 'route.php';
|
| 635 |
+
is_array($rules) && Route::rules($rules);
|
| 636 |
+
} else {
|
| 637 |
+
$files = $config['route_config_file'];
|
| 638 |
+
foreach ($files as $file) {
|
| 639 |
+
if (is_file(CONF_PATH . $file . CONF_EXT)) {
|
| 640 |
+
// 导入路由配置
|
| 641 |
+
$rules = include CONF_PATH . $file . CONF_EXT;
|
| 642 |
+
is_array($rules) && Route::import($rules);
|
| 643 |
+
}
|
| 644 |
+
}
|
| 645 |
+
}
|
| 646 |
+
|
| 647 |
+
// 路由检测(根据路由定义返回不同的URL调度)
|
| 648 |
+
$result = Route::check($request, $path, $depr, $config['url_domain_deploy']);
|
| 649 |
+
$must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must'];
|
| 650 |
+
|
| 651 |
+
if ($must && false === $result) {
|
| 652 |
+
// 路由无效
|
| 653 |
+
throw new RouteNotFoundException();
|
| 654 |
+
}
|
| 655 |
+
}
|
| 656 |
+
|
| 657 |
+
// 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索
|
| 658 |
+
if (false === $result) {
|
| 659 |
+
$result = Route::parseUrl($path, $depr, $config['controller_auto_search']);
|
| 660 |
+
}
|
| 661 |
+
|
| 662 |
+
return $result;
|
| 663 |
+
}
|
| 664 |
+
|
| 665 |
+
/**
|
| 666 |
+
* 设置应用的路由检测机制
|
| 667 |
+
* @access public
|
| 668 |
+
* @param bool $route 是否需要检测路由
|
| 669 |
+
* @param bool $must 是否强制检测路由
|
| 670 |
+
* @return void
|
| 671 |
+
*/
|
| 672 |
+
public static function route($route, $must = false)
|
| 673 |
+
{
|
| 674 |
+
self::$routeCheck = $route;
|
| 675 |
+
self::$routeMust = $must;
|
| 676 |
+
}
|
| 677 |
+
}
|
thinkphp/library/think/Build.php
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
class Build
|
| 15 |
+
{
|
| 16 |
+
/**
|
| 17 |
+
* 根据传入的 build 资料创建目录和文件
|
| 18 |
+
* @access public
|
| 19 |
+
* @param array $build build 列表
|
| 20 |
+
* @param string $namespace 应用类库命名空间
|
| 21 |
+
* @param bool $suffix 类库后缀
|
| 22 |
+
* @return void
|
| 23 |
+
* @throws Exception
|
| 24 |
+
*/
|
| 25 |
+
public static function run(array $build = [], $namespace = 'app', $suffix = false)
|
| 26 |
+
{
|
| 27 |
+
// 锁定
|
| 28 |
+
$lock = APP_PATH . 'build.lock';
|
| 29 |
+
|
| 30 |
+
// 如果锁定文件不可写(不存在)则进行处理,否则表示已经有程序在处理了
|
| 31 |
+
if (!is_writable($lock)) {
|
| 32 |
+
if (!touch($lock)) {
|
| 33 |
+
throw new Exception(
|
| 34 |
+
'应用目录[' . APP_PATH . ']不可写,目录无法自动生成!<BR>请手动生成项目目录~',
|
| 35 |
+
10006
|
| 36 |
+
);
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
foreach ($build as $module => $list) {
|
| 40 |
+
if ('__dir__' == $module) {
|
| 41 |
+
// 创建目录列表
|
| 42 |
+
self::buildDir($list);
|
| 43 |
+
} elseif ('__file__' == $module) {
|
| 44 |
+
// 创建文件列表
|
| 45 |
+
self::buildFile($list);
|
| 46 |
+
} else {
|
| 47 |
+
// 创建模块
|
| 48 |
+
self::module($module, $list, $namespace, $suffix);
|
| 49 |
+
}
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
// 解除锁定
|
| 53 |
+
unlink($lock);
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
/**
|
| 58 |
+
* 创建目录
|
| 59 |
+
* @access protected
|
| 60 |
+
* @param array $list 目录列表
|
| 61 |
+
* @return void
|
| 62 |
+
*/
|
| 63 |
+
protected static function buildDir($list)
|
| 64 |
+
{
|
| 65 |
+
foreach ($list as $dir) {
|
| 66 |
+
// 目录不存在则创建目录
|
| 67 |
+
!is_dir(APP_PATH . $dir) && mkdir(APP_PATH . $dir, 0755, true);
|
| 68 |
+
}
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
/**
|
| 72 |
+
* 创建文件
|
| 73 |
+
* @access protected
|
| 74 |
+
* @param array $list 文件列表
|
| 75 |
+
* @return void
|
| 76 |
+
*/
|
| 77 |
+
protected static function buildFile($list)
|
| 78 |
+
{
|
| 79 |
+
foreach ($list as $file) {
|
| 80 |
+
// 先创建目录
|
| 81 |
+
if (!is_dir(APP_PATH . dirname($file))) {
|
| 82 |
+
mkdir(APP_PATH . dirname($file), 0755, true);
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
// 再创建文件
|
| 86 |
+
if (!is_file(APP_PATH . $file)) {
|
| 87 |
+
file_put_contents(
|
| 88 |
+
APP_PATH . $file,
|
| 89 |
+
'php' == pathinfo($file, PATHINFO_EXTENSION) ? "<?php\n" : ''
|
| 90 |
+
);
|
| 91 |
+
}
|
| 92 |
+
}
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
/**
|
| 96 |
+
* 创建模块
|
| 97 |
+
* @access public
|
| 98 |
+
* @param string $module 模块名
|
| 99 |
+
* @param array $list build 列表
|
| 100 |
+
* @param string $namespace 应用类库命名空间
|
| 101 |
+
* @param bool $suffix 类库后缀
|
| 102 |
+
* @return void
|
| 103 |
+
*/
|
| 104 |
+
public static function module($module = '', $list = [], $namespace = 'app', $suffix = false)
|
| 105 |
+
{
|
| 106 |
+
$module = $module ?: '';
|
| 107 |
+
|
| 108 |
+
// 创建模块目录
|
| 109 |
+
!is_dir(APP_PATH . $module) && mkdir(APP_PATH . $module);
|
| 110 |
+
|
| 111 |
+
// 如果不是 runtime 目录则需要创建配置文件和公共文件、创建模块的默认页面
|
| 112 |
+
if (basename(RUNTIME_PATH) != $module) {
|
| 113 |
+
self::buildCommon($module);
|
| 114 |
+
self::buildHello($module, $namespace, $suffix);
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
// 未指定文件和目录,则创建默认的模块目录和文件
|
| 118 |
+
if (empty($list)) {
|
| 119 |
+
$list = [
|
| 120 |
+
'__file__' => ['config.php', 'common.php'],
|
| 121 |
+
'__dir__' => ['controller', 'model', 'view'],
|
| 122 |
+
];
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
// 创建子目录和文件
|
| 126 |
+
foreach ($list as $path => $file) {
|
| 127 |
+
$modulePath = APP_PATH . $module . DS;
|
| 128 |
+
|
| 129 |
+
if ('__dir__' == $path) {
|
| 130 |
+
// 生成子目录
|
| 131 |
+
foreach ($file as $dir) {
|
| 132 |
+
self::checkDirBuild($modulePath . $dir);
|
| 133 |
+
}
|
| 134 |
+
} elseif ('__file__' == $path) {
|
| 135 |
+
// 生成(空白)文件
|
| 136 |
+
foreach ($file as $name) {
|
| 137 |
+
if (!is_file($modulePath . $name)) {
|
| 138 |
+
file_put_contents(
|
| 139 |
+
$modulePath . $name,
|
| 140 |
+
'php' == pathinfo($name, PATHINFO_EXTENSION) ? "<?php\n" : ''
|
| 141 |
+
);
|
| 142 |
+
}
|
| 143 |
+
}
|
| 144 |
+
} else {
|
| 145 |
+
// 生成相关 MVC 文件
|
| 146 |
+
foreach ($file as $val) {
|
| 147 |
+
$val = trim($val);
|
| 148 |
+
$filename = $modulePath . $path . DS . $val . ($suffix ? ucfirst($path) : '') . EXT;
|
| 149 |
+
$space = $namespace . '\\' . ($module ? $module . '\\' : '') . $path;
|
| 150 |
+
$class = $val . ($suffix ? ucfirst($path) : '');
|
| 151 |
+
|
| 152 |
+
switch ($path) {
|
| 153 |
+
case 'controller': // 控制器
|
| 154 |
+
$content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
|
| 155 |
+
break;
|
| 156 |
+
case 'model': // 模型
|
| 157 |
+
$content = "<?php\nnamespace {$space};\n\nuse think\Model;\n\nclass {$class} extends Model\n{\n\n}";
|
| 158 |
+
break;
|
| 159 |
+
case 'view': // 视图
|
| 160 |
+
$filename = $modulePath . $path . DS . $val . '.html';
|
| 161 |
+
self::checkDirBuild(dirname($filename));
|
| 162 |
+
$content = '';
|
| 163 |
+
break;
|
| 164 |
+
default:
|
| 165 |
+
// 其他文件
|
| 166 |
+
$content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
if (!is_file($filename)) {
|
| 170 |
+
file_put_contents($filename, $content);
|
| 171 |
+
}
|
| 172 |
+
}
|
| 173 |
+
}
|
| 174 |
+
}
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
/**
|
| 178 |
+
* 创建模块的欢迎页面
|
| 179 |
+
* @access protected
|
| 180 |
+
* @param string $module 模块名
|
| 181 |
+
* @param string $namespace 应用类库命名空间
|
| 182 |
+
* @param bool $suffix 类库后缀
|
| 183 |
+
* @return void
|
| 184 |
+
*/
|
| 185 |
+
protected static function buildHello($module, $namespace, $suffix = false)
|
| 186 |
+
{
|
| 187 |
+
$filename = APP_PATH . ($module ? $module . DS : '') .
|
| 188 |
+
'controller' . DS . 'Index' .
|
| 189 |
+
($suffix ? 'Controller' : '') . EXT;
|
| 190 |
+
|
| 191 |
+
if (!is_file($filename)) {
|
| 192 |
+
$module = $module ? $module . '\\' : '';
|
| 193 |
+
$suffix = $suffix ? 'Controller' : '';
|
| 194 |
+
$content = str_replace(
|
| 195 |
+
['{$app}', '{$module}', '{layer}', '{$suffix}'],
|
| 196 |
+
[$namespace, $module, 'controller', $suffix],
|
| 197 |
+
file_get_contents(THINK_PATH . 'tpl' . DS . 'default_index.tpl')
|
| 198 |
+
);
|
| 199 |
+
|
| 200 |
+
self::checkDirBuild(dirname($filename));
|
| 201 |
+
file_put_contents($filename, $content);
|
| 202 |
+
}
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
/**
|
| 206 |
+
* 创建模块的公共文件
|
| 207 |
+
* @access protected
|
| 208 |
+
* @param string $module 模块名
|
| 209 |
+
* @return void
|
| 210 |
+
*/
|
| 211 |
+
protected static function buildCommon($module)
|
| 212 |
+
{
|
| 213 |
+
$config = CONF_PATH . ($module ? $module . DS : '') . 'config.php';
|
| 214 |
+
|
| 215 |
+
self::checkDirBuild(dirname($config));
|
| 216 |
+
|
| 217 |
+
if (!is_file($config)) {
|
| 218 |
+
file_put_contents($config, "<?php\n//配置文件\nreturn [\n\n];");
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
$common = APP_PATH . ($module ? $module . DS : '') . 'common.php';
|
| 222 |
+
if (!is_file($common)) file_put_contents($common, "<?php\n");
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
/**
|
| 226 |
+
* 创建目录
|
| 227 |
+
* @access protected
|
| 228 |
+
* @param string $dirname 目录名称
|
| 229 |
+
* @return void
|
| 230 |
+
*/
|
| 231 |
+
protected static function checkDirBuild($dirname)
|
| 232 |
+
{
|
| 233 |
+
!is_dir($dirname) && mkdir($dirname, 0755, true);
|
| 234 |
+
}
|
| 235 |
+
}
|
thinkphp/library/think/Cache.php
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use think\cache\Driver;
|
| 15 |
+
|
| 16 |
+
class Cache
|
| 17 |
+
{
|
| 18 |
+
/**
|
| 19 |
+
* @var array 缓存的实例
|
| 20 |
+
*/
|
| 21 |
+
public static $instance = [];
|
| 22 |
+
|
| 23 |
+
/**
|
| 24 |
+
* @var int 缓存读取次数
|
| 25 |
+
*/
|
| 26 |
+
public static $readTimes = 0;
|
| 27 |
+
|
| 28 |
+
/**
|
| 29 |
+
* @var int 缓存写入次数
|
| 30 |
+
*/
|
| 31 |
+
public static $writeTimes = 0;
|
| 32 |
+
|
| 33 |
+
/**
|
| 34 |
+
* @var object 操作句柄
|
| 35 |
+
*/
|
| 36 |
+
public static $handler;
|
| 37 |
+
|
| 38 |
+
/**
|
| 39 |
+
* 连接缓存驱动
|
| 40 |
+
* @access public
|
| 41 |
+
* @param array $options 配置数组
|
| 42 |
+
* @param bool|string $name 缓存连接标识 true 强制重新连接
|
| 43 |
+
* @return Driver
|
| 44 |
+
*/
|
| 45 |
+
public static function connect(array $options = [], $name = false)
|
| 46 |
+
{
|
| 47 |
+
$type = !empty($options['type']) ? $options['type'] : 'File';
|
| 48 |
+
|
| 49 |
+
if (false === $name) {
|
| 50 |
+
$name = md5(serialize($options));
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
if (true === $name || !isset(self::$instance[$name])) {
|
| 54 |
+
$class = false === strpos($type, '\\') ?
|
| 55 |
+
'\\think\\cache\\driver\\' . ucwords($type) :
|
| 56 |
+
$type;
|
| 57 |
+
|
| 58 |
+
// 记录初始化信息
|
| 59 |
+
App::$debug && Log::record('[ CACHE ] INIT ' . $type, 'info');
|
| 60 |
+
|
| 61 |
+
if (true === $name) {
|
| 62 |
+
return new $class($options);
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
self::$instance[$name] = new $class($options);
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
return self::$instance[$name];
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
/**
|
| 72 |
+
* 自动初始化缓存
|
| 73 |
+
* @access public
|
| 74 |
+
* @param array $options 配置数组
|
| 75 |
+
* @return Driver
|
| 76 |
+
*/
|
| 77 |
+
public static function init(array $options = [])
|
| 78 |
+
{
|
| 79 |
+
if (is_null(self::$handler)) {
|
| 80 |
+
if (empty($options) && 'complex' == Config::get('cache.type')) {
|
| 81 |
+
$default = Config::get('cache.default');
|
| 82 |
+
// 获取默认缓存配置,并连接
|
| 83 |
+
$options = Config::get('cache.' . $default['type']) ?: $default;
|
| 84 |
+
} elseif (empty($options)) {
|
| 85 |
+
$options = Config::get('cache');
|
| 86 |
+
}
|
| 87 |
+
self::$handler = self::connect($options);
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
return self::$handler;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
public static function reset_init($options)
|
| 94 |
+
{
|
| 95 |
+
self::$handler = self::connect($options);
|
| 96 |
+
return self::$handler;
|
| 97 |
+
}
|
| 98 |
+
/**
|
| 99 |
+
* 切换缓存类型 需要配置 cache.type 为 complex
|
| 100 |
+
* @access public
|
| 101 |
+
* @param string $name 缓存标识
|
| 102 |
+
* @return Driver
|
| 103 |
+
*/
|
| 104 |
+
public static function store($name = '')
|
| 105 |
+
{
|
| 106 |
+
if ('' !== $name && 'complex' == Config::get('cache.type')) {
|
| 107 |
+
return self::connect(Config::get('cache.' . $name), strtolower($name));
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
return self::init();
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
/**
|
| 114 |
+
* 判断缓存是否存在
|
| 115 |
+
* @access public
|
| 116 |
+
* @param string $name 缓存变量名
|
| 117 |
+
* @return bool
|
| 118 |
+
*/
|
| 119 |
+
public static function has($name)
|
| 120 |
+
{
|
| 121 |
+
self::$readTimes++;
|
| 122 |
+
|
| 123 |
+
return self::init()->has($name);
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
/**
|
| 127 |
+
* 读取缓存
|
| 128 |
+
* @access public
|
| 129 |
+
* @param string $name 缓存标识
|
| 130 |
+
* @param mixed $default 默认值
|
| 131 |
+
* @return mixed
|
| 132 |
+
*/
|
| 133 |
+
public static function get($name, $default = false)
|
| 134 |
+
{
|
| 135 |
+
self::$readTimes++;
|
| 136 |
+
|
| 137 |
+
return self::init()->get($name, $default);
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
/**
|
| 141 |
+
* 写入缓存
|
| 142 |
+
* @access public
|
| 143 |
+
* @param string $name 缓存标识
|
| 144 |
+
* @param mixed $value 存储数据
|
| 145 |
+
* @param int|null $expire 有效时间 0为永久
|
| 146 |
+
* @return boolean
|
| 147 |
+
*/
|
| 148 |
+
public static function set($name, $value, $expire = null)
|
| 149 |
+
{
|
| 150 |
+
self::$writeTimes++;
|
| 151 |
+
|
| 152 |
+
return self::init()->set($name, $value, $expire);
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
/**
|
| 156 |
+
* 自增缓存(针对数值缓存)
|
| 157 |
+
* @access public
|
| 158 |
+
* @param string $name 缓存变量名
|
| 159 |
+
* @param int $step 步长
|
| 160 |
+
* @return false|int
|
| 161 |
+
*/
|
| 162 |
+
public static function inc($name, $step = 1)
|
| 163 |
+
{
|
| 164 |
+
self::$writeTimes++;
|
| 165 |
+
|
| 166 |
+
return self::init()->inc($name, $step);
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
/**
|
| 170 |
+
* 自减缓存(针对数值缓存)
|
| 171 |
+
* @access public
|
| 172 |
+
* @param string $name 缓存变量名
|
| 173 |
+
* @param int $step 步长
|
| 174 |
+
* @return false|int
|
| 175 |
+
*/
|
| 176 |
+
public static function dec($name, $step = 1)
|
| 177 |
+
{
|
| 178 |
+
self::$writeTimes++;
|
| 179 |
+
|
| 180 |
+
return self::init()->dec($name, $step);
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
/**
|
| 184 |
+
* 删除缓存
|
| 185 |
+
* @access public
|
| 186 |
+
* @param string $name 缓存标识
|
| 187 |
+
* @return boolean
|
| 188 |
+
*/
|
| 189 |
+
public static function rm($name)
|
| 190 |
+
{
|
| 191 |
+
self::$writeTimes++;
|
| 192 |
+
|
| 193 |
+
return self::init()->rm($name);
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
/**
|
| 197 |
+
* 清除缓存
|
| 198 |
+
* @access public
|
| 199 |
+
* @param string $tag 标签名
|
| 200 |
+
* @return boolean
|
| 201 |
+
*/
|
| 202 |
+
public static function clear($tag = null)
|
| 203 |
+
{
|
| 204 |
+
self::$writeTimes++;
|
| 205 |
+
|
| 206 |
+
return self::init()->clear($tag);
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
/**
|
| 210 |
+
* 读取缓存并删除
|
| 211 |
+
* @access public
|
| 212 |
+
* @param string $name 缓存变量名
|
| 213 |
+
* @return mixed
|
| 214 |
+
*/
|
| 215 |
+
public static function pull($name)
|
| 216 |
+
{
|
| 217 |
+
self::$readTimes++;
|
| 218 |
+
self::$writeTimes++;
|
| 219 |
+
|
| 220 |
+
return self::init()->pull($name);
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
/**
|
| 224 |
+
* 如果不存在则写入缓存
|
| 225 |
+
* @access public
|
| 226 |
+
* @param string $name 缓存变量名
|
| 227 |
+
* @param mixed $value 存储数据
|
| 228 |
+
* @param int $expire 有效时间 0为永久
|
| 229 |
+
* @return mixed
|
| 230 |
+
*/
|
| 231 |
+
public static function remember($name, $value, $expire = null)
|
| 232 |
+
{
|
| 233 |
+
self::$readTimes++;
|
| 234 |
+
|
| 235 |
+
return self::init()->remember($name, $value, $expire);
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
/**
|
| 239 |
+
* 缓存标签
|
| 240 |
+
* @access public
|
| 241 |
+
* @param string $name 标签名
|
| 242 |
+
* @param string|array $keys 缓存标识
|
| 243 |
+
* @param bool $overlay 是否覆盖
|
| 244 |
+
* @return Driver
|
| 245 |
+
*/
|
| 246 |
+
public static function tag($name, $keys = null, $overlay = false)
|
| 247 |
+
{
|
| 248 |
+
return self::init()->tag($name, $keys, $overlay);
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
}
|
thinkphp/library/think/Collection.php
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: zhangyajun <448901948@qq.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use ArrayAccess;
|
| 15 |
+
use ArrayIterator;
|
| 16 |
+
use Countable;
|
| 17 |
+
use IteratorAggregate;
|
| 18 |
+
use JsonSerializable;
|
| 19 |
+
|
| 20 |
+
class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
|
| 21 |
+
{
|
| 22 |
+
/**
|
| 23 |
+
* @var array 数据
|
| 24 |
+
*/
|
| 25 |
+
protected $items = [];
|
| 26 |
+
|
| 27 |
+
/**
|
| 28 |
+
* Collection constructor.
|
| 29 |
+
* @access public
|
| 30 |
+
* @param array $items 数据
|
| 31 |
+
*/
|
| 32 |
+
public function __construct($items = [])
|
| 33 |
+
{
|
| 34 |
+
$this->items = $this->convertToArray($items);
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
/**
|
| 38 |
+
* 创建 Collection 实例
|
| 39 |
+
* @access public
|
| 40 |
+
* @param array $items 数据
|
| 41 |
+
* @return static
|
| 42 |
+
*/
|
| 43 |
+
public static function make($items = [])
|
| 44 |
+
{
|
| 45 |
+
return new static($items);
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
/**
|
| 49 |
+
* 判断数据是否为空
|
| 50 |
+
* @access public
|
| 51 |
+
* @return bool
|
| 52 |
+
*/
|
| 53 |
+
public function isEmpty()
|
| 54 |
+
{
|
| 55 |
+
return empty($this->items);
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
/**
|
| 59 |
+
* 将数据转成数组
|
| 60 |
+
* @access public
|
| 61 |
+
* @return array
|
| 62 |
+
*/
|
| 63 |
+
public function toArray()
|
| 64 |
+
{
|
| 65 |
+
return array_map(function ($value) {
|
| 66 |
+
return ($value instanceof Model || $value instanceof self) ?
|
| 67 |
+
$value->toArray() :
|
| 68 |
+
$value;
|
| 69 |
+
}, $this->items);
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
/**
|
| 73 |
+
* 获取全部的数据
|
| 74 |
+
* @access public
|
| 75 |
+
* @return array
|
| 76 |
+
*/
|
| 77 |
+
public function all()
|
| 78 |
+
{
|
| 79 |
+
return $this->items;
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
/**
|
| 83 |
+
* 交换数组中的键和值
|
| 84 |
+
* @access public
|
| 85 |
+
* @return static
|
| 86 |
+
*/
|
| 87 |
+
public function flip()
|
| 88 |
+
{
|
| 89 |
+
return new static(array_flip($this->items));
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
/**
|
| 93 |
+
* 返回数组中所有的键名组成的新 Collection 实例
|
| 94 |
+
* @access public
|
| 95 |
+
* @return static
|
| 96 |
+
*/
|
| 97 |
+
public function keys()
|
| 98 |
+
{
|
| 99 |
+
return new static(array_keys($this->items));
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
/**
|
| 103 |
+
* 返回数组中所有的值组成的新 Collection 实例
|
| 104 |
+
* @access public
|
| 105 |
+
* @return static
|
| 106 |
+
*/
|
| 107 |
+
public function values()
|
| 108 |
+
{
|
| 109 |
+
return new static(array_values($this->items));
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
/**
|
| 113 |
+
* 合并数组并返回一个新的 Collection 实例
|
| 114 |
+
* @access public
|
| 115 |
+
* @param mixed $items 新的数据
|
| 116 |
+
* @return static
|
| 117 |
+
*/
|
| 118 |
+
public function merge($items)
|
| 119 |
+
{
|
| 120 |
+
return new static(array_merge($this->items, $this->convertToArray($items)));
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
/**
|
| 124 |
+
* 比较数组,返回差集生成的新 Collection 实例
|
| 125 |
+
* @access public
|
| 126 |
+
* @param mixed $items 做比较的数据
|
| 127 |
+
* @return static
|
| 128 |
+
*/
|
| 129 |
+
public function diff($items)
|
| 130 |
+
{
|
| 131 |
+
return new static(array_diff($this->items, $this->convertToArray($items)));
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
/**
|
| 135 |
+
* 比较数组,返回交集组成的 Collection 新实例
|
| 136 |
+
* @access public
|
| 137 |
+
* @param mixed $items 比较数据
|
| 138 |
+
* @return static
|
| 139 |
+
*/
|
| 140 |
+
public function intersect($items)
|
| 141 |
+
{
|
| 142 |
+
return new static(array_intersect($this->items, $this->convertToArray($items)));
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
/**
|
| 146 |
+
* 返回并删除数据中的的最后一个元素(出栈)
|
| 147 |
+
* @access public
|
| 148 |
+
* @return mixed
|
| 149 |
+
*/
|
| 150 |
+
public function pop()
|
| 151 |
+
{
|
| 152 |
+
return array_pop($this->items);
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
/**
|
| 156 |
+
* 返回并删除数据中首个元素
|
| 157 |
+
* @access public
|
| 158 |
+
* @return mixed
|
| 159 |
+
*/
|
| 160 |
+
public function shift()
|
| 161 |
+
{
|
| 162 |
+
return array_shift($this->items);
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
/**
|
| 166 |
+
* 在数组开头插入一个元素
|
| 167 |
+
* @access public
|
| 168 |
+
* @param mixed $value 值
|
| 169 |
+
* @param mixed $key 键名
|
| 170 |
+
* @return void
|
| 171 |
+
*/
|
| 172 |
+
public function unshift($value, $key = null)
|
| 173 |
+
{
|
| 174 |
+
if (is_null($key)) {
|
| 175 |
+
array_unshift($this->items, $value);
|
| 176 |
+
} else {
|
| 177 |
+
$this->items = [$key => $value] + $this->items;
|
| 178 |
+
}
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
/**
|
| 182 |
+
* 在数组结尾插入一个元素
|
| 183 |
+
* @access public
|
| 184 |
+
* @param mixed $value 值
|
| 185 |
+
* @param mixed $key 键名
|
| 186 |
+
* @return void
|
| 187 |
+
*/
|
| 188 |
+
public function push($value, $key = null)
|
| 189 |
+
{
|
| 190 |
+
if (is_null($key)) {
|
| 191 |
+
$this->items[] = $value;
|
| 192 |
+
} else {
|
| 193 |
+
$this->items[$key] = $value;
|
| 194 |
+
}
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
/**
|
| 198 |
+
* 通过使用用户自定义函数,以字符串返回数组
|
| 199 |
+
* @access public
|
| 200 |
+
* @param callable $callback 回调函数
|
| 201 |
+
* @param mixed $initial 初始值
|
| 202 |
+
* @return mixed
|
| 203 |
+
*/
|
| 204 |
+
public function reduce(callable $callback, $initial = null)
|
| 205 |
+
{
|
| 206 |
+
return array_reduce($this->items, $callback, $initial);
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
/**
|
| 210 |
+
* 以相反的顺序创建一个新的 Collection 实例
|
| 211 |
+
* @access public
|
| 212 |
+
* @return static
|
| 213 |
+
*/
|
| 214 |
+
public function reverse()
|
| 215 |
+
{
|
| 216 |
+
return new static(array_reverse($this->items));
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
/**
|
| 220 |
+
* 把数据分割为新的数组块
|
| 221 |
+
* @access public
|
| 222 |
+
* @param int $size 分隔长度
|
| 223 |
+
* @param bool $preserveKeys 是否保持原数据索引
|
| 224 |
+
* @return static
|
| 225 |
+
*/
|
| 226 |
+
public function chunk($size, $preserveKeys = false)
|
| 227 |
+
{
|
| 228 |
+
$chunks = [];
|
| 229 |
+
|
| 230 |
+
foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) {
|
| 231 |
+
$chunks[] = new static($chunk);
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
return new static($chunks);
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
/**
|
| 238 |
+
* 给数据中的每个元素执行回调
|
| 239 |
+
* @access public
|
| 240 |
+
* @param callable $callback 回调函数
|
| 241 |
+
* @return $this
|
| 242 |
+
*/
|
| 243 |
+
public function each(callable $callback)
|
| 244 |
+
{
|
| 245 |
+
foreach ($this->items as $key => $item) {
|
| 246 |
+
$result = $callback($item, $key);
|
| 247 |
+
|
| 248 |
+
if (false === $result) {
|
| 249 |
+
break;
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
if (!is_object($item)) {
|
| 253 |
+
$this->items[$key] = $result;
|
| 254 |
+
}
|
| 255 |
+
}
|
| 256 |
+
|
| 257 |
+
return $this;
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
/**
|
| 261 |
+
* 用回调函数过滤数据中的元素
|
| 262 |
+
* @access public
|
| 263 |
+
* @param callable|null $callback 回调函数
|
| 264 |
+
* @return static
|
| 265 |
+
*/
|
| 266 |
+
public function filter(callable $callback = null)
|
| 267 |
+
{
|
| 268 |
+
return new static(array_filter($this->items, $callback ?: null));
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
/**
|
| 272 |
+
* 返回数据中指定的一列
|
| 273 |
+
* @access public
|
| 274 |
+
* @param mixed $columnKey 键名
|
| 275 |
+
* @param null $indexKey 作为索引值的列
|
| 276 |
+
* @return array
|
| 277 |
+
*/
|
| 278 |
+
public function column($columnKey, $indexKey = null)
|
| 279 |
+
{
|
| 280 |
+
if (function_exists('array_column')) {
|
| 281 |
+
return array_column($this->items, $columnKey, $indexKey);
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
$result = [];
|
| 285 |
+
foreach ($this->items as $row) {
|
| 286 |
+
$key = $value = null;
|
| 287 |
+
$keySet = $valueSet = false;
|
| 288 |
+
|
| 289 |
+
if (null !== $indexKey && array_key_exists($indexKey, $row)) {
|
| 290 |
+
$key = (string) $row[$indexKey];
|
| 291 |
+
$keySet = true;
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
if (null === $columnKey) {
|
| 295 |
+
$valueSet = true;
|
| 296 |
+
$value = $row;
|
| 297 |
+
} elseif (is_array($row) && array_key_exists($columnKey, $row)) {
|
| 298 |
+
$valueSet = true;
|
| 299 |
+
$value = $row[$columnKey];
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
if ($valueSet) {
|
| 303 |
+
if ($keySet) {
|
| 304 |
+
$result[$key] = $value;
|
| 305 |
+
} else {
|
| 306 |
+
$result[] = $value;
|
| 307 |
+
}
|
| 308 |
+
}
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
return $result;
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
/**
|
| 315 |
+
* 对数据排序,并返回排序后的数据组成的新 Collection 实例
|
| 316 |
+
* @access public
|
| 317 |
+
* @param callable|null $callback 回调函数
|
| 318 |
+
* @return static
|
| 319 |
+
*/
|
| 320 |
+
public function sort(callable $callback = null)
|
| 321 |
+
{
|
| 322 |
+
$items = $this->items;
|
| 323 |
+
$callback = $callback ?: function ($a, $b) {
|
| 324 |
+
return $a == $b ? 0 : (($a < $b) ? -1 : 1);
|
| 325 |
+
};
|
| 326 |
+
|
| 327 |
+
uasort($items, $callback);
|
| 328 |
+
return new static($items);
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
/**
|
| 332 |
+
* 将数据打乱后组成新的 Collection 实例
|
| 333 |
+
* @access public
|
| 334 |
+
* @return static
|
| 335 |
+
*/
|
| 336 |
+
public function shuffle()
|
| 337 |
+
{
|
| 338 |
+
$items = $this->items;
|
| 339 |
+
|
| 340 |
+
shuffle($items);
|
| 341 |
+
return new static($items);
|
| 342 |
+
}
|
| 343 |
+
|
| 344 |
+
/**
|
| 345 |
+
* 截取数据并返回新的 Collection 实例
|
| 346 |
+
* @access public
|
| 347 |
+
* @param int $offset 起始位置
|
| 348 |
+
* @param int $length 截取长度
|
| 349 |
+
* @param bool $preserveKeys 是否保持原先的键名
|
| 350 |
+
* @return static
|
| 351 |
+
*/
|
| 352 |
+
public function slice($offset, $length = null, $preserveKeys = false)
|
| 353 |
+
{
|
| 354 |
+
return new static(array_slice($this->items, $offset, $length, $preserveKeys));
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
/**
|
| 358 |
+
* 指定的键是否存在
|
| 359 |
+
* @access public
|
| 360 |
+
* @param mixed $offset 键名
|
| 361 |
+
* @return bool
|
| 362 |
+
*/
|
| 363 |
+
public function offsetExists($offset)
|
| 364 |
+
{
|
| 365 |
+
return array_key_exists($offset, $this->items);
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
/**
|
| 369 |
+
* 获取指定键对应的值
|
| 370 |
+
* @access public
|
| 371 |
+
* @param mixed $offset 键名
|
| 372 |
+
* @return mixed
|
| 373 |
+
*/
|
| 374 |
+
public function offsetGet($offset)
|
| 375 |
+
{
|
| 376 |
+
return $this->items[$offset];
|
| 377 |
+
}
|
| 378 |
+
|
| 379 |
+
/**
|
| 380 |
+
* 设置键值
|
| 381 |
+
* @access public
|
| 382 |
+
* @param mixed $offset 键名
|
| 383 |
+
* @param mixed $value 值
|
| 384 |
+
* @return void
|
| 385 |
+
*/
|
| 386 |
+
public function offsetSet($offset, $value)
|
| 387 |
+
{
|
| 388 |
+
if (is_null($offset)) {
|
| 389 |
+
$this->items[] = $value;
|
| 390 |
+
} else {
|
| 391 |
+
$this->items[$offset] = $value;
|
| 392 |
+
}
|
| 393 |
+
}
|
| 394 |
+
|
| 395 |
+
/**
|
| 396 |
+
* 删除指定键值
|
| 397 |
+
* @access public
|
| 398 |
+
* @param mixed $offset 键名
|
| 399 |
+
* @return void
|
| 400 |
+
*/
|
| 401 |
+
public function offsetUnset($offset)
|
| 402 |
+
{
|
| 403 |
+
unset($this->items[$offset]);
|
| 404 |
+
}
|
| 405 |
+
|
| 406 |
+
/**
|
| 407 |
+
* 统计数据的个数
|
| 408 |
+
* @access public
|
| 409 |
+
* @return int
|
| 410 |
+
*/
|
| 411 |
+
public function count()
|
| 412 |
+
{
|
| 413 |
+
return count($this->items);
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
/**
|
| 417 |
+
* 获取数据的迭代器
|
| 418 |
+
* @access public
|
| 419 |
+
* @return ArrayIterator
|
| 420 |
+
*/
|
| 421 |
+
public function getIterator()
|
| 422 |
+
{
|
| 423 |
+
return new ArrayIterator($this->items);
|
| 424 |
+
}
|
| 425 |
+
|
| 426 |
+
/**
|
| 427 |
+
* 将数据反序列化成数组
|
| 428 |
+
* @access public
|
| 429 |
+
* @return array
|
| 430 |
+
*/
|
| 431 |
+
public function jsonSerialize()
|
| 432 |
+
{
|
| 433 |
+
return $this->toArray();
|
| 434 |
+
}
|
| 435 |
+
|
| 436 |
+
/**
|
| 437 |
+
* 转换当前数据集为 JSON 字符串
|
| 438 |
+
* @access public
|
| 439 |
+
* @param integer $options json 参数
|
| 440 |
+
* @return string
|
| 441 |
+
*/
|
| 442 |
+
public function toJson($options = JSON_UNESCAPED_UNICODE)
|
| 443 |
+
{
|
| 444 |
+
return json_encode($this->toArray(), $options);
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
+
/**
|
| 448 |
+
* 将数据转换成字符串
|
| 449 |
+
* @access public
|
| 450 |
+
* @return string
|
| 451 |
+
*/
|
| 452 |
+
public function __toString()
|
| 453 |
+
{
|
| 454 |
+
return $this->toJson();
|
| 455 |
+
}
|
| 456 |
+
|
| 457 |
+
/**
|
| 458 |
+
* 将数据转换成数组
|
| 459 |
+
* @access protected
|
| 460 |
+
* @param mixed $items 数据
|
| 461 |
+
* @return array
|
| 462 |
+
*/
|
| 463 |
+
protected function convertToArray($items)
|
| 464 |
+
{
|
| 465 |
+
return $items instanceof self ? $items->all() : (array) $items;
|
| 466 |
+
}
|
| 467 |
+
}
|
thinkphp/library/think/Config.php
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
class Config
|
| 15 |
+
{
|
| 16 |
+
/**
|
| 17 |
+
* @var array 配置参数
|
| 18 |
+
*/
|
| 19 |
+
private static $config = [];
|
| 20 |
+
|
| 21 |
+
/**
|
| 22 |
+
* @var string 参数作用域
|
| 23 |
+
*/
|
| 24 |
+
private static $range = '_sys_';
|
| 25 |
+
|
| 26 |
+
/**
|
| 27 |
+
* 设定配置参数的作用域
|
| 28 |
+
* @access public
|
| 29 |
+
* @param string $range 作用域
|
| 30 |
+
* @return void
|
| 31 |
+
*/
|
| 32 |
+
public static function range($range)
|
| 33 |
+
{
|
| 34 |
+
self::$range = $range;
|
| 35 |
+
|
| 36 |
+
if (!isset(self::$config[$range])) self::$config[$range] = [];
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
/**
|
| 40 |
+
* 解析配置文件或内容
|
| 41 |
+
* @access public
|
| 42 |
+
* @param string $config 配置文件路径或内容
|
| 43 |
+
* @param string $type 配置解析类型
|
| 44 |
+
* @param string $name 配置名(如设置即表示二级配置)
|
| 45 |
+
* @param string $range 作用域
|
| 46 |
+
* @return mixed
|
| 47 |
+
*/
|
| 48 |
+
public static function parse($config, $type = '', $name = '', $range = '')
|
| 49 |
+
{
|
| 50 |
+
$range = $range ?: self::$range;
|
| 51 |
+
|
| 52 |
+
if (empty($type)) $type = pathinfo($config, PATHINFO_EXTENSION);
|
| 53 |
+
|
| 54 |
+
$class = false !== strpos($type, '\\') ?
|
| 55 |
+
$type :
|
| 56 |
+
'\\think\\config\\driver\\' . ucwords($type);
|
| 57 |
+
|
| 58 |
+
return self::set((new $class())->parse($config), $name, $range);
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
/**
|
| 62 |
+
* 加载配置文件(PHP格式)
|
| 63 |
+
* @access public
|
| 64 |
+
* @param string $file 配置文件名
|
| 65 |
+
* @param string $name 配置名(如设置即表示二级配置)
|
| 66 |
+
* @param string $range 作用域
|
| 67 |
+
* @return mixed
|
| 68 |
+
*/
|
| 69 |
+
public static function load($file, $name = '', $range = '')
|
| 70 |
+
{
|
| 71 |
+
$range = $range ?: self::$range;
|
| 72 |
+
|
| 73 |
+
if (!isset(self::$config[$range])) self::$config[$range] = [];
|
| 74 |
+
|
| 75 |
+
if (is_file($file)) {
|
| 76 |
+
$name = strtolower($name);
|
| 77 |
+
$type = pathinfo($file, PATHINFO_EXTENSION);
|
| 78 |
+
|
| 79 |
+
if ('php' == $type) {
|
| 80 |
+
return self::set(include $file, $name, $range);
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
if ('yaml' == $type && function_exists('yaml_parse_file')) {
|
| 84 |
+
return self::set(yaml_parse_file($file), $name, $range);
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
return self::parse($file, $type, $name, $range);
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
return self::$config[$range];
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
/**
|
| 94 |
+
* 检测配置是否存在
|
| 95 |
+
* @access public
|
| 96 |
+
* @param string $name 配置参数名(支持二级配置 . 号分割)
|
| 97 |
+
* @param string $range 作用域
|
| 98 |
+
* @return bool
|
| 99 |
+
*/
|
| 100 |
+
public static function has($name, $range = '')
|
| 101 |
+
{
|
| 102 |
+
$range = $range ?: self::$range;
|
| 103 |
+
|
| 104 |
+
if (!strpos($name, '.')) {
|
| 105 |
+
return isset(self::$config[$range][strtolower($name)]);
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
// 二维数组设置和获取支持
|
| 109 |
+
$name = explode('.', $name, 2);
|
| 110 |
+
return isset(self::$config[$range][strtolower($name[0])][$name[1]]);
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
/**
|
| 114 |
+
* 获取配置参数 为空则获取所有配置
|
| 115 |
+
* @access public
|
| 116 |
+
* @param string $name 配置参数名(支持二级配置 . 号分割)
|
| 117 |
+
* @param string $range 作用域
|
| 118 |
+
* @return mixed
|
| 119 |
+
*/
|
| 120 |
+
public static function get($name = null, $range = '')
|
| 121 |
+
{
|
| 122 |
+
$range = $range ?: self::$range;
|
| 123 |
+
|
| 124 |
+
// 无参数时获取所有
|
| 125 |
+
if (empty($name) && isset(self::$config[$range])) {
|
| 126 |
+
return self::$config[$range];
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
// 非二级配置时直接返回
|
| 130 |
+
if (!strpos($name, '.')) {
|
| 131 |
+
$name = strtolower($name);
|
| 132 |
+
return isset(self::$config[$range][$name]) ? self::$config[$range][$name] : null;
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
// 二维数组设置和获取支持
|
| 136 |
+
$name = explode('.', $name, 2);
|
| 137 |
+
$name[0] = strtolower($name[0]);
|
| 138 |
+
|
| 139 |
+
if (!isset(self::$config[$range][$name[0]])) {
|
| 140 |
+
// 动态载入额外配置
|
| 141 |
+
$module = Request::instance()->module();
|
| 142 |
+
$file = CONF_PATH . ($module ? $module . DS : '') . 'extra' . DS . $name[0] . CONF_EXT;
|
| 143 |
+
|
| 144 |
+
is_file($file) && self::load($file, $name[0]);
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
return isset(self::$config[$range][$name[0]][$name[1]]) ?
|
| 148 |
+
self::$config[$range][$name[0]][$name[1]] :
|
| 149 |
+
null;
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
/**
|
| 153 |
+
* 设置配置参数 name 为数组则为批量设置
|
| 154 |
+
* @access public
|
| 155 |
+
* @param string|array $name 配置参数名(支持二级配置 . 号分割)
|
| 156 |
+
* @param mixed $value 配置值
|
| 157 |
+
* @param string $range 作用域
|
| 158 |
+
* @return mixed
|
| 159 |
+
*/
|
| 160 |
+
public static function set($name, $value = null, $range = '')
|
| 161 |
+
{
|
| 162 |
+
$range = $range ?: self::$range;
|
| 163 |
+
|
| 164 |
+
if (!isset(self::$config[$range])) self::$config[$range] = [];
|
| 165 |
+
|
| 166 |
+
// 字符串则表示单个配置设置
|
| 167 |
+
if (is_string($name)) {
|
| 168 |
+
if (!strpos($name, '.')) {
|
| 169 |
+
self::$config[$range][strtolower($name)] = $value;
|
| 170 |
+
} else {
|
| 171 |
+
// 二维数组
|
| 172 |
+
$name = explode('.', $name, 2);
|
| 173 |
+
self::$config[$range][strtolower($name[0])][$name[1]] = $value;
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
return $value;
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
// 数组则表示批量设置
|
| 180 |
+
if (is_array($name)) {
|
| 181 |
+
if (!empty($value)) {
|
| 182 |
+
self::$config[$range][$value] = isset(self::$config[$range][$value]) ?
|
| 183 |
+
array_merge(self::$config[$range][$value], $name) :
|
| 184 |
+
$name;
|
| 185 |
+
|
| 186 |
+
return self::$config[$range][$value];
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
return self::$config[$range] = array_merge(
|
| 190 |
+
self::$config[$range], array_change_key_case($name)
|
| 191 |
+
);
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
// 为空直接返回已有配置
|
| 195 |
+
return self::$config[$range];
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
/**
|
| 199 |
+
* 重置配置参数
|
| 200 |
+
* @access public
|
| 201 |
+
* @param string $range 作用域
|
| 202 |
+
* @return void
|
| 203 |
+
*/
|
| 204 |
+
public static function reset($range = '')
|
| 205 |
+
{
|
| 206 |
+
$range = $range ?: self::$range;
|
| 207 |
+
|
| 208 |
+
if (true === $range) {
|
| 209 |
+
self::$config = [];
|
| 210 |
+
} else {
|
| 211 |
+
self::$config[$range] = [];
|
| 212 |
+
}
|
| 213 |
+
}
|
| 214 |
+
}
|
thinkphp/library/think/Console.php
ADDED
|
@@ -0,0 +1,863 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | TopThink [ WE CAN DO IT JUST THINK IT ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2015 http://www.topthink.com All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Author: zhangyajun <448901948@qq.com>
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
|
| 10 |
+
namespace think;
|
| 11 |
+
|
| 12 |
+
use think\console\Command;
|
| 13 |
+
use think\console\command\Help as HelpCommand;
|
| 14 |
+
use think\console\Input;
|
| 15 |
+
use think\console\input\Argument as InputArgument;
|
| 16 |
+
use think\console\input\Definition as InputDefinition;
|
| 17 |
+
use think\console\input\Option as InputOption;
|
| 18 |
+
use think\console\Output;
|
| 19 |
+
use think\console\output\driver\Buffer;
|
| 20 |
+
|
| 21 |
+
class Console
|
| 22 |
+
{
|
| 23 |
+
/**
|
| 24 |
+
* @var string 命令名称
|
| 25 |
+
*/
|
| 26 |
+
private $name;
|
| 27 |
+
|
| 28 |
+
/**
|
| 29 |
+
* @var string 命令版本
|
| 30 |
+
*/
|
| 31 |
+
private $version;
|
| 32 |
+
|
| 33 |
+
/**
|
| 34 |
+
* @var Command[] 命令
|
| 35 |
+
*/
|
| 36 |
+
private $commands = [];
|
| 37 |
+
|
| 38 |
+
/**
|
| 39 |
+
* @var bool 是否需要帮助信息
|
| 40 |
+
*/
|
| 41 |
+
private $wantHelps = false;
|
| 42 |
+
|
| 43 |
+
/**
|
| 44 |
+
* @var bool 是否捕获异常
|
| 45 |
+
*/
|
| 46 |
+
private $catchExceptions = true;
|
| 47 |
+
|
| 48 |
+
/**
|
| 49 |
+
* @var bool 是否自动退出执行
|
| 50 |
+
*/
|
| 51 |
+
private $autoExit = true;
|
| 52 |
+
|
| 53 |
+
/**
|
| 54 |
+
* @var InputDefinition 输入定义
|
| 55 |
+
*/
|
| 56 |
+
private $definition;
|
| 57 |
+
|
| 58 |
+
/**
|
| 59 |
+
* @var string 默认执行的命令
|
| 60 |
+
*/
|
| 61 |
+
private $defaultCommand;
|
| 62 |
+
|
| 63 |
+
/**
|
| 64 |
+
* @var array 默认提供的命令
|
| 65 |
+
*/
|
| 66 |
+
private static $defaultCommands = [
|
| 67 |
+
"think\\console\\command\\Help",
|
| 68 |
+
"think\\console\\command\\Lists",
|
| 69 |
+
"think\\console\\command\\Build",
|
| 70 |
+
"think\\console\\command\\Clear",
|
| 71 |
+
"think\\console\\command\\make\\Controller",
|
| 72 |
+
"think\\console\\command\\make\\Model",
|
| 73 |
+
"think\\console\\command\\optimize\\Autoload",
|
| 74 |
+
"think\\console\\command\\optimize\\Config",
|
| 75 |
+
"think\\console\\command\\optimize\\Route",
|
| 76 |
+
"think\\console\\command\\optimize\\Schema",
|
| 77 |
+
];
|
| 78 |
+
|
| 79 |
+
/**
|
| 80 |
+
* Console constructor.
|
| 81 |
+
* @access public
|
| 82 |
+
* @param string $name 名称
|
| 83 |
+
* @param string $version 版本
|
| 84 |
+
* @param null|string $user 执行用户
|
| 85 |
+
*/
|
| 86 |
+
public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN', $user = null)
|
| 87 |
+
{
|
| 88 |
+
$this->name = $name;
|
| 89 |
+
$this->version = $version;
|
| 90 |
+
|
| 91 |
+
if ($user) {
|
| 92 |
+
$this->setUser($user);
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
$this->defaultCommand = 'list';
|
| 96 |
+
$this->definition = $this->getDefaultInputDefinition();
|
| 97 |
+
|
| 98 |
+
foreach ($this->getDefaultCommands() as $command) {
|
| 99 |
+
$this->add($command);
|
| 100 |
+
}
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
/**
|
| 104 |
+
* 设置执行用户
|
| 105 |
+
* @param $user
|
| 106 |
+
*/
|
| 107 |
+
public function setUser($user)
|
| 108 |
+
{
|
| 109 |
+
$user = posix_getpwnam($user);
|
| 110 |
+
if ($user) {
|
| 111 |
+
posix_setuid($user['uid']);
|
| 112 |
+
posix_setgid($user['gid']);
|
| 113 |
+
}
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
/**
|
| 117 |
+
* 初始化 Console
|
| 118 |
+
* @access public
|
| 119 |
+
* @param bool $run 是否运行 Console
|
| 120 |
+
* @return int|Console
|
| 121 |
+
*/
|
| 122 |
+
public static function init($run = true)
|
| 123 |
+
{
|
| 124 |
+
static $console;
|
| 125 |
+
|
| 126 |
+
if (!$console) {
|
| 127 |
+
$config = Config::get('console');
|
| 128 |
+
// 实例化 console
|
| 129 |
+
$console = new self($config['name'], $config['version'], $config['user']);
|
| 130 |
+
|
| 131 |
+
// 读取指令集
|
| 132 |
+
if (is_file(CONF_PATH . 'command' . EXT)) {
|
| 133 |
+
$commands = include CONF_PATH . 'command' . EXT;
|
| 134 |
+
|
| 135 |
+
if (is_array($commands)) {
|
| 136 |
+
foreach ($commands as $command) {
|
| 137 |
+
class_exists($command) &&
|
| 138 |
+
is_subclass_of($command, "\\think\\console\\Command") &&
|
| 139 |
+
$console->add(new $command()); // 注册指令
|
| 140 |
+
}
|
| 141 |
+
}
|
| 142 |
+
}
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
return $run ? $console->run() : $console;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
/**
|
| 149 |
+
* 调用命令
|
| 150 |
+
* @access public
|
| 151 |
+
* @param string $command
|
| 152 |
+
* @param array $parameters
|
| 153 |
+
* @param string $driver
|
| 154 |
+
* @return Output
|
| 155 |
+
*/
|
| 156 |
+
public static function call($command, array $parameters = [], $driver = 'buffer')
|
| 157 |
+
{
|
| 158 |
+
$console = self::init(false);
|
| 159 |
+
|
| 160 |
+
array_unshift($parameters, $command);
|
| 161 |
+
|
| 162 |
+
$input = new Input($parameters);
|
| 163 |
+
$output = new Output($driver);
|
| 164 |
+
|
| 165 |
+
$console->setCatchExceptions(false);
|
| 166 |
+
$console->find($command)->run($input, $output);
|
| 167 |
+
|
| 168 |
+
return $output;
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
/**
|
| 172 |
+
* 执行当前的指令
|
| 173 |
+
* @access public
|
| 174 |
+
* @return int
|
| 175 |
+
* @throws \Exception
|
| 176 |
+
*/
|
| 177 |
+
public function run()
|
| 178 |
+
{
|
| 179 |
+
$input = new Input();
|
| 180 |
+
$output = new Output();
|
| 181 |
+
|
| 182 |
+
$this->configureIO($input, $output);
|
| 183 |
+
|
| 184 |
+
try {
|
| 185 |
+
$exitCode = $this->doRun($input, $output);
|
| 186 |
+
} catch (\Exception $e) {
|
| 187 |
+
if (!$this->catchExceptions) throw $e;
|
| 188 |
+
|
| 189 |
+
$output->renderException($e);
|
| 190 |
+
|
| 191 |
+
$exitCode = $e->getCode();
|
| 192 |
+
|
| 193 |
+
if (is_numeric($exitCode)) {
|
| 194 |
+
$exitCode = ((int) $exitCode) ?: 1;
|
| 195 |
+
} else {
|
| 196 |
+
$exitCode = 1;
|
| 197 |
+
}
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
if ($this->autoExit) {
|
| 201 |
+
if ($exitCode > 255) $exitCode = 255;
|
| 202 |
+
|
| 203 |
+
exit($exitCode);
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
return $exitCode;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
/**
|
| 210 |
+
* 执行指令
|
| 211 |
+
* @access public
|
| 212 |
+
* @param Input $input 输入
|
| 213 |
+
* @param Output $output 输出
|
| 214 |
+
* @return int
|
| 215 |
+
*/
|
| 216 |
+
public function doRun(Input $input, Output $output)
|
| 217 |
+
{
|
| 218 |
+
// 获取版本信息
|
| 219 |
+
if (true === $input->hasParameterOption(['--version', '-V'])) {
|
| 220 |
+
$output->writeln($this->getLongVersion());
|
| 221 |
+
|
| 222 |
+
return 0;
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
$name = $this->getCommandName($input);
|
| 226 |
+
|
| 227 |
+
// 获取帮助信息
|
| 228 |
+
if (true === $input->hasParameterOption(['--help', '-h'])) {
|
| 229 |
+
if (!$name) {
|
| 230 |
+
$name = 'help';
|
| 231 |
+
$input = new Input(['help']);
|
| 232 |
+
} else {
|
| 233 |
+
$this->wantHelps = true;
|
| 234 |
+
}
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
if (!$name) {
|
| 238 |
+
$name = $this->defaultCommand;
|
| 239 |
+
$input = new Input([$this->defaultCommand]);
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
return $this->doRunCommand($this->find($name), $input, $output);
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
/**
|
| 246 |
+
* 设置输入参数定义
|
| 247 |
+
* @access public
|
| 248 |
+
* @param InputDefinition $definition 输入定义
|
| 249 |
+
* @return $this;
|
| 250 |
+
*/
|
| 251 |
+
public function setDefinition(InputDefinition $definition)
|
| 252 |
+
{
|
| 253 |
+
$this->definition = $definition;
|
| 254 |
+
|
| 255 |
+
return $this;
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
/**
|
| 259 |
+
* 获取输入参数定义
|
| 260 |
+
* @access public
|
| 261 |
+
* @return InputDefinition
|
| 262 |
+
*/
|
| 263 |
+
public function getDefinition()
|
| 264 |
+
{
|
| 265 |
+
return $this->definition;
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
/**
|
| 269 |
+
* 获取帮助信息
|
| 270 |
+
* @access public
|
| 271 |
+
* @return string
|
| 272 |
+
*/
|
| 273 |
+
public function getHelp()
|
| 274 |
+
{
|
| 275 |
+
return $this->getLongVersion();
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
/**
|
| 279 |
+
* 设置是否捕获异常
|
| 280 |
+
* @access public
|
| 281 |
+
* @param bool $boolean 是否捕获
|
| 282 |
+
* @return $this
|
| 283 |
+
*/
|
| 284 |
+
public function setCatchExceptions($boolean)
|
| 285 |
+
{
|
| 286 |
+
$this->catchExceptions = (bool) $boolean;
|
| 287 |
+
|
| 288 |
+
return $this;
|
| 289 |
+
}
|
| 290 |
+
|
| 291 |
+
/**
|
| 292 |
+
* 设置是否自动退出
|
| 293 |
+
* @access public
|
| 294 |
+
* @param bool $boolean 是否自动退出
|
| 295 |
+
* @return $this
|
| 296 |
+
*/
|
| 297 |
+
public function setAutoExit($boolean)
|
| 298 |
+
{
|
| 299 |
+
$this->autoExit = (bool) $boolean;
|
| 300 |
+
|
| 301 |
+
return $this;
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
/**
|
| 305 |
+
* 获取名称
|
| 306 |
+
* @access public
|
| 307 |
+
* @return string
|
| 308 |
+
*/
|
| 309 |
+
public function getName()
|
| 310 |
+
{
|
| 311 |
+
return $this->name;
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
/**
|
| 315 |
+
* 设置名称
|
| 316 |
+
* @access public
|
| 317 |
+
* @param string $name 名称
|
| 318 |
+
* @return $this
|
| 319 |
+
*/
|
| 320 |
+
public function setName($name)
|
| 321 |
+
{
|
| 322 |
+
$this->name = $name;
|
| 323 |
+
|
| 324 |
+
return $this;
|
| 325 |
+
}
|
| 326 |
+
|
| 327 |
+
/**
|
| 328 |
+
* 获取版本
|
| 329 |
+
* @access public
|
| 330 |
+
* @return string
|
| 331 |
+
*/
|
| 332 |
+
public function getVersion()
|
| 333 |
+
{
|
| 334 |
+
return $this->version;
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
/**
|
| 338 |
+
* 设置版本
|
| 339 |
+
* @access public
|
| 340 |
+
* @param string $version 版本信息
|
| 341 |
+
* @return $this
|
| 342 |
+
*/
|
| 343 |
+
public function setVersion($version)
|
| 344 |
+
{
|
| 345 |
+
$this->version = $version;
|
| 346 |
+
|
| 347 |
+
return $this;
|
| 348 |
+
}
|
| 349 |
+
|
| 350 |
+
/**
|
| 351 |
+
* 获取完整的版本号
|
| 352 |
+
* @access public
|
| 353 |
+
* @return string
|
| 354 |
+
*/
|
| 355 |
+
public function getLongVersion()
|
| 356 |
+
{
|
| 357 |
+
if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
|
| 358 |
+
return sprintf(
|
| 359 |
+
'<info>%s</info> version <comment>%s</comment>',
|
| 360 |
+
$this->getName(),
|
| 361 |
+
$this->getVersion()
|
| 362 |
+
);
|
| 363 |
+
}
|
| 364 |
+
|
| 365 |
+
return '<info>Console Tool</info>';
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
/**
|
| 369 |
+
* 注册一个指令
|
| 370 |
+
* @access public
|
| 371 |
+
* @param string $name 指令名称
|
| 372 |
+
* @return Command
|
| 373 |
+
*/
|
| 374 |
+
public function register($name)
|
| 375 |
+
{
|
| 376 |
+
return $this->add(new Command($name));
|
| 377 |
+
}
|
| 378 |
+
|
| 379 |
+
/**
|
| 380 |
+
* 批量添加指令
|
| 381 |
+
* @access public
|
| 382 |
+
* @param Command[] $commands 指令实例
|
| 383 |
+
* @return $this
|
| 384 |
+
*/
|
| 385 |
+
public function addCommands(array $commands)
|
| 386 |
+
{
|
| 387 |
+
foreach ($commands as $command) $this->add($command);
|
| 388 |
+
|
| 389 |
+
return $this;
|
| 390 |
+
}
|
| 391 |
+
|
| 392 |
+
/**
|
| 393 |
+
* 添加一个指令
|
| 394 |
+
* @access public
|
| 395 |
+
* @param Command $command 命令实例
|
| 396 |
+
* @return Command|bool
|
| 397 |
+
*/
|
| 398 |
+
public function add(Command $command)
|
| 399 |
+
{
|
| 400 |
+
if (!$command->isEnabled()) {
|
| 401 |
+
$command->setConsole(null);
|
| 402 |
+
return false;
|
| 403 |
+
}
|
| 404 |
+
|
| 405 |
+
$command->setConsole($this);
|
| 406 |
+
|
| 407 |
+
if (null === $command->getDefinition()) {
|
| 408 |
+
throw new \LogicException(
|
| 409 |
+
sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command))
|
| 410 |
+
);
|
| 411 |
+
}
|
| 412 |
+
|
| 413 |
+
$this->commands[$command->getName()] = $command;
|
| 414 |
+
|
| 415 |
+
foreach ($command->getAliases() as $alias) {
|
| 416 |
+
$this->commands[$alias] = $command;
|
| 417 |
+
}
|
| 418 |
+
|
| 419 |
+
return $command;
|
| 420 |
+
}
|
| 421 |
+
|
| 422 |
+
/**
|
| 423 |
+
* 获取指令
|
| 424 |
+
* @access public
|
| 425 |
+
* @param string $name 指令名称
|
| 426 |
+
* @return Command
|
| 427 |
+
* @throws \InvalidArgumentException
|
| 428 |
+
*/
|
| 429 |
+
public function get($name)
|
| 430 |
+
{
|
| 431 |
+
if (!isset($this->commands[$name])) {
|
| 432 |
+
throw new \InvalidArgumentException(
|
| 433 |
+
sprintf('The command "%s" does not exist.', $name)
|
| 434 |
+
);
|
| 435 |
+
}
|
| 436 |
+
|
| 437 |
+
$command = $this->commands[$name];
|
| 438 |
+
|
| 439 |
+
if ($this->wantHelps) {
|
| 440 |
+
$this->wantHelps = false;
|
| 441 |
+
|
| 442 |
+
/** @var HelpCommand $helpCommand */
|
| 443 |
+
$helpCommand = $this->get('help');
|
| 444 |
+
$helpCommand->setCommand($command);
|
| 445 |
+
|
| 446 |
+
return $helpCommand;
|
| 447 |
+
}
|
| 448 |
+
|
| 449 |
+
return $command;
|
| 450 |
+
}
|
| 451 |
+
|
| 452 |
+
/**
|
| 453 |
+
* 某个指令是否存在
|
| 454 |
+
* @access public
|
| 455 |
+
* @param string $name 指令名称
|
| 456 |
+
* @return bool
|
| 457 |
+
*/
|
| 458 |
+
public function has($name)
|
| 459 |
+
{
|
| 460 |
+
return isset($this->commands[$name]);
|
| 461 |
+
}
|
| 462 |
+
|
| 463 |
+
/**
|
| 464 |
+
* 获取所有的命名空间
|
| 465 |
+
* @access public
|
| 466 |
+
* @return array
|
| 467 |
+
*/
|
| 468 |
+
public function getNamespaces()
|
| 469 |
+
{
|
| 470 |
+
$namespaces = [];
|
| 471 |
+
|
| 472 |
+
foreach ($this->commands as $command) {
|
| 473 |
+
$namespaces = array_merge(
|
| 474 |
+
$namespaces, $this->extractAllNamespaces($command->getName())
|
| 475 |
+
);
|
| 476 |
+
|
| 477 |
+
foreach ($command->getAliases() as $alias) {
|
| 478 |
+
$namespaces = array_merge(
|
| 479 |
+
$namespaces, $this->extractAllNamespaces($alias)
|
| 480 |
+
);
|
| 481 |
+
}
|
| 482 |
+
}
|
| 483 |
+
|
| 484 |
+
return array_values(array_unique(array_filter($namespaces)));
|
| 485 |
+
}
|
| 486 |
+
|
| 487 |
+
/**
|
| 488 |
+
* 查找注册命名空间中的名称或缩写
|
| 489 |
+
* @access public
|
| 490 |
+
* @param string $namespace
|
| 491 |
+
* @return string
|
| 492 |
+
* @throws \InvalidArgumentException
|
| 493 |
+
*/
|
| 494 |
+
public function findNamespace($namespace)
|
| 495 |
+
{
|
| 496 |
+
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
|
| 497 |
+
return preg_quote($matches[1]) . '[^:]*';
|
| 498 |
+
}, $namespace);
|
| 499 |
+
|
| 500 |
+
$allNamespaces = $this->getNamespaces();
|
| 501 |
+
$namespaces = preg_grep('{^' . $expr . '}', $allNamespaces);
|
| 502 |
+
|
| 503 |
+
if (empty($namespaces)) {
|
| 504 |
+
$message = sprintf(
|
| 505 |
+
'There are no commands defined in the "%s" namespace.', $namespace
|
| 506 |
+
);
|
| 507 |
+
|
| 508 |
+
if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
|
| 509 |
+
if (1 == count($alternatives)) {
|
| 510 |
+
$message .= "\n\nDid you mean this?\n ";
|
| 511 |
+
} else {
|
| 512 |
+
$message .= "\n\nDid you mean one of these?\n ";
|
| 513 |
+
}
|
| 514 |
+
|
| 515 |
+
$message .= implode("\n ", $alternatives);
|
| 516 |
+
}
|
| 517 |
+
|
| 518 |
+
throw new \InvalidArgumentException($message);
|
| 519 |
+
}
|
| 520 |
+
|
| 521 |
+
$exact = in_array($namespace, $namespaces, true);
|
| 522 |
+
|
| 523 |
+
if (count($namespaces) > 1 && !$exact) {
|
| 524 |
+
throw new \InvalidArgumentException(
|
| 525 |
+
sprintf(
|
| 526 |
+
'The namespace "%s" is ambiguous (%s).',
|
| 527 |
+
$namespace,
|
| 528 |
+
$this->getAbbreviationSuggestions(array_values($namespaces)))
|
| 529 |
+
);
|
| 530 |
+
}
|
| 531 |
+
|
| 532 |
+
return $exact ? $namespace : reset($namespaces);
|
| 533 |
+
}
|
| 534 |
+
|
| 535 |
+
/**
|
| 536 |
+
* 查找指令
|
| 537 |
+
* @access public
|
| 538 |
+
* @param string $name 名称或者别名
|
| 539 |
+
* @return Command
|
| 540 |
+
* @throws \InvalidArgumentException
|
| 541 |
+
*/
|
| 542 |
+
public function find($name)
|
| 543 |
+
{
|
| 544 |
+
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
|
| 545 |
+
return preg_quote($matches[1]) . '[^:]*';
|
| 546 |
+
}, $name);
|
| 547 |
+
|
| 548 |
+
$allCommands = array_keys($this->commands);
|
| 549 |
+
$commands = preg_grep('{^' . $expr . '}', $allCommands);
|
| 550 |
+
|
| 551 |
+
if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) {
|
| 552 |
+
if (false !== ($pos = strrpos($name, ':'))) {
|
| 553 |
+
$this->findNamespace(substr($name, 0, $pos));
|
| 554 |
+
}
|
| 555 |
+
|
| 556 |
+
$message = sprintf('Command "%s" is not defined.', $name);
|
| 557 |
+
|
| 558 |
+
if ($alternatives = $this->findAlternatives($name, $allCommands)) {
|
| 559 |
+
if (1 == count($alternatives)) {
|
| 560 |
+
$message .= "\n\nDid you mean this?\n ";
|
| 561 |
+
} else {
|
| 562 |
+
$message .= "\n\nDid you mean one of these?\n ";
|
| 563 |
+
}
|
| 564 |
+
$message .= implode("\n ", $alternatives);
|
| 565 |
+
}
|
| 566 |
+
|
| 567 |
+
throw new \InvalidArgumentException($message);
|
| 568 |
+
}
|
| 569 |
+
|
| 570 |
+
if (count($commands) > 1) {
|
| 571 |
+
$commandList = $this->commands;
|
| 572 |
+
$commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) {
|
| 573 |
+
$commandName = $commandList[$nameOrAlias]->getName();
|
| 574 |
+
|
| 575 |
+
return $commandName === $nameOrAlias || !in_array($commandName, $commands);
|
| 576 |
+
});
|
| 577 |
+
}
|
| 578 |
+
|
| 579 |
+
$exact = in_array($name, $commands, true);
|
| 580 |
+
if (count($commands) > 1 && !$exact) {
|
| 581 |
+
$suggestions = $this->getAbbreviationSuggestions(array_values($commands));
|
| 582 |
+
|
| 583 |
+
throw new \InvalidArgumentException(
|
| 584 |
+
sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions)
|
| 585 |
+
);
|
| 586 |
+
}
|
| 587 |
+
|
| 588 |
+
return $this->get($exact ? $name : reset($commands));
|
| 589 |
+
}
|
| 590 |
+
|
| 591 |
+
/**
|
| 592 |
+
* 获取所有的指令
|
| 593 |
+
* @access public
|
| 594 |
+
* @param string $namespace 命名空间
|
| 595 |
+
* @return Command[]
|
| 596 |
+
*/
|
| 597 |
+
public function all($namespace = null)
|
| 598 |
+
{
|
| 599 |
+
if (null === $namespace) return $this->commands;
|
| 600 |
+
|
| 601 |
+
$commands = [];
|
| 602 |
+
|
| 603 |
+
foreach ($this->commands as $name => $command) {
|
| 604 |
+
$ext = $this->extractNamespace($name, substr_count($namespace, ':') + 1);
|
| 605 |
+
|
| 606 |
+
if ($ext === $namespace) $commands[$name] = $command;
|
| 607 |
+
}
|
| 608 |
+
|
| 609 |
+
return $commands;
|
| 610 |
+
}
|
| 611 |
+
|
| 612 |
+
/**
|
| 613 |
+
* 获取可能的指令名
|
| 614 |
+
* @access public
|
| 615 |
+
* @param array $names 指令名
|
| 616 |
+
* @return array
|
| 617 |
+
*/
|
| 618 |
+
public static function getAbbreviations($names)
|
| 619 |
+
{
|
| 620 |
+
$abbrevs = [];
|
| 621 |
+
foreach ($names as $name) {
|
| 622 |
+
for ($len = strlen($name); $len > 0; --$len) {
|
| 623 |
+
$abbrev = substr($name, 0, $len);
|
| 624 |
+
$abbrevs[$abbrev][] = $name;
|
| 625 |
+
}
|
| 626 |
+
}
|
| 627 |
+
|
| 628 |
+
return $abbrevs;
|
| 629 |
+
}
|
| 630 |
+
|
| 631 |
+
/**
|
| 632 |
+
* 配置基于用户的参数和选项的输入和输出实例
|
| 633 |
+
* @access protected
|
| 634 |
+
* @param Input $input 输入实例
|
| 635 |
+
* @param Output $output 输出实例
|
| 636 |
+
* @return void
|
| 637 |
+
*/
|
| 638 |
+
protected function configureIO(Input $input, Output $output)
|
| 639 |
+
{
|
| 640 |
+
if (true === $input->hasParameterOption(['--ansi'])) {
|
| 641 |
+
$output->setDecorated(true);
|
| 642 |
+
} elseif (true === $input->hasParameterOption(['--no-ansi'])) {
|
| 643 |
+
$output->setDecorated(false);
|
| 644 |
+
}
|
| 645 |
+
|
| 646 |
+
if (true === $input->hasParameterOption(['--no-interaction', '-n'])) {
|
| 647 |
+
$input->setInteractive(false);
|
| 648 |
+
}
|
| 649 |
+
|
| 650 |
+
if (true === $input->hasParameterOption(['--quiet', '-q'])) {
|
| 651 |
+
$output->setVerbosity(Output::VERBOSITY_QUIET);
|
| 652 |
+
} else {
|
| 653 |
+
if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) {
|
| 654 |
+
$output->setVerbosity(Output::VERBOSITY_DEBUG);
|
| 655 |
+
} elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) {
|
| 656 |
+
$output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE);
|
| 657 |
+
} elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) {
|
| 658 |
+
$output->setVerbosity(Output::VERBOSITY_VERBOSE);
|
| 659 |
+
}
|
| 660 |
+
}
|
| 661 |
+
}
|
| 662 |
+
|
| 663 |
+
/**
|
| 664 |
+
* 执行指令
|
| 665 |
+
* @access protected
|
| 666 |
+
* @param Command $command 指令实例
|
| 667 |
+
* @param Input $input 输入实例
|
| 668 |
+
* @param Output $output 输出实例
|
| 669 |
+
* @return int
|
| 670 |
+
* @throws \Exception
|
| 671 |
+
*/
|
| 672 |
+
protected function doRunCommand(Command $command, Input $input, Output $output)
|
| 673 |
+
{
|
| 674 |
+
return $command->run($input, $output);
|
| 675 |
+
}
|
| 676 |
+
|
| 677 |
+
/**
|
| 678 |
+
* 获取指令的名称
|
| 679 |
+
* @access protected
|
| 680 |
+
* @param Input $input 输入实例
|
| 681 |
+
* @return string
|
| 682 |
+
*/
|
| 683 |
+
protected function getCommandName(Input $input)
|
| 684 |
+
{
|
| 685 |
+
return $input->getFirstArgument();
|
| 686 |
+
}
|
| 687 |
+
|
| 688 |
+
/**
|
| 689 |
+
* 获取默认输入定义
|
| 690 |
+
* @access protected
|
| 691 |
+
* @return InputDefinition
|
| 692 |
+
*/
|
| 693 |
+
protected function getDefaultInputDefinition()
|
| 694 |
+
{
|
| 695 |
+
return new InputDefinition([
|
| 696 |
+
new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
|
| 697 |
+
new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'),
|
| 698 |
+
new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this console version'),
|
| 699 |
+
new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
|
| 700 |
+
new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
|
| 701 |
+
new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
|
| 702 |
+
new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
|
| 703 |
+
new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
|
| 704 |
+
]);
|
| 705 |
+
}
|
| 706 |
+
|
| 707 |
+
/**
|
| 708 |
+
* 获取默认命令
|
| 709 |
+
* @access protected
|
| 710 |
+
* @return Command[]
|
| 711 |
+
*/
|
| 712 |
+
protected function getDefaultCommands()
|
| 713 |
+
{
|
| 714 |
+
$defaultCommands = [];
|
| 715 |
+
|
| 716 |
+
foreach (self::$defaultCommands as $class) {
|
| 717 |
+
if (class_exists($class) && is_subclass_of($class, "think\\console\\Command")) {
|
| 718 |
+
$defaultCommands[] = new $class();
|
| 719 |
+
}
|
| 720 |
+
}
|
| 721 |
+
|
| 722 |
+
return $defaultCommands;
|
| 723 |
+
}
|
| 724 |
+
|
| 725 |
+
/**
|
| 726 |
+
* 添加默认指令
|
| 727 |
+
* @access public
|
| 728 |
+
* @param array $classes 指令
|
| 729 |
+
* @return void
|
| 730 |
+
*/
|
| 731 |
+
public static function addDefaultCommands(array $classes)
|
| 732 |
+
{
|
| 733 |
+
self::$defaultCommands = array_merge(self::$defaultCommands, $classes);
|
| 734 |
+
}
|
| 735 |
+
|
| 736 |
+
/**
|
| 737 |
+
* 获取可能的建议
|
| 738 |
+
* @access private
|
| 739 |
+
* @param array $abbrevs
|
| 740 |
+
* @return string
|
| 741 |
+
*/
|
| 742 |
+
private function getAbbreviationSuggestions($abbrevs)
|
| 743 |
+
{
|
| 744 |
+
return sprintf(
|
| 745 |
+
'%s, %s%s',
|
| 746 |
+
$abbrevs[0],
|
| 747 |
+
$abbrevs[1],
|
| 748 |
+
count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''
|
| 749 |
+
);
|
| 750 |
+
}
|
| 751 |
+
|
| 752 |
+
/**
|
| 753 |
+
* 返回指令的命名空间部分
|
| 754 |
+
* @access public
|
| 755 |
+
* @param string $name 指令名称
|
| 756 |
+
* @param string $limit 部分的命名空间的最大数量
|
| 757 |
+
* @return string
|
| 758 |
+
*/
|
| 759 |
+
public function extractNamespace($name, $limit = null)
|
| 760 |
+
{
|
| 761 |
+
$parts = explode(':', $name);
|
| 762 |
+
array_pop($parts);
|
| 763 |
+
|
| 764 |
+
return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit));
|
| 765 |
+
}
|
| 766 |
+
|
| 767 |
+
/**
|
| 768 |
+
* 查找可替代的建议
|
| 769 |
+
* @access private
|
| 770 |
+
* @param string $name 指令名称
|
| 771 |
+
* @param array|\Traversable $collection 建议集合
|
| 772 |
+
* @return array
|
| 773 |
+
*/
|
| 774 |
+
private function findAlternatives($name, $collection)
|
| 775 |
+
{
|
| 776 |
+
$threshold = 1e3;
|
| 777 |
+
$alternatives = [];
|
| 778 |
+
$collectionParts = [];
|
| 779 |
+
|
| 780 |
+
foreach ($collection as $item) {
|
| 781 |
+
$collectionParts[$item] = explode(':', $item);
|
| 782 |
+
}
|
| 783 |
+
|
| 784 |
+
foreach (explode(':', $name) as $i => $subname) {
|
| 785 |
+
foreach ($collectionParts as $collectionName => $parts) {
|
| 786 |
+
$exists = isset($alternatives[$collectionName]);
|
| 787 |
+
|
| 788 |
+
if (!isset($parts[$i]) && $exists) {
|
| 789 |
+
$alternatives[$collectionName] += $threshold;
|
| 790 |
+
continue;
|
| 791 |
+
} elseif (!isset($parts[$i])) {
|
| 792 |
+
continue;
|
| 793 |
+
}
|
| 794 |
+
|
| 795 |
+
$lev = levenshtein($subname, $parts[$i]);
|
| 796 |
+
|
| 797 |
+
if ($lev <= strlen($subname) / 3 ||
|
| 798 |
+
'' !== $subname &&
|
| 799 |
+
false !== strpos($parts[$i], $subname)
|
| 800 |
+
) {
|
| 801 |
+
$alternatives[$collectionName] = $exists ?
|
| 802 |
+
$alternatives[$collectionName] + $lev :
|
| 803 |
+
$lev;
|
| 804 |
+
} elseif ($exists) {
|
| 805 |
+
$alternatives[$collectionName] += $threshold;
|
| 806 |
+
}
|
| 807 |
+
}
|
| 808 |
+
}
|
| 809 |
+
|
| 810 |
+
foreach ($collection as $item) {
|
| 811 |
+
$lev = levenshtein($name, $item);
|
| 812 |
+
|
| 813 |
+
if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
|
| 814 |
+
$alternatives[$item] = isset($alternatives[$item]) ?
|
| 815 |
+
$alternatives[$item] - $lev :
|
| 816 |
+
$lev;
|
| 817 |
+
}
|
| 818 |
+
}
|
| 819 |
+
|
| 820 |
+
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) {
|
| 821 |
+
return $lev < 2 * $threshold;
|
| 822 |
+
});
|
| 823 |
+
|
| 824 |
+
asort($alternatives);
|
| 825 |
+
|
| 826 |
+
return array_keys($alternatives);
|
| 827 |
+
}
|
| 828 |
+
|
| 829 |
+
/**
|
| 830 |
+
* 设置默认的指令
|
| 831 |
+
* @access public
|
| 832 |
+
* @param string $commandName 指令名称
|
| 833 |
+
* @return $this
|
| 834 |
+
*/
|
| 835 |
+
public function setDefaultCommand($commandName)
|
| 836 |
+
{
|
| 837 |
+
$this->defaultCommand = $commandName;
|
| 838 |
+
|
| 839 |
+
return $this;
|
| 840 |
+
}
|
| 841 |
+
|
| 842 |
+
/**
|
| 843 |
+
* 返回所有的命名空间
|
| 844 |
+
* @access private
|
| 845 |
+
* @param string $name 指令名称
|
| 846 |
+
* @return array
|
| 847 |
+
*/
|
| 848 |
+
private function extractAllNamespaces($name)
|
| 849 |
+
{
|
| 850 |
+
$namespaces = [];
|
| 851 |
+
|
| 852 |
+
foreach (explode(':', $name, -1) as $part) {
|
| 853 |
+
if (count($namespaces)) {
|
| 854 |
+
$namespaces[] = end($namespaces) . ':' . $part;
|
| 855 |
+
} else {
|
| 856 |
+
$namespaces[] = $part;
|
| 857 |
+
}
|
| 858 |
+
}
|
| 859 |
+
|
| 860 |
+
return $namespaces;
|
| 861 |
+
}
|
| 862 |
+
|
| 863 |
+
}
|
thinkphp/library/think/Controller.php
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use think\exception\ValidateException;
|
| 15 |
+
use traits\controller\Jump;
|
| 16 |
+
use think\Log;
|
| 17 |
+
|
| 18 |
+
Loader::import('controller/Jump', TRAIT_PATH, EXT);
|
| 19 |
+
|
| 20 |
+
class Controller
|
| 21 |
+
{
|
| 22 |
+
use Jump;
|
| 23 |
+
|
| 24 |
+
/**
|
| 25 |
+
* @var \think\View 视图类实例
|
| 26 |
+
*/
|
| 27 |
+
protected $view;
|
| 28 |
+
|
| 29 |
+
/**
|
| 30 |
+
* @var \think\Request Request 实例
|
| 31 |
+
*/
|
| 32 |
+
protected $request;
|
| 33 |
+
|
| 34 |
+
/**
|
| 35 |
+
* @var bool 验证失败是否抛出异常
|
| 36 |
+
*/
|
| 37 |
+
protected $failException = false;
|
| 38 |
+
|
| 39 |
+
/**
|
| 40 |
+
* @var bool 是否批量验证
|
| 41 |
+
*/
|
| 42 |
+
protected $batchValidate = false;
|
| 43 |
+
|
| 44 |
+
/**
|
| 45 |
+
* @var array 前置操作方法列表
|
| 46 |
+
*/
|
| 47 |
+
protected $beforeActionList = [];
|
| 48 |
+
|
| 49 |
+
/**
|
| 50 |
+
* 构造方法
|
| 51 |
+
* @access public
|
| 52 |
+
* @param Request $request Request 对象
|
| 53 |
+
*/
|
| 54 |
+
public function __construct(Request $request = null)
|
| 55 |
+
{
|
| 56 |
+
$this->view = View::instance(Config::get('template'), Config::get('view_replace_str'));
|
| 57 |
+
$this->request = is_null($request) ? Request::instance() : $request;
|
| 58 |
+
|
| 59 |
+
// 控制器初始化
|
| 60 |
+
$this->_initialize();
|
| 61 |
+
|
| 62 |
+
// 前置操作方法
|
| 63 |
+
if ($this->beforeActionList) {
|
| 64 |
+
foreach ($this->beforeActionList as $method => $options) {
|
| 65 |
+
is_numeric($method) ?
|
| 66 |
+
$this->beforeAction($options) :
|
| 67 |
+
$this->beforeAction($method, $options);
|
| 68 |
+
}
|
| 69 |
+
}
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
/**
|
| 73 |
+
* 初始化操作
|
| 74 |
+
* @access protected
|
| 75 |
+
*/
|
| 76 |
+
protected function _initialize()
|
| 77 |
+
{
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
/**
|
| 81 |
+
* 前置操作
|
| 82 |
+
* @access protected
|
| 83 |
+
* @param string $method 前置操作方法名
|
| 84 |
+
* @param array $options 调用参数 ['only'=>[...]] 或者 ['except'=>[...]]
|
| 85 |
+
* @return void
|
| 86 |
+
*/
|
| 87 |
+
protected function beforeAction($method, $options = [])
|
| 88 |
+
{
|
| 89 |
+
if (isset($options['only'])) {
|
| 90 |
+
if (is_string($options['only'])) {
|
| 91 |
+
$options['only'] = explode(',', $options['only']);
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
if (!in_array($this->request->action(), $options['only'])) {
|
| 95 |
+
return;
|
| 96 |
+
}
|
| 97 |
+
} elseif (isset($options['except'])) {
|
| 98 |
+
if (is_string($options['except'])) {
|
| 99 |
+
$options['except'] = explode(',', $options['except']);
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
if (in_array($this->request->action(), $options['except'])) {
|
| 103 |
+
return;
|
| 104 |
+
}
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
call_user_func([$this, $method]);
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
/**
|
| 111 |
+
* 加载模板输出
|
| 112 |
+
* @access protected
|
| 113 |
+
* @param string $template 模板文件名
|
| 114 |
+
* @param array $vars 模板输出变量
|
| 115 |
+
* @param array $replace 模板替换
|
| 116 |
+
* @param array $config 模板参数
|
| 117 |
+
* @return mixed
|
| 118 |
+
*/
|
| 119 |
+
protected function fetch($template = '', $vars = [], $replace = [], $config = [])
|
| 120 |
+
{
|
| 121 |
+
// 如果是新版本
|
| 122 |
+
if($GLOBALS['config']['site']['new_version'] == 1 || !isset($GLOBALS['config']['site']['new_version']) || (empty($GLOBALS['config']['site']['new_version']) && $GLOBALS['config']['site']['new_version'] != 0)){
|
| 123 |
+
// 如果模板路径以admin@开头
|
| 124 |
+
if (strpos($template, 'admin@') === 0) {
|
| 125 |
+
$parts = explode('@', $template);
|
| 126 |
+
$result = $parts[1];
|
| 127 |
+
$template = APP_PATH . request()->module() . '/view_new/' . $result . '.html';
|
| 128 |
+
}
|
| 129 |
+
}
|
| 130 |
+
return $this->view->fetch($template, $vars, $replace, $config);
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
/**
|
| 134 |
+
* 渲染内容输出
|
| 135 |
+
* @access protected
|
| 136 |
+
* @param string $content 模板内容
|
| 137 |
+
* @param array $vars 模板输出变量
|
| 138 |
+
* @param array $replace 替换内容
|
| 139 |
+
* @param array $config 模板参数
|
| 140 |
+
* @return mixed
|
| 141 |
+
*/
|
| 142 |
+
protected function display($content = '', $vars = [], $replace = [], $config = [])
|
| 143 |
+
{
|
| 144 |
+
return $this->view->display($content, $vars, $replace, $config);
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
/**
|
| 148 |
+
* 模板变量赋值
|
| 149 |
+
* @access protected
|
| 150 |
+
* @param mixed $name 要显示的模板变量
|
| 151 |
+
* @param mixed $value 变量的值
|
| 152 |
+
* @return $this
|
| 153 |
+
*/
|
| 154 |
+
protected function assign($name, $value = '')
|
| 155 |
+
{
|
| 156 |
+
$this->view->assign($name, $value);
|
| 157 |
+
|
| 158 |
+
return $this;
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
/**
|
| 162 |
+
* 初始化模板引擎
|
| 163 |
+
* @access protected
|
| 164 |
+
* @param array|string $engine 引擎参数
|
| 165 |
+
* @return $this
|
| 166 |
+
*/
|
| 167 |
+
protected function engine($engine)
|
| 168 |
+
{
|
| 169 |
+
$this->view->engine($engine);
|
| 170 |
+
|
| 171 |
+
return $this;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
/**
|
| 175 |
+
* 设置验证失败后是否抛出异常
|
| 176 |
+
* @access protected
|
| 177 |
+
* @param bool $fail 是否抛出异常
|
| 178 |
+
* @return $this
|
| 179 |
+
*/
|
| 180 |
+
protected function validateFailException($fail = true)
|
| 181 |
+
{
|
| 182 |
+
$this->failException = $fail;
|
| 183 |
+
|
| 184 |
+
return $this;
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
/**
|
| 188 |
+
* 验证数据
|
| 189 |
+
* @access protected
|
| 190 |
+
* @param array $data 数据
|
| 191 |
+
* @param string|array $validate 验证器名或者验证规则数组
|
| 192 |
+
* @param array $message 提示信息
|
| 193 |
+
* @param bool $batch 是否批量验证
|
| 194 |
+
* @param mixed $callback 回调方法(闭包)
|
| 195 |
+
* @return array|string|true
|
| 196 |
+
* @throws ValidateException
|
| 197 |
+
*/
|
| 198 |
+
protected function validate($data, $validate, $message = [], $batch = false, $callback = null)
|
| 199 |
+
{
|
| 200 |
+
if (is_array($validate)) {
|
| 201 |
+
$v = Loader::validate();
|
| 202 |
+
$v->rule($validate);
|
| 203 |
+
} else {
|
| 204 |
+
// 支持场景
|
| 205 |
+
if (strpos($validate, '.')) {
|
| 206 |
+
list($validate, $scene) = explode('.', $validate);
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
$v = Loader::validate($validate);
|
| 210 |
+
|
| 211 |
+
!empty($scene) && $v->scene($scene);
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
// 批量验证
|
| 215 |
+
if ($batch || $this->batchValidate) {
|
| 216 |
+
$v->batch(true);
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
// 设置错误信息
|
| 220 |
+
if (is_array($message)) {
|
| 221 |
+
$v->message($message);
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
// 使用回调验证
|
| 225 |
+
if ($callback && is_callable($callback)) {
|
| 226 |
+
call_user_func_array($callback, [$v, &$data]);
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
if (!$v->check($data)) {
|
| 230 |
+
if ($this->failException) {
|
| 231 |
+
throw new ValidateException($v->getError());
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
return $v->getError();
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
return true;
|
| 238 |
+
}
|
| 239 |
+
}
|
thinkphp/library/think/Cookie.php
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
class Cookie
|
| 15 |
+
{
|
| 16 |
+
/**
|
| 17 |
+
* @var array cookie 设置参数
|
| 18 |
+
*/
|
| 19 |
+
protected static $config = [
|
| 20 |
+
'prefix' => '', // cookie 名称前缀
|
| 21 |
+
'expire' => 0, // cookie 保存时间
|
| 22 |
+
'path' => '/', // cookie 保存路径
|
| 23 |
+
'domain' => '', // cookie 有效域名
|
| 24 |
+
'secure' => false, // cookie 启用安全传输
|
| 25 |
+
'httponly' => false, // httponly 设置
|
| 26 |
+
'setcookie' => true, // 是否使用 setcookie
|
| 27 |
+
];
|
| 28 |
+
|
| 29 |
+
/**
|
| 30 |
+
* @var bool 是否完成初始化了
|
| 31 |
+
*/
|
| 32 |
+
protected static $init;
|
| 33 |
+
|
| 34 |
+
/**
|
| 35 |
+
* Cookie初始化
|
| 36 |
+
* @access public
|
| 37 |
+
* @param array $config 配置参数
|
| 38 |
+
* @return void
|
| 39 |
+
*/
|
| 40 |
+
public static function init(array $config = [])
|
| 41 |
+
{
|
| 42 |
+
if (empty($config)) {
|
| 43 |
+
$config = Config::get('cookie');
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
self::$config = array_merge(self::$config, array_change_key_case($config));
|
| 47 |
+
|
| 48 |
+
if (!empty(self::$config['httponly'])) {
|
| 49 |
+
ini_set('session.cookie_httponly', 1);
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
self::$init = true;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
/**
|
| 56 |
+
* 设置或者获取 cookie 作用域(前缀)
|
| 57 |
+
* @access public
|
| 58 |
+
* @param string $prefix 前缀
|
| 59 |
+
* @return string|
|
| 60 |
+
*/
|
| 61 |
+
public static function prefix($prefix = '')
|
| 62 |
+
{
|
| 63 |
+
if (empty($prefix)) {
|
| 64 |
+
return self::$config['prefix'];
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
return self::$config['prefix'] = $prefix;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
/**
|
| 71 |
+
* Cookie 设置、获取、删除
|
| 72 |
+
* @access public
|
| 73 |
+
* @param string $name cookie 名称
|
| 74 |
+
* @param mixed $value cookie 值
|
| 75 |
+
* @param mixed $option 可选参数 可能会是 null|integer|string
|
| 76 |
+
* @return void
|
| 77 |
+
*/
|
| 78 |
+
public static function set($name, $value = '', $option = null)
|
| 79 |
+
{
|
| 80 |
+
!isset(self::$init) && self::init();
|
| 81 |
+
|
| 82 |
+
// 参数设置(会覆盖黙认设置)
|
| 83 |
+
if (!is_null($option)) {
|
| 84 |
+
if (is_numeric($option)) {
|
| 85 |
+
$option = ['expire' => $option];
|
| 86 |
+
} elseif (is_string($option)) {
|
| 87 |
+
parse_str($option, $option);
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
$config = array_merge(self::$config, array_change_key_case($option));
|
| 91 |
+
} else {
|
| 92 |
+
$config = self::$config;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
$name = $config['prefix'] . $name;
|
| 96 |
+
|
| 97 |
+
// 设置 cookie
|
| 98 |
+
if (is_array($value)) {
|
| 99 |
+
array_walk_recursive($value, 'self::jsonFormatProtect', 'encode');
|
| 100 |
+
$value = 'think:' . json_encode($value);
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
$expire = !empty($config['expire']) ?
|
| 104 |
+
$_SERVER['REQUEST_TIME'] + intval($config['expire']) :
|
| 105 |
+
0;
|
| 106 |
+
|
| 107 |
+
if ($config['setcookie']) {
|
| 108 |
+
setcookie(
|
| 109 |
+
$name, $value, $expire, $config['path'], $config['domain'],
|
| 110 |
+
$config['secure'], $config['httponly']
|
| 111 |
+
);
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
$_COOKIE[$name] = $value;
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
/**
|
| 118 |
+
* 永久保存 Cookie 数据
|
| 119 |
+
* @access public
|
| 120 |
+
* @param string $name cookie 名称
|
| 121 |
+
* @param mixed $value cookie 值
|
| 122 |
+
* @param mixed $option 可选参数 可能会是 null|integer|string
|
| 123 |
+
* @return void
|
| 124 |
+
*/
|
| 125 |
+
public static function forever($name, $value = '', $option = null)
|
| 126 |
+
{
|
| 127 |
+
if (is_null($option) || is_numeric($option)) {
|
| 128 |
+
$option = [];
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
$option['expire'] = 315360000;
|
| 132 |
+
|
| 133 |
+
self::set($name, $value, $option);
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
/**
|
| 137 |
+
* 判断是否有 Cookie 数据
|
| 138 |
+
* @access public
|
| 139 |
+
* @param string $name cookie 名称
|
| 140 |
+
* @param string|null $prefix cookie 前缀
|
| 141 |
+
* @return bool
|
| 142 |
+
*/
|
| 143 |
+
public static function has($name, $prefix = null)
|
| 144 |
+
{
|
| 145 |
+
!isset(self::$init) && self::init();
|
| 146 |
+
|
| 147 |
+
$prefix = !is_null($prefix) ? $prefix : self::$config['prefix'];
|
| 148 |
+
|
| 149 |
+
return isset($_COOKIE[$prefix . $name]);
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
/**
|
| 153 |
+
* 获取 Cookie 的值
|
| 154 |
+
* @access public
|
| 155 |
+
* @param string $name cookie 名称
|
| 156 |
+
* @param string|null $prefix cookie 前缀
|
| 157 |
+
* @return mixed
|
| 158 |
+
*/
|
| 159 |
+
public static function get($name = '', $prefix = null)
|
| 160 |
+
{
|
| 161 |
+
!isset(self::$init) && self::init();
|
| 162 |
+
|
| 163 |
+
$prefix = !is_null($prefix) ? $prefix : self::$config['prefix'];
|
| 164 |
+
$key = $prefix . $name;
|
| 165 |
+
|
| 166 |
+
if ('' == $name) {
|
| 167 |
+
// 获取全部
|
| 168 |
+
if ($prefix) {
|
| 169 |
+
$value = [];
|
| 170 |
+
|
| 171 |
+
foreach ($_COOKIE as $k => $val) {
|
| 172 |
+
if (0 === strpos($k, $prefix)) {
|
| 173 |
+
$value[$k] = $val;
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
}
|
| 177 |
+
} else {
|
| 178 |
+
$value = $_COOKIE;
|
| 179 |
+
}
|
| 180 |
+
} elseif (isset($_COOKIE[$key])) {
|
| 181 |
+
$value = $_COOKIE[$key];
|
| 182 |
+
|
| 183 |
+
if (0 === strpos($value, 'think:')) {
|
| 184 |
+
$value = json_decode(substr($value, 6), true);
|
| 185 |
+
array_walk_recursive($value, 'self::jsonFormatProtect', 'decode');
|
| 186 |
+
}
|
| 187 |
+
} else {
|
| 188 |
+
$value = null;
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
return $value;
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
/**
|
| 195 |
+
* 删除 Cookie
|
| 196 |
+
* @access public
|
| 197 |
+
* @param string $name cookie 名称
|
| 198 |
+
* @param string|null $prefix cookie 前缀
|
| 199 |
+
* @return void
|
| 200 |
+
*/
|
| 201 |
+
public static function delete($name, $prefix = null)
|
| 202 |
+
{
|
| 203 |
+
!isset(self::$init) && self::init();
|
| 204 |
+
|
| 205 |
+
$config = self::$config;
|
| 206 |
+
$prefix = !is_null($prefix) ? $prefix : $config['prefix'];
|
| 207 |
+
$name = $prefix . $name;
|
| 208 |
+
|
| 209 |
+
if ($config['setcookie']) {
|
| 210 |
+
setcookie(
|
| 211 |
+
$name, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'],
|
| 212 |
+
$config['domain'], $config['secure'], $config['httponly']
|
| 213 |
+
);
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
// 删除指定 cookie
|
| 217 |
+
unset($_COOKIE[$name]);
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
/**
|
| 221 |
+
* 清除指定前缀的所有 cookie
|
| 222 |
+
* @access public
|
| 223 |
+
* @param string|null $prefix cookie 前缀
|
| 224 |
+
* @return void
|
| 225 |
+
*/
|
| 226 |
+
public static function clear($prefix = null)
|
| 227 |
+
{
|
| 228 |
+
if (empty($_COOKIE)) {
|
| 229 |
+
return;
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
!isset(self::$init) && self::init();
|
| 233 |
+
|
| 234 |
+
// 要删除的 cookie 前缀,不指定则删除 config 设置的指定前缀
|
| 235 |
+
$config = self::$config;
|
| 236 |
+
$prefix = !is_null($prefix) ? $prefix : $config['prefix'];
|
| 237 |
+
|
| 238 |
+
if ($prefix) {
|
| 239 |
+
foreach ($_COOKIE as $key => $val) {
|
| 240 |
+
if (0 === strpos($key, $prefix)) {
|
| 241 |
+
if ($config['setcookie']) {
|
| 242 |
+
setcookie(
|
| 243 |
+
$key, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'],
|
| 244 |
+
$config['domain'], $config['secure'], $config['httponly']
|
| 245 |
+
);
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
unset($_COOKIE[$key]);
|
| 249 |
+
}
|
| 250 |
+
}
|
| 251 |
+
}
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
/**
|
| 255 |
+
* json 转换时的格式保护
|
| 256 |
+
* @access protected
|
| 257 |
+
* @param mixed $val 要转换的值
|
| 258 |
+
* @param string $key 键名
|
| 259 |
+
* @param string $type 转换类别
|
| 260 |
+
* @return void
|
| 261 |
+
*/
|
| 262 |
+
protected static function jsonFormatProtect(&$val, $key, $type = 'encode')
|
| 263 |
+
{
|
| 264 |
+
if (!empty($val) && true !== $val) {
|
| 265 |
+
$val = 'decode' == $type ? urldecode($val) : urlencode($val);
|
| 266 |
+
}
|
| 267 |
+
}
|
| 268 |
+
}
|
thinkphp/library/think/Db.php
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use think\db\Connection;
|
| 15 |
+
use think\db\Query;
|
| 16 |
+
|
| 17 |
+
/**
|
| 18 |
+
* Class Db
|
| 19 |
+
* @package think
|
| 20 |
+
* @method Query table(string $table) static 指定数据表(含前缀)
|
| 21 |
+
* @method Query name(string $name) static 指定数据表(不含前缀)
|
| 22 |
+
* @method Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件
|
| 23 |
+
* @method Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询
|
| 24 |
+
* @method Query union(mixed $union, boolean $all = false) static UNION查询
|
| 25 |
+
* @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT
|
| 26 |
+
* @method Query order(mixed $field, string $order = null) static 查询ORDER
|
| 27 |
+
* @method Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存
|
| 28 |
+
* @method mixed value(string $field) static 获取某个字段的值
|
| 29 |
+
* @method array column(string $field, string $key = '') static 获取某个列的值
|
| 30 |
+
* @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询
|
| 31 |
+
* @method mixed find(mixed $data = null) static 查询单个记录
|
| 32 |
+
* @method mixed select(mixed $data = null) static 查询多个记录
|
| 33 |
+
* @method integer insert(array $data, boolean $replace = false, boolean $getLastInsID = false, string $sequence = null) static 插入一条记录
|
| 34 |
+
* @method integer insertGetId(array $data, boolean $replace = false, string $sequence = null) static 插入一条记录并返回自增ID
|
| 35 |
+
* @method integer insertAll(array $dataSet) static 插入多条记录
|
| 36 |
+
* @method integer update(array $data) static 更新记录
|
| 37 |
+
* @method integer delete(mixed $data = null) static 删除记录
|
| 38 |
+
* @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据
|
| 39 |
+
* @method mixed query(string $sql, array $bind = [], boolean $master = false, bool $pdo = false) static SQL查询
|
| 40 |
+
* @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行
|
| 41 |
+
* @method Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询
|
| 42 |
+
* @method mixed transaction(callable $callback) static 执行数据库事务
|
| 43 |
+
* @method void startTrans() static 启动事务
|
| 44 |
+
* @method void commit() static 用于非自动提交状态下面的查询提交
|
| 45 |
+
* @method void rollback() static 事务回滚
|
| 46 |
+
* @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句
|
| 47 |
+
* @method string quote(string $str) static SQL指令安全过滤
|
| 48 |
+
* @method string getLastInsID($sequence = null) static 获取最近插入的ID
|
| 49 |
+
*/
|
| 50 |
+
class Db
|
| 51 |
+
{
|
| 52 |
+
/**
|
| 53 |
+
* @var Connection[] 数据库连接实例
|
| 54 |
+
*/
|
| 55 |
+
private static $instance = [];
|
| 56 |
+
|
| 57 |
+
/**
|
| 58 |
+
* @var int 查询次数
|
| 59 |
+
*/
|
| 60 |
+
public static $queryTimes = 0;
|
| 61 |
+
|
| 62 |
+
/**
|
| 63 |
+
* @var int 执行次数
|
| 64 |
+
*/
|
| 65 |
+
public static $executeTimes = 0;
|
| 66 |
+
|
| 67 |
+
/**
|
| 68 |
+
* 数据库初始化,并取得数据库类实例
|
| 69 |
+
* @access public
|
| 70 |
+
* @param mixed $config 连接配置
|
| 71 |
+
* @param bool|string $name 连接标识 true 强制重新连接
|
| 72 |
+
* @return Connection
|
| 73 |
+
* @throws Exception
|
| 74 |
+
*/
|
| 75 |
+
public static function connect($config = [], $name = false)
|
| 76 |
+
{
|
| 77 |
+
if (false === $name) {
|
| 78 |
+
$name = md5(serialize($config));
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
if (true === $name || !isset(self::$instance[$name])) {
|
| 82 |
+
// 解析连接参数 支持数组和字符串
|
| 83 |
+
$options = self::parseConfig($config);
|
| 84 |
+
|
| 85 |
+
if (empty($options['type'])) {
|
| 86 |
+
throw new \InvalidArgumentException('Undefined db type');
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
$class = false !== strpos($options['type'], '\\') ?
|
| 90 |
+
$options['type'] :
|
| 91 |
+
'\\think\\db\\connector\\' . ucwords($options['type']);
|
| 92 |
+
|
| 93 |
+
// 记录初始化信息
|
| 94 |
+
if (App::$debug) {
|
| 95 |
+
Log::record('[ DB ] INIT ' . $options['type'], 'info');
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
if (true === $name) {
|
| 99 |
+
$name = md5(serialize($config));
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
self::$instance[$name] = new $class($options);
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
return self::$instance[$name];
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
/**
|
| 109 |
+
* 清除连接实例
|
| 110 |
+
* @access public
|
| 111 |
+
* @return void
|
| 112 |
+
*/
|
| 113 |
+
public static function clear()
|
| 114 |
+
{
|
| 115 |
+
self::$instance = [];
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
/**
|
| 119 |
+
* 数据库连接参数解析
|
| 120 |
+
* @access private
|
| 121 |
+
* @param mixed $config 连接参数
|
| 122 |
+
* @return array
|
| 123 |
+
*/
|
| 124 |
+
private static function parseConfig($config)
|
| 125 |
+
{
|
| 126 |
+
if (empty($config)) {
|
| 127 |
+
$config = Config::get('database');
|
| 128 |
+
} elseif (is_string($config) && false === strpos($config, '/')) {
|
| 129 |
+
$config = Config::get($config); // 支持读取配置参数
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
return is_string($config) ? self::parseDsn($config) : $config;
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
/**
|
| 136 |
+
* DSN 解析
|
| 137 |
+
* 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1¶m2=val2#utf8
|
| 138 |
+
* @access private
|
| 139 |
+
* @param string $dsnStr 数据库 DSN 字符串解析
|
| 140 |
+
* @return array
|
| 141 |
+
*/
|
| 142 |
+
private static function parseDsn($dsnStr)
|
| 143 |
+
{
|
| 144 |
+
$info = parse_url($dsnStr);
|
| 145 |
+
|
| 146 |
+
if (!$info) {
|
| 147 |
+
return [];
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
$dsn = [
|
| 151 |
+
'type' => $info['scheme'],
|
| 152 |
+
'username' => isset($info['user']) ? $info['user'] : '',
|
| 153 |
+
'password' => isset($info['pass']) ? $info['pass'] : '',
|
| 154 |
+
'hostname' => isset($info['host']) ? $info['host'] : '',
|
| 155 |
+
'hostport' => isset($info['port']) ? $info['port'] : '',
|
| 156 |
+
'database' => !empty($info['path']) ? ltrim($info['path'], '/') : '',
|
| 157 |
+
'charset' => isset($info['fragment']) ? $info['fragment'] : 'utf8',
|
| 158 |
+
];
|
| 159 |
+
|
| 160 |
+
if (isset($info['query'])) {
|
| 161 |
+
parse_str($info['query'], $dsn['params']);
|
| 162 |
+
} else {
|
| 163 |
+
$dsn['params'] = [];
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
return $dsn;
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
/**
|
| 170 |
+
* 调用驱动类的方法
|
| 171 |
+
* @access public
|
| 172 |
+
* @param string $method 方法名
|
| 173 |
+
* @param array $params 参数
|
| 174 |
+
* @return mixed
|
| 175 |
+
*/
|
| 176 |
+
public static function __callStatic($method, $params)
|
| 177 |
+
{
|
| 178 |
+
return call_user_func_array([self::connect(), $method], $params);
|
| 179 |
+
}
|
| 180 |
+
}
|
thinkphp/library/think/Debug.php
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use think\exception\ClassNotFoundException;
|
| 15 |
+
use think\response\Redirect;
|
| 16 |
+
|
| 17 |
+
class Debug
|
| 18 |
+
{
|
| 19 |
+
/**
|
| 20 |
+
* @var array 区间时间信息
|
| 21 |
+
*/
|
| 22 |
+
protected static $info = [];
|
| 23 |
+
|
| 24 |
+
/**
|
| 25 |
+
* @var array 区间内存信息
|
| 26 |
+
*/
|
| 27 |
+
protected static $mem = [];
|
| 28 |
+
|
| 29 |
+
/**
|
| 30 |
+
* 记录时间(微秒)和内存使用情况
|
| 31 |
+
* @access public
|
| 32 |
+
* @param string $name 标记位置
|
| 33 |
+
* @param mixed $value 标记值(留空则取当前 time 表示仅记录时间 否则同时记录时间和内存)
|
| 34 |
+
* @return void
|
| 35 |
+
*/
|
| 36 |
+
public static function remark($name, $value = '')
|
| 37 |
+
{
|
| 38 |
+
self::$info[$name] = is_float($value) ? $value : microtime(true);
|
| 39 |
+
|
| 40 |
+
if ('time' != $value) {
|
| 41 |
+
self::$mem['mem'][$name] = is_float($value) ? $value : memory_get_usage();
|
| 42 |
+
self::$mem['peak'][$name] = memory_get_peak_usage();
|
| 43 |
+
}
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
/**
|
| 47 |
+
* 统计某个区间的时间(微秒)使用情况 返回值以秒为单位
|
| 48 |
+
* @access public
|
| 49 |
+
* @param string $start 开始标签
|
| 50 |
+
* @param string $end 结束标签
|
| 51 |
+
* @param integer $dec 小数位
|
| 52 |
+
* @return string
|
| 53 |
+
*/
|
| 54 |
+
public static function getRangeTime($start, $end, $dec = 6)
|
| 55 |
+
{
|
| 56 |
+
if (!isset(self::$info[$end])) {
|
| 57 |
+
self::$info[$end] = microtime(true);
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
return number_format((self::$info[$end] - self::$info[$start]), $dec);
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
/**
|
| 64 |
+
* 统计从开始到统计时的时间(微秒)使用情况 返回值以秒为单位
|
| 65 |
+
* @access public
|
| 66 |
+
* @param integer $dec 小数位
|
| 67 |
+
* @return string
|
| 68 |
+
*/
|
| 69 |
+
public static function getUseTime($dec = 6)
|
| 70 |
+
{
|
| 71 |
+
return number_format((microtime(true) - THINK_START_TIME), $dec);
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
/**
|
| 75 |
+
* 获取当前访问的吞吐率情况
|
| 76 |
+
* @access public
|
| 77 |
+
* @return string
|
| 78 |
+
*/
|
| 79 |
+
public static function getThroughputRate()
|
| 80 |
+
{
|
| 81 |
+
return number_format(1 / self::getUseTime(), 2) . 'req/s';
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
/**
|
| 85 |
+
* 记录区间的内存使用情况
|
| 86 |
+
* @access public
|
| 87 |
+
* @param string $start 开始标签
|
| 88 |
+
* @param string $end 结束标签
|
| 89 |
+
* @param integer $dec 小数位
|
| 90 |
+
* @return string
|
| 91 |
+
*/
|
| 92 |
+
public static function getRangeMem($start, $end, $dec = 2)
|
| 93 |
+
{
|
| 94 |
+
if (!isset(self::$mem['mem'][$end])) {
|
| 95 |
+
self::$mem['mem'][$end] = memory_get_usage();
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
$size = self::$mem['mem'][$end] - self::$mem['mem'][$start];
|
| 99 |
+
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
|
| 100 |
+
$pos = 0;
|
| 101 |
+
|
| 102 |
+
while ($size >= 1024) {
|
| 103 |
+
$size /= 1024;
|
| 104 |
+
$pos++;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
return round($size, $dec) . " " . $a[$pos];
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
/**
|
| 111 |
+
* 统计从开始到统计时的内存使用情况
|
| 112 |
+
* @access public
|
| 113 |
+
* @param integer $dec 小数位
|
| 114 |
+
* @return string
|
| 115 |
+
*/
|
| 116 |
+
public static function getUseMem($dec = 2)
|
| 117 |
+
{
|
| 118 |
+
$size = memory_get_usage() - THINK_START_MEM;
|
| 119 |
+
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
|
| 120 |
+
$pos = 0;
|
| 121 |
+
|
| 122 |
+
while ($size >= 1024) {
|
| 123 |
+
$size /= 1024;
|
| 124 |
+
$pos++;
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
return round($size, $dec) . " " . $a[$pos];
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
/**
|
| 131 |
+
* 统计区间的内存峰值情况
|
| 132 |
+
* @access public
|
| 133 |
+
* @param string $start 开始标签
|
| 134 |
+
* @param string $end 结束标签
|
| 135 |
+
* @param integer $dec 小数位
|
| 136 |
+
* @return string
|
| 137 |
+
*/
|
| 138 |
+
public static function getMemPeak($start, $end, $dec = 2)
|
| 139 |
+
{
|
| 140 |
+
if (!isset(self::$mem['peak'][$end])) {
|
| 141 |
+
self::$mem['peak'][$end] = memory_get_peak_usage();
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
$size = self::$mem['peak'][$end] - self::$mem['peak'][$start];
|
| 145 |
+
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
|
| 146 |
+
$pos = 0;
|
| 147 |
+
|
| 148 |
+
while ($size >= 1024) {
|
| 149 |
+
$size /= 1024;
|
| 150 |
+
$pos++;
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
return round($size, $dec) . " " . $a[$pos];
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
/**
|
| 157 |
+
* 获取文件加载信息
|
| 158 |
+
* @access public
|
| 159 |
+
* @param bool $detail 是否显示详细
|
| 160 |
+
* @return integer|array
|
| 161 |
+
*/
|
| 162 |
+
public static function getFile($detail = false)
|
| 163 |
+
{
|
| 164 |
+
$files = get_included_files();
|
| 165 |
+
|
| 166 |
+
if ($detail) {
|
| 167 |
+
$info = [];
|
| 168 |
+
|
| 169 |
+
foreach ($files as $file) {
|
| 170 |
+
$info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )';
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
return $info;
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
return count($files);
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
/**
|
| 180 |
+
* 浏览器友好的变量输出
|
| 181 |
+
* @access public
|
| 182 |
+
* @param mixed $var 变量
|
| 183 |
+
* @param boolean $echo 是否输出(默认为 true,为 false 则返回输出字符串)
|
| 184 |
+
* @param string|null $label 标签(默认为空)
|
| 185 |
+
* @param integer $flags htmlspecialchars 的标志
|
| 186 |
+
* @return null|string
|
| 187 |
+
*/
|
| 188 |
+
public static function dump($var, $echo = true, $label = null, $flags = ENT_SUBSTITUTE)
|
| 189 |
+
{
|
| 190 |
+
$label = (null === $label) ? '' : rtrim($label) . ':';
|
| 191 |
+
|
| 192 |
+
ob_start();
|
| 193 |
+
var_dump($var);
|
| 194 |
+
$output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', ob_get_clean());
|
| 195 |
+
|
| 196 |
+
if (IS_CLI) {
|
| 197 |
+
$output = PHP_EOL . $label . $output . PHP_EOL;
|
| 198 |
+
} else {
|
| 199 |
+
if (!extension_loaded('xdebug')) {
|
| 200 |
+
$output = htmlspecialchars($output, $flags);
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
$output = '<pre>' . $label . $output . '</pre>';
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
if ($echo) {
|
| 207 |
+
echo($output);
|
| 208 |
+
return;
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
return $output;
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
/**
|
| 215 |
+
* 调试信息注入到响应中
|
| 216 |
+
* @access public
|
| 217 |
+
* @param Response $response 响应实例
|
| 218 |
+
* @param string $content 返回的字符串
|
| 219 |
+
* @return void
|
| 220 |
+
*/
|
| 221 |
+
public static function inject(Response $response, &$content)
|
| 222 |
+
{
|
| 223 |
+
$config = Config::get('trace');
|
| 224 |
+
$type = isset($config['type']) ? $config['type'] : 'Html';
|
| 225 |
+
$class = false !== strpos($type, '\\') ? $type : '\\think\\debug\\' . ucwords($type);
|
| 226 |
+
|
| 227 |
+
unset($config['type']);
|
| 228 |
+
|
| 229 |
+
if (!class_exists($class)) {
|
| 230 |
+
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
/** @var \think\debug\Console|\think\debug\Html $trace */
|
| 234 |
+
$trace = new $class($config);
|
| 235 |
+
|
| 236 |
+
if ($response instanceof Redirect) {
|
| 237 |
+
// TODO 记录
|
| 238 |
+
} else {
|
| 239 |
+
$output = $trace->output($response, Log::getLog());
|
| 240 |
+
|
| 241 |
+
if (is_string($output)) {
|
| 242 |
+
// trace 调试信息注入
|
| 243 |
+
$pos = strripos($content, '</body>');
|
| 244 |
+
if (false !== $pos) {
|
| 245 |
+
$content = substr($content, 0, $pos) . $output . substr($content, $pos);
|
| 246 |
+
} else {
|
| 247 |
+
$content = $content . $output;
|
| 248 |
+
}
|
| 249 |
+
}
|
| 250 |
+
}
|
| 251 |
+
}
|
| 252 |
+
}
|
thinkphp/library/think/Env.php
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
class Env
|
| 15 |
+
{
|
| 16 |
+
/**
|
| 17 |
+
* 获取环境变量值
|
| 18 |
+
* @access public
|
| 19 |
+
* @param string $name 环境变量名(支持二级 . 号分割)
|
| 20 |
+
* @param string $default 默认值
|
| 21 |
+
* @return mixed
|
| 22 |
+
*/
|
| 23 |
+
public static function get($name, $default = null)
|
| 24 |
+
{
|
| 25 |
+
$result = getenv(ENV_PREFIX . strtoupper(str_replace('.', '_', $name)));
|
| 26 |
+
|
| 27 |
+
if (false !== $result) {
|
| 28 |
+
if ('false' === $result) {
|
| 29 |
+
$result = false;
|
| 30 |
+
} elseif ('true' === $result) {
|
| 31 |
+
$result = true;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
return $result;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
return $default;
|
| 38 |
+
}
|
| 39 |
+
}
|
thinkphp/library/think/Error.php
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use think\console\Output as ConsoleOutput;
|
| 15 |
+
use think\exception\ErrorException;
|
| 16 |
+
use think\exception\Handle;
|
| 17 |
+
use think\exception\ThrowableError;
|
| 18 |
+
|
| 19 |
+
class Error
|
| 20 |
+
{
|
| 21 |
+
/**
|
| 22 |
+
* 注册异常处理
|
| 23 |
+
* @access public
|
| 24 |
+
* @return void
|
| 25 |
+
*/
|
| 26 |
+
public static function register()
|
| 27 |
+
{
|
| 28 |
+
error_reporting(E_ALL);
|
| 29 |
+
set_error_handler([__CLASS__, 'appError']);
|
| 30 |
+
set_exception_handler([__CLASS__, 'appException']);
|
| 31 |
+
register_shutdown_function([__CLASS__, 'appShutdown']);
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
/**
|
| 35 |
+
* 异常处理
|
| 36 |
+
* @access public
|
| 37 |
+
* @param \Exception|\Throwable $e 异常
|
| 38 |
+
* @return void
|
| 39 |
+
*/
|
| 40 |
+
public static function appException($e)
|
| 41 |
+
{
|
| 42 |
+
if (!$e instanceof \Exception) {
|
| 43 |
+
$e = new ThrowableError($e);
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
$handler = self::getExceptionHandler();
|
| 47 |
+
$handler->report($e);
|
| 48 |
+
|
| 49 |
+
if (IS_CLI) {
|
| 50 |
+
$handler->renderForConsole(new ConsoleOutput, $e);
|
| 51 |
+
} else {
|
| 52 |
+
$handler->render($e)->send();
|
| 53 |
+
}
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
/**
|
| 57 |
+
* 错误处理
|
| 58 |
+
* @access public
|
| 59 |
+
* @param integer $errno 错误编号
|
| 60 |
+
* @param integer $errstr 详细错误信息
|
| 61 |
+
* @param string $errfile 出错的文件
|
| 62 |
+
* @param integer $errline 出错行号
|
| 63 |
+
* @return void
|
| 64 |
+
* @throws ErrorException
|
| 65 |
+
*/
|
| 66 |
+
public static function appError($errno, $errstr, $errfile = '', $errline = 0)
|
| 67 |
+
{
|
| 68 |
+
$exception = new ErrorException($errno, $errstr, $errfile, $errline);
|
| 69 |
+
|
| 70 |
+
// 符合异常处理的则将错误信息托管至 think\exception\ErrorException
|
| 71 |
+
if (error_reporting() & $errno) {
|
| 72 |
+
throw $exception;
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
self::getExceptionHandler()->report($exception);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
/**
|
| 79 |
+
* 异常中止处理
|
| 80 |
+
* @access public
|
| 81 |
+
* @return void
|
| 82 |
+
*/
|
| 83 |
+
public static function appShutdown()
|
| 84 |
+
{
|
| 85 |
+
// 将错误信息托管至 think\ErrorException
|
| 86 |
+
if (!is_null($error = error_get_last()) && self::isFatal($error['type'])) {
|
| 87 |
+
self::appException(new ErrorException(
|
| 88 |
+
$error['type'], $error['message'], $error['file'], $error['line']
|
| 89 |
+
));
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
// 写入日志
|
| 93 |
+
Log::save();
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
/**
|
| 97 |
+
* 确定错误类型是否致命
|
| 98 |
+
* @access protected
|
| 99 |
+
* @param int $type 错误类型
|
| 100 |
+
* @return bool
|
| 101 |
+
*/
|
| 102 |
+
protected static function isFatal($type)
|
| 103 |
+
{
|
| 104 |
+
return in_array($type, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]);
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
/**
|
| 108 |
+
* 获取异常处理的实例
|
| 109 |
+
* @access public
|
| 110 |
+
* @return Handle
|
| 111 |
+
*/
|
| 112 |
+
public static function getExceptionHandler()
|
| 113 |
+
{
|
| 114 |
+
static $handle;
|
| 115 |
+
|
| 116 |
+
if (!$handle) {
|
| 117 |
+
// 异常处理 handle
|
| 118 |
+
$class = Config::get('exception_handle');
|
| 119 |
+
|
| 120 |
+
if ($class && is_string($class) && class_exists($class) &&
|
| 121 |
+
is_subclass_of($class, "\\think\\exception\\Handle")
|
| 122 |
+
) {
|
| 123 |
+
$handle = new $class;
|
| 124 |
+
} else {
|
| 125 |
+
$handle = new Handle;
|
| 126 |
+
|
| 127 |
+
if ($class instanceof \Closure) {
|
| 128 |
+
$handle->setRender($class);
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
}
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
return $handle;
|
| 135 |
+
}
|
| 136 |
+
}
|
thinkphp/library/think/Exception.php
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
class Exception extends \Exception
|
| 15 |
+
{
|
| 16 |
+
/**
|
| 17 |
+
* @var array 保存异常页面显示的额外 Debug 数据
|
| 18 |
+
*/
|
| 19 |
+
protected $data = [];
|
| 20 |
+
|
| 21 |
+
/**
|
| 22 |
+
* 设置异常额外的 Debug 数据
|
| 23 |
+
* 数据将会显示为下面的格式
|
| 24 |
+
*
|
| 25 |
+
* Exception Data
|
| 26 |
+
* --------------------------------------------------
|
| 27 |
+
* Label 1
|
| 28 |
+
* key1 value1
|
| 29 |
+
* key2 value2
|
| 30 |
+
* Label 2
|
| 31 |
+
* key1 value1
|
| 32 |
+
* key2 value2
|
| 33 |
+
*
|
| 34 |
+
* @access protected
|
| 35 |
+
* @param string $label 数据分类,用于异常页面显示
|
| 36 |
+
* @param array $data 需要显示的数据,必须为关联数组
|
| 37 |
+
* @return void
|
| 38 |
+
*/
|
| 39 |
+
final protected function setData($label, array $data)
|
| 40 |
+
{
|
| 41 |
+
$this->data[$label] = $data;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
/**
|
| 45 |
+
* 获取异常额外 Debug 数据
|
| 46 |
+
* 主要用于输出到异常页面便于调试
|
| 47 |
+
* @access public
|
| 48 |
+
* @return array
|
| 49 |
+
*/
|
| 50 |
+
final public function getData()
|
| 51 |
+
{
|
| 52 |
+
return $this->data;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
}
|
thinkphp/library/think/File.php
ADDED
|
@@ -0,0 +1,478 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use SplFileObject;
|
| 15 |
+
|
| 16 |
+
class File extends SplFileObject
|
| 17 |
+
{
|
| 18 |
+
/**
|
| 19 |
+
* @var string 错误信息
|
| 20 |
+
*/
|
| 21 |
+
private $error = '';
|
| 22 |
+
|
| 23 |
+
/**
|
| 24 |
+
* @var string 当前完整文件名
|
| 25 |
+
*/
|
| 26 |
+
protected $filename;
|
| 27 |
+
|
| 28 |
+
/**
|
| 29 |
+
* @var string 上传文件名
|
| 30 |
+
*/
|
| 31 |
+
protected $saveName;
|
| 32 |
+
|
| 33 |
+
/**
|
| 34 |
+
* @var string 文件上传命名规则
|
| 35 |
+
*/
|
| 36 |
+
protected $rule = 'date';
|
| 37 |
+
|
| 38 |
+
/**
|
| 39 |
+
* @var array 文件上传验证规则
|
| 40 |
+
*/
|
| 41 |
+
protected $validate = [];
|
| 42 |
+
|
| 43 |
+
/**
|
| 44 |
+
* @var bool 单元测试
|
| 45 |
+
*/
|
| 46 |
+
protected $isTest;
|
| 47 |
+
|
| 48 |
+
/**
|
| 49 |
+
* @var array 上传文件信息
|
| 50 |
+
*/
|
| 51 |
+
protected $info;
|
| 52 |
+
|
| 53 |
+
/**
|
| 54 |
+
* @var array 文件 hash 信息
|
| 55 |
+
*/
|
| 56 |
+
protected $hash = [];
|
| 57 |
+
|
| 58 |
+
/**
|
| 59 |
+
* File constructor.
|
| 60 |
+
* @access public
|
| 61 |
+
* @param string $filename 文件名称
|
| 62 |
+
* @param string $mode 访问模式
|
| 63 |
+
*/
|
| 64 |
+
public function __construct($filename, $mode = 'r')
|
| 65 |
+
{
|
| 66 |
+
parent::__construct($filename, $mode);
|
| 67 |
+
$this->filename = $this->getRealPath() ?: $this->getPathname();
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
/**
|
| 71 |
+
* 设置是否是单元测试
|
| 72 |
+
* @access public
|
| 73 |
+
* @param bool $test 是否是测试
|
| 74 |
+
* @return $this
|
| 75 |
+
*/
|
| 76 |
+
public function isTest($test = false)
|
| 77 |
+
{
|
| 78 |
+
$this->isTest = $test;
|
| 79 |
+
|
| 80 |
+
return $this;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
/**
|
| 84 |
+
* 设置上传信息
|
| 85 |
+
* @access public
|
| 86 |
+
* @param array $info 上传文件信息
|
| 87 |
+
* @return $this
|
| 88 |
+
*/
|
| 89 |
+
public function setUploadInfo($info)
|
| 90 |
+
{
|
| 91 |
+
$this->info = $info;
|
| 92 |
+
|
| 93 |
+
return $this;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
/**
|
| 97 |
+
* 获取上传文件的信息
|
| 98 |
+
* @access public
|
| 99 |
+
* @param string $name 信息名称
|
| 100 |
+
* @return array|string
|
| 101 |
+
*/
|
| 102 |
+
public function getInfo($name = '')
|
| 103 |
+
{
|
| 104 |
+
return isset($this->info[$name]) ? $this->info[$name] : $this->info;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
/**
|
| 108 |
+
* 获取上传文件的文件名
|
| 109 |
+
* @access public
|
| 110 |
+
* @return string
|
| 111 |
+
*/
|
| 112 |
+
public function getSaveName()
|
| 113 |
+
{
|
| 114 |
+
return $this->saveName;
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
/**
|
| 118 |
+
* 设置上传文件的保存文件名
|
| 119 |
+
* @access public
|
| 120 |
+
* @param string $saveName 保存名称
|
| 121 |
+
* @return $this
|
| 122 |
+
*/
|
| 123 |
+
public function setSaveName($saveName)
|
| 124 |
+
{
|
| 125 |
+
$this->saveName = $saveName;
|
| 126 |
+
|
| 127 |
+
return $this;
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
/**
|
| 131 |
+
* 获取文件的哈希散列值
|
| 132 |
+
* @access public
|
| 133 |
+
* @param string $type 类型
|
| 134 |
+
* @return string
|
| 135 |
+
*/
|
| 136 |
+
public function hash($type = 'sha1')
|
| 137 |
+
{
|
| 138 |
+
if (!isset($this->hash[$type])) {
|
| 139 |
+
$this->hash[$type] = hash_file($type, $this->filename);
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
return $this->hash[$type];
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
/**
|
| 146 |
+
* 检查目录是否可写
|
| 147 |
+
* @access protected
|
| 148 |
+
* @param string $path 目录
|
| 149 |
+
* @return boolean
|
| 150 |
+
*/
|
| 151 |
+
protected function checkPath($path)
|
| 152 |
+
{
|
| 153 |
+
if (is_dir($path) || mkdir($path, 0755, true)) {
|
| 154 |
+
return true;
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
$this->error = ['directory {:path} creation failed', ['path' => $path]];
|
| 158 |
+
|
| 159 |
+
return false;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
/**
|
| 163 |
+
* 获取文件类型信息
|
| 164 |
+
* @access public
|
| 165 |
+
* @return string
|
| 166 |
+
*/
|
| 167 |
+
public function getMime()
|
| 168 |
+
{
|
| 169 |
+
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
| 170 |
+
|
| 171 |
+
return finfo_file($finfo, $this->filename);
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
/**
|
| 175 |
+
* 设置文件的命名规则
|
| 176 |
+
* @access public
|
| 177 |
+
* @param string $rule 文件命名规则
|
| 178 |
+
* @return $this
|
| 179 |
+
*/
|
| 180 |
+
public function rule($rule)
|
| 181 |
+
{
|
| 182 |
+
$this->rule = $rule;
|
| 183 |
+
|
| 184 |
+
return $this;
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
/**
|
| 188 |
+
* 设置上传文件的验证规则
|
| 189 |
+
* @access public
|
| 190 |
+
* @param array $rule 验证规则
|
| 191 |
+
* @return $this
|
| 192 |
+
*/
|
| 193 |
+
public function validate(array $rule = [])
|
| 194 |
+
{
|
| 195 |
+
$this->validate = $rule;
|
| 196 |
+
|
| 197 |
+
return $this;
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
/**
|
| 201 |
+
* 检测是否合法的上传文件
|
| 202 |
+
* @access public
|
| 203 |
+
* @return bool
|
| 204 |
+
*/
|
| 205 |
+
public function isValid()
|
| 206 |
+
{
|
| 207 |
+
return $this->isTest ? is_file($this->filename) : is_uploaded_file($this->filename);
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
/**
|
| 211 |
+
* 检测上传文件
|
| 212 |
+
* @access public
|
| 213 |
+
* @param array $rule 验证规则
|
| 214 |
+
* @return bool
|
| 215 |
+
*/
|
| 216 |
+
public function check($rule = [])
|
| 217 |
+
{
|
| 218 |
+
$rule = $rule ?: $this->validate;
|
| 219 |
+
|
| 220 |
+
/* 检查文件大小 */
|
| 221 |
+
if (isset($rule['size']) && !$this->checkSize($rule['size'])) {
|
| 222 |
+
$this->error = 'filesize not match';
|
| 223 |
+
return false;
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
/* 检查文件 Mime 类型 */
|
| 227 |
+
if (isset($rule['type']) && !$this->checkMime($rule['type'])) {
|
| 228 |
+
$this->error = 'mimetype to upload is not allowed';
|
| 229 |
+
return false;
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
/* 检查文件后缀 */
|
| 233 |
+
if (isset($rule['ext']) && !$this->checkExt($rule['ext'])) {
|
| 234 |
+
$this->error = 'extensions to upload is not allowed';
|
| 235 |
+
return false;
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
/* 检查图像文件 */
|
| 239 |
+
if (!$this->checkImg()) {
|
| 240 |
+
$this->error = 'illegal image files';
|
| 241 |
+
return false;
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
return true;
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
/**
|
| 248 |
+
* 检测上传文件后缀
|
| 249 |
+
* @access public
|
| 250 |
+
* @param array|string $ext 允许后缀
|
| 251 |
+
* @return bool
|
| 252 |
+
*/
|
| 253 |
+
public function checkExt($ext)
|
| 254 |
+
{
|
| 255 |
+
if (is_string($ext)) {
|
| 256 |
+
$ext = explode(',', $ext);
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
|
| 260 |
+
|
| 261 |
+
return in_array($extension, $ext);
|
| 262 |
+
}
|
| 263 |
+
|
| 264 |
+
/**
|
| 265 |
+
* 检测图像文件
|
| 266 |
+
* @access public
|
| 267 |
+
* @return bool
|
| 268 |
+
*/
|
| 269 |
+
public function checkImg()
|
| 270 |
+
{
|
| 271 |
+
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
|
| 272 |
+
|
| 273 |
+
// 如果上传的不是图片,或者是图片而且后缀确实符合图片类型则返回 true
|
| 274 |
+
return !in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) || in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6, 13]);
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
/**
|
| 278 |
+
* 判断图像类型
|
| 279 |
+
* @access protected
|
| 280 |
+
* @param string $image 图片名称
|
| 281 |
+
* @return bool|int
|
| 282 |
+
*/
|
| 283 |
+
protected function getImageType($image)
|
| 284 |
+
{
|
| 285 |
+
if (function_exists('exif_imagetype')) {
|
| 286 |
+
return exif_imagetype($image);
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
try {
|
| 290 |
+
$info = getimagesize($image);
|
| 291 |
+
return $info ? $info[2] : false;
|
| 292 |
+
} catch (\Exception $e) {
|
| 293 |
+
return false;
|
| 294 |
+
}
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
/**
|
| 298 |
+
* 检测上传文件大小
|
| 299 |
+
* @access public
|
| 300 |
+
* @param integer $size 最大大小
|
| 301 |
+
* @return bool
|
| 302 |
+
*/
|
| 303 |
+
public function checkSize($size)
|
| 304 |
+
{
|
| 305 |
+
return $this->getSize() <= $size;
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
/**
|
| 309 |
+
* 检测上传文件类型
|
| 310 |
+
* @access public
|
| 311 |
+
* @param array|string $mime 允许类型
|
| 312 |
+
* @return bool
|
| 313 |
+
*/
|
| 314 |
+
public function checkMime($mime)
|
| 315 |
+
{
|
| 316 |
+
$mime = is_string($mime) ? explode(',', $mime) : $mime;
|
| 317 |
+
|
| 318 |
+
return in_array(strtolower($this->getMime()), $mime);
|
| 319 |
+
}
|
| 320 |
+
|
| 321 |
+
/**
|
| 322 |
+
* 移动文件
|
| 323 |
+
* @access public
|
| 324 |
+
* @param string $path 保存路径
|
| 325 |
+
* @param string|bool $savename 保存的文件名 默认自动生成
|
| 326 |
+
* @param boolean $replace 同名文件是否覆盖
|
| 327 |
+
* @return false|File
|
| 328 |
+
*/
|
| 329 |
+
public function move($path, $savename = true, $replace = true)
|
| 330 |
+
{
|
| 331 |
+
// 文件上传失败,捕获错误代码
|
| 332 |
+
if (!empty($this->info['error'])) {
|
| 333 |
+
$this->error($this->info['error']);
|
| 334 |
+
return false;
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
// 检测合法性
|
| 338 |
+
if (!$this->isValid()) {
|
| 339 |
+
$this->error = 'upload illegal files';
|
| 340 |
+
return false;
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
// 验证上传
|
| 344 |
+
if (!$this->check()) {
|
| 345 |
+
return false;
|
| 346 |
+
}
|
| 347 |
+
|
| 348 |
+
$path = rtrim($path, DS) . DS;
|
| 349 |
+
// 文件保存命名规则
|
| 350 |
+
$saveName = $this->buildSaveName($savename);
|
| 351 |
+
$filename = $path . $saveName;
|
| 352 |
+
|
| 353 |
+
// 检测目录
|
| 354 |
+
if (false === $this->checkPath(dirname($filename))) {
|
| 355 |
+
return false;
|
| 356 |
+
}
|
| 357 |
+
|
| 358 |
+
// 不覆盖同名文件
|
| 359 |
+
if (!$replace && is_file($filename)) {
|
| 360 |
+
$this->error = ['has the same filename: {:filename}', ['filename' => $filename]];
|
| 361 |
+
return false;
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
/* 移动文件 */
|
| 365 |
+
if ($this->isTest) {
|
| 366 |
+
rename($this->filename, $filename);
|
| 367 |
+
} elseif (!move_uploaded_file($this->filename, $filename)) {
|
| 368 |
+
$this->error = 'upload write error';
|
| 369 |
+
return false;
|
| 370 |
+
}
|
| 371 |
+
|
| 372 |
+
// 返回 File 对象实例
|
| 373 |
+
$file = new self($filename);
|
| 374 |
+
$file->setSaveName($saveName)->setUploadInfo($this->info);
|
| 375 |
+
|
| 376 |
+
return $file;
|
| 377 |
+
}
|
| 378 |
+
|
| 379 |
+
/**
|
| 380 |
+
* 获取保存文件名
|
| 381 |
+
* @access protected
|
| 382 |
+
* @param string|bool $savename 保存的文件名 默认自动生成
|
| 383 |
+
* @return string
|
| 384 |
+
*/
|
| 385 |
+
protected function buildSaveName($savename)
|
| 386 |
+
{
|
| 387 |
+
// 自动生成文件名
|
| 388 |
+
if (true === $savename) {
|
| 389 |
+
if ($this->rule instanceof \Closure) {
|
| 390 |
+
$savename = call_user_func_array($this->rule, [$this]);
|
| 391 |
+
} else {
|
| 392 |
+
switch ($this->rule) {
|
| 393 |
+
case 'date':
|
| 394 |
+
$savename = date('Ymd') . DS . md5(microtime(true));
|
| 395 |
+
break;
|
| 396 |
+
default:
|
| 397 |
+
if (in_array($this->rule, hash_algos())) {
|
| 398 |
+
$hash = $this->hash($this->rule);
|
| 399 |
+
$savename = substr($hash, 0, 2) . DS . substr($hash, 2);
|
| 400 |
+
} elseif (is_callable($this->rule)) {
|
| 401 |
+
$savename = call_user_func($this->rule);
|
| 402 |
+
} else {
|
| 403 |
+
$savename = date('Ymd') . DS . md5(microtime(true));
|
| 404 |
+
}
|
| 405 |
+
}
|
| 406 |
+
}
|
| 407 |
+
} elseif ('' === $savename || false === $savename) {
|
| 408 |
+
$savename = $this->getInfo('name');
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
if (!strpos($savename, '.')) {
|
| 412 |
+
$savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION);
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
return $savename;
|
| 416 |
+
}
|
| 417 |
+
|
| 418 |
+
/**
|
| 419 |
+
* 获取错误代码信息
|
| 420 |
+
* @access private
|
| 421 |
+
* @param int $errorNo 错误号
|
| 422 |
+
* @return $this
|
| 423 |
+
*/
|
| 424 |
+
private function error($errorNo)
|
| 425 |
+
{
|
| 426 |
+
switch ($errorNo) {
|
| 427 |
+
case 1:
|
| 428 |
+
case 2:
|
| 429 |
+
$this->error = 'upload File size exceeds the maximum value';
|
| 430 |
+
break;
|
| 431 |
+
case 3:
|
| 432 |
+
$this->error = 'only the portion of file is uploaded';
|
| 433 |
+
break;
|
| 434 |
+
case 4:
|
| 435 |
+
$this->error = 'no file to uploaded';
|
| 436 |
+
break;
|
| 437 |
+
case 6:
|
| 438 |
+
$this->error = 'upload temp dir not found';
|
| 439 |
+
break;
|
| 440 |
+
case 7:
|
| 441 |
+
$this->error = 'file write error';
|
| 442 |
+
break;
|
| 443 |
+
default:
|
| 444 |
+
$this->error = 'unknown upload error';
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
+
return $this;
|
| 448 |
+
}
|
| 449 |
+
|
| 450 |
+
/**
|
| 451 |
+
* 获取错误信息(支持多语言)
|
| 452 |
+
* @access public
|
| 453 |
+
* @return string
|
| 454 |
+
*/
|
| 455 |
+
public function getError()
|
| 456 |
+
{
|
| 457 |
+
if (is_array($this->error)) {
|
| 458 |
+
list($msg, $vars) = $this->error;
|
| 459 |
+
} else {
|
| 460 |
+
$msg = $this->error;
|
| 461 |
+
$vars = [];
|
| 462 |
+
}
|
| 463 |
+
|
| 464 |
+
return Lang::has($msg) ? Lang::get($msg, $vars) : $msg;
|
| 465 |
+
}
|
| 466 |
+
|
| 467 |
+
/**
|
| 468 |
+
* 魔法方法,获取文件的 hash 值
|
| 469 |
+
* @access public
|
| 470 |
+
* @param string $method 方法名
|
| 471 |
+
* @param mixed $args 调用参数
|
| 472 |
+
* @return string
|
| 473 |
+
*/
|
| 474 |
+
public function __call($method, $args)
|
| 475 |
+
{
|
| 476 |
+
return $this->hash($method);
|
| 477 |
+
}
|
| 478 |
+
}
|
thinkphp/library/think/Hook.php
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
class Hook
|
| 15 |
+
{
|
| 16 |
+
/**
|
| 17 |
+
* @var array 标签
|
| 18 |
+
*/
|
| 19 |
+
private static $tags = [];
|
| 20 |
+
|
| 21 |
+
/**
|
| 22 |
+
* 动态添加行为扩展到某个标签
|
| 23 |
+
* @access public
|
| 24 |
+
* @param string $tag 标签名称
|
| 25 |
+
* @param mixed $behavior 行为名称
|
| 26 |
+
* @param bool $first 是否放到开头执行
|
| 27 |
+
* @return void
|
| 28 |
+
*/
|
| 29 |
+
public static function add($tag, $behavior, $first = false)
|
| 30 |
+
{
|
| 31 |
+
isset(self::$tags[$tag]) || self::$tags[$tag] = [];
|
| 32 |
+
|
| 33 |
+
if (is_array($behavior) && !is_callable($behavior)) {
|
| 34 |
+
if (!array_key_exists('_overlay', $behavior) || !$behavior['_overlay']) {
|
| 35 |
+
unset($behavior['_overlay']);
|
| 36 |
+
self::$tags[$tag] = array_merge(self::$tags[$tag], $behavior);
|
| 37 |
+
} else {
|
| 38 |
+
unset($behavior['_overlay']);
|
| 39 |
+
self::$tags[$tag] = $behavior;
|
| 40 |
+
}
|
| 41 |
+
} elseif ($first) {
|
| 42 |
+
array_unshift(self::$tags[$tag], $behavior);
|
| 43 |
+
} else {
|
| 44 |
+
self::$tags[$tag][] = $behavior;
|
| 45 |
+
}
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
/**
|
| 49 |
+
* 批量导入插件
|
| 50 |
+
* @access public
|
| 51 |
+
* @param array $tags 插件信息
|
| 52 |
+
* @param boolean $recursive 是否递归合并
|
| 53 |
+
* @return void
|
| 54 |
+
*/
|
| 55 |
+
public static function import(array $tags, $recursive = true)
|
| 56 |
+
{
|
| 57 |
+
if ($recursive) {
|
| 58 |
+
foreach ($tags as $tag => $behavior) {
|
| 59 |
+
self::add($tag, $behavior);
|
| 60 |
+
}
|
| 61 |
+
} else {
|
| 62 |
+
self::$tags = $tags + self::$tags;
|
| 63 |
+
}
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
/**
|
| 67 |
+
* 获取插件信息
|
| 68 |
+
* @access public
|
| 69 |
+
* @param string $tag 插件位置(留空获取全部)
|
| 70 |
+
* @return array
|
| 71 |
+
*/
|
| 72 |
+
public static function get($tag = '')
|
| 73 |
+
{
|
| 74 |
+
if (empty($tag)) {
|
| 75 |
+
return self::$tags;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : [];
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
/**
|
| 82 |
+
* 监听标签的行为
|
| 83 |
+
* @access public
|
| 84 |
+
* @param string $tag 标签名称
|
| 85 |
+
* @param mixed $params 传入参数
|
| 86 |
+
* @param mixed $extra 额外参数
|
| 87 |
+
* @param bool $once 只获取一个有效返回值
|
| 88 |
+
* @return mixed
|
| 89 |
+
*/
|
| 90 |
+
public static function listen($tag, &$params = null, $extra = null, $once = false)
|
| 91 |
+
{
|
| 92 |
+
$results = [];
|
| 93 |
+
|
| 94 |
+
foreach (static::get($tag) as $key => $name) {
|
| 95 |
+
$results[$key] = self::exec($name, $tag, $params, $extra);
|
| 96 |
+
|
| 97 |
+
// 如果返回 false,或者仅获取一个有效返回则中断行为执行
|
| 98 |
+
if (false === $results[$key] || (!is_null($results[$key]) && $once)) {
|
| 99 |
+
break;
|
| 100 |
+
}
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
return $once ? end($results) : $results;
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
/**
|
| 107 |
+
* 执行某个行为
|
| 108 |
+
* @access public
|
| 109 |
+
* @param mixed $class 要执行的行为
|
| 110 |
+
* @param string $tag 方法名(标签名)
|
| 111 |
+
* @param mixed $params 传人的参数
|
| 112 |
+
* @param mixed $extra 额外参数
|
| 113 |
+
* @return mixed
|
| 114 |
+
*/
|
| 115 |
+
public static function exec($class, $tag = '', &$params = null, $extra = null)
|
| 116 |
+
{
|
| 117 |
+
App::$debug && Debug::remark('behavior_start', 'time');
|
| 118 |
+
|
| 119 |
+
$method = Loader::parseName($tag, 1, false);
|
| 120 |
+
|
| 121 |
+
if ($class instanceof \Closure) {
|
| 122 |
+
$result = call_user_func_array($class, [ & $params, $extra]);
|
| 123 |
+
$class = 'Closure';
|
| 124 |
+
} elseif (is_array($class)) {
|
| 125 |
+
list($class, $method) = $class;
|
| 126 |
+
|
| 127 |
+
$result = (new $class())->$method($params, $extra);
|
| 128 |
+
$class = $class . '->' . $method;
|
| 129 |
+
} elseif (is_object($class)) {
|
| 130 |
+
$result = $class->$method($params, $extra);
|
| 131 |
+
$class = get_class($class);
|
| 132 |
+
} elseif (strpos($class, '::')) {
|
| 133 |
+
$result = call_user_func_array($class, [ & $params, $extra]);
|
| 134 |
+
} else {
|
| 135 |
+
$obj = new $class();
|
| 136 |
+
$method = ($tag && is_callable([$obj, $method])) ? $method : 'run';
|
| 137 |
+
$result = $obj->$method($params, $extra);
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
if (App::$debug) {
|
| 141 |
+
Debug::remark('behavior_end', 'time');
|
| 142 |
+
Log::record('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info');
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
return $result;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
}
|
thinkphp/library/think/Lang.php
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
class Lang
|
| 15 |
+
{
|
| 16 |
+
/**
|
| 17 |
+
* @var array 语言数据
|
| 18 |
+
*/
|
| 19 |
+
private static $lang = [];
|
| 20 |
+
|
| 21 |
+
/**
|
| 22 |
+
* @var string 语言作用域
|
| 23 |
+
*/
|
| 24 |
+
private static $range = 'zh-cn';
|
| 25 |
+
|
| 26 |
+
/**
|
| 27 |
+
* @var string 语言自动侦测的变量
|
| 28 |
+
*/
|
| 29 |
+
protected static $langDetectVar = 'lang';
|
| 30 |
+
|
| 31 |
+
/**
|
| 32 |
+
* @var string 语言 Cookie 变量
|
| 33 |
+
*/
|
| 34 |
+
protected static $langCookieVar = 'think_var';
|
| 35 |
+
|
| 36 |
+
/**
|
| 37 |
+
* @var int 语言 Cookie 的过期时间
|
| 38 |
+
*/
|
| 39 |
+
protected static $langCookieExpire = 3600;
|
| 40 |
+
|
| 41 |
+
/**
|
| 42 |
+
* @var array 允许语言列表
|
| 43 |
+
*/
|
| 44 |
+
protected static $allowLangList = [];
|
| 45 |
+
|
| 46 |
+
/**
|
| 47 |
+
* @var array Accept-Language 转义为对应语言包名称 系统默认配置
|
| 48 |
+
*/
|
| 49 |
+
protected static $acceptLanguage = ['zh-hans-cn' => 'zh-cn'];
|
| 50 |
+
|
| 51 |
+
/**
|
| 52 |
+
* 设定当前的语言
|
| 53 |
+
* @access public
|
| 54 |
+
* @param string $range 语言作用域
|
| 55 |
+
* @return string
|
| 56 |
+
*/
|
| 57 |
+
public static function range($range = '')
|
| 58 |
+
{
|
| 59 |
+
if ($range) {
|
| 60 |
+
self::$range = $range;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
return self::$range;
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
/**
|
| 67 |
+
* 设置语言定义(不区分大小写)
|
| 68 |
+
* @access public
|
| 69 |
+
* @param string|array $name 语言变量
|
| 70 |
+
* @param string $value 语言值
|
| 71 |
+
* @param string $range 语言作用域
|
| 72 |
+
* @return mixed
|
| 73 |
+
*/
|
| 74 |
+
public static function set($name, $value = null, $range = '')
|
| 75 |
+
{
|
| 76 |
+
$range = $range ?: self::$range;
|
| 77 |
+
|
| 78 |
+
if (!isset(self::$lang[$range])) {
|
| 79 |
+
self::$lang[$range] = [];
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
if (is_array($name)) {
|
| 83 |
+
return self::$lang[$range] = array_change_key_case($name) + self::$lang[$range];
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
return self::$lang[$range][strtolower($name)] = $value;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
/**
|
| 90 |
+
* 加载语言定义(不区分大小写)
|
| 91 |
+
* @access public
|
| 92 |
+
* @param array|string $file 语言文件
|
| 93 |
+
* @param string $range 语言作用域
|
| 94 |
+
* @return mixed
|
| 95 |
+
*/
|
| 96 |
+
public static function load($file, $range = '')
|
| 97 |
+
{
|
| 98 |
+
$range = $range ?: self::$range;
|
| 99 |
+
$file = is_string($file) ? [$file] : $file;
|
| 100 |
+
|
| 101 |
+
if (!isset(self::$lang[$range])) {
|
| 102 |
+
self::$lang[$range] = [];
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
$lang = [];
|
| 106 |
+
|
| 107 |
+
foreach ($file as $_file) {
|
| 108 |
+
if (is_file($_file)) {
|
| 109 |
+
// 记录加载信息
|
| 110 |
+
App::$debug && Log::record('[ LANG ] ' . $_file, 'info');
|
| 111 |
+
|
| 112 |
+
$_lang = include $_file;
|
| 113 |
+
|
| 114 |
+
if (is_array($_lang)) {
|
| 115 |
+
$lang = array_change_key_case($_lang) + $lang;
|
| 116 |
+
}
|
| 117 |
+
}
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
if (!empty($lang)) {
|
| 121 |
+
self::$lang[$range] = $lang + self::$lang[$range];
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
return self::$lang[$range];
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
/**
|
| 128 |
+
* 获取语言定义(不区分大小写)
|
| 129 |
+
* @access public
|
| 130 |
+
* @param string|null $name 语言变量
|
| 131 |
+
* @param string $range 语言作用域
|
| 132 |
+
* @return mixed
|
| 133 |
+
*/
|
| 134 |
+
public static function has($name, $range = '')
|
| 135 |
+
{
|
| 136 |
+
$range = $range ?: self::$range;
|
| 137 |
+
|
| 138 |
+
return isset(self::$lang[$range][strtolower($name)]);
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
/**
|
| 142 |
+
* 获取语言定义(不区分大小写)
|
| 143 |
+
* @access public
|
| 144 |
+
* @param string|null $name 语言变量
|
| 145 |
+
* @param array $vars 变量替换
|
| 146 |
+
* @param string $range 语言作用域
|
| 147 |
+
* @return mixed
|
| 148 |
+
*/
|
| 149 |
+
public static function get($name = null, $vars = [], $range = '')
|
| 150 |
+
{
|
| 151 |
+
$range = $range ?: self::$range;
|
| 152 |
+
|
| 153 |
+
// 空参数返回所有定义
|
| 154 |
+
if (empty($name)) {
|
| 155 |
+
return self::$lang[$range];
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
$key = strtolower($name);
|
| 159 |
+
$value = isset(self::$lang[$range][$key]) ? self::$lang[$range][$key] : $name;
|
| 160 |
+
|
| 161 |
+
// 变量解析
|
| 162 |
+
if (!empty($vars) && is_array($vars)) {
|
| 163 |
+
/**
|
| 164 |
+
* Notes:
|
| 165 |
+
* 为了检测的方便,数字索引的判断仅仅是参数数组的第一个元素的key为数字0
|
| 166 |
+
* 数字索引采用的是系统的 sprintf 函数替换,用法请参考 sprintf 函数
|
| 167 |
+
*/
|
| 168 |
+
if (key($vars) === 0) {
|
| 169 |
+
// 数字索引解析
|
| 170 |
+
array_unshift($vars, $value);
|
| 171 |
+
$value = call_user_func_array('sprintf', $vars);
|
| 172 |
+
} else {
|
| 173 |
+
// 关联索引解析
|
| 174 |
+
$replace = array_keys($vars);
|
| 175 |
+
foreach ($replace as &$v) {
|
| 176 |
+
$v = "{:{$v}}";
|
| 177 |
+
}
|
| 178 |
+
$value = str_replace($replace, $vars, $value);
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
return $value;
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
/**
|
| 187 |
+
* 自动侦测设置获取语言选择
|
| 188 |
+
* @access public
|
| 189 |
+
* @return string
|
| 190 |
+
*/
|
| 191 |
+
public static function detect()
|
| 192 |
+
{
|
| 193 |
+
$langSet = '';
|
| 194 |
+
|
| 195 |
+
if (isset($_GET[self::$langDetectVar])) {
|
| 196 |
+
// url 中设置了语言变量
|
| 197 |
+
$langSet = strtolower($_GET[self::$langDetectVar]);
|
| 198 |
+
} elseif (isset($_COOKIE[self::$langCookieVar])) {
|
| 199 |
+
// Cookie 中设置了语言变量
|
| 200 |
+
$langSet = strtolower($_COOKIE[self::$langCookieVar]);
|
| 201 |
+
} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
| 202 |
+
// 自动侦测浏览器语言
|
| 203 |
+
preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
|
| 204 |
+
$langSet = strtolower($matches[1]);
|
| 205 |
+
$acceptLangs = Config::get('header_accept_lang');
|
| 206 |
+
|
| 207 |
+
if (isset($acceptLangs[$langSet])) {
|
| 208 |
+
$langSet = $acceptLangs[$langSet];
|
| 209 |
+
} elseif (isset(self::$acceptLanguage[$langSet])) {
|
| 210 |
+
$langSet = self::$acceptLanguage[$langSet];
|
| 211 |
+
}
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
// 合法的语言
|
| 215 |
+
if (empty(self::$allowLangList) || in_array($langSet, self::$allowLangList)) {
|
| 216 |
+
self::$range = $langSet ?: self::$range;
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
return self::$range;
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
/**
|
| 223 |
+
* 设置语言自动侦测的变量
|
| 224 |
+
* @access public
|
| 225 |
+
* @param string $var 变量名称
|
| 226 |
+
* @return void
|
| 227 |
+
*/
|
| 228 |
+
public static function setLangDetectVar($var)
|
| 229 |
+
{
|
| 230 |
+
self::$langDetectVar = $var;
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
/**
|
| 234 |
+
* 设置语言的 cookie 保存变量
|
| 235 |
+
* @access public
|
| 236 |
+
* @param string $var 变量名称
|
| 237 |
+
* @return void
|
| 238 |
+
*/
|
| 239 |
+
public static function setLangCookieVar($var)
|
| 240 |
+
{
|
| 241 |
+
self::$langCookieVar = $var;
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
/**
|
| 245 |
+
* 设置语言的 cookie 的过期时间
|
| 246 |
+
* @access public
|
| 247 |
+
* @param string $expire 过期时间
|
| 248 |
+
* @return void
|
| 249 |
+
*/
|
| 250 |
+
public static function setLangCookieExpire($expire)
|
| 251 |
+
{
|
| 252 |
+
self::$langCookieExpire = $expire;
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
/**
|
| 256 |
+
* 设置允许的语言列表
|
| 257 |
+
* @access public
|
| 258 |
+
* @param array $list 语言列表
|
| 259 |
+
* @return void
|
| 260 |
+
*/
|
| 261 |
+
public static function setAllowLangList($list)
|
| 262 |
+
{
|
| 263 |
+
self::$allowLangList = $list;
|
| 264 |
+
}
|
| 265 |
+
}
|
thinkphp/library/think/Loader.php
ADDED
|
@@ -0,0 +1,677 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use think\exception\ClassNotFoundException;
|
| 15 |
+
|
| 16 |
+
class Loader
|
| 17 |
+
{
|
| 18 |
+
/**
|
| 19 |
+
* @var array 实例数组
|
| 20 |
+
*/
|
| 21 |
+
protected static $instance = [];
|
| 22 |
+
|
| 23 |
+
/**
|
| 24 |
+
* @var array 类名映射
|
| 25 |
+
*/
|
| 26 |
+
protected static $classMap = [];
|
| 27 |
+
|
| 28 |
+
/**
|
| 29 |
+
* @var array 命名空间别名
|
| 30 |
+
*/
|
| 31 |
+
protected static $namespaceAlias = [];
|
| 32 |
+
|
| 33 |
+
/**
|
| 34 |
+
* @var array PSR-4 命名空间前缀长度映射
|
| 35 |
+
*/
|
| 36 |
+
private static $prefixLengthsPsr4 = [];
|
| 37 |
+
|
| 38 |
+
/**
|
| 39 |
+
* @var array PSR-4 的加载目录
|
| 40 |
+
*/
|
| 41 |
+
private static $prefixDirsPsr4 = [];
|
| 42 |
+
|
| 43 |
+
/**
|
| 44 |
+
* @var array PSR-4 加载失败的回退目录
|
| 45 |
+
*/
|
| 46 |
+
private static $fallbackDirsPsr4 = [];
|
| 47 |
+
|
| 48 |
+
/**
|
| 49 |
+
* @var array PSR-0 命名空间前缀映射
|
| 50 |
+
*/
|
| 51 |
+
private static $prefixesPsr0 = [];
|
| 52 |
+
|
| 53 |
+
/**
|
| 54 |
+
* @var array PSR-0 加载失败的回退目录
|
| 55 |
+
*/
|
| 56 |
+
private static $fallbackDirsPsr0 = [];
|
| 57 |
+
|
| 58 |
+
/**
|
| 59 |
+
* @var array 需要加载的文件
|
| 60 |
+
*/
|
| 61 |
+
private static $files = [];
|
| 62 |
+
|
| 63 |
+
/**
|
| 64 |
+
* 自动加载
|
| 65 |
+
* @access public
|
| 66 |
+
* @param string $class 类名
|
| 67 |
+
* @return bool
|
| 68 |
+
*/
|
| 69 |
+
public static function autoload($class)
|
| 70 |
+
{
|
| 71 |
+
// 检测命名空间别名
|
| 72 |
+
if (!empty(self::$namespaceAlias)) {
|
| 73 |
+
$namespace = dirname($class);
|
| 74 |
+
if (isset(self::$namespaceAlias[$namespace])) {
|
| 75 |
+
$original = self::$namespaceAlias[$namespace] . '\\' . basename($class);
|
| 76 |
+
if (class_exists($original)) {
|
| 77 |
+
return class_alias($original, $class, false);
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
if ($file = self::findFile($class)) {
|
| 83 |
+
// 非 Win 环境不严格区分大小写
|
| 84 |
+
if (!IS_WIN || pathinfo($file, PATHINFO_FILENAME) == pathinfo(realpath($file), PATHINFO_FILENAME)) {
|
| 85 |
+
__include_file($file);
|
| 86 |
+
return true;
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
return false;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
/**
|
| 94 |
+
* 查找文件
|
| 95 |
+
* @access private
|
| 96 |
+
* @param string $class 类名
|
| 97 |
+
* @return bool|string
|
| 98 |
+
*/
|
| 99 |
+
private static function findFile($class)
|
| 100 |
+
{
|
| 101 |
+
// 类库映射
|
| 102 |
+
if (!empty(self::$classMap[$class])) {
|
| 103 |
+
return self::$classMap[$class];
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
// 查找 PSR-4
|
| 107 |
+
$logicalPathPsr4 = strtr($class, '\\', DS) . EXT;
|
| 108 |
+
$first = $class[0];
|
| 109 |
+
|
| 110 |
+
if (isset(self::$prefixLengthsPsr4[$first])) {
|
| 111 |
+
foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) {
|
| 112 |
+
if (0 === strpos($class, $prefix)) {
|
| 113 |
+
foreach (self::$prefixDirsPsr4[$prefix] as $dir) {
|
| 114 |
+
if (is_file($file = $dir . DS . substr($logicalPathPsr4, $length))) {
|
| 115 |
+
return $file;
|
| 116 |
+
}
|
| 117 |
+
}
|
| 118 |
+
}
|
| 119 |
+
}
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
// 查找 PSR-4 fallback dirs
|
| 123 |
+
foreach (self::$fallbackDirsPsr4 as $dir) {
|
| 124 |
+
if (is_file($file = $dir . DS . $logicalPathPsr4)) {
|
| 125 |
+
return $file;
|
| 126 |
+
}
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
// 查找 PSR-0
|
| 130 |
+
if (false !== $pos = strrpos($class, '\\')) {
|
| 131 |
+
// namespace class name
|
| 132 |
+
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
| 133 |
+
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DS);
|
| 134 |
+
} else {
|
| 135 |
+
// PEAR-like class name
|
| 136 |
+
$logicalPathPsr0 = strtr($class, '_', DS) . EXT;
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
if (isset(self::$prefixesPsr0[$first])) {
|
| 140 |
+
foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) {
|
| 141 |
+
if (0 === strpos($class, $prefix)) {
|
| 142 |
+
foreach ($dirs as $dir) {
|
| 143 |
+
if (is_file($file = $dir . DS . $logicalPathPsr0)) {
|
| 144 |
+
return $file;
|
| 145 |
+
}
|
| 146 |
+
}
|
| 147 |
+
}
|
| 148 |
+
}
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
// 查找 PSR-0 fallback dirs
|
| 152 |
+
foreach (self::$fallbackDirsPsr0 as $dir) {
|
| 153 |
+
if (is_file($file = $dir . DS . $logicalPathPsr0)) {
|
| 154 |
+
return $file;
|
| 155 |
+
}
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
// 找不到则设置映射为 false 并返回
|
| 159 |
+
return self::$classMap[$class] = false;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
/**
|
| 163 |
+
* 注册 classmap
|
| 164 |
+
* @access public
|
| 165 |
+
* @param string|array $class 类名
|
| 166 |
+
* @param string $map 映射
|
| 167 |
+
* @return void
|
| 168 |
+
*/
|
| 169 |
+
public static function addClassMap($class, $map = '')
|
| 170 |
+
{
|
| 171 |
+
if (is_array($class)) {
|
| 172 |
+
self::$classMap = array_merge(self::$classMap, $class);
|
| 173 |
+
} else {
|
| 174 |
+
self::$classMap[$class] = $map;
|
| 175 |
+
}
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
/**
|
| 179 |
+
* 注册命名空间
|
| 180 |
+
* @access public
|
| 181 |
+
* @param string|array $namespace 命名空间
|
| 182 |
+
* @param string $path 路径
|
| 183 |
+
* @return void
|
| 184 |
+
*/
|
| 185 |
+
public static function addNamespace($namespace, $path = '')
|
| 186 |
+
{
|
| 187 |
+
if (is_array($namespace)) {
|
| 188 |
+
foreach ($namespace as $prefix => $paths) {
|
| 189 |
+
self::addPsr4($prefix . '\\', rtrim($paths, DS), true);
|
| 190 |
+
}
|
| 191 |
+
} else {
|
| 192 |
+
self::addPsr4($namespace . '\\', rtrim($path, DS), true);
|
| 193 |
+
}
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
/**
|
| 197 |
+
* 添加 PSR-0 命名空间
|
| 198 |
+
* @access private
|
| 199 |
+
* @param array|string $prefix 空间前缀
|
| 200 |
+
* @param array $paths 路径
|
| 201 |
+
* @param bool $prepend 预先设置的优先级更高
|
| 202 |
+
* @return void
|
| 203 |
+
*/
|
| 204 |
+
private static function addPsr0($prefix, $paths, $prepend = false)
|
| 205 |
+
{
|
| 206 |
+
if (!$prefix) {
|
| 207 |
+
self::$fallbackDirsPsr0 = $prepend ?
|
| 208 |
+
array_merge((array) $paths, self::$fallbackDirsPsr0) :
|
| 209 |
+
array_merge(self::$fallbackDirsPsr0, (array) $paths);
|
| 210 |
+
} else {
|
| 211 |
+
$first = $prefix[0];
|
| 212 |
+
|
| 213 |
+
if (!isset(self::$prefixesPsr0[$first][$prefix])) {
|
| 214 |
+
self::$prefixesPsr0[$first][$prefix] = (array) $paths;
|
| 215 |
+
} else {
|
| 216 |
+
self::$prefixesPsr0[$first][$prefix] = $prepend ?
|
| 217 |
+
array_merge((array) $paths, self::$prefixesPsr0[$first][$prefix]) :
|
| 218 |
+
array_merge(self::$prefixesPsr0[$first][$prefix], (array) $paths);
|
| 219 |
+
}
|
| 220 |
+
}
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
/**
|
| 224 |
+
* 添加 PSR-4 空间
|
| 225 |
+
* @access private
|
| 226 |
+
* @param array|string $prefix 空间前缀
|
| 227 |
+
* @param string $paths 路径
|
| 228 |
+
* @param bool $prepend 预先设置的优先级更高
|
| 229 |
+
* @return void
|
| 230 |
+
*/
|
| 231 |
+
private static function addPsr4($prefix, $paths, $prepend = false)
|
| 232 |
+
{
|
| 233 |
+
if (!$prefix) {
|
| 234 |
+
// Register directories for the root namespace.
|
| 235 |
+
self::$fallbackDirsPsr4 = $prepend ?
|
| 236 |
+
array_merge((array) $paths, self::$fallbackDirsPsr4) :
|
| 237 |
+
array_merge(self::$fallbackDirsPsr4, (array) $paths);
|
| 238 |
+
|
| 239 |
+
} elseif (!isset(self::$prefixDirsPsr4[$prefix])) {
|
| 240 |
+
// Register directories for a new namespace.
|
| 241 |
+
$length = strlen($prefix);
|
| 242 |
+
if ('\\' !== $prefix[$length - 1]) {
|
| 243 |
+
throw new \InvalidArgumentException(
|
| 244 |
+
"A non-empty PSR-4 prefix must end with a namespace separator."
|
| 245 |
+
);
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
| 249 |
+
self::$prefixDirsPsr4[$prefix] = (array) $paths;
|
| 250 |
+
|
| 251 |
+
} else {
|
| 252 |
+
self::$prefixDirsPsr4[$prefix] = $prepend ?
|
| 253 |
+
// Prepend directories for an already registered namespace.
|
| 254 |
+
array_merge((array) $paths, self::$prefixDirsPsr4[$prefix]) :
|
| 255 |
+
// Append directories for an already registered namespace.
|
| 256 |
+
array_merge(self::$prefixDirsPsr4[$prefix], (array) $paths);
|
| 257 |
+
}
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
/**
|
| 261 |
+
* 注册命名空间别名
|
| 262 |
+
* @access public
|
| 263 |
+
* @param array|string $namespace 命名空间
|
| 264 |
+
* @param string $original 源文件
|
| 265 |
+
* @return void
|
| 266 |
+
*/
|
| 267 |
+
public static function addNamespaceAlias($namespace, $original = '')
|
| 268 |
+
{
|
| 269 |
+
if (is_array($namespace)) {
|
| 270 |
+
self::$namespaceAlias = array_merge(self::$namespaceAlias, $namespace);
|
| 271 |
+
} else {
|
| 272 |
+
self::$namespaceAlias[$namespace] = $original;
|
| 273 |
+
}
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
/**
|
| 277 |
+
* 注册自动加载机制
|
| 278 |
+
* @access public
|
| 279 |
+
* @param callable $autoload 自动加载处理方法
|
| 280 |
+
* @return void
|
| 281 |
+
*/
|
| 282 |
+
public static function register($autoload = null)
|
| 283 |
+
{
|
| 284 |
+
// 注册系统自动加载
|
| 285 |
+
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
|
| 286 |
+
|
| 287 |
+
// Composer 自动加载支持
|
| 288 |
+
if (is_dir(VENDOR_PATH . 'composer')) {
|
| 289 |
+
if (PHP_VERSION_ID >= 50600 && is_file(VENDOR_PATH . 'composer' . DS . 'autoload_static.php')) {
|
| 290 |
+
require VENDOR_PATH . 'composer' . DS . 'autoload_static.php';
|
| 291 |
+
|
| 292 |
+
$declaredClass = get_declared_classes();
|
| 293 |
+
$composerClass = array_pop($declaredClass);
|
| 294 |
+
|
| 295 |
+
foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) {
|
| 296 |
+
if (property_exists($composerClass, $attr)) {
|
| 297 |
+
self::${$attr} = $composerClass::${$attr};
|
| 298 |
+
}
|
| 299 |
+
}
|
| 300 |
+
} else {
|
| 301 |
+
self::registerComposerLoader();
|
| 302 |
+
}
|
| 303 |
+
}
|
| 304 |
+
|
| 305 |
+
// 注册命名空间定义
|
| 306 |
+
self::addNamespace([
|
| 307 |
+
'think' => LIB_PATH . 'think' . DS,
|
| 308 |
+
'behavior' => LIB_PATH . 'behavior' . DS,
|
| 309 |
+
'traits' => LIB_PATH . 'traits' . DS,
|
| 310 |
+
]);
|
| 311 |
+
|
| 312 |
+
// 加载类库映射文件
|
| 313 |
+
if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {
|
| 314 |
+
self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));
|
| 315 |
+
}
|
| 316 |
+
|
| 317 |
+
self::loadComposerAutoloadFiles();
|
| 318 |
+
|
| 319 |
+
// 自动加载 extend 目录
|
| 320 |
+
self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
/**
|
| 324 |
+
* 注册 composer 自动加载
|
| 325 |
+
* @access private
|
| 326 |
+
* @return void
|
| 327 |
+
*/
|
| 328 |
+
private static function registerComposerLoader()
|
| 329 |
+
{
|
| 330 |
+
if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) {
|
| 331 |
+
$map = require VENDOR_PATH . 'composer/autoload_namespaces.php';
|
| 332 |
+
foreach ($map as $namespace => $path) {
|
| 333 |
+
self::addPsr0($namespace, $path);
|
| 334 |
+
}
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
if (is_file(VENDOR_PATH . 'composer/autoload_psr4.php')) {
|
| 338 |
+
$map = require VENDOR_PATH . 'composer/autoload_psr4.php';
|
| 339 |
+
foreach ($map as $namespace => $path) {
|
| 340 |
+
self::addPsr4($namespace, $path);
|
| 341 |
+
}
|
| 342 |
+
}
|
| 343 |
+
|
| 344 |
+
if (is_file(VENDOR_PATH . 'composer/autoload_classmap.php')) {
|
| 345 |
+
$classMap = require VENDOR_PATH . 'composer/autoload_classmap.php';
|
| 346 |
+
if ($classMap) {
|
| 347 |
+
self::addClassMap($classMap);
|
| 348 |
+
}
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) {
|
| 352 |
+
self::$files = require VENDOR_PATH . 'composer/autoload_files.php';
|
| 353 |
+
}
|
| 354 |
+
}
|
| 355 |
+
|
| 356 |
+
// 加载composer autofile文件
|
| 357 |
+
public static function loadComposerAutoloadFiles()
|
| 358 |
+
{
|
| 359 |
+
foreach (self::$files as $fileIdentifier => $file) {
|
| 360 |
+
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
| 361 |
+
__require_file($file);
|
| 362 |
+
|
| 363 |
+
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
| 364 |
+
}
|
| 365 |
+
}
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
/**
|
| 369 |
+
* 导入所需的类库 同 Java 的 Import 本函数有缓存功能
|
| 370 |
+
* @access public
|
| 371 |
+
* @param string $class 类库命名空间字符串
|
| 372 |
+
* @param string $baseUrl 起始路径
|
| 373 |
+
* @param string $ext 导入的文件扩展名
|
| 374 |
+
* @return bool
|
| 375 |
+
*/
|
| 376 |
+
public static function import($class, $baseUrl = '', $ext = EXT)
|
| 377 |
+
{
|
| 378 |
+
static $_file = [];
|
| 379 |
+
$key = $class . $baseUrl;
|
| 380 |
+
$class = str_replace(['.', '#'], [DS, '.'], $class);
|
| 381 |
+
|
| 382 |
+
if (isset($_file[$key])) {
|
| 383 |
+
return true;
|
| 384 |
+
}
|
| 385 |
+
|
| 386 |
+
if (empty($baseUrl)) {
|
| 387 |
+
list($name, $class) = explode(DS, $class, 2);
|
| 388 |
+
|
| 389 |
+
if (isset(self::$prefixDirsPsr4[$name . '\\'])) {
|
| 390 |
+
// 注册的命名空间
|
| 391 |
+
$baseUrl = self::$prefixDirsPsr4[$name . '\\'];
|
| 392 |
+
} elseif ('@' == $name) {
|
| 393 |
+
// 加载当前模块应用类库
|
| 394 |
+
$baseUrl = App::$modulePath;
|
| 395 |
+
} elseif (is_dir(EXTEND_PATH . $name)) {
|
| 396 |
+
$baseUrl = EXTEND_PATH . $name . DS;
|
| 397 |
+
} else {
|
| 398 |
+
// 加载其它模块的类库
|
| 399 |
+
$baseUrl = APP_PATH . $name . DS;
|
| 400 |
+
}
|
| 401 |
+
} elseif (substr($baseUrl, -1) != DS) {
|
| 402 |
+
$baseUrl .= DS;
|
| 403 |
+
}
|
| 404 |
+
|
| 405 |
+
// 如果类存在则导入类库文件
|
| 406 |
+
if (is_array($baseUrl)) {
|
| 407 |
+
foreach ($baseUrl as $path) {
|
| 408 |
+
if (is_file($filename = $path . DS . $class . $ext)) {
|
| 409 |
+
break;
|
| 410 |
+
}
|
| 411 |
+
}
|
| 412 |
+
} else {
|
| 413 |
+
$filename = $baseUrl . $class . $ext;
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
if (!empty($filename) &&
|
| 417 |
+
is_file($filename) &&
|
| 418 |
+
(!IS_WIN || pathinfo($filename, PATHINFO_FILENAME) == pathinfo(realpath($filename), PATHINFO_FILENAME))
|
| 419 |
+
) {
|
| 420 |
+
__include_file($filename);
|
| 421 |
+
$_file[$key] = true;
|
| 422 |
+
|
| 423 |
+
return true;
|
| 424 |
+
}
|
| 425 |
+
|
| 426 |
+
return false;
|
| 427 |
+
}
|
| 428 |
+
|
| 429 |
+
/**
|
| 430 |
+
* 实例化(分层)模型
|
| 431 |
+
* @access public
|
| 432 |
+
* @param string $name Model名称
|
| 433 |
+
* @param string $layer 业务层名称
|
| 434 |
+
* @param bool $appendSuffix 是否添加类名后缀
|
| 435 |
+
* @param string $common 公共模块名
|
| 436 |
+
* @return object
|
| 437 |
+
* @throws ClassNotFoundException
|
| 438 |
+
*/
|
| 439 |
+
public static function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common')
|
| 440 |
+
{
|
| 441 |
+
$uid = $name . $layer;
|
| 442 |
+
|
| 443 |
+
if (isset(self::$instance[$uid])) {
|
| 444 |
+
return self::$instance[$uid];
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
+
list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
|
| 448 |
+
|
| 449 |
+
if (class_exists($class)) {
|
| 450 |
+
$model = new $class();
|
| 451 |
+
} else {
|
| 452 |
+
$class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
|
| 453 |
+
|
| 454 |
+
if (class_exists($class)) {
|
| 455 |
+
$model = new $class();
|
| 456 |
+
} else {
|
| 457 |
+
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
| 458 |
+
}
|
| 459 |
+
}
|
| 460 |
+
|
| 461 |
+
return self::$instance[$uid] = $model;
|
| 462 |
+
}
|
| 463 |
+
|
| 464 |
+
/**
|
| 465 |
+
* 实例化(分层)控制器 格式:[模块名/]控制器名
|
| 466 |
+
* @access public
|
| 467 |
+
* @param string $name 资源地址
|
| 468 |
+
* @param string $layer 控制层名称
|
| 469 |
+
* @param bool $appendSuffix 是否添加类名后缀
|
| 470 |
+
* @param string $empty 空控制器名称
|
| 471 |
+
* @return object
|
| 472 |
+
* @throws ClassNotFoundException
|
| 473 |
+
*/
|
| 474 |
+
public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
|
| 475 |
+
{
|
| 476 |
+
list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
|
| 477 |
+
|
| 478 |
+
if (class_exists($class)) {
|
| 479 |
+
return App::invokeClass($class);
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
if ($empty) {
|
| 483 |
+
$emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix);
|
| 484 |
+
|
| 485 |
+
if (class_exists($emptyClass)) {
|
| 486 |
+
return new $emptyClass(Request::instance());
|
| 487 |
+
}
|
| 488 |
+
}
|
| 489 |
+
|
| 490 |
+
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
| 491 |
+
}
|
| 492 |
+
|
| 493 |
+
/**
|
| 494 |
+
* 实例化验证类 格式:[模块名/]验证器名
|
| 495 |
+
* @access public
|
| 496 |
+
* @param string $name 资源地址
|
| 497 |
+
* @param string $layer 验证层名称
|
| 498 |
+
* @param bool $appendSuffix 是否添加类名后缀
|
| 499 |
+
* @param string $common 公共模块名
|
| 500 |
+
* @return object|false
|
| 501 |
+
* @throws ClassNotFoundException
|
| 502 |
+
*/
|
| 503 |
+
public static function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common')
|
| 504 |
+
{
|
| 505 |
+
$name = $name ?: Config::get('default_validate');
|
| 506 |
+
|
| 507 |
+
if (empty($name)) {
|
| 508 |
+
return new Validate;
|
| 509 |
+
}
|
| 510 |
+
|
| 511 |
+
$uid = $name . $layer;
|
| 512 |
+
if (isset(self::$instance[$uid])) {
|
| 513 |
+
return self::$instance[$uid];
|
| 514 |
+
}
|
| 515 |
+
|
| 516 |
+
list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
|
| 517 |
+
|
| 518 |
+
if (class_exists($class)) {
|
| 519 |
+
$validate = new $class;
|
| 520 |
+
} else {
|
| 521 |
+
$class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
|
| 522 |
+
|
| 523 |
+
if (class_exists($class)) {
|
| 524 |
+
$validate = new $class;
|
| 525 |
+
} else {
|
| 526 |
+
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
| 527 |
+
}
|
| 528 |
+
}
|
| 529 |
+
|
| 530 |
+
return self::$instance[$uid] = $validate;
|
| 531 |
+
}
|
| 532 |
+
|
| 533 |
+
/**
|
| 534 |
+
* 解析模块和类名
|
| 535 |
+
* @access protected
|
| 536 |
+
* @param string $name 资源地址
|
| 537 |
+
* @param string $layer 验证层名称
|
| 538 |
+
* @param bool $appendSuffix 是否添加类名后缀
|
| 539 |
+
* @return array
|
| 540 |
+
*/
|
| 541 |
+
protected static function getModuleAndClass($name, $layer, $appendSuffix)
|
| 542 |
+
{
|
| 543 |
+
if (false !== strpos($name, '\\')) {
|
| 544 |
+
$module = Request::instance()->module();
|
| 545 |
+
$class = $name;
|
| 546 |
+
} else {
|
| 547 |
+
if (strpos($name, '/')) {
|
| 548 |
+
list($module, $name) = explode('/', $name, 2);
|
| 549 |
+
} else {
|
| 550 |
+
$module = Request::instance()->module();
|
| 551 |
+
}
|
| 552 |
+
|
| 553 |
+
$class = self::parseClass($module, $layer, $name, $appendSuffix);
|
| 554 |
+
}
|
| 555 |
+
|
| 556 |
+
return [$module, $class];
|
| 557 |
+
}
|
| 558 |
+
|
| 559 |
+
/**
|
| 560 |
+
* 数据库初始化 并取得数据库类实例
|
| 561 |
+
* @access public
|
| 562 |
+
* @param mixed $config 数据库配置
|
| 563 |
+
* @param bool|string $name 连接标识 true 强制重新连接
|
| 564 |
+
* @return \think\db\Connection
|
| 565 |
+
*/
|
| 566 |
+
public static function db($config = [], $name = false)
|
| 567 |
+
{
|
| 568 |
+
return Db::connect($config, $name);
|
| 569 |
+
}
|
| 570 |
+
|
| 571 |
+
/**
|
| 572 |
+
* 远程调用模块的操作方法 参数格式 [模块/控制器/]操作
|
| 573 |
+
* @access public
|
| 574 |
+
* @param string $url 调用地址
|
| 575 |
+
* @param string|array $vars 调用参数 支持字符串和数组
|
| 576 |
+
* @param string $layer 要调用的控制层名称
|
| 577 |
+
* @param bool $appendSuffix 是否添加类名后缀
|
| 578 |
+
* @return mixed
|
| 579 |
+
*/
|
| 580 |
+
public static function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
|
| 581 |
+
{
|
| 582 |
+
$info = pathinfo($url);
|
| 583 |
+
$action = $info['basename'];
|
| 584 |
+
$module = '.' != $info['dirname'] ? $info['dirname'] : Request::instance()->controller();
|
| 585 |
+
$class = self::controller($module, $layer, $appendSuffix);
|
| 586 |
+
|
| 587 |
+
if ($class) {
|
| 588 |
+
if (is_scalar($vars)) {
|
| 589 |
+
if (strpos($vars, '=')) {
|
| 590 |
+
parse_str($vars, $vars);
|
| 591 |
+
} else {
|
| 592 |
+
$vars = [$vars];
|
| 593 |
+
}
|
| 594 |
+
}
|
| 595 |
+
|
| 596 |
+
return App::invokeMethod([$class, $action . Config::get('action_suffix')], $vars);
|
| 597 |
+
}
|
| 598 |
+
|
| 599 |
+
return false;
|
| 600 |
+
}
|
| 601 |
+
|
| 602 |
+
/**
|
| 603 |
+
* 字符串命名风格转换
|
| 604 |
+
* type 0 将 Java 风格转换为 C 的风格 1 将 C 风格转换为 Java 的风格
|
| 605 |
+
* @access public
|
| 606 |
+
* @param string $name 字符串
|
| 607 |
+
* @param integer $type 转换类型
|
| 608 |
+
* @param bool $ucfirst 首字母是否大写(驼峰��则)
|
| 609 |
+
* @return string
|
| 610 |
+
*/
|
| 611 |
+
public static function parseName($name, $type = 0, $ucfirst = true)
|
| 612 |
+
{
|
| 613 |
+
if ($type) {
|
| 614 |
+
$name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
|
| 615 |
+
return strtoupper($match[1]);
|
| 616 |
+
}, $name);
|
| 617 |
+
|
| 618 |
+
return $ucfirst ? ucfirst($name) : lcfirst($name);
|
| 619 |
+
}
|
| 620 |
+
|
| 621 |
+
return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
|
| 622 |
+
}
|
| 623 |
+
|
| 624 |
+
/**
|
| 625 |
+
* 解析应用类的类名
|
| 626 |
+
* @access public
|
| 627 |
+
* @param string $module 模块名
|
| 628 |
+
* @param string $layer 层名 controller model ...
|
| 629 |
+
* @param string $name 类名
|
| 630 |
+
* @param bool $appendSuffix 是否添加类名后缀
|
| 631 |
+
* @return string
|
| 632 |
+
*/
|
| 633 |
+
public static function parseClass($module, $layer, $name, $appendSuffix = false)
|
| 634 |
+
{
|
| 635 |
+
|
| 636 |
+
$array = explode('\\', str_replace(['/', '.'], '\\', $name));
|
| 637 |
+
$class = self::parseName(array_pop($array), 1);
|
| 638 |
+
$class = $class . (App::$suffix || $appendSuffix ? ucfirst($layer) : '');
|
| 639 |
+
$path = $array ? implode('\\', $array) . '\\' : '';
|
| 640 |
+
|
| 641 |
+
return App::$namespace . '\\' .
|
| 642 |
+
($module ? $module . '\\' : '') .
|
| 643 |
+
$layer . '\\' . $path . $class;
|
| 644 |
+
}
|
| 645 |
+
|
| 646 |
+
/**
|
| 647 |
+
* 初始化类的实例
|
| 648 |
+
* @access public
|
| 649 |
+
* @return void
|
| 650 |
+
*/
|
| 651 |
+
public static function clearInstance()
|
| 652 |
+
{
|
| 653 |
+
self::$instance = [];
|
| 654 |
+
}
|
| 655 |
+
}
|
| 656 |
+
|
| 657 |
+
// 作用范围隔离
|
| 658 |
+
|
| 659 |
+
/**
|
| 660 |
+
* include
|
| 661 |
+
* @param string $file 文件路径
|
| 662 |
+
* @return mixed
|
| 663 |
+
*/
|
| 664 |
+
function __include_file($file)
|
| 665 |
+
{
|
| 666 |
+
return include $file;
|
| 667 |
+
}
|
| 668 |
+
|
| 669 |
+
/**
|
| 670 |
+
* require
|
| 671 |
+
* @param string $file 文件路径
|
| 672 |
+
* @return mixed
|
| 673 |
+
*/
|
| 674 |
+
function __require_file($file)
|
| 675 |
+
{
|
| 676 |
+
return require $file;
|
| 677 |
+
}
|
thinkphp/library/think/Log.php
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use think\exception\ClassNotFoundException;
|
| 15 |
+
|
| 16 |
+
/**
|
| 17 |
+
* Class Log
|
| 18 |
+
* @package think
|
| 19 |
+
*
|
| 20 |
+
* @method void log($msg) static 记录一般日志
|
| 21 |
+
* @method void error($msg) static 记录错误日志
|
| 22 |
+
* @method void info($msg) static 记录一般信息日志
|
| 23 |
+
* @method void sql($msg) static 记录 SQL 查询日志
|
| 24 |
+
* @method void notice($msg) static 记录提示日志
|
| 25 |
+
* @method void alert($msg) static 记录报警日志
|
| 26 |
+
*/
|
| 27 |
+
class Log
|
| 28 |
+
{
|
| 29 |
+
const LOG = 'log';
|
| 30 |
+
const ERROR = 'error';
|
| 31 |
+
const INFO = 'info';
|
| 32 |
+
const SQL = 'sql';
|
| 33 |
+
const NOTICE = 'notice';
|
| 34 |
+
const ALERT = 'alert';
|
| 35 |
+
const DEBUG = 'debug';
|
| 36 |
+
|
| 37 |
+
/**
|
| 38 |
+
* @var array 日志信息
|
| 39 |
+
*/
|
| 40 |
+
protected static $log = [];
|
| 41 |
+
|
| 42 |
+
/**
|
| 43 |
+
* @var array 配置参数
|
| 44 |
+
*/
|
| 45 |
+
protected static $config = [];
|
| 46 |
+
|
| 47 |
+
/**
|
| 48 |
+
* @var array 日志类型
|
| 49 |
+
*/
|
| 50 |
+
protected static $type = ['log', 'error', 'info', 'sql', 'notice', 'alert', 'debug'];
|
| 51 |
+
|
| 52 |
+
/**
|
| 53 |
+
* @var log\driver\File|log\driver\Test|log\driver\Socket 日志写入驱动
|
| 54 |
+
*/
|
| 55 |
+
protected static $driver;
|
| 56 |
+
|
| 57 |
+
/**
|
| 58 |
+
* @var string 当前日志授权 key
|
| 59 |
+
*/
|
| 60 |
+
protected static $key;
|
| 61 |
+
|
| 62 |
+
/**
|
| 63 |
+
* 日志初始化
|
| 64 |
+
* @access public
|
| 65 |
+
* @param array $config 配置参数
|
| 66 |
+
* @return void
|
| 67 |
+
*/
|
| 68 |
+
public static function init($config = [])
|
| 69 |
+
{
|
| 70 |
+
$type = isset($config['type']) ? $config['type'] : 'File';
|
| 71 |
+
$class = false !== strpos($type, '\\') ? $type : '\\think\\log\\driver\\' . ucwords($type);
|
| 72 |
+
|
| 73 |
+
self::$config = $config;
|
| 74 |
+
unset($config['type']);
|
| 75 |
+
|
| 76 |
+
if (class_exists($class)) {
|
| 77 |
+
self::$driver = new $class($config);
|
| 78 |
+
} else {
|
| 79 |
+
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
// 记录初始化信息
|
| 83 |
+
App::$debug && Log::record('[ LOG ] INIT ' . $type, 'info');
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
/**
|
| 87 |
+
* 获取日志信息
|
| 88 |
+
* @access public
|
| 89 |
+
* @param string $type 信息类型
|
| 90 |
+
* @return array|string
|
| 91 |
+
*/
|
| 92 |
+
public static function getLog($type = '')
|
| 93 |
+
{
|
| 94 |
+
return $type ? self::$log[$type] : self::$log;
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
/**
|
| 98 |
+
* 记录调试信息
|
| 99 |
+
* @access public
|
| 100 |
+
* @param mixed $msg 调试信息
|
| 101 |
+
* @param string $type 信息类型
|
| 102 |
+
* @return void
|
| 103 |
+
*/
|
| 104 |
+
public static function record($msg, $type = 'log')
|
| 105 |
+
{
|
| 106 |
+
self::$log[$type][] = $msg;
|
| 107 |
+
|
| 108 |
+
// 命令行下面日志写入改进
|
| 109 |
+
IS_CLI && self::save();
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
/**
|
| 113 |
+
* 清空日志信息
|
| 114 |
+
* @access public
|
| 115 |
+
* @return void
|
| 116 |
+
*/
|
| 117 |
+
public static function clear()
|
| 118 |
+
{
|
| 119 |
+
self::$log = [];
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
/**
|
| 123 |
+
* 设置当前日志记录的授权 key
|
| 124 |
+
* @access public
|
| 125 |
+
* @param string $key 授权 key
|
| 126 |
+
* @return void
|
| 127 |
+
*/
|
| 128 |
+
public static function key($key)
|
| 129 |
+
{
|
| 130 |
+
self::$key = $key;
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
/**
|
| 134 |
+
* 检查日志写入权限
|
| 135 |
+
* @access public
|
| 136 |
+
* @param array $config 当前日志配置参数
|
| 137 |
+
* @return bool
|
| 138 |
+
*/
|
| 139 |
+
public static function check($config)
|
| 140 |
+
{
|
| 141 |
+
return !self::$key || empty($config['allow_key']) || in_array(self::$key, $config['allow_key']);
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
/**
|
| 145 |
+
* 保存调试信息
|
| 146 |
+
* @access public
|
| 147 |
+
* @return bool
|
| 148 |
+
*/
|
| 149 |
+
public static function save()
|
| 150 |
+
{
|
| 151 |
+
// 没有需要保存的记录则直接返回
|
| 152 |
+
if (empty(self::$log)) {
|
| 153 |
+
return true;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
is_null(self::$driver) && self::init(Config::get('log'));
|
| 157 |
+
|
| 158 |
+
// 检测日志写入权限
|
| 159 |
+
if (!self::check(self::$config)) {
|
| 160 |
+
return false;
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
if (empty(self::$config['level'])) {
|
| 164 |
+
// 获取全部日志
|
| 165 |
+
$log = self::$log;
|
| 166 |
+
if (!App::$debug && isset($log['debug'])) {
|
| 167 |
+
unset($log['debug']);
|
| 168 |
+
}
|
| 169 |
+
} else {
|
| 170 |
+
// 记录允许级别
|
| 171 |
+
$log = [];
|
| 172 |
+
foreach (self::$config['level'] as $level) {
|
| 173 |
+
if (isset(self::$log[$level])) {
|
| 174 |
+
$log[$level] = self::$log[$level];
|
| 175 |
+
}
|
| 176 |
+
}
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
if ($result = self::$driver->save($log, true)) {
|
| 180 |
+
self::$log = [];
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
Hook::listen('log_write_done', $log);
|
| 184 |
+
|
| 185 |
+
return $result;
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
/**
|
| 189 |
+
* 实时写入日志信息 并支持行为
|
| 190 |
+
* @access public
|
| 191 |
+
* @param mixed $msg 调试信息
|
| 192 |
+
* @param string $type 信息类型
|
| 193 |
+
* @param bool $force 是否强制写入
|
| 194 |
+
* @return bool
|
| 195 |
+
*/
|
| 196 |
+
public static function write($msg, $type = 'log', $force = false)
|
| 197 |
+
{
|
| 198 |
+
$log = self::$log;
|
| 199 |
+
|
| 200 |
+
// 如果不是强制写入,而且信息类型不在可记录的类别中则直接返回 false 不做记录
|
| 201 |
+
if (true !== $force && !empty(self::$config['level']) && !in_array($type, self::$config['level'])) {
|
| 202 |
+
return false;
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
// 封装日志信息
|
| 206 |
+
$log[$type][] = $msg;
|
| 207 |
+
|
| 208 |
+
// 监听 log_write
|
| 209 |
+
Hook::listen('log_write', $log);
|
| 210 |
+
|
| 211 |
+
is_null(self::$driver) && self::init(Config::get('log'));
|
| 212 |
+
|
| 213 |
+
// 写入日志
|
| 214 |
+
if ($result = self::$driver->save($log, false)) {
|
| 215 |
+
self::$log = [];
|
| 216 |
+
}
|
| 217 |
+
|
| 218 |
+
return $result;
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
/**
|
| 222 |
+
* 静态方法调用
|
| 223 |
+
* @access public
|
| 224 |
+
* @param string $method 调用方法
|
| 225 |
+
* @param mixed $args 参数
|
| 226 |
+
* @return void
|
| 227 |
+
*/
|
| 228 |
+
public static function __callStatic($method, $args)
|
| 229 |
+
{
|
| 230 |
+
if (in_array($method, self::$type)) {
|
| 231 |
+
array_push($args, $method);
|
| 232 |
+
|
| 233 |
+
call_user_func_array('\\think\\Log::record', $args);
|
| 234 |
+
}
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
}
|
thinkphp/library/think/Model.php
ADDED
|
@@ -0,0 +1,2350 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use BadMethodCallException;
|
| 15 |
+
use InvalidArgumentException;
|
| 16 |
+
use think\db\Query;
|
| 17 |
+
use think\exception\ValidateException;
|
| 18 |
+
use think\model\Collection as ModelCollection;
|
| 19 |
+
use think\model\Relation;
|
| 20 |
+
use think\model\relation\BelongsTo;
|
| 21 |
+
use think\model\relation\BelongsToMany;
|
| 22 |
+
use think\model\relation\HasMany;
|
| 23 |
+
use think\model\relation\HasManyThrough;
|
| 24 |
+
use think\model\relation\HasOne;
|
| 25 |
+
use think\model\relation\MorphMany;
|
| 26 |
+
use think\model\relation\MorphOne;
|
| 27 |
+
use think\model\relation\MorphTo;
|
| 28 |
+
|
| 29 |
+
/**
|
| 30 |
+
* Class Model
|
| 31 |
+
* @package think
|
| 32 |
+
* @mixin Query
|
| 33 |
+
*/
|
| 34 |
+
abstract class Model implements \JsonSerializable, \ArrayAccess
|
| 35 |
+
{
|
| 36 |
+
// 数据库查询对象池
|
| 37 |
+
protected static $links = [];
|
| 38 |
+
// 数据库配置
|
| 39 |
+
protected $connection = [];
|
| 40 |
+
// 父关联模型对象
|
| 41 |
+
protected $parent;
|
| 42 |
+
// 数据库查询对象
|
| 43 |
+
protected $query;
|
| 44 |
+
// 当前模型名称
|
| 45 |
+
protected $name;
|
| 46 |
+
// 数据表名称
|
| 47 |
+
protected $table;
|
| 48 |
+
// 当前类名称
|
| 49 |
+
protected $class;
|
| 50 |
+
// 回调事件
|
| 51 |
+
private static $event = [];
|
| 52 |
+
// 错误信息
|
| 53 |
+
protected $error;
|
| 54 |
+
// 字段验证规则
|
| 55 |
+
protected $validate;
|
| 56 |
+
// 数据表主键 复合主键使用数组定义 不设置则自动获取
|
| 57 |
+
protected $pk;
|
| 58 |
+
// 数据表字段信息 留空则自动获取
|
| 59 |
+
protected $field = [];
|
| 60 |
+
// 数据排除字段
|
| 61 |
+
protected $except = [];
|
| 62 |
+
// 数据废弃字段
|
| 63 |
+
protected $disuse = [];
|
| 64 |
+
// 只读字段
|
| 65 |
+
protected $readonly = [];
|
| 66 |
+
// 显示属性
|
| 67 |
+
protected $visible = [];
|
| 68 |
+
// 隐藏属性
|
| 69 |
+
protected $hidden = [];
|
| 70 |
+
// 追加属性
|
| 71 |
+
protected $append = [];
|
| 72 |
+
// 数据信息
|
| 73 |
+
protected $data = [];
|
| 74 |
+
// 原始数据
|
| 75 |
+
protected $origin = [];
|
| 76 |
+
// 关联模型
|
| 77 |
+
protected $relation = [];
|
| 78 |
+
|
| 79 |
+
// 保存自动完成列表
|
| 80 |
+
protected $auto = [];
|
| 81 |
+
// 新增自动完成列表
|
| 82 |
+
protected $insert = [];
|
| 83 |
+
// 更新自动完成列表
|
| 84 |
+
protected $update = [];
|
| 85 |
+
// 是否需要自动写入时间戳 如果设置为字符串 则表示时间字段的类型
|
| 86 |
+
protected $autoWriteTimestamp;
|
| 87 |
+
// 创建时间字段
|
| 88 |
+
protected $createTime = 'create_time';
|
| 89 |
+
// 更新时间字段
|
| 90 |
+
protected $updateTime = 'update_time';
|
| 91 |
+
// 时间字段取出后的默认时间格式
|
| 92 |
+
protected $dateFormat;
|
| 93 |
+
// 字段类型或者格式转换
|
| 94 |
+
protected $type = [];
|
| 95 |
+
// 是否为更新数据
|
| 96 |
+
protected $isUpdate = false;
|
| 97 |
+
// 是否使用Replace
|
| 98 |
+
protected $replace = false;
|
| 99 |
+
// 是否强制更新所有数据
|
| 100 |
+
protected $force = false;
|
| 101 |
+
// 更新条件
|
| 102 |
+
protected $updateWhere;
|
| 103 |
+
// 验证失败是否抛出异常
|
| 104 |
+
protected $failException = false;
|
| 105 |
+
// 全局查询范围
|
| 106 |
+
protected $useGlobalScope = true;
|
| 107 |
+
// 是否采用批量验证
|
| 108 |
+
protected $batchValidate = false;
|
| 109 |
+
// 查询数据集对象
|
| 110 |
+
protected $resultSetType;
|
| 111 |
+
// 关联自动写入
|
| 112 |
+
protected $relationWrite;
|
| 113 |
+
|
| 114 |
+
/**
|
| 115 |
+
* 初始化过的模型.
|
| 116 |
+
*
|
| 117 |
+
* @var array
|
| 118 |
+
*/
|
| 119 |
+
protected static $initialized = [];
|
| 120 |
+
|
| 121 |
+
/**
|
| 122 |
+
* 是否从主库读取(主从分布式有效)
|
| 123 |
+
* @var array
|
| 124 |
+
*/
|
| 125 |
+
protected static $readMaster;
|
| 126 |
+
|
| 127 |
+
/**
|
| 128 |
+
* 构造方法
|
| 129 |
+
* @access public
|
| 130 |
+
* @param array|object $data 数据
|
| 131 |
+
*/
|
| 132 |
+
public function __construct($data = [])
|
| 133 |
+
{
|
| 134 |
+
if (is_object($data)) {
|
| 135 |
+
$this->data = get_object_vars($data);
|
| 136 |
+
} else {
|
| 137 |
+
$this->data = $data;
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
if ($this->disuse) {
|
| 141 |
+
// 废弃字段
|
| 142 |
+
foreach ((array) $this->disuse as $key) {
|
| 143 |
+
if (array_key_exists($key, $this->data)) {
|
| 144 |
+
unset($this->data[$key]);
|
| 145 |
+
}
|
| 146 |
+
}
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
// 记录原始数据
|
| 150 |
+
$this->origin = $this->data;
|
| 151 |
+
|
| 152 |
+
// 当前类名
|
| 153 |
+
$this->class = get_called_class();
|
| 154 |
+
|
| 155 |
+
if (empty($this->name)) {
|
| 156 |
+
// 当前模型名
|
| 157 |
+
$name = str_replace('\\', '/', $this->class);
|
| 158 |
+
$this->name = basename($name);
|
| 159 |
+
if (Config::get('class_suffix')) {
|
| 160 |
+
$suffix = basename(dirname($name));
|
| 161 |
+
$this->name = substr($this->name, 0, -strlen($suffix));
|
| 162 |
+
}
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
if (is_null($this->autoWriteTimestamp)) {
|
| 166 |
+
// 自动写入时间戳
|
| 167 |
+
$this->autoWriteTimestamp = $this->getQuery()->getConfig('auto_timestamp');
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
if (is_null($this->dateFormat)) {
|
| 171 |
+
// 设置时间戳格式
|
| 172 |
+
$this->dateFormat = $this->getQuery()->getConfig('datetime_format');
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
if (is_null($this->resultSetType)) {
|
| 176 |
+
$this->resultSetType = $this->getQuery()->getConfig('resultset_type');
|
| 177 |
+
}
|
| 178 |
+
// 执行初始化操作
|
| 179 |
+
$this->initialize();
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
/**
|
| 183 |
+
* 是否从主库读取数据(主从分布有效)
|
| 184 |
+
* @access public
|
| 185 |
+
* @param bool $all 是否所有模型生效
|
| 186 |
+
* @return $this
|
| 187 |
+
*/
|
| 188 |
+
public function readMaster($all = false)
|
| 189 |
+
{
|
| 190 |
+
$model = $all ? '*' : $this->class;
|
| 191 |
+
|
| 192 |
+
static::$readMaster[$model] = true;
|
| 193 |
+
return $this;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
/**
|
| 197 |
+
* 创建模型的查询对象
|
| 198 |
+
* @access protected
|
| 199 |
+
* @return Query
|
| 200 |
+
*/
|
| 201 |
+
protected function buildQuery()
|
| 202 |
+
{
|
| 203 |
+
// 合并数据库配置
|
| 204 |
+
if (!empty($this->connection)) {
|
| 205 |
+
if (is_array($this->connection)) {
|
| 206 |
+
$connection = array_merge(Config::get('database'), $this->connection);
|
| 207 |
+
} else {
|
| 208 |
+
$connection = $this->connection;
|
| 209 |
+
}
|
| 210 |
+
} else {
|
| 211 |
+
$connection = [];
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
$con = Db::connect($connection);
|
| 215 |
+
// 设置当前模型 确保查询返回模型对象
|
| 216 |
+
$queryClass = $this->query ?: $con->getConfig('query');
|
| 217 |
+
$query = new $queryClass($con, $this);
|
| 218 |
+
|
| 219 |
+
if (isset(static::$readMaster['*']) || isset(static::$readMaster[$this->class])) {
|
| 220 |
+
$query->master(true);
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
// 设置当前数据表和模型名
|
| 224 |
+
if (!empty($this->table)) {
|
| 225 |
+
$query->setTable($this->table);
|
| 226 |
+
} else {
|
| 227 |
+
$query->name($this->name);
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
if (!empty($this->pk)) {
|
| 231 |
+
$query->pk($this->pk);
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
return $query;
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
/**
|
| 238 |
+
* 创建新的模型实例
|
| 239 |
+
* @access public
|
| 240 |
+
* @param array|object $data 数据
|
| 241 |
+
* @param bool $isUpdate 是否为更新
|
| 242 |
+
* @param mixed $where 更新条件
|
| 243 |
+
* @return Model
|
| 244 |
+
*/
|
| 245 |
+
public function newInstance($data = [], $isUpdate = false, $where = null)
|
| 246 |
+
{
|
| 247 |
+
return (new static($data))->isUpdate($isUpdate, $where);
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
/**
|
| 251 |
+
* 获取当前模型的查询对象
|
| 252 |
+
* @access public
|
| 253 |
+
* @param bool $buildNewQuery 创建新的查询对象
|
| 254 |
+
* @return Query
|
| 255 |
+
*/
|
| 256 |
+
public function getQuery($buildNewQuery = false)
|
| 257 |
+
{
|
| 258 |
+
if ($buildNewQuery) {
|
| 259 |
+
return $this->buildQuery();
|
| 260 |
+
} elseif (!isset(self::$links[$this->class])) {
|
| 261 |
+
// 创建模型查询对象
|
| 262 |
+
self::$links[$this->class] = $this->buildQuery();
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
return self::$links[$this->class];
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
/**
|
| 269 |
+
* 获取当前模型的数据库查询对象
|
| 270 |
+
* @access public
|
| 271 |
+
* @param bool $useBaseQuery 是否调用全局查询范围
|
| 272 |
+
* @param bool $buildNewQuery 创建新的查询对象
|
| 273 |
+
* @return Query
|
| 274 |
+
*/
|
| 275 |
+
public function db($useBaseQuery = true, $buildNewQuery = true)
|
| 276 |
+
{
|
| 277 |
+
$query = $this->getQuery($buildNewQuery);
|
| 278 |
+
|
| 279 |
+
// 全局作用域
|
| 280 |
+
if ($useBaseQuery && method_exists($this, 'base')) {
|
| 281 |
+
call_user_func_array([$this, 'base'], [ & $query]);
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
// 返回当前模型的数据库查询对象
|
| 285 |
+
return $query;
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
/**
|
| 289 |
+
* 初始化模型
|
| 290 |
+
* @access protected
|
| 291 |
+
* @return void
|
| 292 |
+
*/
|
| 293 |
+
protected function initialize()
|
| 294 |
+
{
|
| 295 |
+
$class = get_class($this);
|
| 296 |
+
if (!isset(static::$initialized[$class])) {
|
| 297 |
+
static::$initialized[$class] = true;
|
| 298 |
+
static::init();
|
| 299 |
+
}
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
/**
|
| 303 |
+
* 初始化处理
|
| 304 |
+
* @access protected
|
| 305 |
+
* @return void
|
| 306 |
+
*/
|
| 307 |
+
protected static function init()
|
| 308 |
+
{
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
/**
|
| 312 |
+
* 设置父关联对象
|
| 313 |
+
* @access public
|
| 314 |
+
* @param Model $model 模型对象
|
| 315 |
+
* @return $this
|
| 316 |
+
*/
|
| 317 |
+
public function setParent($model)
|
| 318 |
+
{
|
| 319 |
+
$this->parent = $model;
|
| 320 |
+
return $this;
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
/**
|
| 324 |
+
* 获取父关联对象
|
| 325 |
+
* @access public
|
| 326 |
+
* @return Model
|
| 327 |
+
*/
|
| 328 |
+
public function getParent()
|
| 329 |
+
{
|
| 330 |
+
return $this->parent;
|
| 331 |
+
}
|
| 332 |
+
|
| 333 |
+
/**
|
| 334 |
+
* 设置数据对象值
|
| 335 |
+
* @access public
|
| 336 |
+
* @param mixed $data 数据或者属性名
|
| 337 |
+
* @param mixed $value 值
|
| 338 |
+
* @return $this
|
| 339 |
+
*/
|
| 340 |
+
public function data($data, $value = null)
|
| 341 |
+
{
|
| 342 |
+
if (is_string($data)) {
|
| 343 |
+
$this->data[$data] = $value;
|
| 344 |
+
} else {
|
| 345 |
+
// 清空数据
|
| 346 |
+
$this->data = [];
|
| 347 |
+
if (is_object($data)) {
|
| 348 |
+
$data = get_object_vars($data);
|
| 349 |
+
}
|
| 350 |
+
if (true === $value) {
|
| 351 |
+
// 数据对象赋值
|
| 352 |
+
foreach ($data as $key => $value) {
|
| 353 |
+
$this->setAttr($key, $value, $data);
|
| 354 |
+
}
|
| 355 |
+
} else {
|
| 356 |
+
$this->data = $data;
|
| 357 |
+
}
|
| 358 |
+
}
|
| 359 |
+
return $this;
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
/**
|
| 363 |
+
* 获取对象原始数据 如果不存在指定字段返回false
|
| 364 |
+
* @access public
|
| 365 |
+
* @param string $name 字段名 留空获取全部
|
| 366 |
+
* @return mixed
|
| 367 |
+
* @throws InvalidArgumentException
|
| 368 |
+
*/
|
| 369 |
+
public function getData($name = null)
|
| 370 |
+
{
|
| 371 |
+
if (is_null($name)) {
|
| 372 |
+
return $this->data;
|
| 373 |
+
} elseif (array_key_exists($name, $this->data)) {
|
| 374 |
+
return $this->data[$name];
|
| 375 |
+
} elseif (array_key_exists($name, $this->relation)) {
|
| 376 |
+
return $this->relation[$name];
|
| 377 |
+
} else {
|
| 378 |
+
throw new InvalidArgumentException('property not exists:' . $this->class . '->' . $name);
|
| 379 |
+
}
|
| 380 |
+
}
|
| 381 |
+
|
| 382 |
+
/**
|
| 383 |
+
* 是否需要自动写入时间字段
|
| 384 |
+
* @access public
|
| 385 |
+
* @param bool $auto
|
| 386 |
+
* @return $this
|
| 387 |
+
*/
|
| 388 |
+
public function isAutoWriteTimestamp($auto)
|
| 389 |
+
{
|
| 390 |
+
$this->autoWriteTimestamp = $auto;
|
| 391 |
+
return $this;
|
| 392 |
+
}
|
| 393 |
+
|
| 394 |
+
/**
|
| 395 |
+
* 更新是否强制写入数据 而不做比较
|
| 396 |
+
* @access public
|
| 397 |
+
* @param bool $force
|
| 398 |
+
* @return $this
|
| 399 |
+
*/
|
| 400 |
+
public function force($force = true)
|
| 401 |
+
{
|
| 402 |
+
$this->force = $force;
|
| 403 |
+
return $this;
|
| 404 |
+
}
|
| 405 |
+
|
| 406 |
+
/**
|
| 407 |
+
* 修改器 设置数据对象值
|
| 408 |
+
* @access public
|
| 409 |
+
* @param string $name 属性名
|
| 410 |
+
* @param mixed $value 属性值
|
| 411 |
+
* @param array $data 数据
|
| 412 |
+
* @return $this
|
| 413 |
+
*/
|
| 414 |
+
public function setAttr($name, $value, $data = [])
|
| 415 |
+
{
|
| 416 |
+
if (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) {
|
| 417 |
+
// 自动写入的时间戳字段
|
| 418 |
+
$value = $this->autoWriteTimestamp($name);
|
| 419 |
+
} else {
|
| 420 |
+
// 检测修改器
|
| 421 |
+
$method = 'set' . Loader::parseName($name, 1) . 'Attr';
|
| 422 |
+
if (method_exists($this, $method)) {
|
| 423 |
+
$value = $this->$method($value, array_merge($this->data, $data), $this->relation);
|
| 424 |
+
} elseif (isset($this->type[$name])) {
|
| 425 |
+
// 类型转换
|
| 426 |
+
$value = $this->writeTransform($value, $this->type[$name]);
|
| 427 |
+
}
|
| 428 |
+
}
|
| 429 |
+
|
| 430 |
+
// 设置数据对象属性
|
| 431 |
+
$this->data[$name] = $value;
|
| 432 |
+
return $this;
|
| 433 |
+
}
|
| 434 |
+
|
| 435 |
+
/**
|
| 436 |
+
* 获取当前模型的关联模型数据
|
| 437 |
+
* @access public
|
| 438 |
+
* @param string $name 关联方法名
|
| 439 |
+
* @return mixed
|
| 440 |
+
*/
|
| 441 |
+
public function getRelation($name = null)
|
| 442 |
+
{
|
| 443 |
+
if (is_null($name)) {
|
| 444 |
+
return $this->relation;
|
| 445 |
+
} elseif (array_key_exists($name, $this->relation)) {
|
| 446 |
+
return $this->relation[$name];
|
| 447 |
+
} else {
|
| 448 |
+
return;
|
| 449 |
+
}
|
| 450 |
+
}
|
| 451 |
+
|
| 452 |
+
/**
|
| 453 |
+
* 设置关联数据对象值
|
| 454 |
+
* @access public
|
| 455 |
+
* @param string $name 属性名
|
| 456 |
+
* @param mixed $value 属性值
|
| 457 |
+
* @return $this
|
| 458 |
+
*/
|
| 459 |
+
public function setRelation($name, $value)
|
| 460 |
+
{
|
| 461 |
+
$this->relation[$name] = $value;
|
| 462 |
+
return $this;
|
| 463 |
+
}
|
| 464 |
+
|
| 465 |
+
/**
|
| 466 |
+
* 自动写入时间戳
|
| 467 |
+
* @access public
|
| 468 |
+
* @param string $name 时间戳字段
|
| 469 |
+
* @return mixed
|
| 470 |
+
*/
|
| 471 |
+
protected function autoWriteTimestamp($name)
|
| 472 |
+
{
|
| 473 |
+
if (isset($this->type[$name])) {
|
| 474 |
+
$type = $this->type[$name];
|
| 475 |
+
if (strpos($type, ':')) {
|
| 476 |
+
list($type, $param) = explode(':', $type, 2);
|
| 477 |
+
}
|
| 478 |
+
switch ($type) {
|
| 479 |
+
case 'datetime':
|
| 480 |
+
case 'date':
|
| 481 |
+
$format = !empty($param) ? $param : $this->dateFormat;
|
| 482 |
+
$value = $this->formatDateTime(time(), $format);
|
| 483 |
+
break;
|
| 484 |
+
case 'timestamp':
|
| 485 |
+
case 'integer':
|
| 486 |
+
default:
|
| 487 |
+
$value = time();
|
| 488 |
+
break;
|
| 489 |
+
}
|
| 490 |
+
} elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [
|
| 491 |
+
'datetime',
|
| 492 |
+
'date',
|
| 493 |
+
'timestamp',
|
| 494 |
+
])
|
| 495 |
+
) {
|
| 496 |
+
$value = $this->formatDateTime(time(), $this->dateFormat);
|
| 497 |
+
} else {
|
| 498 |
+
$value = $this->formatDateTime(time(), $this->dateFormat, true);
|
| 499 |
+
}
|
| 500 |
+
return $value;
|
| 501 |
+
}
|
| 502 |
+
|
| 503 |
+
/**
|
| 504 |
+
* 时间日期字段格式化处理
|
| 505 |
+
* @access public
|
| 506 |
+
* @param mixed $time 时间日期表达式
|
| 507 |
+
* @param mixed $format 日期格式
|
| 508 |
+
* @param bool $timestamp 是否进行时间戳转换
|
| 509 |
+
* @return mixed
|
| 510 |
+
*/
|
| 511 |
+
protected function formatDateTime($time, $format, $timestamp = false)
|
| 512 |
+
{
|
| 513 |
+
if (false !== strpos($format, '\\')) {
|
| 514 |
+
$time = new $format($time);
|
| 515 |
+
} elseif (!$timestamp && false !== $format) {
|
| 516 |
+
$time = date($format, $time);
|
| 517 |
+
}
|
| 518 |
+
return $time;
|
| 519 |
+
}
|
| 520 |
+
|
| 521 |
+
/**
|
| 522 |
+
* 数据写入 类型转换
|
| 523 |
+
* @access public
|
| 524 |
+
* @param mixed $value 值
|
| 525 |
+
* @param string|array $type 要��换的类型
|
| 526 |
+
* @return mixed
|
| 527 |
+
*/
|
| 528 |
+
protected function writeTransform($value, $type)
|
| 529 |
+
{
|
| 530 |
+
if (is_null($value)) {
|
| 531 |
+
return;
|
| 532 |
+
}
|
| 533 |
+
|
| 534 |
+
if (is_array($type)) {
|
| 535 |
+
list($type, $param) = $type;
|
| 536 |
+
} elseif (strpos($type, ':')) {
|
| 537 |
+
list($type, $param) = explode(':', $type, 2);
|
| 538 |
+
}
|
| 539 |
+
switch ($type) {
|
| 540 |
+
case 'integer':
|
| 541 |
+
$value = (int) $value;
|
| 542 |
+
break;
|
| 543 |
+
case 'float':
|
| 544 |
+
if (empty($param)) {
|
| 545 |
+
$value = (float) $value;
|
| 546 |
+
} else {
|
| 547 |
+
$value = (float) number_format($value, $param, '.', '');
|
| 548 |
+
}
|
| 549 |
+
break;
|
| 550 |
+
case 'boolean':
|
| 551 |
+
$value = (bool) $value;
|
| 552 |
+
break;
|
| 553 |
+
case 'timestamp':
|
| 554 |
+
if (!is_numeric($value)) {
|
| 555 |
+
$value = strtotime($value);
|
| 556 |
+
}
|
| 557 |
+
break;
|
| 558 |
+
case 'datetime':
|
| 559 |
+
$format = !empty($param) ? $param : $this->dateFormat;
|
| 560 |
+
$value = is_numeric($value) ? $value : strtotime($value);
|
| 561 |
+
$value = $this->formatDateTime($value, $format);
|
| 562 |
+
break;
|
| 563 |
+
case 'object':
|
| 564 |
+
if (is_object($value)) {
|
| 565 |
+
$value = json_encode($value, JSON_FORCE_OBJECT);
|
| 566 |
+
}
|
| 567 |
+
break;
|
| 568 |
+
case 'array':
|
| 569 |
+
$value = (array) $value;
|
| 570 |
+
case 'json':
|
| 571 |
+
$option = !empty($param) ? (int) $param : JSON_UNESCAPED_UNICODE;
|
| 572 |
+
$value = json_encode($value, $option);
|
| 573 |
+
break;
|
| 574 |
+
case 'serialize':
|
| 575 |
+
$value = serialize($value);
|
| 576 |
+
break;
|
| 577 |
+
|
| 578 |
+
}
|
| 579 |
+
return $value;
|
| 580 |
+
}
|
| 581 |
+
|
| 582 |
+
/**
|
| 583 |
+
* 获取器 获取数据对象的值
|
| 584 |
+
* @access public
|
| 585 |
+
* @param string $name 名称
|
| 586 |
+
* @return mixed
|
| 587 |
+
* @throws InvalidArgumentException
|
| 588 |
+
*/
|
| 589 |
+
public function getAttr($name)
|
| 590 |
+
{
|
| 591 |
+
try {
|
| 592 |
+
$notFound = false;
|
| 593 |
+
$value = $this->getData($name);
|
| 594 |
+
} catch (InvalidArgumentException $e) {
|
| 595 |
+
$notFound = true;
|
| 596 |
+
$value = null;
|
| 597 |
+
}
|
| 598 |
+
|
| 599 |
+
// 检测属性获取器
|
| 600 |
+
$method = 'get' . Loader::parseName($name, 1) . 'Attr';
|
| 601 |
+
if (method_exists($this, $method)) {
|
| 602 |
+
$value = $this->$method($value, $this->data, $this->relation);
|
| 603 |
+
} elseif (isset($this->type[$name])) {
|
| 604 |
+
// 类型转换
|
| 605 |
+
$value = $this->readTransform($value, $this->type[$name]);
|
| 606 |
+
} elseif (in_array($name, [$this->createTime, $this->updateTime])) {
|
| 607 |
+
if (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [
|
| 608 |
+
'datetime',
|
| 609 |
+
'date',
|
| 610 |
+
'timestamp',
|
| 611 |
+
])
|
| 612 |
+
) {
|
| 613 |
+
$value = $this->formatDateTime(strtotime($value), $this->dateFormat);
|
| 614 |
+
} else {
|
| 615 |
+
$value = $this->formatDateTime($value, $this->dateFormat);
|
| 616 |
+
}
|
| 617 |
+
} elseif ($notFound) {
|
| 618 |
+
$relation = Loader::parseName($name, 1, false);
|
| 619 |
+
if (method_exists($this, $relation)) {
|
| 620 |
+
$modelRelation = $this->$relation();
|
| 621 |
+
// 不存在该字段 获取关联数据
|
| 622 |
+
$value = $this->getRelationData($modelRelation);
|
| 623 |
+
// 保存关联对象值
|
| 624 |
+
$this->relation[$name] = $value;
|
| 625 |
+
} else {
|
| 626 |
+
throw new InvalidArgumentException('property not exists:' . $this->class . '->' . $name);
|
| 627 |
+
}
|
| 628 |
+
}
|
| 629 |
+
return $value;
|
| 630 |
+
}
|
| 631 |
+
|
| 632 |
+
/**
|
| 633 |
+
* 获取关联模型数据
|
| 634 |
+
* @access public
|
| 635 |
+
* @param Relation $modelRelation 模型关联对象
|
| 636 |
+
* @return mixed
|
| 637 |
+
* @throws BadMethodCallException
|
| 638 |
+
*/
|
| 639 |
+
protected function getRelationData(Relation $modelRelation)
|
| 640 |
+
{
|
| 641 |
+
if ($this->parent && !$modelRelation->isSelfRelation() && get_class($modelRelation->getModel()) == get_class($this->parent)) {
|
| 642 |
+
$value = $this->parent;
|
| 643 |
+
} else {
|
| 644 |
+
// 首先获取关联数据
|
| 645 |
+
if (method_exists($modelRelation, 'getRelation')) {
|
| 646 |
+
$value = $modelRelation->getRelation();
|
| 647 |
+
} else {
|
| 648 |
+
throw new BadMethodCallException('method not exists:' . get_class($modelRelation) . '-> getRelation');
|
| 649 |
+
}
|
| 650 |
+
}
|
| 651 |
+
return $value;
|
| 652 |
+
}
|
| 653 |
+
|
| 654 |
+
/**
|
| 655 |
+
* 数据读取 类型转换
|
| 656 |
+
* @access public
|
| 657 |
+
* @param mixed $value 值
|
| 658 |
+
* @param string|array $type 要转换的类型
|
| 659 |
+
* @return mixed
|
| 660 |
+
*/
|
| 661 |
+
protected function readTransform($value, $type)
|
| 662 |
+
{
|
| 663 |
+
if (is_null($value)) {
|
| 664 |
+
return;
|
| 665 |
+
}
|
| 666 |
+
|
| 667 |
+
if (is_array($type)) {
|
| 668 |
+
list($type, $param) = $type;
|
| 669 |
+
} elseif (strpos($type, ':')) {
|
| 670 |
+
list($type, $param) = explode(':', $type, 2);
|
| 671 |
+
}
|
| 672 |
+
switch ($type) {
|
| 673 |
+
case 'integer':
|
| 674 |
+
$value = (int) $value;
|
| 675 |
+
break;
|
| 676 |
+
case 'float':
|
| 677 |
+
if (empty($param)) {
|
| 678 |
+
$value = (float) $value;
|
| 679 |
+
} else {
|
| 680 |
+
$value = (float) number_format($value, $param, '.', '');
|
| 681 |
+
}
|
| 682 |
+
break;
|
| 683 |
+
case 'boolean':
|
| 684 |
+
$value = (bool) $value;
|
| 685 |
+
break;
|
| 686 |
+
case 'timestamp':
|
| 687 |
+
if (!is_null($value)) {
|
| 688 |
+
$format = !empty($param) ? $param : $this->dateFormat;
|
| 689 |
+
$value = $this->formatDateTime($value, $format);
|
| 690 |
+
}
|
| 691 |
+
break;
|
| 692 |
+
case 'datetime':
|
| 693 |
+
if (!is_null($value)) {
|
| 694 |
+
$format = !empty($param) ? $param : $this->dateFormat;
|
| 695 |
+
$value = $this->formatDateTime(strtotime($value), $format);
|
| 696 |
+
}
|
| 697 |
+
break;
|
| 698 |
+
case 'json':
|
| 699 |
+
$value = json_decode($value, true);
|
| 700 |
+
break;
|
| 701 |
+
case 'array':
|
| 702 |
+
$value = empty($value) ? [] : json_decode($value, true);
|
| 703 |
+
break;
|
| 704 |
+
case 'object':
|
| 705 |
+
$value = empty($value) ? new \stdClass() : json_decode($value);
|
| 706 |
+
break;
|
| 707 |
+
case 'serialize':
|
| 708 |
+
try {
|
| 709 |
+
$value = unserialize($value);
|
| 710 |
+
} catch (\Exception $e) {
|
| 711 |
+
$value = null;
|
| 712 |
+
}
|
| 713 |
+
break;
|
| 714 |
+
default:
|
| 715 |
+
if (false !== strpos($type, '\\')) {
|
| 716 |
+
// 对象类型
|
| 717 |
+
$value = new $type($value);
|
| 718 |
+
}
|
| 719 |
+
}
|
| 720 |
+
return $value;
|
| 721 |
+
}
|
| 722 |
+
|
| 723 |
+
/**
|
| 724 |
+
* 设置需要追加的输出属性
|
| 725 |
+
* @access public
|
| 726 |
+
* @param array $append 属性列表
|
| 727 |
+
* @param bool $override 是否覆盖
|
| 728 |
+
* @return $this
|
| 729 |
+
*/
|
| 730 |
+
public function append($append = [], $override = false)
|
| 731 |
+
{
|
| 732 |
+
$this->append = $override ? $append : array_merge($this->append, $append);
|
| 733 |
+
return $this;
|
| 734 |
+
}
|
| 735 |
+
|
| 736 |
+
/**
|
| 737 |
+
* 设置附加关联对象的属性
|
| 738 |
+
* @access public
|
| 739 |
+
* @param string $relation 关联方法
|
| 740 |
+
* @param string|array $append 追加属性名
|
| 741 |
+
* @return $this
|
| 742 |
+
* @throws Exception
|
| 743 |
+
*/
|
| 744 |
+
public function appendRelationAttr($relation, $append)
|
| 745 |
+
{
|
| 746 |
+
if (is_string($append)) {
|
| 747 |
+
$append = explode(',', $append);
|
| 748 |
+
}
|
| 749 |
+
|
| 750 |
+
$relation = Loader::parseName($relation, 1, false);
|
| 751 |
+
|
| 752 |
+
// 获取关联数据
|
| 753 |
+
if (isset($this->relation[$relation])) {
|
| 754 |
+
$model = $this->relation[$relation];
|
| 755 |
+
} else {
|
| 756 |
+
$model = $this->getRelationData($this->$relation());
|
| 757 |
+
}
|
| 758 |
+
|
| 759 |
+
if ($model instanceof Model) {
|
| 760 |
+
foreach ($append as $key => $attr) {
|
| 761 |
+
$key = is_numeric($key) ? $attr : $key;
|
| 762 |
+
if (isset($this->data[$key])) {
|
| 763 |
+
throw new Exception('bind attr has exists:' . $key);
|
| 764 |
+
} else {
|
| 765 |
+
$this->data[$key] = $model->getAttr($attr);
|
| 766 |
+
}
|
| 767 |
+
}
|
| 768 |
+
}
|
| 769 |
+
return $this;
|
| 770 |
+
}
|
| 771 |
+
|
| 772 |
+
/**
|
| 773 |
+
* 设置需要隐藏的输出属性
|
| 774 |
+
* @access public
|
| 775 |
+
* @param array $hidden 属性列表
|
| 776 |
+
* @param bool $override 是否覆盖
|
| 777 |
+
* @return $this
|
| 778 |
+
*/
|
| 779 |
+
public function hidden($hidden = [], $override = false)
|
| 780 |
+
{
|
| 781 |
+
$this->hidden = $override ? $hidden : array_merge($this->hidden, $hidden);
|
| 782 |
+
return $this;
|
| 783 |
+
}
|
| 784 |
+
|
| 785 |
+
/**
|
| 786 |
+
* 设置需要输出的属性
|
| 787 |
+
* @access public
|
| 788 |
+
* @param array $visible
|
| 789 |
+
* @param bool $override 是否覆盖
|
| 790 |
+
* @return $this
|
| 791 |
+
*/
|
| 792 |
+
public function visible($visible = [], $override = false)
|
| 793 |
+
{
|
| 794 |
+
$this->visible = $override ? $visible : array_merge($this->visible, $visible);
|
| 795 |
+
return $this;
|
| 796 |
+
}
|
| 797 |
+
|
| 798 |
+
/**
|
| 799 |
+
* 解析隐藏及显示属性
|
| 800 |
+
* @access protected
|
| 801 |
+
* @param array $attrs 属性
|
| 802 |
+
* @param array $result 结果集
|
| 803 |
+
* @param bool $visible
|
| 804 |
+
* @return array
|
| 805 |
+
*/
|
| 806 |
+
protected function parseAttr($attrs, &$result, $visible = true)
|
| 807 |
+
{
|
| 808 |
+
$array = [];
|
| 809 |
+
foreach ($attrs as $key => $val) {
|
| 810 |
+
if (is_array($val)) {
|
| 811 |
+
if ($visible) {
|
| 812 |
+
$array[] = $key;
|
| 813 |
+
}
|
| 814 |
+
$result[$key] = $val;
|
| 815 |
+
} elseif (strpos($val, '.')) {
|
| 816 |
+
list($key, $name) = explode('.', $val);
|
| 817 |
+
if ($visible) {
|
| 818 |
+
$array[] = $key;
|
| 819 |
+
}
|
| 820 |
+
$result[$key][] = $name;
|
| 821 |
+
} else {
|
| 822 |
+
$array[] = $val;
|
| 823 |
+
}
|
| 824 |
+
}
|
| 825 |
+
return $array;
|
| 826 |
+
}
|
| 827 |
+
|
| 828 |
+
/**
|
| 829 |
+
* 转换子模型对象
|
| 830 |
+
* @access protected
|
| 831 |
+
* @param Model|ModelCollection $model
|
| 832 |
+
* @param $visible
|
| 833 |
+
* @param $hidden
|
| 834 |
+
* @param $key
|
| 835 |
+
* @return array
|
| 836 |
+
*/
|
| 837 |
+
protected function subToArray($model, $visible, $hidden, $key)
|
| 838 |
+
{
|
| 839 |
+
// 关联模型对象
|
| 840 |
+
if (isset($visible[$key])) {
|
| 841 |
+
$model->visible($visible[$key]);
|
| 842 |
+
} elseif (isset($hidden[$key])) {
|
| 843 |
+
$model->hidden($hidden[$key]);
|
| 844 |
+
}
|
| 845 |
+
return $model->toArray();
|
| 846 |
+
}
|
| 847 |
+
|
| 848 |
+
/**
|
| 849 |
+
* 转换当前模型对象为数组
|
| 850 |
+
* @access public
|
| 851 |
+
* @return array
|
| 852 |
+
*/
|
| 853 |
+
public function toArray()
|
| 854 |
+
{
|
| 855 |
+
$item = [];
|
| 856 |
+
$visible = [];
|
| 857 |
+
$hidden = [];
|
| 858 |
+
|
| 859 |
+
$data = array_merge($this->data, $this->relation);
|
| 860 |
+
|
| 861 |
+
// 过滤属性
|
| 862 |
+
if (!empty($this->visible)) {
|
| 863 |
+
$array = $this->parseAttr($this->visible, $visible);
|
| 864 |
+
$data = array_intersect_key($data, array_flip($array));
|
| 865 |
+
} elseif (!empty($this->hidden)) {
|
| 866 |
+
$array = $this->parseAttr($this->hidden, $hidden, false);
|
| 867 |
+
$data = array_diff_key($data, array_flip($array));
|
| 868 |
+
}
|
| 869 |
+
|
| 870 |
+
foreach ($data as $key => $val) {
|
| 871 |
+
if ($val instanceof Model || $val instanceof ModelCollection) {
|
| 872 |
+
// 关联模型对象
|
| 873 |
+
$item[$key] = $this->subToArray($val, $visible, $hidden, $key);
|
| 874 |
+
} elseif (is_array($val) && reset($val) instanceof Model) {
|
| 875 |
+
// 关联模型数据集
|
| 876 |
+
$arr = [];
|
| 877 |
+
foreach ($val as $k => $value) {
|
| 878 |
+
$arr[$k] = $this->subToArray($value, $visible, $hidden, $key);
|
| 879 |
+
}
|
| 880 |
+
$item[$key] = $arr;
|
| 881 |
+
} else {
|
| 882 |
+
// 模型属性
|
| 883 |
+
$item[$key] = $this->getAttr($key);
|
| 884 |
+
}
|
| 885 |
+
}
|
| 886 |
+
// 追加属性(必须定义获取器)
|
| 887 |
+
if (!empty($this->append)) {
|
| 888 |
+
foreach ($this->append as $key => $name) {
|
| 889 |
+
if (is_array($name)) {
|
| 890 |
+
// 追加关联对象属性
|
| 891 |
+
$relation = $this->getAttr($key);
|
| 892 |
+
$item[$key] = $relation->append($name)->toArray();
|
| 893 |
+
} elseif (strpos($name, '.')) {
|
| 894 |
+
list($key, $attr) = explode('.', $name);
|
| 895 |
+
// 追加关联对象属性
|
| 896 |
+
$relation = $this->getAttr($key);
|
| 897 |
+
$item[$key] = $relation->append([$attr])->toArray();
|
| 898 |
+
} else {
|
| 899 |
+
$relation = Loader::parseName($name, 1, false);
|
| 900 |
+
if (method_exists($this, $relation)) {
|
| 901 |
+
$modelRelation = $this->$relation();
|
| 902 |
+
$value = $this->getRelationData($modelRelation);
|
| 903 |
+
|
| 904 |
+
if (method_exists($modelRelation, 'getBindAttr')) {
|
| 905 |
+
$bindAttr = $modelRelation->getBindAttr();
|
| 906 |
+
if ($bindAttr) {
|
| 907 |
+
foreach ($bindAttr as $key => $attr) {
|
| 908 |
+
$key = is_numeric($key) ? $attr : $key;
|
| 909 |
+
if (isset($this->data[$key])) {
|
| 910 |
+
throw new Exception('bind attr has exists:' . $key);
|
| 911 |
+
} else {
|
| 912 |
+
$item[$key] = $value ? $value->getAttr($attr) : null;
|
| 913 |
+
}
|
| 914 |
+
}
|
| 915 |
+
continue;
|
| 916 |
+
}
|
| 917 |
+
}
|
| 918 |
+
$item[$name] = $value;
|
| 919 |
+
} else {
|
| 920 |
+
$item[$name] = $this->getAttr($name);
|
| 921 |
+
}
|
| 922 |
+
}
|
| 923 |
+
}
|
| 924 |
+
}
|
| 925 |
+
return !empty($item) ? $item : [];
|
| 926 |
+
}
|
| 927 |
+
|
| 928 |
+
/**
|
| 929 |
+
* 转换当前模型对象为JSON字符串
|
| 930 |
+
* @access public
|
| 931 |
+
* @param integer $options json参数
|
| 932 |
+
* @return string
|
| 933 |
+
*/
|
| 934 |
+
public function toJson($options = JSON_UNESCAPED_UNICODE)
|
| 935 |
+
{
|
| 936 |
+
return json_encode($this->toArray(), $options);
|
| 937 |
+
}
|
| 938 |
+
|
| 939 |
+
/**
|
| 940 |
+
* 移除当前模型的关联属性
|
| 941 |
+
* @access public
|
| 942 |
+
* @return $this
|
| 943 |
+
*/
|
| 944 |
+
public function removeRelation()
|
| 945 |
+
{
|
| 946 |
+
$this->relation = [];
|
| 947 |
+
return $this;
|
| 948 |
+
}
|
| 949 |
+
|
| 950 |
+
/**
|
| 951 |
+
* 转换当前模型数据集为数据集对象
|
| 952 |
+
* @access public
|
| 953 |
+
* @param array|\think\Collection $collection 数据集
|
| 954 |
+
* @return \think\Collection
|
| 955 |
+
*/
|
| 956 |
+
public function toCollection($collection)
|
| 957 |
+
{
|
| 958 |
+
if ($this->resultSetType) {
|
| 959 |
+
if ('collection' == $this->resultSetType) {
|
| 960 |
+
$collection = new ModelCollection($collection);
|
| 961 |
+
} elseif (false !== strpos($this->resultSetType, '\\')) {
|
| 962 |
+
$class = $this->resultSetType;
|
| 963 |
+
$collection = new $class($collection);
|
| 964 |
+
}
|
| 965 |
+
}
|
| 966 |
+
return $collection;
|
| 967 |
+
}
|
| 968 |
+
|
| 969 |
+
/**
|
| 970 |
+
* 关联数据一起更新
|
| 971 |
+
* @access public
|
| 972 |
+
* @param mixed $relation 关联
|
| 973 |
+
* @return $this
|
| 974 |
+
*/
|
| 975 |
+
public function together($relation)
|
| 976 |
+
{
|
| 977 |
+
if (is_string($relation)) {
|
| 978 |
+
$relation = explode(',', $relation);
|
| 979 |
+
}
|
| 980 |
+
$this->relationWrite = $relation;
|
| 981 |
+
return $this;
|
| 982 |
+
}
|
| 983 |
+
|
| 984 |
+
/**
|
| 985 |
+
* 获取模型对象的主键
|
| 986 |
+
* @access public
|
| 987 |
+
* @param string $name 模型名
|
| 988 |
+
* @return mixed
|
| 989 |
+
*/
|
| 990 |
+
public function getPk($name = '')
|
| 991 |
+
{
|
| 992 |
+
if (!empty($name)) {
|
| 993 |
+
$table = $this->getQuery()->getTable($name);
|
| 994 |
+
return $this->getQuery()->getPk($table);
|
| 995 |
+
} elseif (empty($this->pk)) {
|
| 996 |
+
$this->pk = $this->getQuery()->getPk();
|
| 997 |
+
}
|
| 998 |
+
return $this->pk;
|
| 999 |
+
}
|
| 1000 |
+
|
| 1001 |
+
/**
|
| 1002 |
+
* 判断一个字段名是否为主键字段
|
| 1003 |
+
* @access public
|
| 1004 |
+
* @param string $key 名称
|
| 1005 |
+
* @return bool
|
| 1006 |
+
*/
|
| 1007 |
+
protected function isPk($key)
|
| 1008 |
+
{
|
| 1009 |
+
$pk = $this->getPk();
|
| 1010 |
+
if (is_string($pk) && $pk == $key) {
|
| 1011 |
+
return true;
|
| 1012 |
+
} elseif (is_array($pk) && in_array($key, $pk)) {
|
| 1013 |
+
return true;
|
| 1014 |
+
}
|
| 1015 |
+
return false;
|
| 1016 |
+
}
|
| 1017 |
+
|
| 1018 |
+
/**
|
| 1019 |
+
* 新增数据是否使用Replace
|
| 1020 |
+
* @access public
|
| 1021 |
+
* @param bool $replace
|
| 1022 |
+
* @return $this
|
| 1023 |
+
*/
|
| 1024 |
+
public function replace($replace = true)
|
| 1025 |
+
{
|
| 1026 |
+
$this->replace = $replace;
|
| 1027 |
+
return $this;
|
| 1028 |
+
}
|
| 1029 |
+
|
| 1030 |
+
/**
|
| 1031 |
+
* 保存当前数据对象
|
| 1032 |
+
* @access public
|
| 1033 |
+
* @param array $data 数据
|
| 1034 |
+
* @param array $where 更新条件
|
| 1035 |
+
* @param string $sequence 自增序列名
|
| 1036 |
+
* @return integer|false
|
| 1037 |
+
*/
|
| 1038 |
+
public function save($data = [], $where = [], $sequence = null)
|
| 1039 |
+
{
|
| 1040 |
+
if (is_string($data)) {
|
| 1041 |
+
$sequence = $data;
|
| 1042 |
+
$data = [];
|
| 1043 |
+
}
|
| 1044 |
+
|
| 1045 |
+
// 数据自动验证
|
| 1046 |
+
if (!empty($data)) {
|
| 1047 |
+
if (!$this->validateData($data)) {
|
| 1048 |
+
return false;
|
| 1049 |
+
}
|
| 1050 |
+
|
| 1051 |
+
// 数据对象赋值
|
| 1052 |
+
foreach ($data as $key => $value) {
|
| 1053 |
+
$this->setAttr($key, $value, $data);
|
| 1054 |
+
}
|
| 1055 |
+
}
|
| 1056 |
+
|
| 1057 |
+
if (!empty($where)) {
|
| 1058 |
+
$this->isUpdate = true;
|
| 1059 |
+
$this->updateWhere = $where;
|
| 1060 |
+
}
|
| 1061 |
+
|
| 1062 |
+
// 自动关联写入
|
| 1063 |
+
if (!empty($this->relationWrite)) {
|
| 1064 |
+
$relation = [];
|
| 1065 |
+
foreach ($this->relationWrite as $key => $name) {
|
| 1066 |
+
if (is_array($name)) {
|
| 1067 |
+
if (key($name) === 0) {
|
| 1068 |
+
$relation[$key] = [];
|
| 1069 |
+
foreach ($name as $val) {
|
| 1070 |
+
if (isset($this->data[$val])) {
|
| 1071 |
+
$relation[$key][$val] = $this->data[$val];
|
| 1072 |
+
unset($this->data[$val]);
|
| 1073 |
+
}
|
| 1074 |
+
}
|
| 1075 |
+
} else {
|
| 1076 |
+
$relation[$key] = $name;
|
| 1077 |
+
}
|
| 1078 |
+
} elseif (isset($this->relation[$name])) {
|
| 1079 |
+
$relation[$name] = $this->relation[$name];
|
| 1080 |
+
} elseif (isset($this->data[$name])) {
|
| 1081 |
+
$relation[$name] = $this->data[$name];
|
| 1082 |
+
unset($this->data[$name]);
|
| 1083 |
+
}
|
| 1084 |
+
}
|
| 1085 |
+
}
|
| 1086 |
+
|
| 1087 |
+
// 数据自动完成
|
| 1088 |
+
$this->autoCompleteData($this->auto);
|
| 1089 |
+
|
| 1090 |
+
// 事件回调
|
| 1091 |
+
if (false === $this->trigger('before_write', $this)) {
|
| 1092 |
+
return false;
|
| 1093 |
+
}
|
| 1094 |
+
$pk = $this->getPk();
|
| 1095 |
+
if ($this->isUpdate) {
|
| 1096 |
+
// 自动更新
|
| 1097 |
+
$this->autoCompleteData($this->update);
|
| 1098 |
+
|
| 1099 |
+
// 事件回调
|
| 1100 |
+
if (false === $this->trigger('before_update', $this)) {
|
| 1101 |
+
return false;
|
| 1102 |
+
}
|
| 1103 |
+
|
| 1104 |
+
// 获取有更新的数据
|
| 1105 |
+
$data = $this->getChangedData();
|
| 1106 |
+
|
| 1107 |
+
if (empty($data) || (count($data) == 1 && is_string($pk) && isset($data[$pk]))) {
|
| 1108 |
+
// 关联更新
|
| 1109 |
+
if (isset($relation)) {
|
| 1110 |
+
$this->autoRelationUpdate($relation);
|
| 1111 |
+
}
|
| 1112 |
+
return 0;
|
| 1113 |
+
} elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) {
|
| 1114 |
+
// 自动写入更新时间
|
| 1115 |
+
$data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime);
|
| 1116 |
+
$this->data[$this->updateTime] = $data[$this->updateTime];
|
| 1117 |
+
}
|
| 1118 |
+
|
| 1119 |
+
if (empty($where) && !empty($this->updateWhere)) {
|
| 1120 |
+
$where = $this->updateWhere;
|
| 1121 |
+
}
|
| 1122 |
+
|
| 1123 |
+
// 保留主键数据
|
| 1124 |
+
foreach ($this->data as $key => $val) {
|
| 1125 |
+
if ($this->isPk($key)) {
|
| 1126 |
+
$data[$key] = $val;
|
| 1127 |
+
}
|
| 1128 |
+
}
|
| 1129 |
+
|
| 1130 |
+
$array = [];
|
| 1131 |
+
|
| 1132 |
+
foreach ((array) $pk as $key) {
|
| 1133 |
+
if (isset($data[$key])) {
|
| 1134 |
+
$array[$key] = $data[$key];
|
| 1135 |
+
unset($data[$key]);
|
| 1136 |
+
}
|
| 1137 |
+
}
|
| 1138 |
+
|
| 1139 |
+
if (!empty($array)) {
|
| 1140 |
+
$where = $array;
|
| 1141 |
+
}
|
| 1142 |
+
|
| 1143 |
+
// 检测字段
|
| 1144 |
+
$allowFields = $this->checkAllowField(array_merge($this->auto, $this->update));
|
| 1145 |
+
|
| 1146 |
+
// 模型更新
|
| 1147 |
+
if (!empty($allowFields)) {
|
| 1148 |
+
$result = $this->getQuery()->where($where)->strict(false)->field($allowFields)->update($data);
|
| 1149 |
+
} else {
|
| 1150 |
+
$result = $this->getQuery()->where($where)->update($data);
|
| 1151 |
+
}
|
| 1152 |
+
|
| 1153 |
+
// 关联更新
|
| 1154 |
+
if (isset($relation)) {
|
| 1155 |
+
$this->autoRelationUpdate($relation);
|
| 1156 |
+
}
|
| 1157 |
+
|
| 1158 |
+
// 更新回调
|
| 1159 |
+
$this->trigger('after_update', $this);
|
| 1160 |
+
|
| 1161 |
+
} else {
|
| 1162 |
+
// 自动写入
|
| 1163 |
+
$this->autoCompleteData($this->insert);
|
| 1164 |
+
|
| 1165 |
+
// 自动写入创建时间和更新时间
|
| 1166 |
+
if ($this->autoWriteTimestamp) {
|
| 1167 |
+
if ($this->createTime && !isset($this->data[$this->createTime])) {
|
| 1168 |
+
$this->data[$this->createTime] = $this->autoWriteTimestamp($this->createTime);
|
| 1169 |
+
}
|
| 1170 |
+
if ($this->updateTime && !isset($this->data[$this->updateTime])) {
|
| 1171 |
+
$this->data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime);
|
| 1172 |
+
}
|
| 1173 |
+
}
|
| 1174 |
+
|
| 1175 |
+
if (false === $this->trigger('before_insert', $this)) {
|
| 1176 |
+
return false;
|
| 1177 |
+
}
|
| 1178 |
+
|
| 1179 |
+
// 检测字段
|
| 1180 |
+
$allowFields = $this->checkAllowField(array_merge($this->auto, $this->insert));
|
| 1181 |
+
if (!empty($allowFields)) {
|
| 1182 |
+
$result = $this->getQuery()->strict(false)->field($allowFields)->insert($this->data, $this->replace, false, $sequence);
|
| 1183 |
+
} else {
|
| 1184 |
+
$result = $this->getQuery()->insert($this->data, $this->replace, false, $sequence);
|
| 1185 |
+
}
|
| 1186 |
+
|
| 1187 |
+
// 获取自动增长主键
|
| 1188 |
+
if ($result && $insertId = $this->getQuery()->getLastInsID($sequence)) {
|
| 1189 |
+
foreach ((array) $pk as $key) {
|
| 1190 |
+
if (!isset($this->data[$key]) || '' == $this->data[$key]) {
|
| 1191 |
+
$this->data[$key] = $insertId;
|
| 1192 |
+
}
|
| 1193 |
+
}
|
| 1194 |
+
}
|
| 1195 |
+
|
| 1196 |
+
// 关联写入
|
| 1197 |
+
if (isset($relation)) {
|
| 1198 |
+
foreach ($relation as $name => $val) {
|
| 1199 |
+
$method = Loader::parseName($name, 1, false);
|
| 1200 |
+
$this->$method()->save($val);
|
| 1201 |
+
}
|
| 1202 |
+
}
|
| 1203 |
+
|
| 1204 |
+
// 标记为更新
|
| 1205 |
+
$this->isUpdate = true;
|
| 1206 |
+
|
| 1207 |
+
// 新增回调
|
| 1208 |
+
$this->trigger('after_insert', $this);
|
| 1209 |
+
}
|
| 1210 |
+
// 写入回调
|
| 1211 |
+
$this->trigger('after_write', $this);
|
| 1212 |
+
|
| 1213 |
+
// 重新记录原始数据
|
| 1214 |
+
$this->origin = $this->data;
|
| 1215 |
+
|
| 1216 |
+
return $result;
|
| 1217 |
+
}
|
| 1218 |
+
|
| 1219 |
+
protected function checkAllowField($auto = [])
|
| 1220 |
+
{
|
| 1221 |
+
if (true === $this->field) {
|
| 1222 |
+
$this->field = $this->getQuery()->getTableInfo('', 'fields');
|
| 1223 |
+
$field = $this->field;
|
| 1224 |
+
} elseif (!empty($this->field)) {
|
| 1225 |
+
$field = array_merge($this->field, $auto);
|
| 1226 |
+
if ($this->autoWriteTimestamp) {
|
| 1227 |
+
array_push($field, $this->createTime, $this->updateTime);
|
| 1228 |
+
}
|
| 1229 |
+
} elseif (!empty($this->except)) {
|
| 1230 |
+
$fields = $this->getQuery()->getTableInfo('', 'fields');
|
| 1231 |
+
$field = array_diff($fields, (array) $this->except);
|
| 1232 |
+
$this->field = $field;
|
| 1233 |
+
} else {
|
| 1234 |
+
$field = [];
|
| 1235 |
+
}
|
| 1236 |
+
|
| 1237 |
+
if ($this->disuse) {
|
| 1238 |
+
// 废弃字段
|
| 1239 |
+
$field = array_diff($field, (array) $this->disuse);
|
| 1240 |
+
}
|
| 1241 |
+
return $field;
|
| 1242 |
+
}
|
| 1243 |
+
|
| 1244 |
+
protected function autoRelationUpdate($relation)
|
| 1245 |
+
{
|
| 1246 |
+
foreach ($relation as $name => $val) {
|
| 1247 |
+
if ($val instanceof Model) {
|
| 1248 |
+
$val->save();
|
| 1249 |
+
} else {
|
| 1250 |
+
unset($this->data[$name]);
|
| 1251 |
+
$model = $this->getAttr($name);
|
| 1252 |
+
if ($model instanceof Model) {
|
| 1253 |
+
$model->save($val);
|
| 1254 |
+
}
|
| 1255 |
+
}
|
| 1256 |
+
}
|
| 1257 |
+
}
|
| 1258 |
+
|
| 1259 |
+
/**
|
| 1260 |
+
* 获取变化的数据 并排除只读数据
|
| 1261 |
+
* @access public
|
| 1262 |
+
* @return array
|
| 1263 |
+
*/
|
| 1264 |
+
public function getChangedData()
|
| 1265 |
+
{
|
| 1266 |
+
if ($this->force) {
|
| 1267 |
+
$data = $this->data;
|
| 1268 |
+
} else {
|
| 1269 |
+
$data = array_udiff_assoc($this->data, $this->origin, function ($a, $b) {
|
| 1270 |
+
if ((empty($a) || empty($b)) && $a !== $b) {
|
| 1271 |
+
return 1;
|
| 1272 |
+
}
|
| 1273 |
+
return is_object($a) || $a != $b ? 1 : 0;
|
| 1274 |
+
});
|
| 1275 |
+
}
|
| 1276 |
+
|
| 1277 |
+
if (!empty($this->readonly)) {
|
| 1278 |
+
// 只读字段不允许更新
|
| 1279 |
+
foreach ($this->readonly as $key => $field) {
|
| 1280 |
+
if (isset($data[$field])) {
|
| 1281 |
+
unset($data[$field]);
|
| 1282 |
+
}
|
| 1283 |
+
}
|
| 1284 |
+
}
|
| 1285 |
+
|
| 1286 |
+
return $data;
|
| 1287 |
+
}
|
| 1288 |
+
|
| 1289 |
+
/**
|
| 1290 |
+
* 字段值(延迟)增长
|
| 1291 |
+
* @access public
|
| 1292 |
+
* @param string $field 字段名
|
| 1293 |
+
* @param integer $step 增长值
|
| 1294 |
+
* @param integer $lazyTime 延时时间(s)
|
| 1295 |
+
* @return integer|true
|
| 1296 |
+
* @throws Exception
|
| 1297 |
+
*/
|
| 1298 |
+
public function setInc($field, $step = 1, $lazyTime = 0)
|
| 1299 |
+
{
|
| 1300 |
+
// 更新条件
|
| 1301 |
+
$where = $this->getWhere();
|
| 1302 |
+
|
| 1303 |
+
$result = $this->getQuery()->where($where)->setInc($field, $step, $lazyTime);
|
| 1304 |
+
if (true !== $result) {
|
| 1305 |
+
$this->data[$field] += $step;
|
| 1306 |
+
}
|
| 1307 |
+
|
| 1308 |
+
return $result;
|
| 1309 |
+
}
|
| 1310 |
+
|
| 1311 |
+
/**
|
| 1312 |
+
* 字段值(延迟)增长
|
| 1313 |
+
* @access public
|
| 1314 |
+
* @param string $field 字段名
|
| 1315 |
+
* @param integer $step 增长值
|
| 1316 |
+
* @param integer $lazyTime 延时时间(s)
|
| 1317 |
+
* @return integer|true
|
| 1318 |
+
* @throws Exception
|
| 1319 |
+
*/
|
| 1320 |
+
public function setDec($field, $step = 1, $lazyTime = 0)
|
| 1321 |
+
{
|
| 1322 |
+
// 更新条件
|
| 1323 |
+
$where = $this->getWhere();
|
| 1324 |
+
$result = $this->getQuery()->where($where)->setDec($field, $step, $lazyTime);
|
| 1325 |
+
if (true !== $result) {
|
| 1326 |
+
$this->data[$field] -= $step;
|
| 1327 |
+
}
|
| 1328 |
+
|
| 1329 |
+
return $result;
|
| 1330 |
+
}
|
| 1331 |
+
|
| 1332 |
+
/**
|
| 1333 |
+
* 获取更新条件
|
| 1334 |
+
* @access protected
|
| 1335 |
+
* @return mixed
|
| 1336 |
+
*/
|
| 1337 |
+
protected function getWhere()
|
| 1338 |
+
{
|
| 1339 |
+
// 删除条件
|
| 1340 |
+
$pk = $this->getPk();
|
| 1341 |
+
|
| 1342 |
+
if (is_string($pk) && isset($this->data[$pk])) {
|
| 1343 |
+
$where = [$pk => $this->data[$pk]];
|
| 1344 |
+
} elseif (!empty($this->updateWhere)) {
|
| 1345 |
+
$where = $this->updateWhere;
|
| 1346 |
+
} else {
|
| 1347 |
+
$where = null;
|
| 1348 |
+
}
|
| 1349 |
+
return $where;
|
| 1350 |
+
}
|
| 1351 |
+
|
| 1352 |
+
/**
|
| 1353 |
+
* 保存多个数据到当前数据对象
|
| 1354 |
+
* @access public
|
| 1355 |
+
* @param array $dataSet 数据
|
| 1356 |
+
* @param boolean $replace 是否自动识别更新和写入
|
| 1357 |
+
* @return array|false
|
| 1358 |
+
* @throws \Exception
|
| 1359 |
+
*/
|
| 1360 |
+
public function saveAll($dataSet, $replace = true)
|
| 1361 |
+
{
|
| 1362 |
+
if ($this->validate) {
|
| 1363 |
+
// 数据批量验证
|
| 1364 |
+
$validate = $this->validate;
|
| 1365 |
+
foreach ($dataSet as $data) {
|
| 1366 |
+
if (!$this->validateData($data, $validate)) {
|
| 1367 |
+
return false;
|
| 1368 |
+
}
|
| 1369 |
+
}
|
| 1370 |
+
}
|
| 1371 |
+
|
| 1372 |
+
$result = [];
|
| 1373 |
+
$db = $this->getQuery();
|
| 1374 |
+
$db->startTrans();
|
| 1375 |
+
try {
|
| 1376 |
+
$pk = $this->getPk();
|
| 1377 |
+
if (is_string($pk) && $replace) {
|
| 1378 |
+
$auto = true;
|
| 1379 |
+
}
|
| 1380 |
+
foreach ($dataSet as $key => $data) {
|
| 1381 |
+
if ($this->isUpdate || (!empty($auto) && isset($data[$pk]))) {
|
| 1382 |
+
$result[$key] = self::update($data, [], $this->field);
|
| 1383 |
+
} else {
|
| 1384 |
+
$result[$key] = self::create($data, $this->field);
|
| 1385 |
+
}
|
| 1386 |
+
}
|
| 1387 |
+
$db->commit();
|
| 1388 |
+
return $this->toCollection($result);
|
| 1389 |
+
} catch (\Exception $e) {
|
| 1390 |
+
$db->rollback();
|
| 1391 |
+
throw $e;
|
| 1392 |
+
}
|
| 1393 |
+
}
|
| 1394 |
+
|
| 1395 |
+
/**
|
| 1396 |
+
* 设置允许写入的字段
|
| 1397 |
+
* @access public
|
| 1398 |
+
* @param string|array $field 允许写入的字段 如果为true只允许写入数据表字段
|
| 1399 |
+
* @return $this
|
| 1400 |
+
*/
|
| 1401 |
+
public function allowField($field)
|
| 1402 |
+
{
|
| 1403 |
+
if (is_string($field)) {
|
| 1404 |
+
$field = explode(',', $field);
|
| 1405 |
+
}
|
| 1406 |
+
$this->field = $field;
|
| 1407 |
+
return $this;
|
| 1408 |
+
}
|
| 1409 |
+
|
| 1410 |
+
/**
|
| 1411 |
+
* 设置排除写入的字段
|
| 1412 |
+
* @access public
|
| 1413 |
+
* @param string|array $field 排除允许写入的字段
|
| 1414 |
+
* @return $this
|
| 1415 |
+
*/
|
| 1416 |
+
public function except($field)
|
| 1417 |
+
{
|
| 1418 |
+
if (is_string($field)) {
|
| 1419 |
+
$field = explode(',', $field);
|
| 1420 |
+
}
|
| 1421 |
+
$this->except = $field;
|
| 1422 |
+
return $this;
|
| 1423 |
+
}
|
| 1424 |
+
|
| 1425 |
+
/**
|
| 1426 |
+
* 设置只读字段
|
| 1427 |
+
* @access public
|
| 1428 |
+
* @param mixed $field 只读字段
|
| 1429 |
+
* @return $this
|
| 1430 |
+
*/
|
| 1431 |
+
public function readonly($field)
|
| 1432 |
+
{
|
| 1433 |
+
if (is_string($field)) {
|
| 1434 |
+
$field = explode(',', $field);
|
| 1435 |
+
}
|
| 1436 |
+
$this->readonly = $field;
|
| 1437 |
+
return $this;
|
| 1438 |
+
}
|
| 1439 |
+
|
| 1440 |
+
/**
|
| 1441 |
+
* 是否为更新数据
|
| 1442 |
+
* @access public
|
| 1443 |
+
* @param bool $update
|
| 1444 |
+
* @param mixed $where
|
| 1445 |
+
* @return $this
|
| 1446 |
+
*/
|
| 1447 |
+
public function isUpdate($update = true, $where = null)
|
| 1448 |
+
{
|
| 1449 |
+
$this->isUpdate = $update;
|
| 1450 |
+
if (!empty($where)) {
|
| 1451 |
+
$this->updateWhere = $where;
|
| 1452 |
+
}
|
| 1453 |
+
return $this;
|
| 1454 |
+
}
|
| 1455 |
+
|
| 1456 |
+
/**
|
| 1457 |
+
* 数据自动完成
|
| 1458 |
+
* @access public
|
| 1459 |
+
* @param array $auto 要自动更新的字段列表
|
| 1460 |
+
* @return void
|
| 1461 |
+
*/
|
| 1462 |
+
protected function autoCompleteData($auto = [])
|
| 1463 |
+
{
|
| 1464 |
+
foreach ($auto as $field => $value) {
|
| 1465 |
+
if (is_integer($field)) {
|
| 1466 |
+
$field = $value;
|
| 1467 |
+
$value = null;
|
| 1468 |
+
}
|
| 1469 |
+
|
| 1470 |
+
if (!isset($this->data[$field])) {
|
| 1471 |
+
$default = null;
|
| 1472 |
+
} else {
|
| 1473 |
+
$default = $this->data[$field];
|
| 1474 |
+
}
|
| 1475 |
+
|
| 1476 |
+
$this->setAttr($field, !is_null($value) ? $value : $default);
|
| 1477 |
+
}
|
| 1478 |
+
}
|
| 1479 |
+
|
| 1480 |
+
/**
|
| 1481 |
+
* 删除当前的记录
|
| 1482 |
+
* @access public
|
| 1483 |
+
* @return integer
|
| 1484 |
+
*/
|
| 1485 |
+
public function delete()
|
| 1486 |
+
{
|
| 1487 |
+
if (false === $this->trigger('before_delete', $this)) {
|
| 1488 |
+
return false;
|
| 1489 |
+
}
|
| 1490 |
+
|
| 1491 |
+
// 删除条件
|
| 1492 |
+
$where = $this->getWhere();
|
| 1493 |
+
|
| 1494 |
+
// 删除当前模型数据
|
| 1495 |
+
$result = $this->getQuery()->where($where)->delete();
|
| 1496 |
+
|
| 1497 |
+
// 关联删除
|
| 1498 |
+
if (!empty($this->relationWrite)) {
|
| 1499 |
+
foreach ($this->relationWrite as $key => $name) {
|
| 1500 |
+
$name = is_numeric($key) ? $name : $key;
|
| 1501 |
+
$model = $this->getAttr($name);
|
| 1502 |
+
if ($model instanceof Model) {
|
| 1503 |
+
$model->delete();
|
| 1504 |
+
}
|
| 1505 |
+
}
|
| 1506 |
+
}
|
| 1507 |
+
|
| 1508 |
+
$this->trigger('after_delete', $this);
|
| 1509 |
+
// 清空原始数据
|
| 1510 |
+
$this->origin = [];
|
| 1511 |
+
|
| 1512 |
+
return $result;
|
| 1513 |
+
}
|
| 1514 |
+
|
| 1515 |
+
/**
|
| 1516 |
+
* 设置自动完成的字段( 规则通过修改器定义)
|
| 1517 |
+
* @access public
|
| 1518 |
+
* @param array $fields 需要自动完成的字段
|
| 1519 |
+
* @return $this
|
| 1520 |
+
*/
|
| 1521 |
+
public function auto($fields)
|
| 1522 |
+
{
|
| 1523 |
+
$this->auto = $fields;
|
| 1524 |
+
return $this;
|
| 1525 |
+
}
|
| 1526 |
+
|
| 1527 |
+
/**
|
| 1528 |
+
* 设置字段验证
|
| 1529 |
+
* @access public
|
| 1530 |
+
* @param array|string|bool $rule 验证规则 true表示自动读取验证器类
|
| 1531 |
+
* @param array $msg 提示信息
|
| 1532 |
+
* @param bool $batch 批量验证
|
| 1533 |
+
* @return $this
|
| 1534 |
+
*/
|
| 1535 |
+
public function validate($rule = true, $msg = [], $batch = false)
|
| 1536 |
+
{
|
| 1537 |
+
if (is_array($rule)) {
|
| 1538 |
+
$this->validate = [
|
| 1539 |
+
'rule' => $rule,
|
| 1540 |
+
'msg' => $msg,
|
| 1541 |
+
];
|
| 1542 |
+
} else {
|
| 1543 |
+
$this->validate = true === $rule ? $this->name : $rule;
|
| 1544 |
+
}
|
| 1545 |
+
$this->batchValidate = $batch;
|
| 1546 |
+
return $this;
|
| 1547 |
+
}
|
| 1548 |
+
|
| 1549 |
+
/**
|
| 1550 |
+
* 设置验证失败后是否抛出异常
|
| 1551 |
+
* @access public
|
| 1552 |
+
* @param bool $fail 是否抛出异常
|
| 1553 |
+
* @return $this
|
| 1554 |
+
*/
|
| 1555 |
+
public function validateFailException($fail = true)
|
| 1556 |
+
{
|
| 1557 |
+
$this->failException = $fail;
|
| 1558 |
+
return $this;
|
| 1559 |
+
}
|
| 1560 |
+
|
| 1561 |
+
/**
|
| 1562 |
+
* 自动验证数据
|
| 1563 |
+
* @access protected
|
| 1564 |
+
* @param array $data 验证数据
|
| 1565 |
+
* @param mixed $rule 验证规则
|
| 1566 |
+
* @param bool $batch 批量验证
|
| 1567 |
+
* @return bool
|
| 1568 |
+
*/
|
| 1569 |
+
protected function validateData($data, $rule = null, $batch = null)
|
| 1570 |
+
{
|
| 1571 |
+
$info = is_null($rule) ? $this->validate : $rule;
|
| 1572 |
+
|
| 1573 |
+
if (!empty($info)) {
|
| 1574 |
+
if (is_array($info)) {
|
| 1575 |
+
$validate = Loader::validate();
|
| 1576 |
+
$validate->rule($info['rule']);
|
| 1577 |
+
$validate->message($info['msg']);
|
| 1578 |
+
} else {
|
| 1579 |
+
$name = is_string($info) ? $info : $this->name;
|
| 1580 |
+
if (strpos($name, '.')) {
|
| 1581 |
+
list($name, $scene) = explode('.', $name);
|
| 1582 |
+
}
|
| 1583 |
+
$validate = Loader::validate($name);
|
| 1584 |
+
if (!empty($scene)) {
|
| 1585 |
+
$validate->scene($scene);
|
| 1586 |
+
}
|
| 1587 |
+
}
|
| 1588 |
+
$batch = is_null($batch) ? $this->batchValidate : $batch;
|
| 1589 |
+
|
| 1590 |
+
if (!$validate->batch($batch)->check($data)) {
|
| 1591 |
+
$this->error = $validate->getError();
|
| 1592 |
+
if ($this->failException) {
|
| 1593 |
+
throw new ValidateException($this->error);
|
| 1594 |
+
} else {
|
| 1595 |
+
return false;
|
| 1596 |
+
}
|
| 1597 |
+
}
|
| 1598 |
+
$this->validate = null;
|
| 1599 |
+
}
|
| 1600 |
+
return true;
|
| 1601 |
+
}
|
| 1602 |
+
|
| 1603 |
+
/**
|
| 1604 |
+
* 返回模型的错误信息
|
| 1605 |
+
* @access public
|
| 1606 |
+
* @return string|array
|
| 1607 |
+
*/
|
| 1608 |
+
public function getError()
|
| 1609 |
+
{
|
| 1610 |
+
return $this->error;
|
| 1611 |
+
}
|
| 1612 |
+
|
| 1613 |
+
/**
|
| 1614 |
+
* 注册回调方法
|
| 1615 |
+
* @access public
|
| 1616 |
+
* @param string $event 事件名
|
| 1617 |
+
* @param callable $callback 回调方法
|
| 1618 |
+
* @param bool $override 是否覆盖
|
| 1619 |
+
* @return void
|
| 1620 |
+
*/
|
| 1621 |
+
public static function event($event, $callback, $override = false)
|
| 1622 |
+
{
|
| 1623 |
+
$class = get_called_class();
|
| 1624 |
+
if ($override) {
|
| 1625 |
+
self::$event[$class][$event] = [];
|
| 1626 |
+
}
|
| 1627 |
+
self::$event[$class][$event][] = $callback;
|
| 1628 |
+
}
|
| 1629 |
+
|
| 1630 |
+
/**
|
| 1631 |
+
* 触发事件
|
| 1632 |
+
* @access protected
|
| 1633 |
+
* @param string $event 事件名
|
| 1634 |
+
* @param mixed $params 传入参数(引用)
|
| 1635 |
+
* @return bool
|
| 1636 |
+
*/
|
| 1637 |
+
protected function trigger($event, &$params)
|
| 1638 |
+
{
|
| 1639 |
+
if (isset(self::$event[$this->class][$event])) {
|
| 1640 |
+
foreach (self::$event[$this->class][$event] as $callback) {
|
| 1641 |
+
if (is_callable($callback)) {
|
| 1642 |
+
$result = call_user_func_array($callback, [ & $params]);
|
| 1643 |
+
if (false === $result) {
|
| 1644 |
+
return false;
|
| 1645 |
+
}
|
| 1646 |
+
}
|
| 1647 |
+
}
|
| 1648 |
+
}
|
| 1649 |
+
return true;
|
| 1650 |
+
}
|
| 1651 |
+
|
| 1652 |
+
/**
|
| 1653 |
+
* 写入数据
|
| 1654 |
+
* @access public
|
| 1655 |
+
* @param array $data 数据数组
|
| 1656 |
+
* @param array|true $field 允许字段
|
| 1657 |
+
* @return $this
|
| 1658 |
+
*/
|
| 1659 |
+
public static function create($data = [], $field = null)
|
| 1660 |
+
{
|
| 1661 |
+
$model = new static();
|
| 1662 |
+
if (!empty($field)) {
|
| 1663 |
+
$model->allowField($field);
|
| 1664 |
+
}
|
| 1665 |
+
$model->isUpdate(false)->save($data, []);
|
| 1666 |
+
return $model;
|
| 1667 |
+
}
|
| 1668 |
+
|
| 1669 |
+
/**
|
| 1670 |
+
* 更新数据
|
| 1671 |
+
* @access public
|
| 1672 |
+
* @param array $data 数据数组
|
| 1673 |
+
* @param array $where 更新条件
|
| 1674 |
+
* @param array|true $field 允许字段
|
| 1675 |
+
* @return $this
|
| 1676 |
+
*/
|
| 1677 |
+
public static function update($data = [], $where = [], $field = null)
|
| 1678 |
+
{
|
| 1679 |
+
$model = new static();
|
| 1680 |
+
if (!empty($field)) {
|
| 1681 |
+
$model->allowField($field);
|
| 1682 |
+
}
|
| 1683 |
+
$result = $model->isUpdate(true)->save($data, $where);
|
| 1684 |
+
return $model;
|
| 1685 |
+
}
|
| 1686 |
+
|
| 1687 |
+
/**
|
| 1688 |
+
* 查找单条记录
|
| 1689 |
+
* @access public
|
| 1690 |
+
* @param mixed $data 主键值或者查询条件(闭包)
|
| 1691 |
+
* @param array|string $with 关联预查询
|
| 1692 |
+
* @param bool $cache 是否缓存
|
| 1693 |
+
* @return static|null
|
| 1694 |
+
* @throws exception\DbException
|
| 1695 |
+
*/
|
| 1696 |
+
public static function get($data, $with = [], $cache = false)
|
| 1697 |
+
{
|
| 1698 |
+
if (is_null($data)) {
|
| 1699 |
+
return;
|
| 1700 |
+
}
|
| 1701 |
+
|
| 1702 |
+
if (true === $with || is_int($with)) {
|
| 1703 |
+
$cache = $with;
|
| 1704 |
+
$with = [];
|
| 1705 |
+
}
|
| 1706 |
+
$query = static::parseQuery($data, $with, $cache);
|
| 1707 |
+
return $query->find($data);
|
| 1708 |
+
}
|
| 1709 |
+
|
| 1710 |
+
/**
|
| 1711 |
+
* 查找所有记录
|
| 1712 |
+
* @access public
|
| 1713 |
+
* @param mixed $data 主键列表或者查询条件(闭包)
|
| 1714 |
+
* @param array|string $with 关联预查询
|
| 1715 |
+
* @param bool $cache 是否缓存
|
| 1716 |
+
* @return static[]|false
|
| 1717 |
+
* @throws exception\DbException
|
| 1718 |
+
*/
|
| 1719 |
+
public static function all($data = null, $with = [], $cache = false)
|
| 1720 |
+
{
|
| 1721 |
+
if (true === $with || is_int($with)) {
|
| 1722 |
+
$cache = $with;
|
| 1723 |
+
$with = [];
|
| 1724 |
+
}
|
| 1725 |
+
$query = static::parseQuery($data, $with, $cache);
|
| 1726 |
+
return $query->select($data);
|
| 1727 |
+
}
|
| 1728 |
+
|
| 1729 |
+
/**
|
| 1730 |
+
* 分析查询表达式
|
| 1731 |
+
* @access public
|
| 1732 |
+
* @param mixed $data 主键列表或者查询条件(闭包)
|
| 1733 |
+
* @param string $with 关联预查询
|
| 1734 |
+
* @param bool $cache 是否缓存
|
| 1735 |
+
* @return Query
|
| 1736 |
+
*/
|
| 1737 |
+
protected static function parseQuery(&$data, $with, $cache)
|
| 1738 |
+
{
|
| 1739 |
+
$result = self::with($with)->cache($cache);
|
| 1740 |
+
if (is_array($data) && key($data) !== 0) {
|
| 1741 |
+
$result = $result->where($data);
|
| 1742 |
+
$data = null;
|
| 1743 |
+
} elseif ($data instanceof \Closure) {
|
| 1744 |
+
call_user_func_array($data, [ & $result]);
|
| 1745 |
+
$data = null;
|
| 1746 |
+
} elseif ($data instanceof Query) {
|
| 1747 |
+
$result = $data->with($with)->cache($cache);
|
| 1748 |
+
$data = null;
|
| 1749 |
+
}
|
| 1750 |
+
return $result;
|
| 1751 |
+
}
|
| 1752 |
+
|
| 1753 |
+
/**
|
| 1754 |
+
* 删除记录
|
| 1755 |
+
* @access public
|
| 1756 |
+
* @param mixed $data 主键列表 支持闭包查询条件
|
| 1757 |
+
* @return integer 成功删除的记录数
|
| 1758 |
+
*/
|
| 1759 |
+
public static function destroy($data)
|
| 1760 |
+
{
|
| 1761 |
+
$model = new static();
|
| 1762 |
+
$query = $model->db();
|
| 1763 |
+
if (empty($data) && 0 !== $data) {
|
| 1764 |
+
return 0;
|
| 1765 |
+
} elseif (is_array($data) && key($data) !== 0) {
|
| 1766 |
+
$query->where($data);
|
| 1767 |
+
$data = null;
|
| 1768 |
+
} elseif ($data instanceof \Closure) {
|
| 1769 |
+
call_user_func_array($data, [ & $query]);
|
| 1770 |
+
$data = null;
|
| 1771 |
+
}
|
| 1772 |
+
$resultSet = $query->select($data);
|
| 1773 |
+
$count = 0;
|
| 1774 |
+
if ($resultSet) {
|
| 1775 |
+
foreach ($resultSet as $data) {
|
| 1776 |
+
$result = $data->delete();
|
| 1777 |
+
$count += $result;
|
| 1778 |
+
}
|
| 1779 |
+
}
|
| 1780 |
+
return $count;
|
| 1781 |
+
}
|
| 1782 |
+
|
| 1783 |
+
/**
|
| 1784 |
+
* 命名范围
|
| 1785 |
+
* @access public
|
| 1786 |
+
* @param string|array|\Closure $name 命名范围名称 逗号分隔
|
| 1787 |
+
* @internal mixed ...$params 参数调用
|
| 1788 |
+
* @return Query
|
| 1789 |
+
*/
|
| 1790 |
+
public static function scope($name)
|
| 1791 |
+
{
|
| 1792 |
+
$model = new static();
|
| 1793 |
+
$query = $model->db();
|
| 1794 |
+
$params = func_get_args();
|
| 1795 |
+
array_shift($params);
|
| 1796 |
+
array_unshift($params, $query);
|
| 1797 |
+
if ($name instanceof \Closure) {
|
| 1798 |
+
call_user_func_array($name, $params);
|
| 1799 |
+
} elseif (is_string($name)) {
|
| 1800 |
+
$name = explode(',', $name);
|
| 1801 |
+
}
|
| 1802 |
+
if (is_array($name)) {
|
| 1803 |
+
foreach ($name as $scope) {
|
| 1804 |
+
$method = 'scope' . trim($scope);
|
| 1805 |
+
if (method_exists($model, $method)) {
|
| 1806 |
+
call_user_func_array([$model, $method], $params);
|
| 1807 |
+
}
|
| 1808 |
+
}
|
| 1809 |
+
}
|
| 1810 |
+
return $query;
|
| 1811 |
+
}
|
| 1812 |
+
|
| 1813 |
+
/**
|
| 1814 |
+
* 设置是否使用全局查询范围
|
| 1815 |
+
* @param bool $use 是否启用全局查询范围
|
| 1816 |
+
* @access public
|
| 1817 |
+
* @return Query
|
| 1818 |
+
*/
|
| 1819 |
+
public static function useGlobalScope($use)
|
| 1820 |
+
{
|
| 1821 |
+
$model = new static();
|
| 1822 |
+
return $model->db($use);
|
| 1823 |
+
}
|
| 1824 |
+
|
| 1825 |
+
/**
|
| 1826 |
+
* 根据关联条件查询当前模型
|
| 1827 |
+
* @access public
|
| 1828 |
+
* @param string $relation 关联方法名
|
| 1829 |
+
* @param mixed $operator 比���操作符
|
| 1830 |
+
* @param integer $count 个数
|
| 1831 |
+
* @param string $id 关联表的统计字段
|
| 1832 |
+
* @return Relation|Query
|
| 1833 |
+
*/
|
| 1834 |
+
public static function has($relation, $operator = '>=', $count = 1, $id = '*')
|
| 1835 |
+
{
|
| 1836 |
+
$relation = (new static())->$relation();
|
| 1837 |
+
if (is_array($operator) || $operator instanceof \Closure) {
|
| 1838 |
+
return $relation->hasWhere($operator);
|
| 1839 |
+
}
|
| 1840 |
+
return $relation->has($operator, $count, $id);
|
| 1841 |
+
}
|
| 1842 |
+
|
| 1843 |
+
/**
|
| 1844 |
+
* 根据关联条件查询当前模型
|
| 1845 |
+
* @access public
|
| 1846 |
+
* @param string $relation 关联方法名
|
| 1847 |
+
* @param mixed $where 查询条件(数组或者闭包)
|
| 1848 |
+
* @param mixed $fields 字段
|
| 1849 |
+
* @return Relation|Query
|
| 1850 |
+
*/
|
| 1851 |
+
public static function hasWhere($relation, $where = [], $fields = null)
|
| 1852 |
+
{
|
| 1853 |
+
return (new static())->$relation()->hasWhere($where, $fields);
|
| 1854 |
+
}
|
| 1855 |
+
|
| 1856 |
+
/**
|
| 1857 |
+
* 解析模型的完整命名空间
|
| 1858 |
+
* @access public
|
| 1859 |
+
* @param string $model 模型名(或者完整类名)
|
| 1860 |
+
* @return string
|
| 1861 |
+
*/
|
| 1862 |
+
protected function parseModel($model)
|
| 1863 |
+
{
|
| 1864 |
+
if (false === strpos($model, '\\')) {
|
| 1865 |
+
$path = explode('\\', get_called_class());
|
| 1866 |
+
array_pop($path);
|
| 1867 |
+
array_push($path, Loader::parseName($model, 1));
|
| 1868 |
+
$model = implode('\\', $path);
|
| 1869 |
+
}
|
| 1870 |
+
return $model;
|
| 1871 |
+
}
|
| 1872 |
+
|
| 1873 |
+
/**
|
| 1874 |
+
* 查询当前模型的关联数据
|
| 1875 |
+
* @access public
|
| 1876 |
+
* @param string|array $relations 关联名
|
| 1877 |
+
* @return $this
|
| 1878 |
+
*/
|
| 1879 |
+
public function relationQuery($relations)
|
| 1880 |
+
{
|
| 1881 |
+
if (is_string($relations)) {
|
| 1882 |
+
$relations = explode(',', $relations);
|
| 1883 |
+
}
|
| 1884 |
+
|
| 1885 |
+
foreach ($relations as $key => $relation) {
|
| 1886 |
+
$subRelation = '';
|
| 1887 |
+
$closure = null;
|
| 1888 |
+
if ($relation instanceof \Closure) {
|
| 1889 |
+
// 支持闭包查询过滤关联条件
|
| 1890 |
+
$closure = $relation;
|
| 1891 |
+
$relation = $key;
|
| 1892 |
+
}
|
| 1893 |
+
if (is_array($relation)) {
|
| 1894 |
+
$subRelation = $relation;
|
| 1895 |
+
$relation = $key;
|
| 1896 |
+
} elseif (strpos($relation, '.')) {
|
| 1897 |
+
list($relation, $subRelation) = explode('.', $relation, 2);
|
| 1898 |
+
}
|
| 1899 |
+
$method = Loader::parseName($relation, 1, false);
|
| 1900 |
+
$this->data[$relation] = $this->$method()->getRelation($subRelation, $closure);
|
| 1901 |
+
}
|
| 1902 |
+
return $this;
|
| 1903 |
+
}
|
| 1904 |
+
|
| 1905 |
+
/**
|
| 1906 |
+
* 预载入关联查询 返回数据集
|
| 1907 |
+
* @access public
|
| 1908 |
+
* @param array $resultSet 数据集
|
| 1909 |
+
* @param string $relation 关联名
|
| 1910 |
+
* @return array
|
| 1911 |
+
*/
|
| 1912 |
+
public function eagerlyResultSet(&$resultSet, $relation)
|
| 1913 |
+
{
|
| 1914 |
+
$relations = is_string($relation) ? explode(',', $relation) : $relation;
|
| 1915 |
+
foreach ($relations as $key => $relation) {
|
| 1916 |
+
$subRelation = '';
|
| 1917 |
+
$closure = false;
|
| 1918 |
+
if ($relation instanceof \Closure) {
|
| 1919 |
+
$closure = $relation;
|
| 1920 |
+
$relation = $key;
|
| 1921 |
+
}
|
| 1922 |
+
if (is_array($relation)) {
|
| 1923 |
+
$subRelation = $relation;
|
| 1924 |
+
$relation = $key;
|
| 1925 |
+
} elseif (strpos($relation, '.')) {
|
| 1926 |
+
list($relation, $subRelation) = explode('.', $relation, 2);
|
| 1927 |
+
}
|
| 1928 |
+
$relation = Loader::parseName($relation, 1, false);
|
| 1929 |
+
$this->$relation()->eagerlyResultSet($resultSet, $relation, $subRelation, $closure);
|
| 1930 |
+
}
|
| 1931 |
+
}
|
| 1932 |
+
|
| 1933 |
+
/**
|
| 1934 |
+
* 预载入关联查询 返回模型对象
|
| 1935 |
+
* @access public
|
| 1936 |
+
* @param Model $result 数据对象
|
| 1937 |
+
* @param string $relation 关联名
|
| 1938 |
+
* @return Model
|
| 1939 |
+
*/
|
| 1940 |
+
public function eagerlyResult(&$result, $relation)
|
| 1941 |
+
{
|
| 1942 |
+
$relations = is_string($relation) ? explode(',', $relation) : $relation;
|
| 1943 |
+
|
| 1944 |
+
foreach ($relations as $key => $relation) {
|
| 1945 |
+
$subRelation = '';
|
| 1946 |
+
$closure = false;
|
| 1947 |
+
if ($relation instanceof \Closure) {
|
| 1948 |
+
$closure = $relation;
|
| 1949 |
+
$relation = $key;
|
| 1950 |
+
}
|
| 1951 |
+
if (is_array($relation)) {
|
| 1952 |
+
$subRelation = $relation;
|
| 1953 |
+
$relation = $key;
|
| 1954 |
+
} elseif (strpos($relation, '.')) {
|
| 1955 |
+
list($relation, $subRelation) = explode('.', $relation, 2);
|
| 1956 |
+
}
|
| 1957 |
+
$relation = Loader::parseName($relation, 1, false);
|
| 1958 |
+
$this->$relation()->eagerlyResult($result, $relation, $subRelation, $closure);
|
| 1959 |
+
}
|
| 1960 |
+
}
|
| 1961 |
+
|
| 1962 |
+
/**
|
| 1963 |
+
* 关联统计
|
| 1964 |
+
* @access public
|
| 1965 |
+
* @param Model $result 数据对象
|
| 1966 |
+
* @param string|array $relation 关联名
|
| 1967 |
+
* @return void
|
| 1968 |
+
*/
|
| 1969 |
+
public function relationCount(&$result, $relation)
|
| 1970 |
+
{
|
| 1971 |
+
$relations = is_string($relation) ? explode(',', $relation) : $relation;
|
| 1972 |
+
|
| 1973 |
+
foreach ($relations as $key => $relation) {
|
| 1974 |
+
$closure = false;
|
| 1975 |
+
if ($relation instanceof \Closure) {
|
| 1976 |
+
$closure = $relation;
|
| 1977 |
+
$relation = $key;
|
| 1978 |
+
} elseif (is_string($key)) {
|
| 1979 |
+
$name = $relation;
|
| 1980 |
+
$relation = $key;
|
| 1981 |
+
}
|
| 1982 |
+
$relation = Loader::parseName($relation, 1, false);
|
| 1983 |
+
$count = $this->$relation()->relationCount($result, $closure);
|
| 1984 |
+
if (!isset($name)) {
|
| 1985 |
+
$name = Loader::parseName($relation) . '_count';
|
| 1986 |
+
}
|
| 1987 |
+
$result->setAttr($name, $count);
|
| 1988 |
+
}
|
| 1989 |
+
}
|
| 1990 |
+
|
| 1991 |
+
/**
|
| 1992 |
+
* 获取模型的默认外键名
|
| 1993 |
+
* @access public
|
| 1994 |
+
* @param string $name 模型名
|
| 1995 |
+
* @return string
|
| 1996 |
+
*/
|
| 1997 |
+
protected function getForeignKey($name)
|
| 1998 |
+
{
|
| 1999 |
+
if (strpos($name, '\\')) {
|
| 2000 |
+
$name = basename(str_replace('\\', '/', $name));
|
| 2001 |
+
}
|
| 2002 |
+
return Loader::parseName($name) . '_id';
|
| 2003 |
+
}
|
| 2004 |
+
|
| 2005 |
+
/**
|
| 2006 |
+
* HAS ONE 关联定义
|
| 2007 |
+
* @access public
|
| 2008 |
+
* @param string $model 模型名
|
| 2009 |
+
* @param string $foreignKey 关联外键
|
| 2010 |
+
* @param string $localKey 当前模型主键
|
| 2011 |
+
* @param array $alias 别名定义(已经废弃)
|
| 2012 |
+
* @param string $joinType JOIN类型
|
| 2013 |
+
* @return HasOne
|
| 2014 |
+
*/
|
| 2015 |
+
public function hasOne($model, $foreignKey = '', $localKey = '', $alias = [], $joinType = 'INNER')
|
| 2016 |
+
{
|
| 2017 |
+
// 记录当前关联信息
|
| 2018 |
+
$model = $this->parseModel($model);
|
| 2019 |
+
$localKey = $localKey ?: $this->getPk();
|
| 2020 |
+
$foreignKey = $foreignKey ?: $this->getForeignKey($this->name);
|
| 2021 |
+
return new HasOne($this, $model, $foreignKey, $localKey, $joinType);
|
| 2022 |
+
}
|
| 2023 |
+
|
| 2024 |
+
/**
|
| 2025 |
+
* BELONGS TO 关联定义
|
| 2026 |
+
* @access public
|
| 2027 |
+
* @param string $model 模型名
|
| 2028 |
+
* @param string $foreignKey 关联外键
|
| 2029 |
+
* @param string $localKey 关联主键
|
| 2030 |
+
* @param array $alias 别名定义(已经废弃)
|
| 2031 |
+
* @param string $joinType JOIN类型
|
| 2032 |
+
* @return BelongsTo
|
| 2033 |
+
*/
|
| 2034 |
+
public function belongsTo($model, $foreignKey = '', $localKey = '', $alias = [], $joinType = 'INNER')
|
| 2035 |
+
{
|
| 2036 |
+
// 记录当前关联信息
|
| 2037 |
+
$model = $this->parseModel($model);
|
| 2038 |
+
$foreignKey = $foreignKey ?: $this->getForeignKey($model);
|
| 2039 |
+
$localKey = $localKey ?: (new $model)->getPk();
|
| 2040 |
+
$trace = debug_backtrace(false, 2);
|
| 2041 |
+
$relation = Loader::parseName($trace[1]['function']);
|
| 2042 |
+
return new BelongsTo($this, $model, $foreignKey, $localKey, $joinType, $relation);
|
| 2043 |
+
}
|
| 2044 |
+
|
| 2045 |
+
/**
|
| 2046 |
+
* HAS MANY 关联定义
|
| 2047 |
+
* @access public
|
| 2048 |
+
* @param string $model 模型名
|
| 2049 |
+
* @param string $foreignKey 关联外键
|
| 2050 |
+
* @param string $localKey 当前模型主键
|
| 2051 |
+
* @return HasMany
|
| 2052 |
+
*/
|
| 2053 |
+
public function hasMany($model, $foreignKey = '', $localKey = '')
|
| 2054 |
+
{
|
| 2055 |
+
// 记录当前关联信息
|
| 2056 |
+
$model = $this->parseModel($model);
|
| 2057 |
+
$localKey = $localKey ?: $this->getPk();
|
| 2058 |
+
$foreignKey = $foreignKey ?: $this->getForeignKey($this->name);
|
| 2059 |
+
return new HasMany($this, $model, $foreignKey, $localKey);
|
| 2060 |
+
}
|
| 2061 |
+
|
| 2062 |
+
/**
|
| 2063 |
+
* HAS MANY 远程关联定义
|
| 2064 |
+
* @access public
|
| 2065 |
+
* @param string $model 模型名
|
| 2066 |
+
* @param string $through 中间模型名
|
| 2067 |
+
* @param string $foreignKey 关联外键
|
| 2068 |
+
* @param string $throughKey 关联外键
|
| 2069 |
+
* @param string $localKey 当前模型主键
|
| 2070 |
+
* @return HasManyThrough
|
| 2071 |
+
*/
|
| 2072 |
+
public function hasManyThrough($model, $through, $foreignKey = '', $throughKey = '', $localKey = '')
|
| 2073 |
+
{
|
| 2074 |
+
// 记录当前关联信息
|
| 2075 |
+
$model = $this->parseModel($model);
|
| 2076 |
+
$through = $this->parseModel($through);
|
| 2077 |
+
$localKey = $localKey ?: $this->getPk();
|
| 2078 |
+
$foreignKey = $foreignKey ?: $this->getForeignKey($this->name);
|
| 2079 |
+
$throughKey = $throughKey ?: $this->getForeignKey($through);
|
| 2080 |
+
return new HasManyThrough($this, $model, $through, $foreignKey, $throughKey, $localKey);
|
| 2081 |
+
}
|
| 2082 |
+
|
| 2083 |
+
/**
|
| 2084 |
+
* BELONGS TO MANY 关联定义
|
| 2085 |
+
* @access public
|
| 2086 |
+
* @param string $model 模型名
|
| 2087 |
+
* @param string $table 中间表名
|
| 2088 |
+
* @param string $foreignKey 关联外键
|
| 2089 |
+
* @param string $localKey 当前模型关联键
|
| 2090 |
+
* @return BelongsToMany
|
| 2091 |
+
*/
|
| 2092 |
+
public function belongsToMany($model, $table = '', $foreignKey = '', $localKey = '')
|
| 2093 |
+
{
|
| 2094 |
+
// 记录当前关联信息
|
| 2095 |
+
$model = $this->parseModel($model);
|
| 2096 |
+
$name = Loader::parseName(basename(str_replace('\\', '/', $model)));
|
| 2097 |
+
$table = $table ?: Loader::parseName($this->name) . '_' . $name;
|
| 2098 |
+
$foreignKey = $foreignKey ?: $name . '_id';
|
| 2099 |
+
$localKey = $localKey ?: $this->getForeignKey($this->name);
|
| 2100 |
+
return new BelongsToMany($this, $model, $table, $foreignKey, $localKey);
|
| 2101 |
+
}
|
| 2102 |
+
|
| 2103 |
+
/**
|
| 2104 |
+
* MORPH MANY 关联定义
|
| 2105 |
+
* @access public
|
| 2106 |
+
* @param string $model 模型名
|
| 2107 |
+
* @param string|array $morph 多态字段信息
|
| 2108 |
+
* @param string $type 多态类型
|
| 2109 |
+
* @return MorphMany
|
| 2110 |
+
*/
|
| 2111 |
+
public function morphMany($model, $morph = null, $type = '')
|
| 2112 |
+
{
|
| 2113 |
+
// 记录当前关联信息
|
| 2114 |
+
$model = $this->parseModel($model);
|
| 2115 |
+
if (is_null($morph)) {
|
| 2116 |
+
$trace = debug_backtrace(false, 2);
|
| 2117 |
+
$morph = Loader::parseName($trace[1]['function']);
|
| 2118 |
+
}
|
| 2119 |
+
$type = $type ?: get_class($this);
|
| 2120 |
+
if (is_array($morph)) {
|
| 2121 |
+
list($morphType, $foreignKey) = $morph;
|
| 2122 |
+
} else {
|
| 2123 |
+
$morphType = $morph . '_type';
|
| 2124 |
+
$foreignKey = $morph . '_id';
|
| 2125 |
+
}
|
| 2126 |
+
return new MorphMany($this, $model, $foreignKey, $morphType, $type);
|
| 2127 |
+
}
|
| 2128 |
+
|
| 2129 |
+
/**
|
| 2130 |
+
* MORPH One 关联定义
|
| 2131 |
+
* @access public
|
| 2132 |
+
* @param string $model 模型名
|
| 2133 |
+
* @param string|array $morph 多态字段信息
|
| 2134 |
+
* @param string $type 多态类型
|
| 2135 |
+
* @return MorphOne
|
| 2136 |
+
*/
|
| 2137 |
+
public function morphOne($model, $morph = null, $type = '')
|
| 2138 |
+
{
|
| 2139 |
+
// 记录当前关联信息
|
| 2140 |
+
$model = $this->parseModel($model);
|
| 2141 |
+
if (is_null($morph)) {
|
| 2142 |
+
$trace = debug_backtrace(false, 2);
|
| 2143 |
+
$morph = Loader::parseName($trace[1]['function']);
|
| 2144 |
+
}
|
| 2145 |
+
$type = $type ?: get_class($this);
|
| 2146 |
+
if (is_array($morph)) {
|
| 2147 |
+
list($morphType, $foreignKey) = $morph;
|
| 2148 |
+
} else {
|
| 2149 |
+
$morphType = $morph . '_type';
|
| 2150 |
+
$foreignKey = $morph . '_id';
|
| 2151 |
+
}
|
| 2152 |
+
return new MorphOne($this, $model, $foreignKey, $morphType, $type);
|
| 2153 |
+
}
|
| 2154 |
+
|
| 2155 |
+
/**
|
| 2156 |
+
* MORPH TO 关联定义
|
| 2157 |
+
* @access public
|
| 2158 |
+
* @param string|array $morph 多态字段信息
|
| 2159 |
+
* @param array $alias 多态别名定义
|
| 2160 |
+
* @return MorphTo
|
| 2161 |
+
*/
|
| 2162 |
+
public function morphTo($morph = null, $alias = [])
|
| 2163 |
+
{
|
| 2164 |
+
$trace = debug_backtrace(false, 2);
|
| 2165 |
+
$relation = Loader::parseName($trace[1]['function']);
|
| 2166 |
+
|
| 2167 |
+
if (is_null($morph)) {
|
| 2168 |
+
$morph = $relation;
|
| 2169 |
+
}
|
| 2170 |
+
// 记录当前关联信息
|
| 2171 |
+
if (is_array($morph)) {
|
| 2172 |
+
list($morphType, $foreignKey) = $morph;
|
| 2173 |
+
} else {
|
| 2174 |
+
$morphType = $morph . '_type';
|
| 2175 |
+
$foreignKey = $morph . '_id';
|
| 2176 |
+
}
|
| 2177 |
+
return new MorphTo($this, $morphType, $foreignKey, $alias, $relation);
|
| 2178 |
+
}
|
| 2179 |
+
|
| 2180 |
+
public function __call($method, $args)
|
| 2181 |
+
{
|
| 2182 |
+
$query = $this->db(true, false);
|
| 2183 |
+
if (method_exists($this, 'scope' . $method)) {
|
| 2184 |
+
// 动态调用命名范围
|
| 2185 |
+
$method = 'scope' . $method;
|
| 2186 |
+
array_unshift($args, $query);
|
| 2187 |
+
call_user_func_array([$this, $method], $args);
|
| 2188 |
+
return $this;
|
| 2189 |
+
} else {
|
| 2190 |
+
return call_user_func_array([$query, $method], $args);
|
| 2191 |
+
}
|
| 2192 |
+
}
|
| 2193 |
+
|
| 2194 |
+
public static function __callStatic($method, $args)
|
| 2195 |
+
{
|
| 2196 |
+
$model = new static();
|
| 2197 |
+
$query = $model->db();
|
| 2198 |
+
if (method_exists($model, 'scope' . $method)) {
|
| 2199 |
+
// 动态调用命名范围
|
| 2200 |
+
$method = 'scope' . $method;
|
| 2201 |
+
array_unshift($args, $query);
|
| 2202 |
+
|
| 2203 |
+
call_user_func_array([$model, $method], $args);
|
| 2204 |
+
return $query;
|
| 2205 |
+
} else {
|
| 2206 |
+
return call_user_func_array([$query, $method], $args);
|
| 2207 |
+
}
|
| 2208 |
+
}
|
| 2209 |
+
|
| 2210 |
+
/**
|
| 2211 |
+
* 修改器 设置数据对象的值
|
| 2212 |
+
* @access public
|
| 2213 |
+
* @param string $name 名称
|
| 2214 |
+
* @param mixed $value 值
|
| 2215 |
+
* @return void
|
| 2216 |
+
*/
|
| 2217 |
+
public function __set($name, $value)
|
| 2218 |
+
{
|
| 2219 |
+
$this->setAttr($name, $value);
|
| 2220 |
+
}
|
| 2221 |
+
|
| 2222 |
+
/**
|
| 2223 |
+
* 获取器 获取数据对象的值
|
| 2224 |
+
* @access public
|
| 2225 |
+
* @param string $name 名称
|
| 2226 |
+
* @return mixed
|
| 2227 |
+
*/
|
| 2228 |
+
public function __get($name)
|
| 2229 |
+
{
|
| 2230 |
+
return $this->getAttr($name);
|
| 2231 |
+
}
|
| 2232 |
+
|
| 2233 |
+
/**
|
| 2234 |
+
* 检测数据对象的值
|
| 2235 |
+
* @access public
|
| 2236 |
+
* @param string $name 名称
|
| 2237 |
+
* @return boolean
|
| 2238 |
+
*/
|
| 2239 |
+
public function __isset($name)
|
| 2240 |
+
{
|
| 2241 |
+
try {
|
| 2242 |
+
if (array_key_exists($name, $this->data) || array_key_exists($name, $this->relation)) {
|
| 2243 |
+
return true;
|
| 2244 |
+
} else {
|
| 2245 |
+
$this->getAttr($name);
|
| 2246 |
+
return true;
|
| 2247 |
+
}
|
| 2248 |
+
} catch (InvalidArgumentException $e) {
|
| 2249 |
+
return false;
|
| 2250 |
+
}
|
| 2251 |
+
|
| 2252 |
+
}
|
| 2253 |
+
|
| 2254 |
+
/**
|
| 2255 |
+
* 销毁数据对象的值
|
| 2256 |
+
* @access public
|
| 2257 |
+
* @param string $name 名称
|
| 2258 |
+
* @return void
|
| 2259 |
+
*/
|
| 2260 |
+
public function __unset($name)
|
| 2261 |
+
{
|
| 2262 |
+
unset($this->data[$name], $this->relation[$name]);
|
| 2263 |
+
}
|
| 2264 |
+
|
| 2265 |
+
public function __toString()
|
| 2266 |
+
{
|
| 2267 |
+
return $this->toJson();
|
| 2268 |
+
}
|
| 2269 |
+
|
| 2270 |
+
// JsonSerializable
|
| 2271 |
+
public function jsonSerialize()
|
| 2272 |
+
{
|
| 2273 |
+
return $this->toArray();
|
| 2274 |
+
}
|
| 2275 |
+
|
| 2276 |
+
// ArrayAccess
|
| 2277 |
+
public function offsetSet($name, $value)
|
| 2278 |
+
{
|
| 2279 |
+
$this->setAttr($name, $value);
|
| 2280 |
+
}
|
| 2281 |
+
|
| 2282 |
+
public function offsetExists($name)
|
| 2283 |
+
{
|
| 2284 |
+
return $this->__isset($name);
|
| 2285 |
+
}
|
| 2286 |
+
|
| 2287 |
+
public function offsetUnset($name)
|
| 2288 |
+
{
|
| 2289 |
+
$this->__unset($name);
|
| 2290 |
+
}
|
| 2291 |
+
|
| 2292 |
+
public function offsetGet($name)
|
| 2293 |
+
{
|
| 2294 |
+
return $this->getAttr($name);
|
| 2295 |
+
}
|
| 2296 |
+
|
| 2297 |
+
/**
|
| 2298 |
+
* 解序列化后处理
|
| 2299 |
+
*/
|
| 2300 |
+
public function __wakeup()
|
| 2301 |
+
{
|
| 2302 |
+
$this->initialize();
|
| 2303 |
+
}
|
| 2304 |
+
|
| 2305 |
+
/**
|
| 2306 |
+
* 模型事件快捷方法
|
| 2307 |
+
* @param $callback
|
| 2308 |
+
* @param bool $override
|
| 2309 |
+
*/
|
| 2310 |
+
protected static function beforeInsert($callback, $override = false)
|
| 2311 |
+
{
|
| 2312 |
+
self::event('before_insert', $callback, $override);
|
| 2313 |
+
}
|
| 2314 |
+
|
| 2315 |
+
protected static function afterInsert($callback, $override = false)
|
| 2316 |
+
{
|
| 2317 |
+
self::event('after_insert', $callback, $override);
|
| 2318 |
+
}
|
| 2319 |
+
|
| 2320 |
+
protected static function beforeUpdate($callback, $override = false)
|
| 2321 |
+
{
|
| 2322 |
+
self::event('before_update', $callback, $override);
|
| 2323 |
+
}
|
| 2324 |
+
|
| 2325 |
+
protected static function afterUpdate($callback, $override = false)
|
| 2326 |
+
{
|
| 2327 |
+
self::event('after_update', $callback, $override);
|
| 2328 |
+
}
|
| 2329 |
+
|
| 2330 |
+
protected static function beforeWrite($callback, $override = false)
|
| 2331 |
+
{
|
| 2332 |
+
self::event('before_write', $callback, $override);
|
| 2333 |
+
}
|
| 2334 |
+
|
| 2335 |
+
protected static function afterWrite($callback, $override = false)
|
| 2336 |
+
{
|
| 2337 |
+
self::event('after_write', $callback, $override);
|
| 2338 |
+
}
|
| 2339 |
+
|
| 2340 |
+
protected static function beforeDelete($callback, $override = false)
|
| 2341 |
+
{
|
| 2342 |
+
self::event('before_delete', $callback, $override);
|
| 2343 |
+
}
|
| 2344 |
+
|
| 2345 |
+
protected static function afterDelete($callback, $override = false)
|
| 2346 |
+
{
|
| 2347 |
+
self::event('after_delete', $callback, $override);
|
| 2348 |
+
}
|
| 2349 |
+
|
| 2350 |
+
}
|
thinkphp/library/think/Paginator.php
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: zhangyajun <448901948@qq.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use ArrayAccess;
|
| 15 |
+
use ArrayIterator;
|
| 16 |
+
use Countable;
|
| 17 |
+
use IteratorAggregate;
|
| 18 |
+
use JsonSerializable;
|
| 19 |
+
use Traversable;
|
| 20 |
+
|
| 21 |
+
abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
|
| 22 |
+
{
|
| 23 |
+
/** @var bool 是否为简洁模式 */
|
| 24 |
+
protected $simple = false;
|
| 25 |
+
|
| 26 |
+
/** @var Collection 数据集 */
|
| 27 |
+
protected $items;
|
| 28 |
+
|
| 29 |
+
/** @var integer 当前页 */
|
| 30 |
+
protected $currentPage;
|
| 31 |
+
|
| 32 |
+
/** @var integer 最后一页 */
|
| 33 |
+
protected $lastPage;
|
| 34 |
+
|
| 35 |
+
/** @var integer|null 数据总数 */
|
| 36 |
+
protected $total;
|
| 37 |
+
|
| 38 |
+
/** @var integer 每页的数量 */
|
| 39 |
+
protected $listRows;
|
| 40 |
+
|
| 41 |
+
/** @var bool 是否有下一页 */
|
| 42 |
+
protected $hasMore;
|
| 43 |
+
|
| 44 |
+
/** @var array 一些配置 */
|
| 45 |
+
protected $options = [
|
| 46 |
+
'var_page' => 'page',
|
| 47 |
+
'path' => '/',
|
| 48 |
+
'query' => [],
|
| 49 |
+
'fragment' => '',
|
| 50 |
+
];
|
| 51 |
+
|
| 52 |
+
/** @var mixed simple模式下的下个元素 */
|
| 53 |
+
protected $nextItem;
|
| 54 |
+
|
| 55 |
+
public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
|
| 56 |
+
{
|
| 57 |
+
$this->options = array_merge($this->options, $options);
|
| 58 |
+
|
| 59 |
+
$this->options['path'] = '/' != $this->options['path'] ? rtrim($this->options['path'], '/') : $this->options['path'];
|
| 60 |
+
|
| 61 |
+
$this->simple = $simple;
|
| 62 |
+
$this->listRows = $listRows;
|
| 63 |
+
|
| 64 |
+
if (!$items instanceof Collection) {
|
| 65 |
+
$items = Collection::make($items);
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
if ($simple) {
|
| 69 |
+
$this->currentPage = $this->setCurrentPage($currentPage);
|
| 70 |
+
$this->hasMore = count($items) > ($this->listRows);
|
| 71 |
+
if ($this->hasMore) {
|
| 72 |
+
$this->nextItem = $items->slice($this->listRows, 1);
|
| 73 |
+
}
|
| 74 |
+
$items = $items->slice(0, $this->listRows);
|
| 75 |
+
} else {
|
| 76 |
+
$this->total = $total;
|
| 77 |
+
$this->lastPage = (int) ceil($total / $listRows);
|
| 78 |
+
$this->currentPage = $this->setCurrentPage($currentPage);
|
| 79 |
+
$this->hasMore = $this->currentPage < $this->lastPage;
|
| 80 |
+
}
|
| 81 |
+
$this->items = $items;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
/**
|
| 85 |
+
* @param $items
|
| 86 |
+
* @param $listRows
|
| 87 |
+
* @param null $currentPage
|
| 88 |
+
* @param bool $simple
|
| 89 |
+
* @param null $total
|
| 90 |
+
* @param array $options
|
| 91 |
+
* @return Paginator
|
| 92 |
+
*/
|
| 93 |
+
public static function make($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
|
| 94 |
+
{
|
| 95 |
+
return new static($items, $listRows, $currentPage, $total, $simple, $options);
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
protected function setCurrentPage($currentPage)
|
| 99 |
+
{
|
| 100 |
+
if (!$this->simple && $currentPage > $this->lastPage) {
|
| 101 |
+
return $this->lastPage > 0 ? $this->lastPage : 1;
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
return $currentPage;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
/**
|
| 108 |
+
* 获取页码对应的链接
|
| 109 |
+
*
|
| 110 |
+
* @param $page
|
| 111 |
+
* @return string
|
| 112 |
+
*/
|
| 113 |
+
protected function url($page)
|
| 114 |
+
{
|
| 115 |
+
if ($page <= 0) {
|
| 116 |
+
$page = 1;
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
if (strpos($this->options['path'], '[PAGE]') === false) {
|
| 120 |
+
$parameters = [$this->options['var_page'] => $page];
|
| 121 |
+
$path = $this->options['path'];
|
| 122 |
+
} else {
|
| 123 |
+
$parameters = [];
|
| 124 |
+
$path = str_replace('[PAGE]', $page, $this->options['path']);
|
| 125 |
+
}
|
| 126 |
+
if (count($this->options['query']) > 0) {
|
| 127 |
+
$parameters = array_merge($this->options['query'], $parameters);
|
| 128 |
+
}
|
| 129 |
+
$url = $path;
|
| 130 |
+
if (!empty($parameters)) {
|
| 131 |
+
$url .= '?' . http_build_query($parameters, null, '&');
|
| 132 |
+
}
|
| 133 |
+
return $url . $this->buildFragment();
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
/**
|
| 137 |
+
* 自动获取当前页码
|
| 138 |
+
* @param string $varPage
|
| 139 |
+
* @param int $default
|
| 140 |
+
* @return int
|
| 141 |
+
*/
|
| 142 |
+
public static function getCurrentPage($varPage = 'page', $default = 1)
|
| 143 |
+
{
|
| 144 |
+
$page = (int) Request::instance()->param($varPage);
|
| 145 |
+
|
| 146 |
+
if (filter_var($page, FILTER_VALIDATE_INT) !== false && $page >= 1) {
|
| 147 |
+
return $page;
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
return $default;
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
/**
|
| 154 |
+
* 自动获取当前的path
|
| 155 |
+
* @return string
|
| 156 |
+
*/
|
| 157 |
+
public static function getCurrentPath()
|
| 158 |
+
{
|
| 159 |
+
return Request::instance()->baseUrl();
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
public function total()
|
| 163 |
+
{
|
| 164 |
+
if ($this->simple) {
|
| 165 |
+
throw new \DomainException('not support total');
|
| 166 |
+
}
|
| 167 |
+
return $this->total;
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
public function listRows()
|
| 171 |
+
{
|
| 172 |
+
return $this->listRows;
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
public function currentPage()
|
| 176 |
+
{
|
| 177 |
+
return $this->currentPage;
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
public function lastPage()
|
| 181 |
+
{
|
| 182 |
+
if ($this->simple) {
|
| 183 |
+
throw new \DomainException('not support last');
|
| 184 |
+
}
|
| 185 |
+
return $this->lastPage;
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
/**
|
| 189 |
+
* 数据是否足够分页
|
| 190 |
+
* @return boolean
|
| 191 |
+
*/
|
| 192 |
+
public function hasPages()
|
| 193 |
+
{
|
| 194 |
+
return !(1 == $this->currentPage && !$this->hasMore);
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
/**
|
| 198 |
+
* 创建一组分页链接
|
| 199 |
+
*
|
| 200 |
+
* @param int $start
|
| 201 |
+
* @param int $end
|
| 202 |
+
* @return array
|
| 203 |
+
*/
|
| 204 |
+
public function getUrlRange($start, $end)
|
| 205 |
+
{
|
| 206 |
+
$urls = [];
|
| 207 |
+
|
| 208 |
+
for ($page = $start; $page <= $end; $page++) {
|
| 209 |
+
$urls[$page] = $this->url($page);
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
return $urls;
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
/**
|
| 216 |
+
* 设置URL锚点
|
| 217 |
+
*
|
| 218 |
+
* @param string|null $fragment
|
| 219 |
+
* @return $this
|
| 220 |
+
*/
|
| 221 |
+
public function fragment($fragment)
|
| 222 |
+
{
|
| 223 |
+
$this->options['fragment'] = $fragment;
|
| 224 |
+
return $this;
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
/**
|
| 228 |
+
* 添加URL参数
|
| 229 |
+
*
|
| 230 |
+
* @param array|string $key
|
| 231 |
+
* @param string|null $value
|
| 232 |
+
* @return $this
|
| 233 |
+
*/
|
| 234 |
+
public function appends($key, $value = null)
|
| 235 |
+
{
|
| 236 |
+
if (!is_array($key)) {
|
| 237 |
+
$queries = [$key => $value];
|
| 238 |
+
} else {
|
| 239 |
+
$queries = $key;
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
foreach ($queries as $k => $v) {
|
| 243 |
+
if ($k !== $this->options['var_page']) {
|
| 244 |
+
$this->options['query'][$k] = $v;
|
| 245 |
+
}
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
return $this;
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
/**
|
| 252 |
+
* 构造锚点字符串
|
| 253 |
+
*
|
| 254 |
+
* @return string
|
| 255 |
+
*/
|
| 256 |
+
protected function buildFragment()
|
| 257 |
+
{
|
| 258 |
+
return $this->options['fragment'] ? '#' . $this->options['fragment'] : '';
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
/**
|
| 262 |
+
* 渲染分页html
|
| 263 |
+
* @return mixed
|
| 264 |
+
*/
|
| 265 |
+
abstract public function render();
|
| 266 |
+
|
| 267 |
+
public function items()
|
| 268 |
+
{
|
| 269 |
+
return $this->items->all();
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
public function getCollection()
|
| 273 |
+
{
|
| 274 |
+
return $this->items;
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
public function isEmpty()
|
| 278 |
+
{
|
| 279 |
+
return $this->items->isEmpty();
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
/**
|
| 283 |
+
* 给每个元素执行个回调
|
| 284 |
+
*
|
| 285 |
+
* @param callable $callback
|
| 286 |
+
* @return $this
|
| 287 |
+
*/
|
| 288 |
+
public function each(callable $callback)
|
| 289 |
+
{
|
| 290 |
+
foreach ($this->items as $key => $item) {
|
| 291 |
+
$result = $callback($item, $key);
|
| 292 |
+
if (false === $result) {
|
| 293 |
+
break;
|
| 294 |
+
} elseif (!is_object($item)) {
|
| 295 |
+
$this->items[$key] = $result;
|
| 296 |
+
}
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
return $this;
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
/**
|
| 303 |
+
* Retrieve an external iterator
|
| 304 |
+
* @return Traversable An instance of an object implementing <b>Iterator</b> or
|
| 305 |
+
* <b>Traversable</b>
|
| 306 |
+
*/
|
| 307 |
+
public function getIterator()
|
| 308 |
+
{
|
| 309 |
+
return new ArrayIterator($this->items->all());
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
/**
|
| 313 |
+
* Whether a offset exists
|
| 314 |
+
* @param mixed $offset
|
| 315 |
+
* @return bool
|
| 316 |
+
*/
|
| 317 |
+
public function offsetExists($offset)
|
| 318 |
+
{
|
| 319 |
+
return $this->items->offsetExists($offset);
|
| 320 |
+
}
|
| 321 |
+
|
| 322 |
+
/**
|
| 323 |
+
* Offset to retrieve
|
| 324 |
+
* @param mixed $offset
|
| 325 |
+
* @return mixed
|
| 326 |
+
*/
|
| 327 |
+
public function offsetGet($offset)
|
| 328 |
+
{
|
| 329 |
+
return $this->items->offsetGet($offset);
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
+
/**
|
| 333 |
+
* Offset to set
|
| 334 |
+
* @param mixed $offset
|
| 335 |
+
* @param mixed $value
|
| 336 |
+
*/
|
| 337 |
+
public function offsetSet($offset, $value)
|
| 338 |
+
{
|
| 339 |
+
$this->items->offsetSet($offset, $value);
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
/**
|
| 343 |
+
* Offset to unset
|
| 344 |
+
* @param mixed $offset
|
| 345 |
+
* @return void
|
| 346 |
+
* @since 5.0.0
|
| 347 |
+
*/
|
| 348 |
+
public function offsetUnset($offset)
|
| 349 |
+
{
|
| 350 |
+
$this->items->offsetUnset($offset);
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
/**
|
| 354 |
+
* Count elements of an object
|
| 355 |
+
*/
|
| 356 |
+
public function count()
|
| 357 |
+
{
|
| 358 |
+
return $this->items->count();
|
| 359 |
+
}
|
| 360 |
+
|
| 361 |
+
public function __toString()
|
| 362 |
+
{
|
| 363 |
+
return (string) $this->render();
|
| 364 |
+
}
|
| 365 |
+
|
| 366 |
+
public function toArray()
|
| 367 |
+
{
|
| 368 |
+
if ($this->simple) {
|
| 369 |
+
return [
|
| 370 |
+
'per_page' => $this->listRows,
|
| 371 |
+
'current_page' => $this->currentPage,
|
| 372 |
+
'has_more' => $this->hasMore,
|
| 373 |
+
'next_item' => $this->nextItem,
|
| 374 |
+
'data' => $this->items->toArray(),
|
| 375 |
+
];
|
| 376 |
+
} else {
|
| 377 |
+
return [
|
| 378 |
+
'total' => $this->total,
|
| 379 |
+
'per_page' => $this->listRows,
|
| 380 |
+
'current_page' => $this->currentPage,
|
| 381 |
+
'last_page' => $this->lastPage,
|
| 382 |
+
'data' => $this->items->toArray(),
|
| 383 |
+
];
|
| 384 |
+
}
|
| 385 |
+
|
| 386 |
+
}
|
| 387 |
+
|
| 388 |
+
/**
|
| 389 |
+
* Specify data which should be serialized to JSON
|
| 390 |
+
*/
|
| 391 |
+
public function jsonSerialize()
|
| 392 |
+
{
|
| 393 |
+
return $this->toArray();
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
public function __call($name, $arguments)
|
| 397 |
+
{
|
| 398 |
+
$collection = $this->getCollection();
|
| 399 |
+
|
| 400 |
+
$result = call_user_func_array([$collection, $name], $arguments);
|
| 401 |
+
|
| 402 |
+
if ($result === $collection) {
|
| 403 |
+
return $this;
|
| 404 |
+
}
|
| 405 |
+
|
| 406 |
+
return $result;
|
| 407 |
+
}
|
| 408 |
+
|
| 409 |
+
}
|
thinkphp/library/think/Process.php
ADDED
|
@@ -0,0 +1,1205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: yunwuxin <448901948@qq.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use think\process\exception\Failed as ProcessFailedException;
|
| 15 |
+
use think\process\exception\Timeout as ProcessTimeoutException;
|
| 16 |
+
use think\process\pipes\Pipes;
|
| 17 |
+
use think\process\pipes\Unix as UnixPipes;
|
| 18 |
+
use think\process\pipes\Windows as WindowsPipes;
|
| 19 |
+
use think\process\Utils;
|
| 20 |
+
|
| 21 |
+
class Process
|
| 22 |
+
{
|
| 23 |
+
|
| 24 |
+
const ERR = 'err';
|
| 25 |
+
const OUT = 'out';
|
| 26 |
+
|
| 27 |
+
const STATUS_READY = 'ready';
|
| 28 |
+
const STATUS_STARTED = 'started';
|
| 29 |
+
const STATUS_TERMINATED = 'terminated';
|
| 30 |
+
|
| 31 |
+
const STDIN = 0;
|
| 32 |
+
const STDOUT = 1;
|
| 33 |
+
const STDERR = 2;
|
| 34 |
+
|
| 35 |
+
const TIMEOUT_PRECISION = 0.2;
|
| 36 |
+
|
| 37 |
+
private $callback;
|
| 38 |
+
private $commandline;
|
| 39 |
+
private $cwd;
|
| 40 |
+
private $env;
|
| 41 |
+
private $input;
|
| 42 |
+
private $starttime;
|
| 43 |
+
private $lastOutputTime;
|
| 44 |
+
private $timeout;
|
| 45 |
+
private $idleTimeout;
|
| 46 |
+
private $options;
|
| 47 |
+
private $exitcode;
|
| 48 |
+
private $fallbackExitcode;
|
| 49 |
+
private $processInformation;
|
| 50 |
+
private $outputDisabled = false;
|
| 51 |
+
private $stdout;
|
| 52 |
+
private $stderr;
|
| 53 |
+
private $enhanceWindowsCompatibility = true;
|
| 54 |
+
private $enhanceSigchildCompatibility;
|
| 55 |
+
private $process;
|
| 56 |
+
private $status = self::STATUS_READY;
|
| 57 |
+
private $incrementalOutputOffset = 0;
|
| 58 |
+
private $incrementalErrorOutputOffset = 0;
|
| 59 |
+
private $tty;
|
| 60 |
+
private $pty;
|
| 61 |
+
|
| 62 |
+
private $useFileHandles = false;
|
| 63 |
+
|
| 64 |
+
/** @var Pipes */
|
| 65 |
+
private $processPipes;
|
| 66 |
+
|
| 67 |
+
private $latestSignal;
|
| 68 |
+
|
| 69 |
+
private static $sigchild;
|
| 70 |
+
|
| 71 |
+
/**
|
| 72 |
+
* @var array
|
| 73 |
+
*/
|
| 74 |
+
public static $exitCodes = [
|
| 75 |
+
0 => 'OK',
|
| 76 |
+
1 => 'General error',
|
| 77 |
+
2 => 'Misuse of shell builtins',
|
| 78 |
+
126 => 'Invoked command cannot execute',
|
| 79 |
+
127 => 'Command not found',
|
| 80 |
+
128 => 'Invalid exit argument',
|
| 81 |
+
// signals
|
| 82 |
+
129 => 'Hangup',
|
| 83 |
+
130 => 'Interrupt',
|
| 84 |
+
131 => 'Quit and dump core',
|
| 85 |
+
132 => 'Illegal instruction',
|
| 86 |
+
133 => 'Trace/breakpoint trap',
|
| 87 |
+
134 => 'Process aborted',
|
| 88 |
+
135 => 'Bus error: "access to undefined portion of memory object"',
|
| 89 |
+
136 => 'Floating point exception: "erroneous arithmetic operation"',
|
| 90 |
+
137 => 'Kill (terminate immediately)',
|
| 91 |
+
138 => 'User-defined 1',
|
| 92 |
+
139 => 'Segmentation violation',
|
| 93 |
+
140 => 'User-defined 2',
|
| 94 |
+
141 => 'Write to pipe with no one reading',
|
| 95 |
+
142 => 'Signal raised by alarm',
|
| 96 |
+
143 => 'Termination (request to terminate)',
|
| 97 |
+
// 144 - not defined
|
| 98 |
+
145 => 'Child process terminated, stopped (or continued*)',
|
| 99 |
+
146 => 'Continue if stopped',
|
| 100 |
+
147 => 'Stop executing temporarily',
|
| 101 |
+
148 => 'Terminal stop signal',
|
| 102 |
+
149 => 'Background process attempting to read from tty ("in")',
|
| 103 |
+
150 => 'Background process attempting to write to tty ("out")',
|
| 104 |
+
151 => 'Urgent data available on socket',
|
| 105 |
+
152 => 'CPU time limit exceeded',
|
| 106 |
+
153 => 'File size limit exceeded',
|
| 107 |
+
154 => 'Signal raised by timer counting virtual time: "virtual timer expired"',
|
| 108 |
+
155 => 'Profiling timer expired',
|
| 109 |
+
// 156 - not defined
|
| 110 |
+
157 => 'Pollable event',
|
| 111 |
+
// 158 - not defined
|
| 112 |
+
159 => 'Bad syscall',
|
| 113 |
+
];
|
| 114 |
+
|
| 115 |
+
/**
|
| 116 |
+
* 构造方法
|
| 117 |
+
* @param string $commandline 指令
|
| 118 |
+
* @param string|null $cwd 工作目录
|
| 119 |
+
* @param array|null $env 环境变量
|
| 120 |
+
* @param string|null $input 输入
|
| 121 |
+
* @param int|float|null $timeout 超时时间
|
| 122 |
+
* @param array $options proc_open的选项
|
| 123 |
+
* @throws \RuntimeException
|
| 124 |
+
* @api
|
| 125 |
+
*/
|
| 126 |
+
public function __construct($commandline, $cwd = null, array $env = null, $input = null, $timeout = 60, array $options = [])
|
| 127 |
+
{
|
| 128 |
+
if (!function_exists('proc_open')) {
|
| 129 |
+
throw new \RuntimeException('The Process class relies on proc_open, which is not available on your PHP installation.');
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
$this->commandline = $commandline;
|
| 133 |
+
$this->cwd = $cwd;
|
| 134 |
+
|
| 135 |
+
if (null === $this->cwd && (defined('ZEND_THREAD_SAFE') || '\\' === DS)) {
|
| 136 |
+
$this->cwd = getcwd();
|
| 137 |
+
}
|
| 138 |
+
if (null !== $env) {
|
| 139 |
+
$this->setEnv($env);
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
$this->input = $input;
|
| 143 |
+
$this->setTimeout($timeout);
|
| 144 |
+
$this->useFileHandles = '\\' === DS;
|
| 145 |
+
$this->pty = false;
|
| 146 |
+
$this->enhanceWindowsCompatibility = true;
|
| 147 |
+
$this->enhanceSigchildCompatibility = '\\' !== DS && $this->isSigchildEnabled();
|
| 148 |
+
$this->options = array_replace([
|
| 149 |
+
'suppress_errors' => true,
|
| 150 |
+
'binary_pipes' => true,
|
| 151 |
+
], $options);
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
public function __destruct()
|
| 155 |
+
{
|
| 156 |
+
$this->stop();
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
public function __clone()
|
| 160 |
+
{
|
| 161 |
+
$this->resetProcessData();
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
/**
|
| 165 |
+
* 运行指令
|
| 166 |
+
* @param callback|null $callback
|
| 167 |
+
* @return int
|
| 168 |
+
*/
|
| 169 |
+
public function run($callback = null)
|
| 170 |
+
{
|
| 171 |
+
$this->start($callback);
|
| 172 |
+
|
| 173 |
+
return $this->wait();
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
/**
|
| 177 |
+
* 运行指令
|
| 178 |
+
* @param callable|null $callback
|
| 179 |
+
* @return self
|
| 180 |
+
* @throws \RuntimeException
|
| 181 |
+
* @throws ProcessFailedException
|
| 182 |
+
*/
|
| 183 |
+
public function mustRun($callback = null)
|
| 184 |
+
{
|
| 185 |
+
if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) {
|
| 186 |
+
throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.');
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
if (0 !== $this->run($callback)) {
|
| 190 |
+
throw new ProcessFailedException($this);
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
return $this;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
/**
|
| 197 |
+
* 启动进程并写到 STDIN 输入后返回。
|
| 198 |
+
* @param callable|null $callback
|
| 199 |
+
* @throws \RuntimeException
|
| 200 |
+
* @throws \RuntimeException
|
| 201 |
+
* @throws \LogicException
|
| 202 |
+
*/
|
| 203 |
+
public function start($callback = null)
|
| 204 |
+
{
|
| 205 |
+
if ($this->isRunning()) {
|
| 206 |
+
throw new \RuntimeException('Process is already running');
|
| 207 |
+
}
|
| 208 |
+
if ($this->outputDisabled && null !== $callback) {
|
| 209 |
+
throw new \LogicException('Output has been disabled, enable it to allow the use of a callback.');
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
$this->resetProcessData();
|
| 213 |
+
$this->starttime = $this->lastOutputTime = microtime(true);
|
| 214 |
+
$this->callback = $this->buildCallback($callback);
|
| 215 |
+
$descriptors = $this->getDescriptors();
|
| 216 |
+
|
| 217 |
+
$commandline = $this->commandline;
|
| 218 |
+
|
| 219 |
+
if ('\\' === DS && $this->enhanceWindowsCompatibility) {
|
| 220 |
+
$commandline = 'cmd /V:ON /E:ON /C "(' . $commandline . ')';
|
| 221 |
+
foreach ($this->processPipes->getFiles() as $offset => $filename) {
|
| 222 |
+
$commandline .= ' ' . $offset . '>' . Utils::escapeArgument($filename);
|
| 223 |
+
}
|
| 224 |
+
$commandline .= '"';
|
| 225 |
+
|
| 226 |
+
if (!isset($this->options['bypass_shell'])) {
|
| 227 |
+
$this->options['bypass_shell'] = true;
|
| 228 |
+
}
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
$this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $this->env, $this->options);
|
| 232 |
+
|
| 233 |
+
if (!is_resource($this->process)) {
|
| 234 |
+
throw new \RuntimeException('Unable to launch a new process.');
|
| 235 |
+
}
|
| 236 |
+
$this->status = self::STATUS_STARTED;
|
| 237 |
+
|
| 238 |
+
if ($this->tty) {
|
| 239 |
+
return;
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
$this->updateStatus(false);
|
| 243 |
+
$this->checkTimeout();
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
/**
|
| 247 |
+
* 重启进程
|
| 248 |
+
* @param callable|null $callback
|
| 249 |
+
* @return Process
|
| 250 |
+
* @throws \RuntimeException
|
| 251 |
+
* @throws \RuntimeException
|
| 252 |
+
*/
|
| 253 |
+
public function restart($callback = null)
|
| 254 |
+
{
|
| 255 |
+
if ($this->isRunning()) {
|
| 256 |
+
throw new \RuntimeException('Process is already running');
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
$process = clone $this;
|
| 260 |
+
$process->start($callback);
|
| 261 |
+
|
| 262 |
+
return $process;
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
/**
|
| 266 |
+
* 等待要终止的进程
|
| 267 |
+
* @param callable|null $callback
|
| 268 |
+
* @return int
|
| 269 |
+
*/
|
| 270 |
+
public function wait($callback = null)
|
| 271 |
+
{
|
| 272 |
+
$this->requireProcessIsStarted(__FUNCTION__);
|
| 273 |
+
|
| 274 |
+
$this->updateStatus(false);
|
| 275 |
+
if (null !== $callback) {
|
| 276 |
+
$this->callback = $this->buildCallback($callback);
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
do {
|
| 280 |
+
$this->checkTimeout();
|
| 281 |
+
$running = '\\' === DS ? $this->isRunning() : $this->processPipes->areOpen();
|
| 282 |
+
$close = '\\' !== DS || !$running;
|
| 283 |
+
$this->readPipes(true, $close);
|
| 284 |
+
} while ($running);
|
| 285 |
+
|
| 286 |
+
while ($this->isRunning()) {
|
| 287 |
+
usleep(1000);
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
if ($this->processInformation['signaled'] && $this->processInformation['termsig'] !== $this->latestSignal) {
|
| 291 |
+
throw new \RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig']));
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
return $this->exitcode;
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
/**
|
| 298 |
+
* 获取PID
|
| 299 |
+
* @return int|null
|
| 300 |
+
* @throws \RuntimeException
|
| 301 |
+
*/
|
| 302 |
+
public function getPid()
|
| 303 |
+
{
|
| 304 |
+
if ($this->isSigchildEnabled()) {
|
| 305 |
+
throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.');
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
$this->updateStatus(false);
|
| 309 |
+
|
| 310 |
+
return $this->isRunning() ? $this->processInformation['pid'] : null;
|
| 311 |
+
}
|
| 312 |
+
|
| 313 |
+
/**
|
| 314 |
+
* 将一个 POSIX 信号发送到进程中
|
| 315 |
+
* @param int $signal
|
| 316 |
+
* @return Process
|
| 317 |
+
*/
|
| 318 |
+
public function signal($signal)
|
| 319 |
+
{
|
| 320 |
+
$this->doSignal($signal, true);
|
| 321 |
+
|
| 322 |
+
return $this;
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
/**
|
| 326 |
+
* 禁用从底层过程获取输出和错误输出。
|
| 327 |
+
* @return Process
|
| 328 |
+
*/
|
| 329 |
+
public function disableOutput()
|
| 330 |
+
{
|
| 331 |
+
if ($this->isRunning()) {
|
| 332 |
+
throw new \RuntimeException('Disabling output while the process is running is not possible.');
|
| 333 |
+
}
|
| 334 |
+
if (null !== $this->idleTimeout) {
|
| 335 |
+
throw new \LogicException('Output can not be disabled while an idle timeout is set.');
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
$this->outputDisabled = true;
|
| 339 |
+
|
| 340 |
+
return $this;
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
/**
|
| 344 |
+
* 开启从底层过程获取输出和错误输出。
|
| 345 |
+
* @return Process
|
| 346 |
+
* @throws \RuntimeException
|
| 347 |
+
*/
|
| 348 |
+
public function enableOutput()
|
| 349 |
+
{
|
| 350 |
+
if ($this->isRunning()) {
|
| 351 |
+
throw new \RuntimeException('Enabling output while the process is running is not possible.');
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
$this->outputDisabled = false;
|
| 355 |
+
|
| 356 |
+
return $this;
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
/**
|
| 360 |
+
* 输出是否禁用
|
| 361 |
+
* @return bool
|
| 362 |
+
*/
|
| 363 |
+
public function isOutputDisabled()
|
| 364 |
+
{
|
| 365 |
+
return $this->outputDisabled;
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
/**
|
| 369 |
+
* 获取当前的输出管道
|
| 370 |
+
* @return string
|
| 371 |
+
* @throws \LogicException
|
| 372 |
+
* @throws \LogicException
|
| 373 |
+
* @api
|
| 374 |
+
*/
|
| 375 |
+
public function getOutput()
|
| 376 |
+
{
|
| 377 |
+
if ($this->outputDisabled) {
|
| 378 |
+
throw new \LogicException('Output has been disabled.');
|
| 379 |
+
}
|
| 380 |
+
|
| 381 |
+
$this->requireProcessIsStarted(__FUNCTION__);
|
| 382 |
+
|
| 383 |
+
$this->readPipes(false, '\\' === DS ? !$this->processInformation['running'] : true);
|
| 384 |
+
|
| 385 |
+
return $this->stdout;
|
| 386 |
+
}
|
| 387 |
+
|
| 388 |
+
/**
|
| 389 |
+
* 以增量方式返回的输出结果。
|
| 390 |
+
* @return string
|
| 391 |
+
*/
|
| 392 |
+
public function getIncrementalOutput()
|
| 393 |
+
{
|
| 394 |
+
$this->requireProcessIsStarted(__FUNCTION__);
|
| 395 |
+
|
| 396 |
+
$data = $this->getOutput();
|
| 397 |
+
|
| 398 |
+
$latest = substr($data, $this->incrementalOutputOffset);
|
| 399 |
+
|
| 400 |
+
if (false === $latest) {
|
| 401 |
+
return '';
|
| 402 |
+
}
|
| 403 |
+
|
| 404 |
+
$this->incrementalOutputOffset = strlen($data);
|
| 405 |
+
|
| 406 |
+
return $latest;
|
| 407 |
+
}
|
| 408 |
+
|
| 409 |
+
/**
|
| 410 |
+
* 清空输出
|
| 411 |
+
* @return Process
|
| 412 |
+
*/
|
| 413 |
+
public function clearOutput()
|
| 414 |
+
{
|
| 415 |
+
$this->stdout = '';
|
| 416 |
+
$this->incrementalOutputOffset = 0;
|
| 417 |
+
|
| 418 |
+
return $this;
|
| 419 |
+
}
|
| 420 |
+
|
| 421 |
+
/**
|
| 422 |
+
* 返回当前的错误输出的过程 (STDERR)。
|
| 423 |
+
* @return string
|
| 424 |
+
*/
|
| 425 |
+
public function getErrorOutput()
|
| 426 |
+
{
|
| 427 |
+
if ($this->outputDisabled) {
|
| 428 |
+
throw new \LogicException('Output has been disabled.');
|
| 429 |
+
}
|
| 430 |
+
|
| 431 |
+
$this->requireProcessIsStarted(__FUNCTION__);
|
| 432 |
+
|
| 433 |
+
$this->readPipes(false, '\\' === DS ? !$this->processInformation['running'] : true);
|
| 434 |
+
|
| 435 |
+
return $this->stderr;
|
| 436 |
+
}
|
| 437 |
+
|
| 438 |
+
/**
|
| 439 |
+
* 以增量方式返回 errorOutput
|
| 440 |
+
* @return string
|
| 441 |
+
*/
|
| 442 |
+
public function getIncrementalErrorOutput()
|
| 443 |
+
{
|
| 444 |
+
$this->requireProcessIsStarted(__FUNCTION__);
|
| 445 |
+
|
| 446 |
+
$data = $this->getErrorOutput();
|
| 447 |
+
|
| 448 |
+
$latest = substr($data, $this->incrementalErrorOutputOffset);
|
| 449 |
+
|
| 450 |
+
if (false === $latest) {
|
| 451 |
+
return '';
|
| 452 |
+
}
|
| 453 |
+
|
| 454 |
+
$this->incrementalErrorOutputOffset = strlen($data);
|
| 455 |
+
|
| 456 |
+
return $latest;
|
| 457 |
+
}
|
| 458 |
+
|
| 459 |
+
/**
|
| 460 |
+
* 清空 errorOutput
|
| 461 |
+
* @return Process
|
| 462 |
+
*/
|
| 463 |
+
public function clearErrorOutput()
|
| 464 |
+
{
|
| 465 |
+
$this->stderr = '';
|
| 466 |
+
$this->incrementalErrorOutputOffset = 0;
|
| 467 |
+
|
| 468 |
+
return $this;
|
| 469 |
+
}
|
| 470 |
+
|
| 471 |
+
/**
|
| 472 |
+
* 获取退出码
|
| 473 |
+
* @return null|int
|
| 474 |
+
*/
|
| 475 |
+
public function getExitCode()
|
| 476 |
+
{
|
| 477 |
+
if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) {
|
| 478 |
+
throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.');
|
| 479 |
+
}
|
| 480 |
+
|
| 481 |
+
$this->updateStatus(false);
|
| 482 |
+
|
| 483 |
+
return $this->exitcode;
|
| 484 |
+
}
|
| 485 |
+
|
| 486 |
+
/**
|
| 487 |
+
* 获取退出文本
|
| 488 |
+
* @return null|string
|
| 489 |
+
*/
|
| 490 |
+
public function getExitCodeText()
|
| 491 |
+
{
|
| 492 |
+
if (null === $exitcode = $this->getExitCode()) {
|
| 493 |
+
return;
|
| 494 |
+
}
|
| 495 |
+
|
| 496 |
+
return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error';
|
| 497 |
+
}
|
| 498 |
+
|
| 499 |
+
/**
|
| 500 |
+
* 检查是否成功
|
| 501 |
+
* @return bool
|
| 502 |
+
*/
|
| 503 |
+
public function isSuccessful()
|
| 504 |
+
{
|
| 505 |
+
return 0 === $this->getExitCode();
|
| 506 |
+
}
|
| 507 |
+
|
| 508 |
+
/**
|
| 509 |
+
* 是否未捕获的信号已被终止子进程
|
| 510 |
+
* @return bool
|
| 511 |
+
*/
|
| 512 |
+
public function hasBeenSignaled()
|
| 513 |
+
{
|
| 514 |
+
$this->requireProcessIsTerminated(__FUNCTION__);
|
| 515 |
+
|
| 516 |
+
if ($this->isSigchildEnabled()) {
|
| 517 |
+
throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.');
|
| 518 |
+
}
|
| 519 |
+
|
| 520 |
+
$this->updateStatus(false);
|
| 521 |
+
|
| 522 |
+
return $this->processInformation['signaled'];
|
| 523 |
+
}
|
| 524 |
+
|
| 525 |
+
/**
|
| 526 |
+
* 返回导致子进程终止其执行的数。
|
| 527 |
+
* @return int
|
| 528 |
+
*/
|
| 529 |
+
public function getTermSignal()
|
| 530 |
+
{
|
| 531 |
+
$this->requireProcessIsTerminated(__FUNCTION__);
|
| 532 |
+
|
| 533 |
+
if ($this->isSigchildEnabled()) {
|
| 534 |
+
throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.');
|
| 535 |
+
}
|
| 536 |
+
|
| 537 |
+
$this->updateStatus(false);
|
| 538 |
+
|
| 539 |
+
return $this->processInformation['termsig'];
|
| 540 |
+
}
|
| 541 |
+
|
| 542 |
+
/**
|
| 543 |
+
* 检查子进程信号是否已停止
|
| 544 |
+
* @return bool
|
| 545 |
+
*/
|
| 546 |
+
public function hasBeenStopped()
|
| 547 |
+
{
|
| 548 |
+
$this->requireProcessIsTerminated(__FUNCTION__);
|
| 549 |
+
|
| 550 |
+
$this->updateStatus(false);
|
| 551 |
+
|
| 552 |
+
return $this->processInformation['stopped'];
|
| 553 |
+
}
|
| 554 |
+
|
| 555 |
+
/**
|
| 556 |
+
* 返回导致子进程停止其执行的数。
|
| 557 |
+
* @return int
|
| 558 |
+
*/
|
| 559 |
+
public function getStopSignal()
|
| 560 |
+
{
|
| 561 |
+
$this->requireProcessIsTerminated(__FUNCTION__);
|
| 562 |
+
|
| 563 |
+
$this->updateStatus(false);
|
| 564 |
+
|
| 565 |
+
return $this->processInformation['stopsig'];
|
| 566 |
+
}
|
| 567 |
+
|
| 568 |
+
/**
|
| 569 |
+
* 检查是否正在运行
|
| 570 |
+
* @return bool
|
| 571 |
+
*/
|
| 572 |
+
public function isRunning()
|
| 573 |
+
{
|
| 574 |
+
if (self::STATUS_STARTED !== $this->status) {
|
| 575 |
+
return false;
|
| 576 |
+
}
|
| 577 |
+
|
| 578 |
+
$this->updateStatus(false);
|
| 579 |
+
|
| 580 |
+
return $this->processInformation['running'];
|
| 581 |
+
}
|
| 582 |
+
|
| 583 |
+
/**
|
| 584 |
+
* 检查是否已开始
|
| 585 |
+
* @return bool
|
| 586 |
+
*/
|
| 587 |
+
public function isStarted()
|
| 588 |
+
{
|
| 589 |
+
return self::STATUS_READY != $this->status;
|
| 590 |
+
}
|
| 591 |
+
|
| 592 |
+
/**
|
| 593 |
+
* 检查是否已终止
|
| 594 |
+
* @return bool
|
| 595 |
+
*/
|
| 596 |
+
public function isTerminated()
|
| 597 |
+
{
|
| 598 |
+
$this->updateStatus(false);
|
| 599 |
+
|
| 600 |
+
return self::STATUS_TERMINATED == $this->status;
|
| 601 |
+
}
|
| 602 |
+
|
| 603 |
+
/**
|
| 604 |
+
* 获取当前的状态
|
| 605 |
+
* @return string
|
| 606 |
+
*/
|
| 607 |
+
public function getStatus()
|
| 608 |
+
{
|
| 609 |
+
$this->updateStatus(false);
|
| 610 |
+
|
| 611 |
+
return $this->status;
|
| 612 |
+
}
|
| 613 |
+
|
| 614 |
+
/**
|
| 615 |
+
* 终止进程
|
| 616 |
+
*/
|
| 617 |
+
public function stop()
|
| 618 |
+
{
|
| 619 |
+
if ($this->isRunning()) {
|
| 620 |
+
if ('\\' === DS && !$this->isSigchildEnabled()) {
|
| 621 |
+
exec(sprintf('taskkill /F /T /PID %d 2>&1', $this->getPid()), $output, $exitCode);
|
| 622 |
+
if ($exitCode > 0) {
|
| 623 |
+
throw new \RuntimeException('Unable to kill the process');
|
| 624 |
+
}
|
| 625 |
+
} else {
|
| 626 |
+
$pids = preg_split('/\s+/', `ps -o pid --no-heading --ppid {$this->getPid()}`);
|
| 627 |
+
foreach ($pids as $pid) {
|
| 628 |
+
if (is_numeric($pid)) {
|
| 629 |
+
posix_kill($pid, 9);
|
| 630 |
+
}
|
| 631 |
+
}
|
| 632 |
+
}
|
| 633 |
+
}
|
| 634 |
+
|
| 635 |
+
$this->updateStatus(false);
|
| 636 |
+
if ($this->processInformation['running']) {
|
| 637 |
+
$this->close();
|
| 638 |
+
}
|
| 639 |
+
|
| 640 |
+
return $this->exitcode;
|
| 641 |
+
}
|
| 642 |
+
|
| 643 |
+
/**
|
| 644 |
+
* 添加一行输出
|
| 645 |
+
* @param string $line
|
| 646 |
+
*/
|
| 647 |
+
public function addOutput($line)
|
| 648 |
+
{
|
| 649 |
+
$this->lastOutputTime = microtime(true);
|
| 650 |
+
$this->stdout .= $line;
|
| 651 |
+
}
|
| 652 |
+
|
| 653 |
+
/**
|
| 654 |
+
* 添加一行错误输出
|
| 655 |
+
* @param string $line
|
| 656 |
+
*/
|
| 657 |
+
public function addErrorOutput($line)
|
| 658 |
+
{
|
| 659 |
+
$this->lastOutputTime = microtime(true);
|
| 660 |
+
$this->stderr .= $line;
|
| 661 |
+
}
|
| 662 |
+
|
| 663 |
+
/**
|
| 664 |
+
* 获取被执行的指令
|
| 665 |
+
* @return string
|
| 666 |
+
*/
|
| 667 |
+
public function getCommandLine()
|
| 668 |
+
{
|
| 669 |
+
return $this->commandline;
|
| 670 |
+
}
|
| 671 |
+
|
| 672 |
+
/**
|
| 673 |
+
* 设置指令
|
| 674 |
+
* @param string $commandline
|
| 675 |
+
* @return self
|
| 676 |
+
*/
|
| 677 |
+
public function setCommandLine($commandline)
|
| 678 |
+
{
|
| 679 |
+
$this->commandline = $commandline;
|
| 680 |
+
|
| 681 |
+
return $this;
|
| 682 |
+
}
|
| 683 |
+
|
| 684 |
+
/**
|
| 685 |
+
* 获取超时时间
|
| 686 |
+
* @return float|null
|
| 687 |
+
*/
|
| 688 |
+
public function getTimeout()
|
| 689 |
+
{
|
| 690 |
+
return $this->timeout;
|
| 691 |
+
}
|
| 692 |
+
|
| 693 |
+
/**
|
| 694 |
+
* 获取idle超时时间
|
| 695 |
+
* @return float|null
|
| 696 |
+
*/
|
| 697 |
+
public function getIdleTimeout()
|
| 698 |
+
{
|
| 699 |
+
return $this->idleTimeout;
|
| 700 |
+
}
|
| 701 |
+
|
| 702 |
+
/**
|
| 703 |
+
* 设置超时时间
|
| 704 |
+
* @param int|float|null $timeout
|
| 705 |
+
* @return self
|
| 706 |
+
*/
|
| 707 |
+
public function setTimeout($timeout)
|
| 708 |
+
{
|
| 709 |
+
$this->timeout = $this->validateTimeout($timeout);
|
| 710 |
+
|
| 711 |
+
return $this;
|
| 712 |
+
}
|
| 713 |
+
|
| 714 |
+
/**
|
| 715 |
+
* 设置idle超时时间
|
| 716 |
+
* @param int|float|null $timeout
|
| 717 |
+
* @return self
|
| 718 |
+
*/
|
| 719 |
+
public function setIdleTimeout($timeout)
|
| 720 |
+
{
|
| 721 |
+
if (null !== $timeout && $this->outputDisabled) {
|
| 722 |
+
throw new \LogicException('Idle timeout can not be set while the output is disabled.');
|
| 723 |
+
}
|
| 724 |
+
|
| 725 |
+
$this->idleTimeout = $this->validateTimeout($timeout);
|
| 726 |
+
|
| 727 |
+
return $this;
|
| 728 |
+
}
|
| 729 |
+
|
| 730 |
+
/**
|
| 731 |
+
* 设置TTY
|
| 732 |
+
* @param bool $tty
|
| 733 |
+
* @return self
|
| 734 |
+
*/
|
| 735 |
+
public function setTty($tty)
|
| 736 |
+
{
|
| 737 |
+
if ('\\' === DS && $tty) {
|
| 738 |
+
throw new \RuntimeException('TTY mode is not supported on Windows platform.');
|
| 739 |
+
}
|
| 740 |
+
if ($tty && (!file_exists('/dev/tty') || !is_readable('/dev/tty'))) {
|
| 741 |
+
throw new \RuntimeException('TTY mode requires /dev/tty to be readable.');
|
| 742 |
+
}
|
| 743 |
+
|
| 744 |
+
$this->tty = (bool) $tty;
|
| 745 |
+
|
| 746 |
+
return $this;
|
| 747 |
+
}
|
| 748 |
+
|
| 749 |
+
/**
|
| 750 |
+
* 检查是否是tty模式
|
| 751 |
+
* @return bool
|
| 752 |
+
*/
|
| 753 |
+
public function isTty()
|
| 754 |
+
{
|
| 755 |
+
return $this->tty;
|
| 756 |
+
}
|
| 757 |
+
|
| 758 |
+
/**
|
| 759 |
+
* 设置pty模式
|
| 760 |
+
* @param bool $bool
|
| 761 |
+
* @return self
|
| 762 |
+
*/
|
| 763 |
+
public function setPty($bool)
|
| 764 |
+
{
|
| 765 |
+
$this->pty = (bool) $bool;
|
| 766 |
+
|
| 767 |
+
return $this;
|
| 768 |
+
}
|
| 769 |
+
|
| 770 |
+
/**
|
| 771 |
+
* 是否是pty模式
|
| 772 |
+
* @return bool
|
| 773 |
+
*/
|
| 774 |
+
public function isPty()
|
| 775 |
+
{
|
| 776 |
+
return $this->pty;
|
| 777 |
+
}
|
| 778 |
+
|
| 779 |
+
/**
|
| 780 |
+
* 获取工作目录
|
| 781 |
+
* @return string|null
|
| 782 |
+
*/
|
| 783 |
+
public function getWorkingDirectory()
|
| 784 |
+
{
|
| 785 |
+
if (null === $this->cwd) {
|
| 786 |
+
return getcwd() ?: null;
|
| 787 |
+
}
|
| 788 |
+
|
| 789 |
+
return $this->cwd;
|
| 790 |
+
}
|
| 791 |
+
|
| 792 |
+
/**
|
| 793 |
+
* 设置工作目录
|
| 794 |
+
* @param string $cwd
|
| 795 |
+
* @return self
|
| 796 |
+
*/
|
| 797 |
+
public function setWorkingDirectory($cwd)
|
| 798 |
+
{
|
| 799 |
+
$this->cwd = $cwd;
|
| 800 |
+
|
| 801 |
+
return $this;
|
| 802 |
+
}
|
| 803 |
+
|
| 804 |
+
/**
|
| 805 |
+
* 获取环境变量
|
| 806 |
+
* @return array
|
| 807 |
+
*/
|
| 808 |
+
public function getEnv()
|
| 809 |
+
{
|
| 810 |
+
return $this->env;
|
| 811 |
+
}
|
| 812 |
+
|
| 813 |
+
/**
|
| 814 |
+
* 设置环境变量
|
| 815 |
+
* @param array $env
|
| 816 |
+
* @return self
|
| 817 |
+
*/
|
| 818 |
+
public function setEnv(array $env)
|
| 819 |
+
{
|
| 820 |
+
$env = array_filter($env, function ($value) {
|
| 821 |
+
return !is_array($value);
|
| 822 |
+
});
|
| 823 |
+
|
| 824 |
+
$this->env = [];
|
| 825 |
+
foreach ($env as $key => $value) {
|
| 826 |
+
$this->env[(binary) $key] = (binary) $value;
|
| 827 |
+
}
|
| 828 |
+
|
| 829 |
+
return $this;
|
| 830 |
+
}
|
| 831 |
+
|
| 832 |
+
/**
|
| 833 |
+
* 获取输入
|
| 834 |
+
* @return null|string
|
| 835 |
+
*/
|
| 836 |
+
public function getInput()
|
| 837 |
+
{
|
| 838 |
+
return $this->input;
|
| 839 |
+
}
|
| 840 |
+
|
| 841 |
+
/**
|
| 842 |
+
* 设置输入
|
| 843 |
+
* @param mixed $input
|
| 844 |
+
* @return self
|
| 845 |
+
*/
|
| 846 |
+
public function setInput($input)
|
| 847 |
+
{
|
| 848 |
+
if ($this->isRunning()) {
|
| 849 |
+
throw new \LogicException('Input can not be set while the process is running.');
|
| 850 |
+
}
|
| 851 |
+
|
| 852 |
+
$this->input = Utils::validateInput(sprintf('%s::%s', __CLASS__, __FUNCTION__), $input);
|
| 853 |
+
|
| 854 |
+
return $this;
|
| 855 |
+
}
|
| 856 |
+
|
| 857 |
+
/**
|
| 858 |
+
* 获取proc_open的选项
|
| 859 |
+
* @return array
|
| 860 |
+
*/
|
| 861 |
+
public function getOptions()
|
| 862 |
+
{
|
| 863 |
+
return $this->options;
|
| 864 |
+
}
|
| 865 |
+
|
| 866 |
+
/**
|
| 867 |
+
* 设置proc_open的选项
|
| 868 |
+
* @param array $options
|
| 869 |
+
* @return self
|
| 870 |
+
*/
|
| 871 |
+
public function setOptions(array $options)
|
| 872 |
+
{
|
| 873 |
+
$this->options = $options;
|
| 874 |
+
|
| 875 |
+
return $this;
|
| 876 |
+
}
|
| 877 |
+
|
| 878 |
+
/**
|
| 879 |
+
* 是否兼容windows
|
| 880 |
+
* @return bool
|
| 881 |
+
*/
|
| 882 |
+
public function getEnhanceWindowsCompatibility()
|
| 883 |
+
{
|
| 884 |
+
return $this->enhanceWindowsCompatibility;
|
| 885 |
+
}
|
| 886 |
+
|
| 887 |
+
/**
|
| 888 |
+
* 设置是否兼容windows
|
| 889 |
+
* @param bool $enhance
|
| 890 |
+
* @return self
|
| 891 |
+
*/
|
| 892 |
+
public function setEnhanceWindowsCompatibility($enhance)
|
| 893 |
+
{
|
| 894 |
+
$this->enhanceWindowsCompatibility = (bool) $enhance;
|
| 895 |
+
|
| 896 |
+
return $this;
|
| 897 |
+
}
|
| 898 |
+
|
| 899 |
+
/**
|
| 900 |
+
* 返回是否 sigchild 兼容模式激活
|
| 901 |
+
* @return bool
|
| 902 |
+
*/
|
| 903 |
+
public function getEnhanceSigchildCompatibility()
|
| 904 |
+
{
|
| 905 |
+
return $this->enhanceSigchildCompatibility;
|
| 906 |
+
}
|
| 907 |
+
|
| 908 |
+
/**
|
| 909 |
+
* 激活 sigchild 兼容性模式。
|
| 910 |
+
* @param bool $enhance
|
| 911 |
+
* @return self
|
| 912 |
+
*/
|
| 913 |
+
public function setEnhanceSigchildCompatibility($enhance)
|
| 914 |
+
{
|
| 915 |
+
$this->enhanceSigchildCompatibility = (bool) $enhance;
|
| 916 |
+
|
| 917 |
+
return $this;
|
| 918 |
+
}
|
| 919 |
+
|
| 920 |
+
/**
|
| 921 |
+
* 是否超时
|
| 922 |
+
*/
|
| 923 |
+
public function checkTimeout()
|
| 924 |
+
{
|
| 925 |
+
if (self::STATUS_STARTED !== $this->status) {
|
| 926 |
+
return;
|
| 927 |
+
}
|
| 928 |
+
|
| 929 |
+
if (null !== $this->timeout && $this->timeout < microtime(true) - $this->starttime) {
|
| 930 |
+
$this->stop();
|
| 931 |
+
|
| 932 |
+
throw new ProcessTimeoutException($this, ProcessTimeoutException::TYPE_GENERAL);
|
| 933 |
+
}
|
| 934 |
+
|
| 935 |
+
if (null !== $this->idleTimeout && $this->idleTimeout < microtime(true) - $this->lastOutputTime) {
|
| 936 |
+
$this->stop();
|
| 937 |
+
|
| 938 |
+
throw new ProcessTimeoutException($this, ProcessTimeoutException::TYPE_IDLE);
|
| 939 |
+
}
|
| 940 |
+
}
|
| 941 |
+
|
| 942 |
+
/**
|
| 943 |
+
* 是否支持pty
|
| 944 |
+
* @return bool
|
| 945 |
+
*/
|
| 946 |
+
public static function isPtySupported()
|
| 947 |
+
{
|
| 948 |
+
static $result;
|
| 949 |
+
|
| 950 |
+
if (null !== $result) {
|
| 951 |
+
return $result;
|
| 952 |
+
}
|
| 953 |
+
|
| 954 |
+
if ('\\' === DS) {
|
| 955 |
+
return $result = false;
|
| 956 |
+
}
|
| 957 |
+
|
| 958 |
+
$proc = @proc_open('echo 1', [['pty'], ['pty'], ['pty']], $pipes);
|
| 959 |
+
if (is_resource($proc)) {
|
| 960 |
+
proc_close($proc);
|
| 961 |
+
|
| 962 |
+
return $result = true;
|
| 963 |
+
}
|
| 964 |
+
|
| 965 |
+
return $result = false;
|
| 966 |
+
}
|
| 967 |
+
|
| 968 |
+
/**
|
| 969 |
+
* 创建所需的 proc_open 的描述符
|
| 970 |
+
* @return array
|
| 971 |
+
*/
|
| 972 |
+
private function getDescriptors()
|
| 973 |
+
{
|
| 974 |
+
if ('\\' === DS) {
|
| 975 |
+
$this->processPipes = WindowsPipes::create($this, $this->input);
|
| 976 |
+
} else {
|
| 977 |
+
$this->processPipes = UnixPipes::create($this, $this->input);
|
| 978 |
+
}
|
| 979 |
+
$descriptors = $this->processPipes->getDescriptors($this->outputDisabled);
|
| 980 |
+
|
| 981 |
+
if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
|
| 982 |
+
|
| 983 |
+
$descriptors = array_merge($descriptors, [['pipe', 'w']]);
|
| 984 |
+
|
| 985 |
+
$this->commandline = '(' . $this->commandline . ') 3>/dev/null; code=$?; echo $code >&3; exit $code';
|
| 986 |
+
}
|
| 987 |
+
|
| 988 |
+
return $descriptors;
|
| 989 |
+
}
|
| 990 |
+
|
| 991 |
+
/**
|
| 992 |
+
* 建立 wait () 使用的回调。
|
| 993 |
+
* @param callable|null $callback
|
| 994 |
+
* @return callable
|
| 995 |
+
*/
|
| 996 |
+
protected function buildCallback($callback)
|
| 997 |
+
{
|
| 998 |
+
$out = self::OUT;
|
| 999 |
+
$callback = function ($type, $data) use ($callback, $out) {
|
| 1000 |
+
if ($out == $type) {
|
| 1001 |
+
$this->addOutput($data);
|
| 1002 |
+
} else {
|
| 1003 |
+
$this->addErrorOutput($data);
|
| 1004 |
+
}
|
| 1005 |
+
|
| 1006 |
+
if (null !== $callback) {
|
| 1007 |
+
call_user_func($callback, $type, $data);
|
| 1008 |
+
}
|
| 1009 |
+
};
|
| 1010 |
+
|
| 1011 |
+
return $callback;
|
| 1012 |
+
}
|
| 1013 |
+
|
| 1014 |
+
/**
|
| 1015 |
+
* 更新状态
|
| 1016 |
+
* @param bool $blocking
|
| 1017 |
+
*/
|
| 1018 |
+
protected function updateStatus($blocking)
|
| 1019 |
+
{
|
| 1020 |
+
if (self::STATUS_STARTED !== $this->status) {
|
| 1021 |
+
return;
|
| 1022 |
+
}
|
| 1023 |
+
|
| 1024 |
+
$this->processInformation = proc_get_status($this->process);
|
| 1025 |
+
$this->captureExitCode();
|
| 1026 |
+
|
| 1027 |
+
$this->readPipes($blocking, '\\' === DS ? !$this->processInformation['running'] : true);
|
| 1028 |
+
|
| 1029 |
+
if (!$this->processInformation['running']) {
|
| 1030 |
+
$this->close();
|
| 1031 |
+
}
|
| 1032 |
+
}
|
| 1033 |
+
|
| 1034 |
+
/**
|
| 1035 |
+
* 是否开启 '--enable-sigchild'
|
| 1036 |
+
* @return bool
|
| 1037 |
+
*/
|
| 1038 |
+
protected function isSigchildEnabled()
|
| 1039 |
+
{
|
| 1040 |
+
if (null !== self::$sigchild) {
|
| 1041 |
+
return self::$sigchild;
|
| 1042 |
+
}
|
| 1043 |
+
|
| 1044 |
+
if (!function_exists('phpinfo')) {
|
| 1045 |
+
return self::$sigchild = false;
|
| 1046 |
+
}
|
| 1047 |
+
|
| 1048 |
+
ob_start();
|
| 1049 |
+
phpinfo(INFO_GENERAL);
|
| 1050 |
+
|
| 1051 |
+
return self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild');
|
| 1052 |
+
}
|
| 1053 |
+
|
| 1054 |
+
/**
|
| 1055 |
+
* 验证是否超时
|
| 1056 |
+
* @param int|float|null $timeout
|
| 1057 |
+
* @return float|null
|
| 1058 |
+
*/
|
| 1059 |
+
private function validateTimeout($timeout)
|
| 1060 |
+
{
|
| 1061 |
+
$timeout = (float) $timeout;
|
| 1062 |
+
|
| 1063 |
+
if (0.0 === $timeout) {
|
| 1064 |
+
$timeout = null;
|
| 1065 |
+
} elseif ($timeout < 0) {
|
| 1066 |
+
throw new \InvalidArgumentException('The timeout value must be a valid positive integer or float number.');
|
| 1067 |
+
}
|
| 1068 |
+
|
| 1069 |
+
return $timeout;
|
| 1070 |
+
}
|
| 1071 |
+
|
| 1072 |
+
/**
|
| 1073 |
+
* 读取pipes
|
| 1074 |
+
* @param bool $blocking
|
| 1075 |
+
* @param bool $close
|
| 1076 |
+
*/
|
| 1077 |
+
private function readPipes($blocking, $close)
|
| 1078 |
+
{
|
| 1079 |
+
$result = $this->processPipes->readAndWrite($blocking, $close);
|
| 1080 |
+
|
| 1081 |
+
$callback = $this->callback;
|
| 1082 |
+
foreach ($result as $type => $data) {
|
| 1083 |
+
if (3 == $type) {
|
| 1084 |
+
$this->fallbackExitcode = (int) $data;
|
| 1085 |
+
} else {
|
| 1086 |
+
$callback(self::STDOUT === $type ? self::OUT : self::ERR, $data);
|
| 1087 |
+
}
|
| 1088 |
+
}
|
| 1089 |
+
}
|
| 1090 |
+
|
| 1091 |
+
/**
|
| 1092 |
+
* 捕获退出码
|
| 1093 |
+
*/
|
| 1094 |
+
private function captureExitCode()
|
| 1095 |
+
{
|
| 1096 |
+
if (isset($this->processInformation['exitcode']) && -1 != $this->processInformation['exitcode']) {
|
| 1097 |
+
$this->exitcode = $this->processInformation['exitcode'];
|
| 1098 |
+
}
|
| 1099 |
+
}
|
| 1100 |
+
|
| 1101 |
+
/**
|
| 1102 |
+
* 关闭资源
|
| 1103 |
+
* @return int 退出码
|
| 1104 |
+
*/
|
| 1105 |
+
private function close()
|
| 1106 |
+
{
|
| 1107 |
+
$this->processPipes->close();
|
| 1108 |
+
if (is_resource($this->process)) {
|
| 1109 |
+
$exitcode = proc_close($this->process);
|
| 1110 |
+
} else {
|
| 1111 |
+
$exitcode = -1;
|
| 1112 |
+
}
|
| 1113 |
+
|
| 1114 |
+
$this->exitcode = -1 !== $exitcode ? $exitcode : (null !== $this->exitcode ? $this->exitcode : -1);
|
| 1115 |
+
$this->status = self::STATUS_TERMINATED;
|
| 1116 |
+
|
| 1117 |
+
if (-1 === $this->exitcode && null !== $this->fallbackExitcode) {
|
| 1118 |
+
$this->exitcode = $this->fallbackExitcode;
|
| 1119 |
+
} elseif (-1 === $this->exitcode && $this->processInformation['signaled']
|
| 1120 |
+
&& 0 < $this->processInformation['termsig']
|
| 1121 |
+
) {
|
| 1122 |
+
$this->exitcode = 128 + $this->processInformation['termsig'];
|
| 1123 |
+
}
|
| 1124 |
+
|
| 1125 |
+
return $this->exitcode;
|
| 1126 |
+
}
|
| 1127 |
+
|
| 1128 |
+
/**
|
| 1129 |
+
* 重置数据
|
| 1130 |
+
*/
|
| 1131 |
+
private function resetProcessData()
|
| 1132 |
+
{
|
| 1133 |
+
$this->starttime = null;
|
| 1134 |
+
$this->callback = null;
|
| 1135 |
+
$this->exitcode = null;
|
| 1136 |
+
$this->fallbackExitcode = null;
|
| 1137 |
+
$this->processInformation = null;
|
| 1138 |
+
$this->stdout = null;
|
| 1139 |
+
$this->stderr = null;
|
| 1140 |
+
$this->process = null;
|
| 1141 |
+
$this->latestSignal = null;
|
| 1142 |
+
$this->status = self::STATUS_READY;
|
| 1143 |
+
$this->incrementalOutputOffset = 0;
|
| 1144 |
+
$this->incrementalErrorOutputOffset = 0;
|
| 1145 |
+
}
|
| 1146 |
+
|
| 1147 |
+
/**
|
| 1148 |
+
* 将一个 POSIX 信号发送到进程中。
|
| 1149 |
+
* @param int $signal
|
| 1150 |
+
* @param bool $throwException
|
| 1151 |
+
* @return bool
|
| 1152 |
+
*/
|
| 1153 |
+
private function doSignal($signal, $throwException)
|
| 1154 |
+
{
|
| 1155 |
+
if (!$this->isRunning()) {
|
| 1156 |
+
if ($throwException) {
|
| 1157 |
+
throw new \LogicException('Can not send signal on a non running process.');
|
| 1158 |
+
}
|
| 1159 |
+
|
| 1160 |
+
return false;
|
| 1161 |
+
}
|
| 1162 |
+
|
| 1163 |
+
if ($this->isSigchildEnabled()) {
|
| 1164 |
+
if ($throwException) {
|
| 1165 |
+
throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
|
| 1166 |
+
}
|
| 1167 |
+
|
| 1168 |
+
return false;
|
| 1169 |
+
}
|
| 1170 |
+
|
| 1171 |
+
if (true !== @proc_terminate($this->process, $signal)) {
|
| 1172 |
+
if ($throwException) {
|
| 1173 |
+
throw new \RuntimeException(sprintf('Error while sending signal `%s`.', $signal));
|
| 1174 |
+
}
|
| 1175 |
+
|
| 1176 |
+
return false;
|
| 1177 |
+
}
|
| 1178 |
+
|
| 1179 |
+
$this->latestSignal = $signal;
|
| 1180 |
+
|
| 1181 |
+
return true;
|
| 1182 |
+
}
|
| 1183 |
+
|
| 1184 |
+
/**
|
| 1185 |
+
* 确保进程已经开启
|
| 1186 |
+
* @param string $functionName
|
| 1187 |
+
*/
|
| 1188 |
+
private function requireProcessIsStarted($functionName)
|
| 1189 |
+
{
|
| 1190 |
+
if (!$this->isStarted()) {
|
| 1191 |
+
throw new \LogicException(sprintf('Process must be started before calling %s.', $functionName));
|
| 1192 |
+
}
|
| 1193 |
+
}
|
| 1194 |
+
|
| 1195 |
+
/**
|
| 1196 |
+
* 确保进程已经终止
|
| 1197 |
+
* @param string $functionName
|
| 1198 |
+
*/
|
| 1199 |
+
private function requireProcessIsTerminated($functionName)
|
| 1200 |
+
{
|
| 1201 |
+
if (!$this->isTerminated()) {
|
| 1202 |
+
throw new \LogicException(sprintf('Process must be terminated before calling %s.', $functionName));
|
| 1203 |
+
}
|
| 1204 |
+
}
|
| 1205 |
+
}
|
thinkphp/library/think/Request.php
ADDED
|
@@ -0,0 +1,1690 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
class Request
|
| 15 |
+
{
|
| 16 |
+
/**
|
| 17 |
+
* @var object 对象实例
|
| 18 |
+
*/
|
| 19 |
+
protected static $instance;
|
| 20 |
+
|
| 21 |
+
protected $method;
|
| 22 |
+
/**
|
| 23 |
+
* @var string 域名(含协议和端口)
|
| 24 |
+
*/
|
| 25 |
+
protected $domain;
|
| 26 |
+
|
| 27 |
+
/**
|
| 28 |
+
* @var string URL地址
|
| 29 |
+
*/
|
| 30 |
+
protected $url;
|
| 31 |
+
|
| 32 |
+
/**
|
| 33 |
+
* @var string 基础URL
|
| 34 |
+
*/
|
| 35 |
+
protected $baseUrl;
|
| 36 |
+
|
| 37 |
+
/**
|
| 38 |
+
* @var string 当前执行的文件
|
| 39 |
+
*/
|
| 40 |
+
protected $baseFile;
|
| 41 |
+
|
| 42 |
+
/**
|
| 43 |
+
* @var string 访问的ROOT地址
|
| 44 |
+
*/
|
| 45 |
+
protected $root;
|
| 46 |
+
|
| 47 |
+
/**
|
| 48 |
+
* @var string pathinfo
|
| 49 |
+
*/
|
| 50 |
+
protected $pathinfo;
|
| 51 |
+
|
| 52 |
+
/**
|
| 53 |
+
* @var string pathinfo(不含后缀)
|
| 54 |
+
*/
|
| 55 |
+
protected $path;
|
| 56 |
+
|
| 57 |
+
/**
|
| 58 |
+
* @var array 当前路由信息
|
| 59 |
+
*/
|
| 60 |
+
protected $routeInfo = [];
|
| 61 |
+
|
| 62 |
+
/**
|
| 63 |
+
* @var array 环境变量
|
| 64 |
+
*/
|
| 65 |
+
protected $env;
|
| 66 |
+
|
| 67 |
+
/**
|
| 68 |
+
* @var array 当前调度信息
|
| 69 |
+
*/
|
| 70 |
+
protected $dispatch = [];
|
| 71 |
+
protected $module;
|
| 72 |
+
protected $controller;
|
| 73 |
+
protected $action;
|
| 74 |
+
// 当前语言集
|
| 75 |
+
protected $langset;
|
| 76 |
+
|
| 77 |
+
/**
|
| 78 |
+
* @var array 请求参数
|
| 79 |
+
*/
|
| 80 |
+
protected $param = [];
|
| 81 |
+
protected $get = [];
|
| 82 |
+
protected $post = [];
|
| 83 |
+
protected $request = [];
|
| 84 |
+
protected $route = [];
|
| 85 |
+
protected $put;
|
| 86 |
+
protected $session = [];
|
| 87 |
+
protected $file = [];
|
| 88 |
+
protected $cookie = [];
|
| 89 |
+
protected $server = [];
|
| 90 |
+
protected $header = [];
|
| 91 |
+
|
| 92 |
+
/**
|
| 93 |
+
* @var array 资源类型
|
| 94 |
+
*/
|
| 95 |
+
protected $mimeType = [
|
| 96 |
+
'xml' => 'application/xml,text/xml,application/x-xml',
|
| 97 |
+
'json' => 'application/json,text/x-json,application/jsonrequest,text/json',
|
| 98 |
+
'js' => 'text/javascript,application/javascript,application/x-javascript',
|
| 99 |
+
'css' => 'text/css',
|
| 100 |
+
'rss' => 'application/rss+xml',
|
| 101 |
+
'yaml' => 'application/x-yaml,text/yaml',
|
| 102 |
+
'atom' => 'application/atom+xml',
|
| 103 |
+
'pdf' => 'application/pdf',
|
| 104 |
+
'text' => 'text/plain',
|
| 105 |
+
'image' => 'image/png,image/jpg,image/jpeg,image/pjpeg,image/gif,image/webp,image/*',
|
| 106 |
+
'csv' => 'text/csv',
|
| 107 |
+
'html' => 'text/html,application/xhtml+xml,*/*',
|
| 108 |
+
];
|
| 109 |
+
|
| 110 |
+
protected $content;
|
| 111 |
+
|
| 112 |
+
// 全局过滤规则
|
| 113 |
+
protected $filter;
|
| 114 |
+
// Hook扩展方法
|
| 115 |
+
protected static $hook = [];
|
| 116 |
+
// 绑定的属性
|
| 117 |
+
protected $bind = [];
|
| 118 |
+
// php://input
|
| 119 |
+
protected $input;
|
| 120 |
+
// 请求缓存
|
| 121 |
+
protected $cache;
|
| 122 |
+
// 缓存是否检查
|
| 123 |
+
protected $isCheckCache;
|
| 124 |
+
/**
|
| 125 |
+
* 是否合并Param
|
| 126 |
+
* @var bool
|
| 127 |
+
*/
|
| 128 |
+
protected $mergeParam = false;
|
| 129 |
+
|
| 130 |
+
/**
|
| 131 |
+
* 构造函数
|
| 132 |
+
* @access protected
|
| 133 |
+
* @param array $options 参数
|
| 134 |
+
*/
|
| 135 |
+
protected function __construct($options = [])
|
| 136 |
+
{
|
| 137 |
+
foreach ($options as $name => $item) {
|
| 138 |
+
if (property_exists($this, $name)) {
|
| 139 |
+
$this->$name = $item;
|
| 140 |
+
}
|
| 141 |
+
}
|
| 142 |
+
if (is_null($this->filter)) {
|
| 143 |
+
$this->filter = Config::get('default_filter');
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
// 保存 php://input
|
| 147 |
+
$this->input = file_get_contents('php://input');
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
public function __call($method, $args)
|
| 151 |
+
{
|
| 152 |
+
if (array_key_exists($method, self::$hook)) {
|
| 153 |
+
array_unshift($args, $this);
|
| 154 |
+
return call_user_func_array(self::$hook[$method], $args);
|
| 155 |
+
} else {
|
| 156 |
+
throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
|
| 157 |
+
}
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
/**
|
| 161 |
+
* Hook 方法注入
|
| 162 |
+
* @access public
|
| 163 |
+
* @param string|array $method 方法名
|
| 164 |
+
* @param mixed $callback callable
|
| 165 |
+
* @return void
|
| 166 |
+
*/
|
| 167 |
+
public static function hook($method, $callback = null)
|
| 168 |
+
{
|
| 169 |
+
if (is_array($method)) {
|
| 170 |
+
self::$hook = array_merge(self::$hook, $method);
|
| 171 |
+
} else {
|
| 172 |
+
self::$hook[$method] = $callback;
|
| 173 |
+
}
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
/**
|
| 177 |
+
* 初始化
|
| 178 |
+
* @access public
|
| 179 |
+
* @param array $options 参数
|
| 180 |
+
* @return \think\Request
|
| 181 |
+
*/
|
| 182 |
+
public static function instance($options = [])
|
| 183 |
+
{
|
| 184 |
+
if (is_null(self::$instance)) {
|
| 185 |
+
self::$instance = new static($options);
|
| 186 |
+
}
|
| 187 |
+
return self::$instance;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
/**
|
| 191 |
+
* 销毁当前请求对象
|
| 192 |
+
* @access public
|
| 193 |
+
* @return void
|
| 194 |
+
*/
|
| 195 |
+
public static function destroy()
|
| 196 |
+
{
|
| 197 |
+
if (!is_null(self::$instance)) {
|
| 198 |
+
self::$instance = null;
|
| 199 |
+
}
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
/**
|
| 203 |
+
* 创建一个URL请求
|
| 204 |
+
* @access public
|
| 205 |
+
* @param string $uri URL地址
|
| 206 |
+
* @param string $method 请求类型
|
| 207 |
+
* @param array $params 请求参数
|
| 208 |
+
* @param array $cookie
|
| 209 |
+
* @param array $files
|
| 210 |
+
* @param array $server
|
| 211 |
+
* @param string $content
|
| 212 |
+
* @return \think\Request
|
| 213 |
+
*/
|
| 214 |
+
public static function create($uri, $method = 'GET', $params = [], $cookie = [], $files = [], $server = [], $content = null)
|
| 215 |
+
{
|
| 216 |
+
$server['PATH_INFO'] = '';
|
| 217 |
+
$server['REQUEST_METHOD'] = strtoupper($method);
|
| 218 |
+
$info = parse_url($uri);
|
| 219 |
+
if (isset($info['host'])) {
|
| 220 |
+
$server['SERVER_NAME'] = $info['host'];
|
| 221 |
+
$server['HTTP_HOST'] = $info['host'];
|
| 222 |
+
}
|
| 223 |
+
if (isset($info['scheme'])) {
|
| 224 |
+
if ('https' === $info['scheme']) {
|
| 225 |
+
$server['HTTPS'] = 'on';
|
| 226 |
+
$server['SERVER_PORT'] = 443;
|
| 227 |
+
} else {
|
| 228 |
+
unset($server['HTTPS']);
|
| 229 |
+
$server['SERVER_PORT'] = 80;
|
| 230 |
+
}
|
| 231 |
+
}
|
| 232 |
+
if (isset($info['port'])) {
|
| 233 |
+
$server['SERVER_PORT'] = $info['port'];
|
| 234 |
+
$server['HTTP_HOST'] = $server['HTTP_HOST'] . ':' . $info['port'];
|
| 235 |
+
}
|
| 236 |
+
if (isset($info['user'])) {
|
| 237 |
+
$server['PHP_AUTH_USER'] = $info['user'];
|
| 238 |
+
}
|
| 239 |
+
if (isset($info['pass'])) {
|
| 240 |
+
$server['PHP_AUTH_PW'] = $info['pass'];
|
| 241 |
+
}
|
| 242 |
+
if (!isset($info['path'])) {
|
| 243 |
+
$info['path'] = '/';
|
| 244 |
+
}
|
| 245 |
+
$options = [];
|
| 246 |
+
$options[strtolower($method)] = $params;
|
| 247 |
+
$queryString = '';
|
| 248 |
+
if (isset($info['query'])) {
|
| 249 |
+
parse_str(html_entity_decode($info['query']), $query);
|
| 250 |
+
if (!empty($params)) {
|
| 251 |
+
$params = array_replace($query, $params);
|
| 252 |
+
$queryString = http_build_query($params, '', '&');
|
| 253 |
+
} else {
|
| 254 |
+
$params = $query;
|
| 255 |
+
$queryString = $info['query'];
|
| 256 |
+
}
|
| 257 |
+
} elseif (!empty($params)) {
|
| 258 |
+
$queryString = http_build_query($params, '', '&');
|
| 259 |
+
}
|
| 260 |
+
if ($queryString) {
|
| 261 |
+
parse_str($queryString, $get);
|
| 262 |
+
$options['get'] = isset($options['get']) ? array_merge($get, $options['get']) : $get;
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
$server['REQUEST_URI'] = $info['path'] . ('' !== $queryString ? '?' . $queryString : '');
|
| 266 |
+
$server['QUERY_STRING'] = $queryString;
|
| 267 |
+
$options['cookie'] = $cookie;
|
| 268 |
+
$options['param'] = $params;
|
| 269 |
+
$options['file'] = $files;
|
| 270 |
+
$options['server'] = $server;
|
| 271 |
+
$options['url'] = $server['REQUEST_URI'];
|
| 272 |
+
$options['baseUrl'] = $info['path'];
|
| 273 |
+
$options['pathinfo'] = '/' == $info['path'] ? '/' : ltrim($info['path'], '/');
|
| 274 |
+
$options['method'] = $server['REQUEST_METHOD'];
|
| 275 |
+
$options['domain'] = isset($info['scheme']) ? $info['scheme'] . '://' . $server['HTTP_HOST'] : '';
|
| 276 |
+
$options['content'] = $content;
|
| 277 |
+
self::$instance = new self($options);
|
| 278 |
+
return self::$instance;
|
| 279 |
+
}
|
| 280 |
+
|
| 281 |
+
/**
|
| 282 |
+
* 设置或获取当前包含协议的域名
|
| 283 |
+
* @access public
|
| 284 |
+
* @param string $domain 域名
|
| 285 |
+
* @return string
|
| 286 |
+
*/
|
| 287 |
+
public function domain($domain = null)
|
| 288 |
+
{
|
| 289 |
+
if (!is_null($domain)) {
|
| 290 |
+
$this->domain = $domain;
|
| 291 |
+
return $this;
|
| 292 |
+
} elseif (!$this->domain) {
|
| 293 |
+
$this->domain = $this->scheme() . '://' . $this->host();
|
| 294 |
+
}
|
| 295 |
+
return $this->domain;
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
/**
|
| 299 |
+
* 设置或获取当前完整URL 包括QUERY_STRING
|
| 300 |
+
* @access public
|
| 301 |
+
* @param string|true $url URL地址 true 带域名获取
|
| 302 |
+
* @return string
|
| 303 |
+
*/
|
| 304 |
+
public function url($url = null)
|
| 305 |
+
{
|
| 306 |
+
if (!is_null($url) && true !== $url) {
|
| 307 |
+
$this->url = $url;
|
| 308 |
+
return $this;
|
| 309 |
+
} elseif (!$this->url) {
|
| 310 |
+
if (IS_CLI) {
|
| 311 |
+
$this->url = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : '';
|
| 312 |
+
} elseif (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
|
| 313 |
+
$this->url = $_SERVER['HTTP_X_REWRITE_URL'];
|
| 314 |
+
} elseif (isset($_SERVER['REQUEST_URI'])) {
|
| 315 |
+
$this->url = $_SERVER['REQUEST_URI'];
|
| 316 |
+
} elseif (isset($_SERVER['ORIG_PATH_INFO'])) {
|
| 317 |
+
$this->url = $_SERVER['ORIG_PATH_INFO'] . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '');
|
| 318 |
+
} else {
|
| 319 |
+
$this->url = '';
|
| 320 |
+
}
|
| 321 |
+
}
|
| 322 |
+
return true === $url ? $this->domain() . $this->url : $this->url;
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
/**
|
| 326 |
+
* 设置或获取当前URL 不含QUERY_STRING
|
| 327 |
+
* @access public
|
| 328 |
+
* @param string $url URL地址
|
| 329 |
+
* @return string
|
| 330 |
+
*/
|
| 331 |
+
public function baseUrl($url = null)
|
| 332 |
+
{
|
| 333 |
+
if (!is_null($url) && true !== $url) {
|
| 334 |
+
$this->baseUrl = $url;
|
| 335 |
+
return $this;
|
| 336 |
+
} elseif (!$this->baseUrl) {
|
| 337 |
+
$str = $this->url();
|
| 338 |
+
$this->baseUrl = strpos($str, '?') ? strstr($str, '?', true) : $str;
|
| 339 |
+
}
|
| 340 |
+
return true === $url ? $this->domain() . $this->baseUrl : $this->baseUrl;
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
/**
|
| 344 |
+
* 设置或获取当前执行的文件 SCRIPT_NAME
|
| 345 |
+
* @access public
|
| 346 |
+
* @param string $file 当前执行的文件
|
| 347 |
+
* @return string
|
| 348 |
+
*/
|
| 349 |
+
public function baseFile($file = null)
|
| 350 |
+
{
|
| 351 |
+
if (!is_null($file) && true !== $file) {
|
| 352 |
+
$this->baseFile = $file;
|
| 353 |
+
return $this;
|
| 354 |
+
} elseif (!$this->baseFile) {
|
| 355 |
+
$url = '';
|
| 356 |
+
if (!IS_CLI) {
|
| 357 |
+
$script_name = basename($_SERVER['SCRIPT_FILENAME']);
|
| 358 |
+
if (basename($_SERVER['SCRIPT_NAME']) === $script_name) {
|
| 359 |
+
$url = $_SERVER['SCRIPT_NAME'];
|
| 360 |
+
} elseif (basename($_SERVER['PHP_SELF']) === $script_name) {
|
| 361 |
+
$url = $_SERVER['PHP_SELF'];
|
| 362 |
+
} elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $script_name) {
|
| 363 |
+
$url = $_SERVER['ORIG_SCRIPT_NAME'];
|
| 364 |
+
} elseif (($pos = strpos($_SERVER['PHP_SELF'], '/' . $script_name)) !== false) {
|
| 365 |
+
$url = substr($_SERVER['SCRIPT_NAME'], 0, $pos) . '/' . $script_name;
|
| 366 |
+
} elseif (isset($_SERVER['DOCUMENT_ROOT']) && strpos($_SERVER['SCRIPT_FILENAME'], $_SERVER['DOCUMENT_ROOT']) === 0) {
|
| 367 |
+
$url = str_replace('\\', '/', str_replace($_SERVER['DOCUMENT_ROOT'], '', $_SERVER['SCRIPT_FILENAME']));
|
| 368 |
+
}
|
| 369 |
+
}
|
| 370 |
+
$this->baseFile = $url;
|
| 371 |
+
}
|
| 372 |
+
return true === $file ? $this->domain() . $this->baseFile : $this->baseFile;
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
/**
|
| 376 |
+
* 设置或获取URL访问根地址
|
| 377 |
+
* @access public
|
| 378 |
+
* @param string $url URL地址
|
| 379 |
+
* @return string
|
| 380 |
+
*/
|
| 381 |
+
public function root($url = null)
|
| 382 |
+
{
|
| 383 |
+
if (!is_null($url) && true !== $url) {
|
| 384 |
+
$this->root = $url;
|
| 385 |
+
return $this;
|
| 386 |
+
} elseif (!$this->root) {
|
| 387 |
+
$file = $this->baseFile();
|
| 388 |
+
if ($file && 0 !== strpos($this->url(), $file)) {
|
| 389 |
+
$file = str_replace('\\', '/', dirname($file));
|
| 390 |
+
}
|
| 391 |
+
$this->root = rtrim($file, '/');
|
| 392 |
+
}
|
| 393 |
+
return true === $url ? $this->domain() . $this->root : $this->root;
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
/**
|
| 397 |
+
* 获取当前请求URL的pathinfo信息(含URL后缀)
|
| 398 |
+
* @access public
|
| 399 |
+
* @return string
|
| 400 |
+
*/
|
| 401 |
+
public function pathinfo()
|
| 402 |
+
{
|
| 403 |
+
if (is_null($this->pathinfo)) {
|
| 404 |
+
if (isset($_GET[Config::get('var_pathinfo')])) {
|
| 405 |
+
// 判断URL里面是否有兼容模式参数
|
| 406 |
+
$_SERVER['PATH_INFO'] = $_GET[Config::get('var_pathinfo')];
|
| 407 |
+
unset($_GET[Config::get('var_pathinfo')]);
|
| 408 |
+
} elseif (IS_CLI) {
|
| 409 |
+
// CLI模式下 index.php module/controller/action/params/...
|
| 410 |
+
$_SERVER['PATH_INFO'] = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : '';
|
| 411 |
+
}
|
| 412 |
+
|
| 413 |
+
// 分析PATHINFO信息
|
| 414 |
+
if (!isset($_SERVER['PATH_INFO'])) {
|
| 415 |
+
foreach (Config::get('pathinfo_fetch') as $type) {
|
| 416 |
+
if (!empty($_SERVER[$type])) {
|
| 417 |
+
$_SERVER['PATH_INFO'] = (0 === strpos($_SERVER[$type], $_SERVER['SCRIPT_NAME'])) ?
|
| 418 |
+
substr($_SERVER[$type], strlen($_SERVER['SCRIPT_NAME'])) : $_SERVER[$type];
|
| 419 |
+
break;
|
| 420 |
+
}
|
| 421 |
+
}
|
| 422 |
+
}
|
| 423 |
+
$this->pathinfo = empty($_SERVER['PATH_INFO']) ? '/' : ltrim($_SERVER['PATH_INFO'], '/');
|
| 424 |
+
}
|
| 425 |
+
return $this->pathinfo;
|
| 426 |
+
}
|
| 427 |
+
|
| 428 |
+
/**
|
| 429 |
+
* 获取当前请求URL的pathinfo信息(不含URL后缀)
|
| 430 |
+
* @access public
|
| 431 |
+
* @return string
|
| 432 |
+
*/
|
| 433 |
+
public function path()
|
| 434 |
+
{
|
| 435 |
+
if (is_null($this->path)) {
|
| 436 |
+
$suffix = Config::get('url_html_suffix');
|
| 437 |
+
$pathinfo = $this->pathinfo();
|
| 438 |
+
if (false === $suffix) {
|
| 439 |
+
// 禁止伪静态访问
|
| 440 |
+
$this->path = $pathinfo;
|
| 441 |
+
} elseif ($suffix) {
|
| 442 |
+
// 去除正常的URL后缀
|
| 443 |
+
$this->path = preg_replace('/\.(' . ltrim($suffix, '.') . ')$/i', '', $pathinfo);
|
| 444 |
+
} else {
|
| 445 |
+
// 允许任何后缀访问
|
| 446 |
+
$this->path = preg_replace('/\.' . $this->ext() . '$/i', '', $pathinfo);
|
| 447 |
+
}
|
| 448 |
+
}
|
| 449 |
+
return $this->path;
|
| 450 |
+
}
|
| 451 |
+
|
| 452 |
+
/**
|
| 453 |
+
* 当前URL的访问后缀
|
| 454 |
+
* @access public
|
| 455 |
+
* @return string
|
| 456 |
+
*/
|
| 457 |
+
public function ext()
|
| 458 |
+
{
|
| 459 |
+
return pathinfo($this->pathinfo(), PATHINFO_EXTENSION);
|
| 460 |
+
}
|
| 461 |
+
|
| 462 |
+
/**
|
| 463 |
+
* 获取当前请求的时间
|
| 464 |
+
* @access public
|
| 465 |
+
* @param bool $float 是否使用浮点类型
|
| 466 |
+
* @return integer|float
|
| 467 |
+
*/
|
| 468 |
+
public function time($float = false)
|
| 469 |
+
{
|
| 470 |
+
return $float ? $_SERVER['REQUEST_TIME_FLOAT'] : $_SERVER['REQUEST_TIME'];
|
| 471 |
+
}
|
| 472 |
+
|
| 473 |
+
/**
|
| 474 |
+
* 当前请求的资源类型
|
| 475 |
+
* @access public
|
| 476 |
+
* @return false|string
|
| 477 |
+
*/
|
| 478 |
+
public function type()
|
| 479 |
+
{
|
| 480 |
+
$accept = $this->server('HTTP_ACCEPT');
|
| 481 |
+
if (empty($accept)) {
|
| 482 |
+
return false;
|
| 483 |
+
}
|
| 484 |
+
|
| 485 |
+
foreach ($this->mimeType as $key => $val) {
|
| 486 |
+
$array = explode(',', $val);
|
| 487 |
+
foreach ($array as $k => $v) {
|
| 488 |
+
if (stristr($accept, $v)) {
|
| 489 |
+
return $key;
|
| 490 |
+
}
|
| 491 |
+
}
|
| 492 |
+
}
|
| 493 |
+
return false;
|
| 494 |
+
}
|
| 495 |
+
|
| 496 |
+
/**
|
| 497 |
+
* 设置资源类型
|
| 498 |
+
* @access public
|
| 499 |
+
* @param string|array $type 资源类型名
|
| 500 |
+
* @param string $val 资源类型
|
| 501 |
+
* @return void
|
| 502 |
+
*/
|
| 503 |
+
public function mimeType($type, $val = '')
|
| 504 |
+
{
|
| 505 |
+
if (is_array($type)) {
|
| 506 |
+
$this->mimeType = array_merge($this->mimeType, $type);
|
| 507 |
+
} else {
|
| 508 |
+
$this->mimeType[$type] = $val;
|
| 509 |
+
}
|
| 510 |
+
}
|
| 511 |
+
|
| 512 |
+
/**
|
| 513 |
+
* 当前的请求类型
|
| 514 |
+
* @access public
|
| 515 |
+
* @param bool $method true 获取原始请求类型
|
| 516 |
+
* @return string
|
| 517 |
+
*/
|
| 518 |
+
public function method($method = false)
|
| 519 |
+
{
|
| 520 |
+
if (true === $method) {
|
| 521 |
+
// 获取原始请求类型
|
| 522 |
+
return $this->server('REQUEST_METHOD') ?: 'GET';
|
| 523 |
+
} elseif (!$this->method) {
|
| 524 |
+
if (isset($_POST[Config::get('var_method')])) {
|
| 525 |
+
$method = strtoupper($_POST[Config::get('var_method')]);
|
| 526 |
+
if (in_array($method, ['GET', 'POST', 'DELETE', 'PUT', 'PATCH'])) {
|
| 527 |
+
$this->method = $method;
|
| 528 |
+
$this->{$this->method}($_POST);
|
| 529 |
+
} else {
|
| 530 |
+
$this->method = 'POST';
|
| 531 |
+
}
|
| 532 |
+
unset($_POST[Config::get('var_method')]);
|
| 533 |
+
} elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
|
| 534 |
+
$this->method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
|
| 535 |
+
} else {
|
| 536 |
+
$this->method = $this->server('REQUEST_METHOD') ?: 'GET';
|
| 537 |
+
}
|
| 538 |
+
}
|
| 539 |
+
return $this->method;
|
| 540 |
+
}
|
| 541 |
+
|
| 542 |
+
/**
|
| 543 |
+
* 是否为GET请求
|
| 544 |
+
* @access public
|
| 545 |
+
* @return bool
|
| 546 |
+
*/
|
| 547 |
+
public function isGet()
|
| 548 |
+
{
|
| 549 |
+
return $this->method() == 'GET';
|
| 550 |
+
}
|
| 551 |
+
|
| 552 |
+
/**
|
| 553 |
+
* 是否为POST请求
|
| 554 |
+
* @access public
|
| 555 |
+
* @return bool
|
| 556 |
+
*/
|
| 557 |
+
public function isPost()
|
| 558 |
+
{
|
| 559 |
+
return $this->method() == 'POST';
|
| 560 |
+
}
|
| 561 |
+
|
| 562 |
+
/**
|
| 563 |
+
* 是否为PUT请求
|
| 564 |
+
* @access public
|
| 565 |
+
* @return bool
|
| 566 |
+
*/
|
| 567 |
+
public function isPut()
|
| 568 |
+
{
|
| 569 |
+
return $this->method() == 'PUT';
|
| 570 |
+
}
|
| 571 |
+
|
| 572 |
+
/**
|
| 573 |
+
* 是否为DELTE请求
|
| 574 |
+
* @access public
|
| 575 |
+
* @return bool
|
| 576 |
+
*/
|
| 577 |
+
public function isDelete()
|
| 578 |
+
{
|
| 579 |
+
return $this->method() == 'DELETE';
|
| 580 |
+
}
|
| 581 |
+
|
| 582 |
+
/**
|
| 583 |
+
* 是否为HEAD请求
|
| 584 |
+
* @access public
|
| 585 |
+
* @return bool
|
| 586 |
+
*/
|
| 587 |
+
public function isHead()
|
| 588 |
+
{
|
| 589 |
+
return $this->method() == 'HEAD';
|
| 590 |
+
}
|
| 591 |
+
|
| 592 |
+
/**
|
| 593 |
+
* 是否为PATCH请求
|
| 594 |
+
* @access public
|
| 595 |
+
* @return bool
|
| 596 |
+
*/
|
| 597 |
+
public function isPatch()
|
| 598 |
+
{
|
| 599 |
+
return $this->method() == 'PATCH';
|
| 600 |
+
}
|
| 601 |
+
|
| 602 |
+
/**
|
| 603 |
+
* 是否为OPTIONS请求
|
| 604 |
+
* @access public
|
| 605 |
+
* @return bool
|
| 606 |
+
*/
|
| 607 |
+
public function isOptions()
|
| 608 |
+
{
|
| 609 |
+
return $this->method() == 'OPTIONS';
|
| 610 |
+
}
|
| 611 |
+
|
| 612 |
+
/**
|
| 613 |
+
* 是否为cli
|
| 614 |
+
* @access public
|
| 615 |
+
* @return bool
|
| 616 |
+
*/
|
| 617 |
+
public function isCli()
|
| 618 |
+
{
|
| 619 |
+
return PHP_SAPI == 'cli';
|
| 620 |
+
}
|
| 621 |
+
|
| 622 |
+
/**
|
| 623 |
+
* 是否为cgi
|
| 624 |
+
* @access public
|
| 625 |
+
* @return bool
|
| 626 |
+
*/
|
| 627 |
+
public function isCgi()
|
| 628 |
+
{
|
| 629 |
+
return strpos(PHP_SAPI, 'cgi') === 0;
|
| 630 |
+
}
|
| 631 |
+
|
| 632 |
+
/**
|
| 633 |
+
* 获取当前请求的参数
|
| 634 |
+
* @access public
|
| 635 |
+
* @param string|array $name 变量名
|
| 636 |
+
* @param mixed $default 默认值
|
| 637 |
+
* @param string|array $filter 过滤方法
|
| 638 |
+
* @return mixed
|
| 639 |
+
*/
|
| 640 |
+
public function param($name = '', $default = null, $filter = '')
|
| 641 |
+
{
|
| 642 |
+
if (empty($this->mergeParam)) {
|
| 643 |
+
$method = $this->method(true);
|
| 644 |
+
// 自动获取请求变量
|
| 645 |
+
switch ($method) {
|
| 646 |
+
case 'POST':
|
| 647 |
+
$vars = $this->post(false);
|
| 648 |
+
break;
|
| 649 |
+
case 'PUT':
|
| 650 |
+
case 'DELETE':
|
| 651 |
+
case 'PATCH':
|
| 652 |
+
$vars = $this->put(false);
|
| 653 |
+
break;
|
| 654 |
+
default:
|
| 655 |
+
$vars = [];
|
| 656 |
+
}
|
| 657 |
+
// 当前请求参数和URL地址中的参数合并
|
| 658 |
+
$this->param = array_merge($this->param, $this->get(false), $vars, $this->route(false));
|
| 659 |
+
$this->mergeParam = true;
|
| 660 |
+
}
|
| 661 |
+
if (true === $name) {
|
| 662 |
+
// 获取包含文件上传信息的数组
|
| 663 |
+
$file = $this->file();
|
| 664 |
+
$data = is_array($file) ? array_merge($this->param, $file) : $this->param;
|
| 665 |
+
return $this->input($data, '', $default, $filter);
|
| 666 |
+
}
|
| 667 |
+
return $this->input($this->param, $name, $default, $filter);
|
| 668 |
+
}
|
| 669 |
+
|
| 670 |
+
/**
|
| 671 |
+
* 设置获取路由参数
|
| 672 |
+
* @access public
|
| 673 |
+
* @param string|array $name 变量名
|
| 674 |
+
* @param mixed $default 默认值
|
| 675 |
+
* @param string|array $filter 过滤方法
|
| 676 |
+
* @return mixed
|
| 677 |
+
*/
|
| 678 |
+
public function route($name = '', $default = null, $filter = '')
|
| 679 |
+
{
|
| 680 |
+
if (is_array($name)) {
|
| 681 |
+
$this->param = [];
|
| 682 |
+
$this->mergeParam = false;
|
| 683 |
+
return $this->route = array_merge($this->route, $name);
|
| 684 |
+
}
|
| 685 |
+
return $this->input($this->route, $name, $default, $filter);
|
| 686 |
+
}
|
| 687 |
+
|
| 688 |
+
/**
|
| 689 |
+
* 设置获取GET参数
|
| 690 |
+
* @access public
|
| 691 |
+
* @param string|array $name 变量名
|
| 692 |
+
* @param mixed $default 默认值
|
| 693 |
+
* @param string|array $filter 过滤方法
|
| 694 |
+
* @return mixed
|
| 695 |
+
*/
|
| 696 |
+
public function get($name = '', $default = null, $filter = '')
|
| 697 |
+
{
|
| 698 |
+
if (empty($this->get)) {
|
| 699 |
+
$this->get = $_GET;
|
| 700 |
+
}
|
| 701 |
+
if (is_array($name)) {
|
| 702 |
+
$this->param = [];
|
| 703 |
+
$this->mergeParam = false;
|
| 704 |
+
return $this->get = array_merge($this->get, $name);
|
| 705 |
+
}
|
| 706 |
+
return $this->input($this->get, $name, $default, $filter);
|
| 707 |
+
}
|
| 708 |
+
|
| 709 |
+
/**
|
| 710 |
+
* 设置获取POST参数
|
| 711 |
+
* @access public
|
| 712 |
+
* @param string $name 变量名
|
| 713 |
+
* @param mixed $default 默认值
|
| 714 |
+
* @param string|array $filter 过滤方法
|
| 715 |
+
* @return mixed
|
| 716 |
+
*/
|
| 717 |
+
public function post($name = '', $default = null, $filter = '')
|
| 718 |
+
{
|
| 719 |
+
if (empty($this->post)) {
|
| 720 |
+
$content = $this->input;
|
| 721 |
+
if (empty($_POST) && false !== strpos($this->contentType(), 'application/json')) {
|
| 722 |
+
$this->post = (array) json_decode($content, true);
|
| 723 |
+
} else {
|
| 724 |
+
$this->post = $_POST;
|
| 725 |
+
}
|
| 726 |
+
}
|
| 727 |
+
if (is_array($name)) {
|
| 728 |
+
$this->param = [];
|
| 729 |
+
$this->mergeParam = false;
|
| 730 |
+
return $this->post = array_merge($this->post, $name);
|
| 731 |
+
}
|
| 732 |
+
return $this->input($this->post, $name, $default, $filter);
|
| 733 |
+
}
|
| 734 |
+
|
| 735 |
+
/**
|
| 736 |
+
* 设置获取PUT参数
|
| 737 |
+
* @access public
|
| 738 |
+
* @param string|array $name 变量名
|
| 739 |
+
* @param mixed $default 默认值
|
| 740 |
+
* @param string|array $filter 过滤方法
|
| 741 |
+
* @return mixed
|
| 742 |
+
*/
|
| 743 |
+
public function put($name = '', $default = null, $filter = '')
|
| 744 |
+
{
|
| 745 |
+
if (is_null($this->put)) {
|
| 746 |
+
$content = $this->input;
|
| 747 |
+
if (false !== strpos($this->contentType(), 'application/json')) {
|
| 748 |
+
$this->put = (array) json_decode($content, true);
|
| 749 |
+
} else {
|
| 750 |
+
parse_str($content, $this->put);
|
| 751 |
+
}
|
| 752 |
+
}
|
| 753 |
+
if (is_array($name)) {
|
| 754 |
+
$this->param = [];
|
| 755 |
+
$this->mergeParam = false;
|
| 756 |
+
return $this->put = is_null($this->put) ? $name : array_merge($this->put, $name);
|
| 757 |
+
}
|
| 758 |
+
|
| 759 |
+
return $this->input($this->put, $name, $default, $filter);
|
| 760 |
+
}
|
| 761 |
+
|
| 762 |
+
/**
|
| 763 |
+
* 设置获取DELETE参数
|
| 764 |
+
* @access public
|
| 765 |
+
* @param string|array $name 变量名
|
| 766 |
+
* @param mixed $default 默认值
|
| 767 |
+
* @param string|array $filter 过滤方法
|
| 768 |
+
* @return mixed
|
| 769 |
+
*/
|
| 770 |
+
public function delete($name = '', $default = null, $filter = '')
|
| 771 |
+
{
|
| 772 |
+
return $this->put($name, $default, $filter);
|
| 773 |
+
}
|
| 774 |
+
|
| 775 |
+
/**
|
| 776 |
+
* 设置获取PATCH参数
|
| 777 |
+
* @access public
|
| 778 |
+
* @param string|array $name 变量名
|
| 779 |
+
* @param mixed $default 默认值
|
| 780 |
+
* @param string|array $filter 过滤方法
|
| 781 |
+
* @return mixed
|
| 782 |
+
*/
|
| 783 |
+
public function patch($name = '', $default = null, $filter = '')
|
| 784 |
+
{
|
| 785 |
+
return $this->put($name, $default, $filter);
|
| 786 |
+
}
|
| 787 |
+
|
| 788 |
+
/**
|
| 789 |
+
* 获取request变量
|
| 790 |
+
* @param string $name 数据名称
|
| 791 |
+
* @param string $default 默认值
|
| 792 |
+
* @param string|array $filter 过滤方法
|
| 793 |
+
* @return mixed
|
| 794 |
+
*/
|
| 795 |
+
public function request($name = '', $default = null, $filter = '')
|
| 796 |
+
{
|
| 797 |
+
if (empty($this->request)) {
|
| 798 |
+
$this->request = $_REQUEST;
|
| 799 |
+
}
|
| 800 |
+
if (is_array($name)) {
|
| 801 |
+
$this->param = [];
|
| 802 |
+
$this->mergeParam = false;
|
| 803 |
+
return $this->request = array_merge($this->request, $name);
|
| 804 |
+
}
|
| 805 |
+
return $this->input($this->request, $name, $default, $filter);
|
| 806 |
+
}
|
| 807 |
+
|
| 808 |
+
/**
|
| 809 |
+
* 获取session数据
|
| 810 |
+
* @access public
|
| 811 |
+
* @param string|array $name 数据名称
|
| 812 |
+
* @param string $default 默认值
|
| 813 |
+
* @param string|array $filter 过滤方法
|
| 814 |
+
* @return mixed
|
| 815 |
+
*/
|
| 816 |
+
public function session($name = '', $default = null, $filter = '')
|
| 817 |
+
{
|
| 818 |
+
if (empty($this->session)) {
|
| 819 |
+
$this->session = Session::get();
|
| 820 |
+
}
|
| 821 |
+
if (is_array($name)) {
|
| 822 |
+
return $this->session = array_merge($this->session, $name);
|
| 823 |
+
}
|
| 824 |
+
return $this->input($this->session, $name, $default, $filter);
|
| 825 |
+
}
|
| 826 |
+
|
| 827 |
+
/**
|
| 828 |
+
* 获取cookie参数
|
| 829 |
+
* @access public
|
| 830 |
+
* @param string|array $name 数据名称
|
| 831 |
+
* @param string $default 默认值
|
| 832 |
+
* @param string|array $filter 过滤方法
|
| 833 |
+
* @return mixed
|
| 834 |
+
*/
|
| 835 |
+
public function cookie($name = '', $default = null, $filter = '')
|
| 836 |
+
{
|
| 837 |
+
if (empty($this->cookie)) {
|
| 838 |
+
$this->cookie = Cookie::get();
|
| 839 |
+
}
|
| 840 |
+
if (is_array($name)) {
|
| 841 |
+
return $this->cookie = array_merge($this->cookie, $name);
|
| 842 |
+
} elseif (!empty($name)) {
|
| 843 |
+
$data = Cookie::has($name) ? Cookie::get($name) : $default;
|
| 844 |
+
} else {
|
| 845 |
+
$data = $this->cookie;
|
| 846 |
+
}
|
| 847 |
+
|
| 848 |
+
// 解析过滤器
|
| 849 |
+
$filter = $this->getFilter($filter, $default);
|
| 850 |
+
|
| 851 |
+
if (is_array($data)) {
|
| 852 |
+
array_walk_recursive($data, [$this, 'filterValue'], $filter);
|
| 853 |
+
reset($data);
|
| 854 |
+
} else {
|
| 855 |
+
$this->filterValue($data, $name, $filter);
|
| 856 |
+
}
|
| 857 |
+
return $data;
|
| 858 |
+
}
|
| 859 |
+
|
| 860 |
+
/**
|
| 861 |
+
* 获取server参数
|
| 862 |
+
* @access public
|
| 863 |
+
* @param string|array $name 数据名称
|
| 864 |
+
* @param string $default 默认值
|
| 865 |
+
* @param string|array $filter 过滤方法
|
| 866 |
+
* @return mixed
|
| 867 |
+
*/
|
| 868 |
+
public function server($name = '', $default = null, $filter = '')
|
| 869 |
+
{
|
| 870 |
+
if (empty($this->server)) {
|
| 871 |
+
$this->server = $_SERVER;
|
| 872 |
+
}
|
| 873 |
+
if (is_array($name)) {
|
| 874 |
+
return $this->server = array_merge($this->server, $name);
|
| 875 |
+
}
|
| 876 |
+
return $this->input($this->server, false === $name ? false : strtoupper($name), $default, $filter);
|
| 877 |
+
}
|
| 878 |
+
|
| 879 |
+
/**
|
| 880 |
+
* 获取上传的文件信息
|
| 881 |
+
* @access public
|
| 882 |
+
* @param string|array $name 名称
|
| 883 |
+
* @return null|array|\think\File
|
| 884 |
+
*/
|
| 885 |
+
public function file($name = '')
|
| 886 |
+
{
|
| 887 |
+
if (empty($this->file)) {
|
| 888 |
+
$this->file = isset($_FILES) ? $_FILES : [];
|
| 889 |
+
}
|
| 890 |
+
if (is_array($name)) {
|
| 891 |
+
return $this->file = array_merge($this->file, $name);
|
| 892 |
+
}
|
| 893 |
+
$files = $this->file;
|
| 894 |
+
if (!empty($files)) {
|
| 895 |
+
// 处理上传文件
|
| 896 |
+
$array = [];
|
| 897 |
+
foreach ($files as $key => $file) {
|
| 898 |
+
if (is_array($file['name'])) {
|
| 899 |
+
$item = [];
|
| 900 |
+
$keys = array_keys($file);
|
| 901 |
+
$count = count($file['name']);
|
| 902 |
+
for ($i = 0; $i < $count; $i++) {
|
| 903 |
+
if (empty($file['tmp_name'][$i]) || !is_file($file['tmp_name'][$i])) {
|
| 904 |
+
continue;
|
| 905 |
+
}
|
| 906 |
+
$temp['key'] = $key;
|
| 907 |
+
foreach ($keys as $_key) {
|
| 908 |
+
$temp[$_key] = $file[$_key][$i];
|
| 909 |
+
}
|
| 910 |
+
$item[] = (new File($temp['tmp_name']))->setUploadInfo($temp);
|
| 911 |
+
}
|
| 912 |
+
$array[$key] = $item;
|
| 913 |
+
} else {
|
| 914 |
+
if ($file instanceof File) {
|
| 915 |
+
$array[$key] = $file;
|
| 916 |
+
} else {
|
| 917 |
+
if (empty($file['tmp_name']) || !is_file($file['tmp_name'])) {
|
| 918 |
+
continue;
|
| 919 |
+
}
|
| 920 |
+
$array[$key] = (new File($file['tmp_name']))->setUploadInfo($file);
|
| 921 |
+
}
|
| 922 |
+
}
|
| 923 |
+
}
|
| 924 |
+
if (strpos($name, '.')) {
|
| 925 |
+
list($name, $sub) = explode('.', $name);
|
| 926 |
+
}
|
| 927 |
+
if ('' === $name) {
|
| 928 |
+
// 获取全部文件
|
| 929 |
+
return $array;
|
| 930 |
+
} elseif (isset($sub) && isset($array[$name][$sub])) {
|
| 931 |
+
return $array[$name][$sub];
|
| 932 |
+
} elseif (isset($array[$name])) {
|
| 933 |
+
return $array[$name];
|
| 934 |
+
}
|
| 935 |
+
}
|
| 936 |
+
return;
|
| 937 |
+
}
|
| 938 |
+
|
| 939 |
+
/**
|
| 940 |
+
* 获取环境变量
|
| 941 |
+
* @param string|array $name 数据名称
|
| 942 |
+
* @param string $default 默认值
|
| 943 |
+
* @param string|array $filter 过滤方法
|
| 944 |
+
* @return mixed
|
| 945 |
+
*/
|
| 946 |
+
public function env($name = '', $default = null, $filter = '')
|
| 947 |
+
{
|
| 948 |
+
if (empty($this->env)) {
|
| 949 |
+
$this->env = $_ENV;
|
| 950 |
+
}
|
| 951 |
+
if (is_array($name)) {
|
| 952 |
+
return $this->env = array_merge($this->env, $name);
|
| 953 |
+
}
|
| 954 |
+
return $this->input($this->env, false === $name ? false : strtoupper($name), $default, $filter);
|
| 955 |
+
}
|
| 956 |
+
|
| 957 |
+
/**
|
| 958 |
+
* 设置或者获取当前的Header
|
| 959 |
+
* @access public
|
| 960 |
+
* @param string|array $name header名称
|
| 961 |
+
* @param string $default 默认值
|
| 962 |
+
* @return string
|
| 963 |
+
*/
|
| 964 |
+
public function header($name = '', $default = null)
|
| 965 |
+
{
|
| 966 |
+
if (empty($this->header)) {
|
| 967 |
+
$header = [];
|
| 968 |
+
if (function_exists('apache_request_headers') && $result = apache_request_headers()) {
|
| 969 |
+
$header = $result;
|
| 970 |
+
} else {
|
| 971 |
+
$server = $this->server ?: $_SERVER;
|
| 972 |
+
foreach ($server as $key => $val) {
|
| 973 |
+
if (0 === strpos($key, 'HTTP_')) {
|
| 974 |
+
$key = str_replace('_', '-', strtolower(substr($key, 5)));
|
| 975 |
+
$header[$key] = $val;
|
| 976 |
+
}
|
| 977 |
+
}
|
| 978 |
+
if (isset($server['CONTENT_TYPE'])) {
|
| 979 |
+
$header['content-type'] = $server['CONTENT_TYPE'];
|
| 980 |
+
}
|
| 981 |
+
if (isset($server['CONTENT_LENGTH'])) {
|
| 982 |
+
$header['content-length'] = $server['CONTENT_LENGTH'];
|
| 983 |
+
}
|
| 984 |
+
}
|
| 985 |
+
$this->header = array_change_key_case($header);
|
| 986 |
+
}
|
| 987 |
+
if (is_array($name)) {
|
| 988 |
+
return $this->header = array_merge($this->header, $name);
|
| 989 |
+
}
|
| 990 |
+
if ('' === $name) {
|
| 991 |
+
return $this->header;
|
| 992 |
+
}
|
| 993 |
+
$name = str_replace('_', '-', strtolower($name));
|
| 994 |
+
return isset($this->header[$name]) ? $this->header[$name] : $default;
|
| 995 |
+
}
|
| 996 |
+
|
| 997 |
+
/**
|
| 998 |
+
* 获取变量 支持过滤和默认值
|
| 999 |
+
* @param array $data 数据源
|
| 1000 |
+
* @param string|false $name 字段名
|
| 1001 |
+
* @param mixed $default 默认值
|
| 1002 |
+
* @param string|array $filter 过滤函数
|
| 1003 |
+
* @return mixed
|
| 1004 |
+
*/
|
| 1005 |
+
public function input($data = [], $name = '', $default = null, $filter = '')
|
| 1006 |
+
{
|
| 1007 |
+
if (false === $name) {
|
| 1008 |
+
// 获取原始数据
|
| 1009 |
+
return $data;
|
| 1010 |
+
}
|
| 1011 |
+
$name = (string) $name;
|
| 1012 |
+
if ('' != $name) {
|
| 1013 |
+
// 解析name
|
| 1014 |
+
if (strpos($name, '/')) {
|
| 1015 |
+
list($name, $type) = explode('/', $name);
|
| 1016 |
+
} else {
|
| 1017 |
+
$type = 's';
|
| 1018 |
+
}
|
| 1019 |
+
// 按.拆分成多维数组进行判断
|
| 1020 |
+
foreach (explode('.', $name) as $val) {
|
| 1021 |
+
if (isset($data[$val])) {
|
| 1022 |
+
$data = $data[$val];
|
| 1023 |
+
} else {
|
| 1024 |
+
// 无输入数据,返回默认值
|
| 1025 |
+
return $default;
|
| 1026 |
+
}
|
| 1027 |
+
}
|
| 1028 |
+
if (is_object($data)) {
|
| 1029 |
+
return $data;
|
| 1030 |
+
}
|
| 1031 |
+
}
|
| 1032 |
+
|
| 1033 |
+
// 解析过滤器
|
| 1034 |
+
$filter = $this->getFilter($filter, $default);
|
| 1035 |
+
|
| 1036 |
+
if (is_array($data)) {
|
| 1037 |
+
array_walk_recursive($data, [$this, 'filterValue'], $filter);
|
| 1038 |
+
reset($data);
|
| 1039 |
+
} else {
|
| 1040 |
+
$this->filterValue($data, $name, $filter);
|
| 1041 |
+
}
|
| 1042 |
+
|
| 1043 |
+
if (isset($type) && $data !== $default) {
|
| 1044 |
+
// 强制类型转换
|
| 1045 |
+
$this->typeCast($data, $type);
|
| 1046 |
+
}
|
| 1047 |
+
return $data;
|
| 1048 |
+
}
|
| 1049 |
+
|
| 1050 |
+
/**
|
| 1051 |
+
* 设置或获取当前的过滤规则
|
| 1052 |
+
* @param mixed $filter 过滤规则
|
| 1053 |
+
* @return mixed
|
| 1054 |
+
*/
|
| 1055 |
+
public function filter($filter = null)
|
| 1056 |
+
{
|
| 1057 |
+
if (is_null($filter)) {
|
| 1058 |
+
return $this->filter;
|
| 1059 |
+
} else {
|
| 1060 |
+
$this->filter = $filter;
|
| 1061 |
+
}
|
| 1062 |
+
}
|
| 1063 |
+
|
| 1064 |
+
protected function getFilter($filter, $default)
|
| 1065 |
+
{
|
| 1066 |
+
if (is_null($filter)) {
|
| 1067 |
+
$filter = [];
|
| 1068 |
+
} else {
|
| 1069 |
+
$filter = $filter ?: $this->filter;
|
| 1070 |
+
if (is_string($filter) && false === strpos($filter, '/')) {
|
| 1071 |
+
$filter = explode(',', $filter);
|
| 1072 |
+
} else {
|
| 1073 |
+
$filter = (array) $filter;
|
| 1074 |
+
}
|
| 1075 |
+
}
|
| 1076 |
+
|
| 1077 |
+
$filter[] = $default;
|
| 1078 |
+
return $filter;
|
| 1079 |
+
}
|
| 1080 |
+
|
| 1081 |
+
/**
|
| 1082 |
+
* 递归过滤给定的值
|
| 1083 |
+
* @param mixed $value 键值
|
| 1084 |
+
* @param mixed $key 键名
|
| 1085 |
+
* @param array $filters 过滤方法+默认值
|
| 1086 |
+
* @return mixed
|
| 1087 |
+
*/
|
| 1088 |
+
private function filterValue(&$value, $key, $filters)
|
| 1089 |
+
{
|
| 1090 |
+
$default = array_pop($filters);
|
| 1091 |
+
foreach ($filters as $filter) {
|
| 1092 |
+
if (is_callable($filter)) {
|
| 1093 |
+
// 调用函数或者方法过滤
|
| 1094 |
+
$value = call_user_func($filter, $value);
|
| 1095 |
+
} elseif (is_scalar($value)) {
|
| 1096 |
+
if (false !== strpos($filter, '/')) {
|
| 1097 |
+
// 正则过滤
|
| 1098 |
+
if (!preg_match($filter, $value)) {
|
| 1099 |
+
// 匹配不成功返回默认值
|
| 1100 |
+
$value = $default;
|
| 1101 |
+
break;
|
| 1102 |
+
}
|
| 1103 |
+
} elseif (!empty($filter)) {
|
| 1104 |
+
// filter函数不存在时, 则使用filter_var进行过滤
|
| 1105 |
+
// filter为非整形值时, 调用filter_id取得过滤id
|
| 1106 |
+
$value = filter_var($value, is_int($filter) ? $filter : filter_id($filter));
|
| 1107 |
+
if (false === $value) {
|
| 1108 |
+
$value = $default;
|
| 1109 |
+
break;
|
| 1110 |
+
}
|
| 1111 |
+
}
|
| 1112 |
+
}
|
| 1113 |
+
}
|
| 1114 |
+
return $this->filterExp($value);
|
| 1115 |
+
}
|
| 1116 |
+
|
| 1117 |
+
/**
|
| 1118 |
+
* 过滤表单中的表达式
|
| 1119 |
+
* @param string $value
|
| 1120 |
+
* @return void
|
| 1121 |
+
*/
|
| 1122 |
+
public function filterExp(&$value)
|
| 1123 |
+
{
|
| 1124 |
+
// 过滤查询特殊字符
|
| 1125 |
+
if (is_string($value) && preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT LIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOT EXISTS|NOTEXISTS|EXISTS|NOT NULL|NOTNULL|NULL|BETWEEN TIME|NOT BETWEEN TIME|NOTBETWEEN TIME|NOTIN|NOT IN|IN)$/i', $value)) {
|
| 1126 |
+
$value .= ' ';
|
| 1127 |
+
}
|
| 1128 |
+
// TODO 其他安全过滤
|
| 1129 |
+
}
|
| 1130 |
+
|
| 1131 |
+
/**
|
| 1132 |
+
* 强制类型转换
|
| 1133 |
+
* @param string $data
|
| 1134 |
+
* @param string $type
|
| 1135 |
+
* @return mixed
|
| 1136 |
+
*/
|
| 1137 |
+
private function typeCast(&$data, $type)
|
| 1138 |
+
{
|
| 1139 |
+
switch (strtolower($type)) {
|
| 1140 |
+
// 数组
|
| 1141 |
+
case 'a':
|
| 1142 |
+
$data = (array) $data;
|
| 1143 |
+
break;
|
| 1144 |
+
// 数字
|
| 1145 |
+
case 'd':
|
| 1146 |
+
$data = (int) $data;
|
| 1147 |
+
break;
|
| 1148 |
+
// 浮点
|
| 1149 |
+
case 'f':
|
| 1150 |
+
$data = (float) $data;
|
| 1151 |
+
break;
|
| 1152 |
+
// 布尔
|
| 1153 |
+
case 'b':
|
| 1154 |
+
$data = (boolean) $data;
|
| 1155 |
+
break;
|
| 1156 |
+
// 字符串
|
| 1157 |
+
case 's':
|
| 1158 |
+
default:
|
| 1159 |
+
if (is_scalar($data)) {
|
| 1160 |
+
$data = (string) $data;
|
| 1161 |
+
} else {
|
| 1162 |
+
throw new \InvalidArgumentException('variable type error:' . gettype($data));
|
| 1163 |
+
}
|
| 1164 |
+
}
|
| 1165 |
+
}
|
| 1166 |
+
|
| 1167 |
+
/**
|
| 1168 |
+
* 是否存在某个请求参数
|
| 1169 |
+
* @access public
|
| 1170 |
+
* @param string $name 变量名
|
| 1171 |
+
* @param string $type 变量类型
|
| 1172 |
+
* @param bool $checkEmpty 是否检测空值
|
| 1173 |
+
* @return mixed
|
| 1174 |
+
*/
|
| 1175 |
+
public function has($name, $type = 'param', $checkEmpty = false)
|
| 1176 |
+
{
|
| 1177 |
+
if (empty($this->$type)) {
|
| 1178 |
+
$param = $this->$type();
|
| 1179 |
+
} else {
|
| 1180 |
+
$param = $this->$type;
|
| 1181 |
+
}
|
| 1182 |
+
// 按.拆分成多维数组进行判断
|
| 1183 |
+
foreach (explode('.', $name) as $val) {
|
| 1184 |
+
if (isset($param[$val])) {
|
| 1185 |
+
$param = $param[$val];
|
| 1186 |
+
} else {
|
| 1187 |
+
return false;
|
| 1188 |
+
}
|
| 1189 |
+
}
|
| 1190 |
+
return ($checkEmpty && '' === $param) ? false : true;
|
| 1191 |
+
}
|
| 1192 |
+
|
| 1193 |
+
/**
|
| 1194 |
+
* 获取指定的参数
|
| 1195 |
+
* @access public
|
| 1196 |
+
* @param string|array $name 变量名
|
| 1197 |
+
* @param string $type 变量类型
|
| 1198 |
+
* @return mixed
|
| 1199 |
+
*/
|
| 1200 |
+
public function only($name, $type = 'param')
|
| 1201 |
+
{
|
| 1202 |
+
$param = $this->$type();
|
| 1203 |
+
if (is_string($name)) {
|
| 1204 |
+
$name = explode(',', $name);
|
| 1205 |
+
}
|
| 1206 |
+
$item = [];
|
| 1207 |
+
foreach ($name as $key) {
|
| 1208 |
+
if (isset($param[$key])) {
|
| 1209 |
+
$item[$key] = $param[$key];
|
| 1210 |
+
}
|
| 1211 |
+
}
|
| 1212 |
+
return $item;
|
| 1213 |
+
}
|
| 1214 |
+
|
| 1215 |
+
/**
|
| 1216 |
+
* 排除指定参数获取
|
| 1217 |
+
* @access public
|
| 1218 |
+
* @param string|array $name 变量名
|
| 1219 |
+
* @param string $type 变量类型
|
| 1220 |
+
* @return mixed
|
| 1221 |
+
*/
|
| 1222 |
+
public function except($name, $type = 'param')
|
| 1223 |
+
{
|
| 1224 |
+
$param = $this->$type();
|
| 1225 |
+
if (is_string($name)) {
|
| 1226 |
+
$name = explode(',', $name);
|
| 1227 |
+
}
|
| 1228 |
+
foreach ($name as $key) {
|
| 1229 |
+
if (isset($param[$key])) {
|
| 1230 |
+
unset($param[$key]);
|
| 1231 |
+
}
|
| 1232 |
+
}
|
| 1233 |
+
return $param;
|
| 1234 |
+
}
|
| 1235 |
+
|
| 1236 |
+
/**
|
| 1237 |
+
* 当前是否ssl
|
| 1238 |
+
* @access public
|
| 1239 |
+
* @return bool
|
| 1240 |
+
*/
|
| 1241 |
+
public function isSsl()
|
| 1242 |
+
{
|
| 1243 |
+
$server = array_merge($_SERVER, $this->server);
|
| 1244 |
+
if (isset($server['HTTPS']) && ('1' == $server['HTTPS'] || 'on' == strtolower($server['HTTPS']))) {
|
| 1245 |
+
return true;
|
| 1246 |
+
} elseif (isset($server['REQUEST_SCHEME']) && 'https' == $server['REQUEST_SCHEME']) {
|
| 1247 |
+
return true;
|
| 1248 |
+
} elseif (isset($server['SERVER_PORT']) && ('443' == $server['SERVER_PORT'])) {
|
| 1249 |
+
return true;
|
| 1250 |
+
} elseif (isset($server['HTTP_X_FORWARDED_PROTO']) && 'https' == $server['HTTP_X_FORWARDED_PROTO']) {
|
| 1251 |
+
return true;
|
| 1252 |
+
} elseif (Config::get('https_agent_name') && isset($server[Config::get('https_agent_name')])) {
|
| 1253 |
+
return true;
|
| 1254 |
+
}
|
| 1255 |
+
return false;
|
| 1256 |
+
}
|
| 1257 |
+
|
| 1258 |
+
/**
|
| 1259 |
+
* 当前是否Ajax请求
|
| 1260 |
+
* @access public
|
| 1261 |
+
* @param bool $ajax true 获取原始ajax请求
|
| 1262 |
+
* @return bool
|
| 1263 |
+
*/
|
| 1264 |
+
public function isAjax($ajax = false)
|
| 1265 |
+
{
|
| 1266 |
+
$value = $this->server('HTTP_X_REQUESTED_WITH', '', 'strtolower');
|
| 1267 |
+
$result = ('xmlhttprequest' == $value) ? true : false;
|
| 1268 |
+
if (true === $ajax) {
|
| 1269 |
+
return $result;
|
| 1270 |
+
} else {
|
| 1271 |
+
$result = $this->param(Config::get('var_ajax')) ? true : $result;
|
| 1272 |
+
$this->mergeParam = false;
|
| 1273 |
+
return $result;
|
| 1274 |
+
}
|
| 1275 |
+
}
|
| 1276 |
+
|
| 1277 |
+
/**
|
| 1278 |
+
* 当前是否Pjax请求
|
| 1279 |
+
* @access public
|
| 1280 |
+
* @param bool $pjax true 获取原始pjax请求
|
| 1281 |
+
* @return bool
|
| 1282 |
+
*/
|
| 1283 |
+
public function isPjax($pjax = false)
|
| 1284 |
+
{
|
| 1285 |
+
$result = !is_null($this->server('HTTP_X_PJAX')) ? true : false;
|
| 1286 |
+
if (true === $pjax) {
|
| 1287 |
+
return $result;
|
| 1288 |
+
} else {
|
| 1289 |
+
$result = $this->param(Config::get('var_pjax')) ? true : $result;
|
| 1290 |
+
$this->mergeParam = false;
|
| 1291 |
+
return $result;
|
| 1292 |
+
}
|
| 1293 |
+
}
|
| 1294 |
+
|
| 1295 |
+
/**
|
| 1296 |
+
* 获取客户端IP地址
|
| 1297 |
+
* @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
|
| 1298 |
+
* @param boolean $adv 是否进行高级模式获取(有可能被伪装)
|
| 1299 |
+
* @return mixed
|
| 1300 |
+
*/
|
| 1301 |
+
public function ip($type = 0, $adv = true)
|
| 1302 |
+
{
|
| 1303 |
+
$type = $type ? 1 : 0;
|
| 1304 |
+
static $ip = null;
|
| 1305 |
+
if (null !== $ip) {
|
| 1306 |
+
return $ip[$type];
|
| 1307 |
+
}
|
| 1308 |
+
|
| 1309 |
+
$httpAgentIp = Config::get('http_agent_ip');
|
| 1310 |
+
|
| 1311 |
+
if ($httpAgentIp && isset($_SERVER[$httpAgentIp])) {
|
| 1312 |
+
$ip = $_SERVER[$httpAgentIp];
|
| 1313 |
+
} elseif ($adv) {
|
| 1314 |
+
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
| 1315 |
+
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
|
| 1316 |
+
$pos = array_search('unknown', $arr);
|
| 1317 |
+
if (false !== $pos) {
|
| 1318 |
+
unset($arr[$pos]);
|
| 1319 |
+
}
|
| 1320 |
+
$ip = trim(current($arr));
|
| 1321 |
+
} elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
|
| 1322 |
+
$ip = $_SERVER['HTTP_CLIENT_IP'];
|
| 1323 |
+
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
|
| 1324 |
+
$ip = $_SERVER['REMOTE_ADDR'];
|
| 1325 |
+
}
|
| 1326 |
+
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
|
| 1327 |
+
$ip = $_SERVER['REMOTE_ADDR'];
|
| 1328 |
+
}
|
| 1329 |
+
// IP地址合法验证
|
| 1330 |
+
$long = sprintf("%u", ip2long($ip));
|
| 1331 |
+
$ip = $long ? [$ip, $long] : ['0.0.0.0', 0];
|
| 1332 |
+
return $ip[$type];
|
| 1333 |
+
}
|
| 1334 |
+
|
| 1335 |
+
/**
|
| 1336 |
+
* 检测是否使用手机访问
|
| 1337 |
+
* @access public
|
| 1338 |
+
* @return bool
|
| 1339 |
+
*/
|
| 1340 |
+
public function isMobile()
|
| 1341 |
+
{
|
| 1342 |
+
if (isset($_SERVER['HTTP_VIA']) && stristr($_SERVER['HTTP_VIA'], "wap")) {
|
| 1343 |
+
return true;
|
| 1344 |
+
} elseif (isset($_SERVER['HTTP_ACCEPT']) && strpos(strtoupper($_SERVER['HTTP_ACCEPT']), "VND.WAP.WML")) {
|
| 1345 |
+
return true;
|
| 1346 |
+
} elseif (isset($_SERVER['HTTP_X_WAP_PROFILE']) || isset($_SERVER['HTTP_PROFILE'])) {
|
| 1347 |
+
return true;
|
| 1348 |
+
} elseif (isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/(blackberry|configuration\/cldc|hp |hp-|htc |htc_|htc-|iemobile|kindle|midp|mmp|motorola|mobile|nokia|opera mini|opera |Googlebot-Mobile|YahooSeeker\/M1A1-R2D2|android|iphone|ipod|mobi|palm|palmos|pocket|portalmmm|ppc;|smartphone|sonyericsson|sqh|spv|symbian|treo|up.browser|up.link|vodafone|windows ce|xda |xda_)/i', $_SERVER['HTTP_USER_AGENT'])) {
|
| 1349 |
+
return true;
|
| 1350 |
+
} else {
|
| 1351 |
+
return false;
|
| 1352 |
+
}
|
| 1353 |
+
}
|
| 1354 |
+
|
| 1355 |
+
/**
|
| 1356 |
+
* 当前URL地址中的scheme参数
|
| 1357 |
+
* @access public
|
| 1358 |
+
* @return string
|
| 1359 |
+
*/
|
| 1360 |
+
public function scheme()
|
| 1361 |
+
{
|
| 1362 |
+
return $this->isSsl() ? 'https' : 'http';
|
| 1363 |
+
}
|
| 1364 |
+
|
| 1365 |
+
/**
|
| 1366 |
+
* 当前请求URL地址中的query参数
|
| 1367 |
+
* @access public
|
| 1368 |
+
* @return string
|
| 1369 |
+
*/
|
| 1370 |
+
public function query()
|
| 1371 |
+
{
|
| 1372 |
+
return $this->server('QUERY_STRING');
|
| 1373 |
+
}
|
| 1374 |
+
|
| 1375 |
+
/**
|
| 1376 |
+
* 当前请求的host
|
| 1377 |
+
* @access public
|
| 1378 |
+
* @param bool $strict true 仅仅获取HOST
|
| 1379 |
+
* @return string
|
| 1380 |
+
*/
|
| 1381 |
+
public function host($strict = false)
|
| 1382 |
+
{
|
| 1383 |
+
if (isset($_SERVER['HTTP_X_REAL_HOST'])) {
|
| 1384 |
+
$host = $_SERVER['HTTP_X_REAL_HOST'];
|
| 1385 |
+
} else {
|
| 1386 |
+
$host = $this->server('HTTP_HOST');
|
| 1387 |
+
}
|
| 1388 |
+
|
| 1389 |
+
return true === $strict && strpos($host, ':') ? strstr($host, ':', true) : $host;
|
| 1390 |
+
}
|
| 1391 |
+
|
| 1392 |
+
/**
|
| 1393 |
+
* 当前请求URL地址中的port参数
|
| 1394 |
+
* @access public
|
| 1395 |
+
* @return integer
|
| 1396 |
+
*/
|
| 1397 |
+
public function port()
|
| 1398 |
+
{
|
| 1399 |
+
return $this->server('SERVER_PORT');
|
| 1400 |
+
}
|
| 1401 |
+
|
| 1402 |
+
/**
|
| 1403 |
+
* 当前请求 SERVER_PROTOCOL
|
| 1404 |
+
* @access public
|
| 1405 |
+
* @return integer
|
| 1406 |
+
*/
|
| 1407 |
+
public function protocol()
|
| 1408 |
+
{
|
| 1409 |
+
return $this->server('SERVER_PROTOCOL');
|
| 1410 |
+
}
|
| 1411 |
+
|
| 1412 |
+
/**
|
| 1413 |
+
* 当前请求 REMOTE_PORT
|
| 1414 |
+
* @access public
|
| 1415 |
+
* @return integer
|
| 1416 |
+
*/
|
| 1417 |
+
public function remotePort()
|
| 1418 |
+
{
|
| 1419 |
+
return $this->server('REMOTE_PORT');
|
| 1420 |
+
}
|
| 1421 |
+
|
| 1422 |
+
/**
|
| 1423 |
+
* 当前请求 HTTP_CONTENT_TYPE
|
| 1424 |
+
* @access public
|
| 1425 |
+
* @return string
|
| 1426 |
+
*/
|
| 1427 |
+
public function contentType()
|
| 1428 |
+
{
|
| 1429 |
+
$contentType = $this->server('CONTENT_TYPE');
|
| 1430 |
+
if ($contentType) {
|
| 1431 |
+
if (strpos($contentType, ';')) {
|
| 1432 |
+
list($type) = explode(';', $contentType);
|
| 1433 |
+
} else {
|
| 1434 |
+
$type = $contentType;
|
| 1435 |
+
}
|
| 1436 |
+
return trim($type);
|
| 1437 |
+
}
|
| 1438 |
+
return '';
|
| 1439 |
+
}
|
| 1440 |
+
|
| 1441 |
+
/**
|
| 1442 |
+
* 获取当前请求的路由信息
|
| 1443 |
+
* @access public
|
| 1444 |
+
* @param array $route 路由名称
|
| 1445 |
+
* @return array
|
| 1446 |
+
*/
|
| 1447 |
+
public function routeInfo($route = [])
|
| 1448 |
+
{
|
| 1449 |
+
if (!empty($route)) {
|
| 1450 |
+
$this->routeInfo = $route;
|
| 1451 |
+
} else {
|
| 1452 |
+
return $this->routeInfo;
|
| 1453 |
+
}
|
| 1454 |
+
}
|
| 1455 |
+
|
| 1456 |
+
/**
|
| 1457 |
+
* 设置或者获取当前请求的调度信息
|
| 1458 |
+
* @access public
|
| 1459 |
+
* @param array $dispatch 调度信息
|
| 1460 |
+
* @return array
|
| 1461 |
+
*/
|
| 1462 |
+
public function dispatch($dispatch = null)
|
| 1463 |
+
{
|
| 1464 |
+
if (!is_null($dispatch)) {
|
| 1465 |
+
$this->dispatch = $dispatch;
|
| 1466 |
+
}
|
| 1467 |
+
return $this->dispatch;
|
| 1468 |
+
}
|
| 1469 |
+
|
| 1470 |
+
/**
|
| 1471 |
+
* 设置或者获取当前的模块名
|
| 1472 |
+
* @access public
|
| 1473 |
+
* @param string $module 模块名
|
| 1474 |
+
* @return string|Request
|
| 1475 |
+
*/
|
| 1476 |
+
public function module($module = null)
|
| 1477 |
+
{
|
| 1478 |
+
if (!is_null($module)) {
|
| 1479 |
+
$this->module = $module;
|
| 1480 |
+
return $this;
|
| 1481 |
+
} else {
|
| 1482 |
+
return $this->module ?: '';
|
| 1483 |
+
}
|
| 1484 |
+
}
|
| 1485 |
+
|
| 1486 |
+
/**
|
| 1487 |
+
* 设置或者获取当前的控制器名
|
| 1488 |
+
* @access public
|
| 1489 |
+
* @param string $controller 控制器名
|
| 1490 |
+
* @return string|Request
|
| 1491 |
+
*/
|
| 1492 |
+
public function controller($controller = null)
|
| 1493 |
+
{
|
| 1494 |
+
if (!is_null($controller)) {
|
| 1495 |
+
$this->controller = $controller;
|
| 1496 |
+
return $this;
|
| 1497 |
+
} else {
|
| 1498 |
+
return $this->controller ?: '';
|
| 1499 |
+
}
|
| 1500 |
+
}
|
| 1501 |
+
|
| 1502 |
+
/**
|
| 1503 |
+
* 设置或者获取当前的操作名
|
| 1504 |
+
* @access public
|
| 1505 |
+
* @param string $action 操作名
|
| 1506 |
+
* @return string|Request
|
| 1507 |
+
*/
|
| 1508 |
+
public function action($action = null)
|
| 1509 |
+
{
|
| 1510 |
+
if (!is_null($action) && !is_bool($action)) {
|
| 1511 |
+
$this->action = $action;
|
| 1512 |
+
return $this;
|
| 1513 |
+
} else {
|
| 1514 |
+
$name = $this->action ?: '';
|
| 1515 |
+
return true === $action ? $name : strtolower($name);
|
| 1516 |
+
}
|
| 1517 |
+
}
|
| 1518 |
+
|
| 1519 |
+
/**
|
| 1520 |
+
* 设置或者获取当前的语言
|
| 1521 |
+
* @access public
|
| 1522 |
+
* @param string $lang 语言名
|
| 1523 |
+
* @return string|Request
|
| 1524 |
+
*/
|
| 1525 |
+
public function langset($lang = null)
|
| 1526 |
+
{
|
| 1527 |
+
if (!is_null($lang)) {
|
| 1528 |
+
$this->langset = $lang;
|
| 1529 |
+
return $this;
|
| 1530 |
+
} else {
|
| 1531 |
+
return $this->langset ?: '';
|
| 1532 |
+
}
|
| 1533 |
+
}
|
| 1534 |
+
|
| 1535 |
+
/**
|
| 1536 |
+
* 设置或者获取当前请求的content
|
| 1537 |
+
* @access public
|
| 1538 |
+
* @return string
|
| 1539 |
+
*/
|
| 1540 |
+
public function getContent()
|
| 1541 |
+
{
|
| 1542 |
+
if (is_null($this->content)) {
|
| 1543 |
+
$this->content = $this->input;
|
| 1544 |
+
}
|
| 1545 |
+
return $this->content;
|
| 1546 |
+
}
|
| 1547 |
+
|
| 1548 |
+
/**
|
| 1549 |
+
* 获取当前请求的php://input
|
| 1550 |
+
* @access public
|
| 1551 |
+
* @return string
|
| 1552 |
+
*/
|
| 1553 |
+
public function getInput()
|
| 1554 |
+
{
|
| 1555 |
+
return $this->input;
|
| 1556 |
+
}
|
| 1557 |
+
|
| 1558 |
+
/**
|
| 1559 |
+
* 生成请求令牌
|
| 1560 |
+
* @access public
|
| 1561 |
+
* @param string $name 令牌名称
|
| 1562 |
+
* @param mixed $type 令牌生成方法
|
| 1563 |
+
* @return string
|
| 1564 |
+
*/
|
| 1565 |
+
public function token($name = '__token__', $type = 'md5')
|
| 1566 |
+
{
|
| 1567 |
+
$type = is_callable($type) ? $type : 'md5';
|
| 1568 |
+
$token = call_user_func($type, $_SERVER['REQUEST_TIME_FLOAT']);
|
| 1569 |
+
if ($this->isAjax()) {
|
| 1570 |
+
header($name . ': ' . $token);
|
| 1571 |
+
}
|
| 1572 |
+
Session::set($name, $token);
|
| 1573 |
+
return $token;
|
| 1574 |
+
}
|
| 1575 |
+
|
| 1576 |
+
/**
|
| 1577 |
+
* 设置当前地址的请求缓存
|
| 1578 |
+
* @access public
|
| 1579 |
+
* @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id
|
| 1580 |
+
* @param mixed $expire 缓存有效期
|
| 1581 |
+
* @param array $except 缓存排除
|
| 1582 |
+
* @param string $tag 缓存标签
|
| 1583 |
+
* @return void
|
| 1584 |
+
*/
|
| 1585 |
+
public function cache($key, $expire = null, $except = [], $tag = null)
|
| 1586 |
+
{
|
| 1587 |
+
if (!is_array($except)) {
|
| 1588 |
+
$tag = $except;
|
| 1589 |
+
$except = [];
|
| 1590 |
+
}
|
| 1591 |
+
|
| 1592 |
+
if (false !== $key && $this->isGet() && !$this->isCheckCache) {
|
| 1593 |
+
// 标记请求缓存检查
|
| 1594 |
+
$this->isCheckCache = true;
|
| 1595 |
+
if (false === $expire) {
|
| 1596 |
+
// 关闭当前缓存
|
| 1597 |
+
return;
|
| 1598 |
+
}
|
| 1599 |
+
if ($key instanceof \Closure) {
|
| 1600 |
+
$key = call_user_func_array($key, [$this]);
|
| 1601 |
+
} elseif (true === $key) {
|
| 1602 |
+
foreach ($except as $rule) {
|
| 1603 |
+
if (0 === stripos($this->url(), $rule)) {
|
| 1604 |
+
return;
|
| 1605 |
+
}
|
| 1606 |
+
}
|
| 1607 |
+
// 自动缓存功能
|
| 1608 |
+
$key = '__URL__';
|
| 1609 |
+
} elseif (strpos($key, '|')) {
|
| 1610 |
+
list($key, $fun) = explode('|', $key);
|
| 1611 |
+
}
|
| 1612 |
+
// 特殊规则替换
|
| 1613 |
+
if (false !== strpos($key, '__')) {
|
| 1614 |
+
$key = str_replace(['__MODULE__', '__CONTROLLER__', '__ACTION__', '__URL__', ''], [$this->module, $this->controller, $this->action, md5($this->url(true))], $key);
|
| 1615 |
+
}
|
| 1616 |
+
|
| 1617 |
+
if (false !== strpos($key, ':')) {
|
| 1618 |
+
$param = $this->param();
|
| 1619 |
+
foreach ($param as $item => $val) {
|
| 1620 |
+
if (is_string($val) && false !== strpos($key, ':' . $item)) {
|
| 1621 |
+
$key = str_replace(':' . $item, $val, $key);
|
| 1622 |
+
}
|
| 1623 |
+
}
|
| 1624 |
+
} elseif (strpos($key, ']')) {
|
| 1625 |
+
if ('[' . $this->ext() . ']' == $key) {
|
| 1626 |
+
// 缓存某个后缀的请求
|
| 1627 |
+
$key = md5($this->url());
|
| 1628 |
+
} else {
|
| 1629 |
+
return;
|
| 1630 |
+
}
|
| 1631 |
+
}
|
| 1632 |
+
if (isset($fun)) {
|
| 1633 |
+
$key = $fun($key);
|
| 1634 |
+
}
|
| 1635 |
+
|
| 1636 |
+
if (strtotime($this->server('HTTP_IF_MODIFIED_SINCE')) + $expire > $_SERVER['REQUEST_TIME']) {
|
| 1637 |
+
// 读取缓存
|
| 1638 |
+
$response = Response::create()->code(304);
|
| 1639 |
+
throw new \think\exception\HttpResponseException($response);
|
| 1640 |
+
} elseif (Cache::has($key)) {
|
| 1641 |
+
list($content, $header) = Cache::get($key);
|
| 1642 |
+
$response = Response::create($content)->header($header);
|
| 1643 |
+
throw new \think\exception\HttpResponseException($response);
|
| 1644 |
+
} else {
|
| 1645 |
+
$this->cache = [$key, $expire, $tag];
|
| 1646 |
+
}
|
| 1647 |
+
}
|
| 1648 |
+
}
|
| 1649 |
+
|
| 1650 |
+
/**
|
| 1651 |
+
* 读取请求缓存设置
|
| 1652 |
+
* @access public
|
| 1653 |
+
* @return array
|
| 1654 |
+
*/
|
| 1655 |
+
public function getCache()
|
| 1656 |
+
{
|
| 1657 |
+
return $this->cache;
|
| 1658 |
+
}
|
| 1659 |
+
|
| 1660 |
+
/**
|
| 1661 |
+
* 设置当前请求绑定的对象实例
|
| 1662 |
+
* @access public
|
| 1663 |
+
* @param string|array $name 绑定的对象标识
|
| 1664 |
+
* @param mixed $obj 绑定的对象实例
|
| 1665 |
+
* @return mixed
|
| 1666 |
+
*/
|
| 1667 |
+
public function bind($name, $obj = null)
|
| 1668 |
+
{
|
| 1669 |
+
if (is_array($name)) {
|
| 1670 |
+
$this->bind = array_merge($this->bind, $name);
|
| 1671 |
+
} else {
|
| 1672 |
+
$this->bind[$name] = $obj;
|
| 1673 |
+
}
|
| 1674 |
+
}
|
| 1675 |
+
|
| 1676 |
+
public function __set($name, $value)
|
| 1677 |
+
{
|
| 1678 |
+
$this->bind[$name] = $value;
|
| 1679 |
+
}
|
| 1680 |
+
|
| 1681 |
+
public function __get($name)
|
| 1682 |
+
{
|
| 1683 |
+
return isset($this->bind[$name]) ? $this->bind[$name] : null;
|
| 1684 |
+
}
|
| 1685 |
+
|
| 1686 |
+
public function __isset($name)
|
| 1687 |
+
{
|
| 1688 |
+
return isset($this->bind[$name]);
|
| 1689 |
+
}
|
| 1690 |
+
}
|
thinkphp/library/think/Response.php
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use think\response\Json as JsonResponse;
|
| 15 |
+
use think\response\Jsonp as JsonpResponse;
|
| 16 |
+
use think\response\Redirect as RedirectResponse;
|
| 17 |
+
use think\response\View as ViewResponse;
|
| 18 |
+
use think\response\Xml as XmlResponse;
|
| 19 |
+
|
| 20 |
+
class Response
|
| 21 |
+
{
|
| 22 |
+
// 原始数据
|
| 23 |
+
protected $data;
|
| 24 |
+
|
| 25 |
+
// 当前的contentType
|
| 26 |
+
protected $contentType = 'text/html';
|
| 27 |
+
|
| 28 |
+
// 字符集
|
| 29 |
+
protected $charset = 'utf-8';
|
| 30 |
+
|
| 31 |
+
//状态
|
| 32 |
+
protected $code = 200;
|
| 33 |
+
|
| 34 |
+
// 输出参数
|
| 35 |
+
protected $options = [];
|
| 36 |
+
// header参数
|
| 37 |
+
protected $header = [];
|
| 38 |
+
|
| 39 |
+
protected $content = null;
|
| 40 |
+
|
| 41 |
+
/**
|
| 42 |
+
* 构造函数
|
| 43 |
+
* @access public
|
| 44 |
+
* @param mixed $data 输出数据
|
| 45 |
+
* @param int $code
|
| 46 |
+
* @param array $header
|
| 47 |
+
* @param array $options 输出参数
|
| 48 |
+
*/
|
| 49 |
+
public function __construct($data = '', $code = 200, array $header = [], $options = [])
|
| 50 |
+
{
|
| 51 |
+
$this->data($data);
|
| 52 |
+
if (!empty($options)) {
|
| 53 |
+
$this->options = array_merge($this->options, $options);
|
| 54 |
+
}
|
| 55 |
+
$this->contentType($this->contentType, $this->charset);
|
| 56 |
+
$this->header = array_merge($this->header, $header);
|
| 57 |
+
$this->code = $code;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
/**
|
| 61 |
+
* 创建Response对象
|
| 62 |
+
* @access public
|
| 63 |
+
* @param mixed $data 输出数据
|
| 64 |
+
* @param string $type 输出类型
|
| 65 |
+
* @param int $code
|
| 66 |
+
* @param array $header
|
| 67 |
+
* @param array $options 输出参数
|
| 68 |
+
* @return Response|JsonResponse|ViewResponse|XmlResponse|RedirectResponse|JsonpResponse
|
| 69 |
+
*/
|
| 70 |
+
public static function create($data = '', $type = '', $code = 200, array $header = [], $options = [])
|
| 71 |
+
{
|
| 72 |
+
$class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst(strtolower($type));
|
| 73 |
+
if (class_exists($class)) {
|
| 74 |
+
$response = new $class($data, $code, $header, $options);
|
| 75 |
+
} else {
|
| 76 |
+
$response = new static($data, $code, $header, $options);
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
return $response;
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
/**
|
| 83 |
+
* 发送数据到客户端
|
| 84 |
+
* @access public
|
| 85 |
+
* @return mixed
|
| 86 |
+
* @throws \InvalidArgumentException
|
| 87 |
+
*/
|
| 88 |
+
public function send()
|
| 89 |
+
{
|
| 90 |
+
// 监听response_send
|
| 91 |
+
Hook::listen('response_send', $this);
|
| 92 |
+
|
| 93 |
+
// 处理输出数据
|
| 94 |
+
$data = $this->getContent();
|
| 95 |
+
|
| 96 |
+
// Trace调试注入
|
| 97 |
+
if (Env::get('app_trace', Config::get('app_trace'))) {
|
| 98 |
+
Debug::inject($this, $data);
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
if (200 == $this->code) {
|
| 102 |
+
$cache = Request::instance()->getCache();
|
| 103 |
+
if ($cache) {
|
| 104 |
+
$this->header['Cache-Control'] = 'max-age=' . $cache[1] . ',must-revalidate';
|
| 105 |
+
$this->header['Last-Modified'] = gmdate('D, d M Y H:i:s') . ' GMT';
|
| 106 |
+
$this->header['Expires'] = gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME'] + $cache[1]) . ' GMT';
|
| 107 |
+
Cache::tag($cache[2])->set($cache[0], [$data, $this->header], $cache[1]);
|
| 108 |
+
}
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
if (!headers_sent() && !empty($this->header)) {
|
| 112 |
+
// 发送状态码
|
| 113 |
+
http_response_code($this->code);
|
| 114 |
+
// 发送头部信息
|
| 115 |
+
foreach ($this->header as $name => $val) {
|
| 116 |
+
if (is_null($val)) {
|
| 117 |
+
header($name);
|
| 118 |
+
} else {
|
| 119 |
+
header($name . ':' . $val);
|
| 120 |
+
}
|
| 121 |
+
}
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
echo $data;
|
| 125 |
+
|
| 126 |
+
if (function_exists('fastcgi_finish_request')) {
|
| 127 |
+
// 提高页面响应
|
| 128 |
+
fastcgi_finish_request();
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
// 监听response_end
|
| 132 |
+
Hook::listen('response_end', $this);
|
| 133 |
+
|
| 134 |
+
// 清空当次请求有效的数据
|
| 135 |
+
if (!($this instanceof RedirectResponse)) {
|
| 136 |
+
Session::flush();
|
| 137 |
+
}
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
/**
|
| 141 |
+
* 处理数据
|
| 142 |
+
* @access protected
|
| 143 |
+
* @param mixed $data 要处理的数据
|
| 144 |
+
* @return mixed
|
| 145 |
+
*/
|
| 146 |
+
protected function output($data)
|
| 147 |
+
{
|
| 148 |
+
return $data;
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
/**
|
| 152 |
+
* 输出的参数
|
| 153 |
+
* @access public
|
| 154 |
+
* @param mixed $options 输出参数
|
| 155 |
+
* @return $this
|
| 156 |
+
*/
|
| 157 |
+
public function options($options = [])
|
| 158 |
+
{
|
| 159 |
+
$this->options = array_merge($this->options, $options);
|
| 160 |
+
return $this;
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
/**
|
| 164 |
+
* 输出数据设置
|
| 165 |
+
* @access public
|
| 166 |
+
* @param mixed $data 输出数据
|
| 167 |
+
* @return $this
|
| 168 |
+
*/
|
| 169 |
+
public function data($data)
|
| 170 |
+
{
|
| 171 |
+
$this->data = $data;
|
| 172 |
+
return $this;
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
/**
|
| 176 |
+
* 设置响应头
|
| 177 |
+
* @access public
|
| 178 |
+
* @param string|array $name 参数名
|
| 179 |
+
* @param string $value 参数值
|
| 180 |
+
* @return $this
|
| 181 |
+
*/
|
| 182 |
+
public function header($name, $value = null)
|
| 183 |
+
{
|
| 184 |
+
if (is_array($name)) {
|
| 185 |
+
$this->header = array_merge($this->header, $name);
|
| 186 |
+
} else {
|
| 187 |
+
$this->header[$name] = $value;
|
| 188 |
+
}
|
| 189 |
+
return $this;
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
/**
|
| 193 |
+
* 设置页面输出内容
|
| 194 |
+
* @param $content
|
| 195 |
+
* @return $this
|
| 196 |
+
*/
|
| 197 |
+
public function content($content)
|
| 198 |
+
{
|
| 199 |
+
if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([
|
| 200 |
+
$content,
|
| 201 |
+
'__toString',
|
| 202 |
+
])
|
| 203 |
+
) {
|
| 204 |
+
throw new \InvalidArgumentException(sprintf('variable type error: %s', gettype($content)));
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
$this->content = (string) $content;
|
| 208 |
+
|
| 209 |
+
return $this;
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
/**
|
| 213 |
+
* 发送HTTP状态
|
| 214 |
+
* @param integer $code 状态码
|
| 215 |
+
* @return $this
|
| 216 |
+
*/
|
| 217 |
+
public function code($code)
|
| 218 |
+
{
|
| 219 |
+
$this->code = $code;
|
| 220 |
+
return $this;
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
/**
|
| 224 |
+
* LastModified
|
| 225 |
+
* @param string $time
|
| 226 |
+
* @return $this
|
| 227 |
+
*/
|
| 228 |
+
public function lastModified($time)
|
| 229 |
+
{
|
| 230 |
+
$this->header['Last-Modified'] = $time;
|
| 231 |
+
return $this;
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
/**
|
| 235 |
+
* Expires
|
| 236 |
+
* @param string $time
|
| 237 |
+
* @return $this
|
| 238 |
+
*/
|
| 239 |
+
public function expires($time)
|
| 240 |
+
{
|
| 241 |
+
$this->header['Expires'] = $time;
|
| 242 |
+
return $this;
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
/**
|
| 246 |
+
* ETag
|
| 247 |
+
* @param string $eTag
|
| 248 |
+
* @return $this
|
| 249 |
+
*/
|
| 250 |
+
public function eTag($eTag)
|
| 251 |
+
{
|
| 252 |
+
$this->header['ETag'] = $eTag;
|
| 253 |
+
return $this;
|
| 254 |
+
}
|
| 255 |
+
|
| 256 |
+
/**
|
| 257 |
+
* 页面缓存控制
|
| 258 |
+
* @param string $cache 状态码
|
| 259 |
+
* @return $this
|
| 260 |
+
*/
|
| 261 |
+
public function cacheControl($cache)
|
| 262 |
+
{
|
| 263 |
+
$this->header['Cache-control'] = $cache;
|
| 264 |
+
return $this;
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
/**
|
| 268 |
+
* 页面输出类型
|
| 269 |
+
* @param string $contentType 输出类型
|
| 270 |
+
* @param string $charset 输出编码
|
| 271 |
+
* @return $this
|
| 272 |
+
*/
|
| 273 |
+
public function contentType($contentType, $charset = 'utf-8')
|
| 274 |
+
{
|
| 275 |
+
$this->header['Content-Type'] = $contentType . '; charset=' . $charset;
|
| 276 |
+
return $this;
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
/**
|
| 280 |
+
* 获取头部信息
|
| 281 |
+
* @param string $name 头部名称
|
| 282 |
+
* @return mixed
|
| 283 |
+
*/
|
| 284 |
+
public function getHeader($name = '')
|
| 285 |
+
{
|
| 286 |
+
if (!empty($name)) {
|
| 287 |
+
return isset($this->header[$name]) ? $this->header[$name] : null;
|
| 288 |
+
} else {
|
| 289 |
+
return $this->header;
|
| 290 |
+
}
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
/**
|
| 294 |
+
* 获取原始数据
|
| 295 |
+
* @return mixed
|
| 296 |
+
*/
|
| 297 |
+
public function getData()
|
| 298 |
+
{
|
| 299 |
+
return $this->data;
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
/**
|
| 303 |
+
* 获取输出数据
|
| 304 |
+
* @return mixed
|
| 305 |
+
*/
|
| 306 |
+
public function getContent()
|
| 307 |
+
{
|
| 308 |
+
if (null == $this->content) {
|
| 309 |
+
$content = $this->output($this->data);
|
| 310 |
+
|
| 311 |
+
if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([
|
| 312 |
+
$content,
|
| 313 |
+
'__toString',
|
| 314 |
+
])
|
| 315 |
+
) {
|
| 316 |
+
throw new \InvalidArgumentException(sprintf('variable type error: %s', gettype($content)));
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
$this->content = (string) $content;
|
| 320 |
+
}
|
| 321 |
+
return $this->content;
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
/**
|
| 325 |
+
* 获取状态码
|
| 326 |
+
* @return integer
|
| 327 |
+
*/
|
| 328 |
+
public function getCode()
|
| 329 |
+
{
|
| 330 |
+
return $this->code;
|
| 331 |
+
}
|
| 332 |
+
}
|
thinkphp/library/think/Route.php
ADDED
|
@@ -0,0 +1,1645 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use think\exception\HttpException;
|
| 15 |
+
|
| 16 |
+
class Route
|
| 17 |
+
{
|
| 18 |
+
// 路由规则
|
| 19 |
+
private static $rules = [
|
| 20 |
+
'get' => [],
|
| 21 |
+
'post' => [],
|
| 22 |
+
'put' => [],
|
| 23 |
+
'delete' => [],
|
| 24 |
+
'patch' => [],
|
| 25 |
+
'head' => [],
|
| 26 |
+
'options' => [],
|
| 27 |
+
'*' => [],
|
| 28 |
+
'alias' => [],
|
| 29 |
+
'domain' => [],
|
| 30 |
+
'pattern' => [],
|
| 31 |
+
'name' => [],
|
| 32 |
+
];
|
| 33 |
+
|
| 34 |
+
// REST路由操作方法定义
|
| 35 |
+
private static $rest = [
|
| 36 |
+
'index' => ['get', '', 'index'],
|
| 37 |
+
'create' => ['get', '/create', 'create'],
|
| 38 |
+
'edit' => ['get', '/:id/edit', 'edit'],
|
| 39 |
+
'read' => ['get', '/:id', 'read'],
|
| 40 |
+
'save' => ['post', '', 'save'],
|
| 41 |
+
'update' => ['put', '/:id', 'update'],
|
| 42 |
+
'delete' => ['delete', '/:id', 'delete'],
|
| 43 |
+
];
|
| 44 |
+
|
| 45 |
+
// 不同请求类型的方法前缀
|
| 46 |
+
private static $methodPrefix = [
|
| 47 |
+
'get' => 'get',
|
| 48 |
+
'post' => 'post',
|
| 49 |
+
'put' => 'put',
|
| 50 |
+
'delete' => 'delete',
|
| 51 |
+
'patch' => 'patch',
|
| 52 |
+
];
|
| 53 |
+
|
| 54 |
+
// 子域名
|
| 55 |
+
private static $subDomain = '';
|
| 56 |
+
// 域名绑定
|
| 57 |
+
private static $bind = [];
|
| 58 |
+
// 当前分组信息
|
| 59 |
+
private static $group = [];
|
| 60 |
+
// 当前子域名绑定
|
| 61 |
+
private static $domainBind;
|
| 62 |
+
private static $domainRule;
|
| 63 |
+
// 当前域名
|
| 64 |
+
private static $domain;
|
| 65 |
+
// 当前路由执行过程中的参数
|
| 66 |
+
private static $option = [];
|
| 67 |
+
|
| 68 |
+
/**
|
| 69 |
+
* 注册变量规则
|
| 70 |
+
* @access public
|
| 71 |
+
* @param string|array $name 变量名
|
| 72 |
+
* @param string $rule 变量规则
|
| 73 |
+
* @return void
|
| 74 |
+
*/
|
| 75 |
+
public static function pattern($name = null, $rule = '')
|
| 76 |
+
{
|
| 77 |
+
if (is_array($name)) {
|
| 78 |
+
self::$rules['pattern'] = array_merge(self::$rules['pattern'], $name);
|
| 79 |
+
} else {
|
| 80 |
+
self::$rules['pattern'][$name] = $rule;
|
| 81 |
+
}
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
/**
|
| 85 |
+
* 注册子域名部署规则
|
| 86 |
+
* @access public
|
| 87 |
+
* @param string|array $domain 子域名
|
| 88 |
+
* @param mixed $rule 路由规则
|
| 89 |
+
* @param array $option 路由参数
|
| 90 |
+
* @param array $pattern 变量规则
|
| 91 |
+
* @return void
|
| 92 |
+
*/
|
| 93 |
+
public static function domain($domain, $rule = '', $option = [], $pattern = [])
|
| 94 |
+
{
|
| 95 |
+
if (is_array($domain)) {
|
| 96 |
+
foreach ($domain as $key => $item) {
|
| 97 |
+
self::domain($key, $item, $option, $pattern);
|
| 98 |
+
}
|
| 99 |
+
} elseif ($rule instanceof \Closure) {
|
| 100 |
+
// 执行闭包
|
| 101 |
+
self::setDomain($domain);
|
| 102 |
+
call_user_func_array($rule, []);
|
| 103 |
+
self::setDomain(null);
|
| 104 |
+
} elseif (is_array($rule)) {
|
| 105 |
+
self::setDomain($domain);
|
| 106 |
+
self::group('', function () use ($rule) {
|
| 107 |
+
// 动态注册域名的路由规则
|
| 108 |
+
self::registerRules($rule);
|
| 109 |
+
}, $option, $pattern);
|
| 110 |
+
self::setDomain(null);
|
| 111 |
+
} else {
|
| 112 |
+
self::$rules['domain'][$domain]['[bind]'] = [$rule, $option, $pattern];
|
| 113 |
+
}
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
private static function setDomain($domain)
|
| 117 |
+
{
|
| 118 |
+
self::$domain = $domain;
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
/**
|
| 122 |
+
* 设置路由绑定
|
| 123 |
+
* @access public
|
| 124 |
+
* @param mixed $bind 绑定信息
|
| 125 |
+
* @param string $type 绑定类型 默认为module 支持 namespace class controller
|
| 126 |
+
* @return mixed
|
| 127 |
+
*/
|
| 128 |
+
public static function bind($bind, $type = 'module')
|
| 129 |
+
{
|
| 130 |
+
self::$bind = ['type' => $type, $type => $bind];
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
/**
|
| 134 |
+
* 设置或者获取路由标识
|
| 135 |
+
* @access public
|
| 136 |
+
* @param string|array $name 路由命名标识 数组表示批量设置
|
| 137 |
+
* @param array $value 路由地址及变量信息
|
| 138 |
+
* @return array
|
| 139 |
+
*/
|
| 140 |
+
public static function name($name = '', $value = null)
|
| 141 |
+
{
|
| 142 |
+
if (is_array($name)) {
|
| 143 |
+
return self::$rules['name'] = $name;
|
| 144 |
+
} elseif ('' === $name) {
|
| 145 |
+
return self::$rules['name'];
|
| 146 |
+
} elseif (!is_null($value)) {
|
| 147 |
+
self::$rules['name'][strtolower($name)][] = $value;
|
| 148 |
+
} else {
|
| 149 |
+
$name = strtolower($name);
|
| 150 |
+
return isset(self::$rules['name'][$name]) ? self::$rules['name'][$name] : null;
|
| 151 |
+
}
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
/**
|
| 155 |
+
* 读取路由绑定
|
| 156 |
+
* @access public
|
| 157 |
+
* @param string $type 绑定类型
|
| 158 |
+
* @return mixed
|
| 159 |
+
*/
|
| 160 |
+
public static function getBind($type)
|
| 161 |
+
{
|
| 162 |
+
return isset(self::$bind[$type]) ? self::$bind[$type] : null;
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
/**
|
| 166 |
+
* 导入配置文件的路由规则
|
| 167 |
+
* @access public
|
| 168 |
+
* @param array $rule 路由规则
|
| 169 |
+
* @param string $type 请求类型
|
| 170 |
+
* @return void
|
| 171 |
+
*/
|
| 172 |
+
public static function import(array $rule, $type = '*')
|
| 173 |
+
{
|
| 174 |
+
// 检查域名部署
|
| 175 |
+
if (isset($rule['__domain__'])) {
|
| 176 |
+
self::domain($rule['__domain__']);
|
| 177 |
+
unset($rule['__domain__']);
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
// 检查变量规则
|
| 181 |
+
if (isset($rule['__pattern__'])) {
|
| 182 |
+
self::pattern($rule['__pattern__']);
|
| 183 |
+
unset($rule['__pattern__']);
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
// 检查路由别名
|
| 187 |
+
if (isset($rule['__alias__'])) {
|
| 188 |
+
self::alias($rule['__alias__']);
|
| 189 |
+
unset($rule['__alias__']);
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
// 检查资源路由
|
| 193 |
+
if (isset($rule['__rest__'])) {
|
| 194 |
+
self::resource($rule['__rest__']);
|
| 195 |
+
unset($rule['__rest__']);
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
self::registerRules($rule, strtolower($type));
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
// 批量注册路由
|
| 202 |
+
protected static function registerRules($rules, $type = '*')
|
| 203 |
+
{
|
| 204 |
+
foreach ($rules as $key => $val) {
|
| 205 |
+
if (is_numeric($key)) {
|
| 206 |
+
$key = array_shift($val);
|
| 207 |
+
}
|
| 208 |
+
if (empty($val)) {
|
| 209 |
+
continue;
|
| 210 |
+
}
|
| 211 |
+
if (is_string($key) && 0 === strpos($key, '[')) {
|
| 212 |
+
$key = substr($key, 1, -1);
|
| 213 |
+
self::group($key, $val);
|
| 214 |
+
} elseif (is_array($val)) {
|
| 215 |
+
self::setRule($key, $val[0], $type, $val[1], isset($val[2]) ? $val[2] : []);
|
| 216 |
+
} else {
|
| 217 |
+
self::setRule($key, $val, $type);
|
| 218 |
+
}
|
| 219 |
+
}
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
/**
|
| 223 |
+
* 注册路由规则
|
| 224 |
+
* @access public
|
| 225 |
+
* @param string|array $rule 路由规则
|
| 226 |
+
* @param string $route 路由地址
|
| 227 |
+
* @param string $type 请求类型
|
| 228 |
+
* @param array $option 路由参数
|
| 229 |
+
* @param array $pattern 变量规则
|
| 230 |
+
* @return void
|
| 231 |
+
*/
|
| 232 |
+
public static function rule($rule, $route = '', $type = '*', $option = [], $pattern = [])
|
| 233 |
+
{
|
| 234 |
+
$group = self::getGroup('name');
|
| 235 |
+
|
| 236 |
+
if (!is_null($group)) {
|
| 237 |
+
// 路由分组
|
| 238 |
+
$option = array_merge(self::getGroup('option'), $option);
|
| 239 |
+
$pattern = array_merge(self::getGroup('pattern'), $pattern);
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
$type = strtolower($type);
|
| 243 |
+
|
| 244 |
+
if (strpos($type, '|')) {
|
| 245 |
+
$option['method'] = $type;
|
| 246 |
+
$type = '*';
|
| 247 |
+
}
|
| 248 |
+
if (is_array($rule) && empty($route)) {
|
| 249 |
+
foreach ($rule as $key => $val) {
|
| 250 |
+
if (is_numeric($key)) {
|
| 251 |
+
$key = array_shift($val);
|
| 252 |
+
}
|
| 253 |
+
if (is_array($val)) {
|
| 254 |
+
$route = $val[0];
|
| 255 |
+
$option1 = array_merge($option, $val[1]);
|
| 256 |
+
$pattern1 = array_merge($pattern, isset($val[2]) ? $val[2] : []);
|
| 257 |
+
} else {
|
| 258 |
+
$option1 = null;
|
| 259 |
+
$pattern1 = null;
|
| 260 |
+
$route = $val;
|
| 261 |
+
}
|
| 262 |
+
self::setRule($key, $route, $type, !is_null($option1) ? $option1 : $option, !is_null($pattern1) ? $pattern1 : $pattern, $group);
|
| 263 |
+
}
|
| 264 |
+
} else {
|
| 265 |
+
self::setRule($rule, $route, $type, $option, $pattern, $group);
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
/**
|
| 271 |
+
* 设置路由规则
|
| 272 |
+
* @access public
|
| 273 |
+
* @param string|array $rule 路由规则
|
| 274 |
+
* @param string $route 路由地址
|
| 275 |
+
* @param string $type 请求类型
|
| 276 |
+
* @param array $option 路由参数
|
| 277 |
+
* @param array $pattern 变量规则
|
| 278 |
+
* @param string $group 所属分组
|
| 279 |
+
* @return void
|
| 280 |
+
*/
|
| 281 |
+
protected static function setRule($rule, $route, $type = '*', $option = [], $pattern = [], $group = '')
|
| 282 |
+
{
|
| 283 |
+
if (is_array($rule)) {
|
| 284 |
+
$name = $rule[0];
|
| 285 |
+
$rule = $rule[1];
|
| 286 |
+
} elseif (is_string($route)) {
|
| 287 |
+
$name = $route;
|
| 288 |
+
}
|
| 289 |
+
if (!isset($option['complete_match'])) {
|
| 290 |
+
if (Config::get('route_complete_match')) {
|
| 291 |
+
$option['complete_match'] = true;
|
| 292 |
+
} elseif ('$' == substr($rule, -1, 1)) {
|
| 293 |
+
// 是否完整匹配
|
| 294 |
+
$option['complete_match'] = true;
|
| 295 |
+
}
|
| 296 |
+
} elseif (empty($option['complete_match']) && '$' == substr($rule, -1, 1)) {
|
| 297 |
+
// 是否完整匹配
|
| 298 |
+
$option['complete_match'] = true;
|
| 299 |
+
}
|
| 300 |
+
|
| 301 |
+
if ('$' == substr($rule, -1, 1)) {
|
| 302 |
+
$rule = substr($rule, 0, -1);
|
| 303 |
+
}
|
| 304 |
+
|
| 305 |
+
if ('/' != $rule || $group) {
|
| 306 |
+
$rule = trim($rule, '/');
|
| 307 |
+
}
|
| 308 |
+
$vars = self::parseVar($rule);
|
| 309 |
+
if (isset($name)) {
|
| 310 |
+
$key = $group ? $group . ($rule ? '/' . $rule : '') : $rule;
|
| 311 |
+
$suffix = isset($option['ext']) ? $option['ext'] : null;
|
| 312 |
+
self::name($name, [$key, $vars, self::$domain, $suffix]);
|
| 313 |
+
}
|
| 314 |
+
if (isset($option['modular'])) {
|
| 315 |
+
$route = $option['modular'] . '/' . $route;
|
| 316 |
+
}
|
| 317 |
+
if ($group) {
|
| 318 |
+
if ('*' != $type) {
|
| 319 |
+
$option['method'] = $type;
|
| 320 |
+
}
|
| 321 |
+
if (self::$domain) {
|
| 322 |
+
self::$rules['domain'][self::$domain]['*'][$group]['rule'][] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
|
| 323 |
+
} else {
|
| 324 |
+
self::$rules['*'][$group]['rule'][] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
|
| 325 |
+
}
|
| 326 |
+
} else {
|
| 327 |
+
if ('*' != $type && isset(self::$rules['*'][$rule])) {
|
| 328 |
+
unset(self::$rules['*'][$rule]);
|
| 329 |
+
}
|
| 330 |
+
if (self::$domain) {
|
| 331 |
+
self::$rules['domain'][self::$domain][$type][$rule] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
|
| 332 |
+
} else {
|
| 333 |
+
self::$rules[$type][$rule] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
|
| 334 |
+
}
|
| 335 |
+
if ('*' == $type) {
|
| 336 |
+
// 注册路由快捷方式
|
| 337 |
+
foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) {
|
| 338 |
+
if (self::$domain && !isset(self::$rules['domain'][self::$domain][$method][$rule])) {
|
| 339 |
+
self::$rules['domain'][self::$domain][$method][$rule] = true;
|
| 340 |
+
} elseif (!self::$domain && !isset(self::$rules[$method][$rule])) {
|
| 341 |
+
self::$rules[$method][$rule] = true;
|
| 342 |
+
}
|
| 343 |
+
}
|
| 344 |
+
}
|
| 345 |
+
}
|
| 346 |
+
}
|
| 347 |
+
|
| 348 |
+
/**
|
| 349 |
+
* 设置当前执行的参数信息
|
| 350 |
+
* @access public
|
| 351 |
+
* @param array $options 参数信息
|
| 352 |
+
* @return mixed
|
| 353 |
+
*/
|
| 354 |
+
protected static function setOption($options = [])
|
| 355 |
+
{
|
| 356 |
+
self::$option[] = $options;
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
/**
|
| 360 |
+
* 获取当前执行的所有参数信息
|
| 361 |
+
* @access public
|
| 362 |
+
* @return array
|
| 363 |
+
*/
|
| 364 |
+
public static function getOption()
|
| 365 |
+
{
|
| 366 |
+
return self::$option;
|
| 367 |
+
}
|
| 368 |
+
|
| 369 |
+
/**
|
| 370 |
+
* 获取当前的分组信息
|
| 371 |
+
* @access public
|
| 372 |
+
* @param string $type 分组信息名称 name option pattern
|
| 373 |
+
* @return mixed
|
| 374 |
+
*/
|
| 375 |
+
public static function getGroup($type)
|
| 376 |
+
{
|
| 377 |
+
if (isset(self::$group[$type])) {
|
| 378 |
+
return self::$group[$type];
|
| 379 |
+
} else {
|
| 380 |
+
return 'name' == $type ? null : [];
|
| 381 |
+
}
|
| 382 |
+
}
|
| 383 |
+
|
| 384 |
+
/**
|
| 385 |
+
* 设置当前的路由分组
|
| 386 |
+
* @access public
|
| 387 |
+
* @param string $name 分组名称
|
| 388 |
+
* @param array $option 分组路由参数
|
| 389 |
+
* @param array $pattern 分组变量规则
|
| 390 |
+
* @return void
|
| 391 |
+
*/
|
| 392 |
+
public static function setGroup($name, $option = [], $pattern = [])
|
| 393 |
+
{
|
| 394 |
+
self::$group['name'] = $name;
|
| 395 |
+
self::$group['option'] = $option ?: [];
|
| 396 |
+
self::$group['pattern'] = $pattern ?: [];
|
| 397 |
+
}
|
| 398 |
+
|
| 399 |
+
/**
|
| 400 |
+
* 注册路由分组
|
| 401 |
+
* @access public
|
| 402 |
+
* @param string|array $name 分组名称或者参数
|
| 403 |
+
* @param array|\Closure $routes 路由地址
|
| 404 |
+
* @param array $option 路由参数
|
| 405 |
+
* @param array $pattern 变量规则
|
| 406 |
+
* @return void
|
| 407 |
+
*/
|
| 408 |
+
public static function group($name, $routes, $option = [], $pattern = [])
|
| 409 |
+
{
|
| 410 |
+
if (is_array($name)) {
|
| 411 |
+
$option = $name;
|
| 412 |
+
$name = isset($option['name']) ? $option['name'] : '';
|
| 413 |
+
}
|
| 414 |
+
// 分组
|
| 415 |
+
$currentGroup = self::getGroup('name');
|
| 416 |
+
if ($currentGroup) {
|
| 417 |
+
$name = $currentGroup . ($name ? '/' . ltrim($name, '/') : '');
|
| 418 |
+
}
|
| 419 |
+
if (!empty($name)) {
|
| 420 |
+
if ($routes instanceof \Closure) {
|
| 421 |
+
$currentOption = self::getGroup('option');
|
| 422 |
+
$currentPattern = self::getGroup('pattern');
|
| 423 |
+
self::setGroup($name, array_merge($currentOption, $option), array_merge($currentPattern, $pattern));
|
| 424 |
+
call_user_func_array($routes, []);
|
| 425 |
+
self::setGroup($currentGroup, $currentOption, $currentPattern);
|
| 426 |
+
if ($currentGroup != $name) {
|
| 427 |
+
self::$rules['*'][$name]['route'] = '';
|
| 428 |
+
self::$rules['*'][$name]['var'] = self::parseVar($name);
|
| 429 |
+
self::$rules['*'][$name]['option'] = $option;
|
| 430 |
+
self::$rules['*'][$name]['pattern'] = $pattern;
|
| 431 |
+
}
|
| 432 |
+
} else {
|
| 433 |
+
$item = [];
|
| 434 |
+
$completeMatch = Config::get('route_complete_match');
|
| 435 |
+
foreach ($routes as $key => $val) {
|
| 436 |
+
if (is_numeric($key)) {
|
| 437 |
+
$key = array_shift($val);
|
| 438 |
+
}
|
| 439 |
+
if (is_array($val)) {
|
| 440 |
+
$route = $val[0];
|
| 441 |
+
$option1 = array_merge($option, isset($val[1]) ? $val[1] : []);
|
| 442 |
+
$pattern1 = array_merge($pattern, isset($val[2]) ? $val[2] : []);
|
| 443 |
+
} else {
|
| 444 |
+
$route = $val;
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
+
$options = isset($option1) ? $option1 : $option;
|
| 448 |
+
$patterns = isset($pattern1) ? $pattern1 : $pattern;
|
| 449 |
+
if ('$' == substr($key, -1, 1)) {
|
| 450 |
+
// 是否完整匹配
|
| 451 |
+
$options['complete_match'] = true;
|
| 452 |
+
$key = substr($key, 0, -1);
|
| 453 |
+
} elseif ($completeMatch) {
|
| 454 |
+
$options['complete_match'] = true;
|
| 455 |
+
}
|
| 456 |
+
$key = trim($key, '/');
|
| 457 |
+
$vars = self::parseVar($key);
|
| 458 |
+
$item[] = ['rule' => $key, 'route' => $route, 'var' => $vars, 'option' => $options, 'pattern' => $patterns];
|
| 459 |
+
// 设置路由标识
|
| 460 |
+
$suffix = isset($options['ext']) ? $options['ext'] : null;
|
| 461 |
+
self::name($route, [$name . ($key ? '/' . $key : ''), $vars, self::$domain, $suffix]);
|
| 462 |
+
}
|
| 463 |
+
self::$rules['*'][$name] = ['rule' => $item, 'route' => '', 'var' => [], 'option' => $option, 'pattern' => $pattern];
|
| 464 |
+
}
|
| 465 |
+
|
| 466 |
+
foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) {
|
| 467 |
+
if (!isset(self::$rules[$method][$name])) {
|
| 468 |
+
self::$rules[$method][$name] = true;
|
| 469 |
+
} elseif (is_array(self::$rules[$method][$name])) {
|
| 470 |
+
self::$rules[$method][$name] = array_merge(self::$rules['*'][$name], self::$rules[$method][$name]);
|
| 471 |
+
}
|
| 472 |
+
}
|
| 473 |
+
|
| 474 |
+
} elseif ($routes instanceof \Closure) {
|
| 475 |
+
// 闭包注册
|
| 476 |
+
$currentOption = self::getGroup('option');
|
| 477 |
+
$currentPattern = self::getGroup('pattern');
|
| 478 |
+
self::setGroup('', array_merge($currentOption, $option), array_merge($currentPattern, $pattern));
|
| 479 |
+
call_user_func_array($routes, []);
|
| 480 |
+
self::setGroup($currentGroup, $currentOption, $currentPattern);
|
| 481 |
+
} else {
|
| 482 |
+
// 批量注册路由
|
| 483 |
+
self::rule($routes, '', '*', $option, $pattern);
|
| 484 |
+
}
|
| 485 |
+
}
|
| 486 |
+
|
| 487 |
+
/**
|
| 488 |
+
* 注册路由
|
| 489 |
+
* @access public
|
| 490 |
+
* @param string|array $rule 路由规则
|
| 491 |
+
* @param string $route 路由地址
|
| 492 |
+
* @param array $option 路由参数
|
| 493 |
+
* @param array $pattern 变量规则
|
| 494 |
+
* @return void
|
| 495 |
+
*/
|
| 496 |
+
public static function any($rule, $route = '', $option = [], $pattern = [])
|
| 497 |
+
{
|
| 498 |
+
self::rule($rule, $route, '*', $option, $pattern);
|
| 499 |
+
}
|
| 500 |
+
|
| 501 |
+
/**
|
| 502 |
+
* 注册GET路由
|
| 503 |
+
* @access public
|
| 504 |
+
* @param string|array $rule 路由规则
|
| 505 |
+
* @param string $route 路由地址
|
| 506 |
+
* @param array $option 路由参数
|
| 507 |
+
* @param array $pattern 变量规则
|
| 508 |
+
* @return void
|
| 509 |
+
*/
|
| 510 |
+
public static function get($rule, $route = '', $option = [], $pattern = [])
|
| 511 |
+
{
|
| 512 |
+
self::rule($rule, $route, 'GET', $option, $pattern);
|
| 513 |
+
}
|
| 514 |
+
|
| 515 |
+
/**
|
| 516 |
+
* 注册POST路由
|
| 517 |
+
* @access public
|
| 518 |
+
* @param string|array $rule 路由规则
|
| 519 |
+
* @param string $route 路由地址
|
| 520 |
+
* @param array $option 路由参数
|
| 521 |
+
* @param array $pattern 变量规则
|
| 522 |
+
* @return void
|
| 523 |
+
*/
|
| 524 |
+
public static function post($rule, $route = '', $option = [], $pattern = [])
|
| 525 |
+
{
|
| 526 |
+
self::rule($rule, $route, 'POST', $option, $pattern);
|
| 527 |
+
}
|
| 528 |
+
|
| 529 |
+
/**
|
| 530 |
+
* 注册PUT路由
|
| 531 |
+
* @access public
|
| 532 |
+
* @param string|array $rule 路由规则
|
| 533 |
+
* @param string $route 路由地址
|
| 534 |
+
* @param array $option 路由参数
|
| 535 |
+
* @param array $pattern 变量规则
|
| 536 |
+
* @return void
|
| 537 |
+
*/
|
| 538 |
+
public static function put($rule, $route = '', $option = [], $pattern = [])
|
| 539 |
+
{
|
| 540 |
+
self::rule($rule, $route, 'PUT', $option, $pattern);
|
| 541 |
+
}
|
| 542 |
+
|
| 543 |
+
/**
|
| 544 |
+
* 注册DELETE路由
|
| 545 |
+
* @access public
|
| 546 |
+
* @param string|array $rule 路由规则
|
| 547 |
+
* @param string $route 路由地址
|
| 548 |
+
* @param array $option 路由参数
|
| 549 |
+
* @param array $pattern 变量规则
|
| 550 |
+
* @return void
|
| 551 |
+
*/
|
| 552 |
+
public static function delete($rule, $route = '', $option = [], $pattern = [])
|
| 553 |
+
{
|
| 554 |
+
self::rule($rule, $route, 'DELETE', $option, $pattern);
|
| 555 |
+
}
|
| 556 |
+
|
| 557 |
+
/**
|
| 558 |
+
* 注册PATCH路由
|
| 559 |
+
* @access public
|
| 560 |
+
* @param string|array $rule 路由规则
|
| 561 |
+
* @param string $route 路由地址
|
| 562 |
+
* @param array $option 路由参数
|
| 563 |
+
* @param array $pattern 变量规则
|
| 564 |
+
* @return void
|
| 565 |
+
*/
|
| 566 |
+
public static function patch($rule, $route = '', $option = [], $pattern = [])
|
| 567 |
+
{
|
| 568 |
+
self::rule($rule, $route, 'PATCH', $option, $pattern);
|
| 569 |
+
}
|
| 570 |
+
|
| 571 |
+
/**
|
| 572 |
+
* 注册资源路由
|
| 573 |
+
* @access public
|
| 574 |
+
* @param string|array $rule 路由规则
|
| 575 |
+
* @param string $route 路由地址
|
| 576 |
+
* @param array $option 路由参数
|
| 577 |
+
* @param array $pattern 变量规则
|
| 578 |
+
* @return void
|
| 579 |
+
*/
|
| 580 |
+
public static function resource($rule, $route = '', $option = [], $pattern = [])
|
| 581 |
+
{
|
| 582 |
+
if (is_array($rule)) {
|
| 583 |
+
foreach ($rule as $key => $val) {
|
| 584 |
+
if (is_array($val)) {
|
| 585 |
+
list($val, $option, $pattern) = array_pad($val, 3, []);
|
| 586 |
+
}
|
| 587 |
+
self::resource($key, $val, $option, $pattern);
|
| 588 |
+
}
|
| 589 |
+
} else {
|
| 590 |
+
if (strpos($rule, '.')) {
|
| 591 |
+
// 注册嵌套资源路由
|
| 592 |
+
$array = explode('.', $rule);
|
| 593 |
+
$last = array_pop($array);
|
| 594 |
+
$item = [];
|
| 595 |
+
foreach ($array as $val) {
|
| 596 |
+
$item[] = $val . '/:' . (isset($option['var'][$val]) ? $option['var'][$val] : $val . '_id');
|
| 597 |
+
}
|
| 598 |
+
$rule = implode('/', $item) . '/' . $last;
|
| 599 |
+
}
|
| 600 |
+
// 注册资源路由
|
| 601 |
+
foreach (self::$rest as $key => $val) {
|
| 602 |
+
if ((isset($option['only']) && !in_array($key, $option['only']))
|
| 603 |
+
|| (isset($option['except']) && in_array($key, $option['except']))) {
|
| 604 |
+
continue;
|
| 605 |
+
}
|
| 606 |
+
if (isset($last) && strpos($val[1], ':id') && isset($option['var'][$last])) {
|
| 607 |
+
$val[1] = str_replace(':id', ':' . $option['var'][$last], $val[1]);
|
| 608 |
+
} elseif (strpos($val[1], ':id') && isset($option['var'][$rule])) {
|
| 609 |
+
$val[1] = str_replace(':id', ':' . $option['var'][$rule], $val[1]);
|
| 610 |
+
}
|
| 611 |
+
$item = ltrim($rule . $val[1], '/');
|
| 612 |
+
$option['rest'] = $key;
|
| 613 |
+
self::rule($item . '$', $route . '/' . $val[2], $val[0], $option, $pattern);
|
| 614 |
+
}
|
| 615 |
+
}
|
| 616 |
+
}
|
| 617 |
+
|
| 618 |
+
/**
|
| 619 |
+
* 注册控制器路由 操作方法对应不同的请求后缀
|
| 620 |
+
* @access public
|
| 621 |
+
* @param string $rule 路由规则
|
| 622 |
+
* @param string $route 路由地址
|
| 623 |
+
* @param array $option 路由参数
|
| 624 |
+
* @param array $pattern 变量规则
|
| 625 |
+
* @return void
|
| 626 |
+
*/
|
| 627 |
+
public static function controller($rule, $route = '', $option = [], $pattern = [])
|
| 628 |
+
{
|
| 629 |
+
foreach (self::$methodPrefix as $type => $val) {
|
| 630 |
+
self::$type($rule . '/:action', $route . '/' . $val . ':action', $option, $pattern);
|
| 631 |
+
}
|
| 632 |
+
}
|
| 633 |
+
|
| 634 |
+
/**
|
| 635 |
+
* 注册别名路由
|
| 636 |
+
* @access public
|
| 637 |
+
* @param string|array $rule 路由别名
|
| 638 |
+
* @param string $route 路由地址
|
| 639 |
+
* @param array $option 路由参数
|
| 640 |
+
* @return void
|
| 641 |
+
*/
|
| 642 |
+
public static function alias($rule = null, $route = '', $option = [])
|
| 643 |
+
{
|
| 644 |
+
if (is_array($rule)) {
|
| 645 |
+
self::$rules['alias'] = array_merge(self::$rules['alias'], $rule);
|
| 646 |
+
} else {
|
| 647 |
+
self::$rules['alias'][$rule] = $option ? [$route, $option] : $route;
|
| 648 |
+
}
|
| 649 |
+
}
|
| 650 |
+
|
| 651 |
+
/**
|
| 652 |
+
* 设置不同请求类型下面的方法前缀
|
| 653 |
+
* @access public
|
| 654 |
+
* @param string $method 请求类型
|
| 655 |
+
* @param string $prefix 类型前缀
|
| 656 |
+
* @return void
|
| 657 |
+
*/
|
| 658 |
+
public static function setMethodPrefix($method, $prefix = '')
|
| 659 |
+
{
|
| 660 |
+
if (is_array($method)) {
|
| 661 |
+
self::$methodPrefix = array_merge(self::$methodPrefix, array_change_key_case($method));
|
| 662 |
+
} else {
|
| 663 |
+
self::$methodPrefix[strtolower($method)] = $prefix;
|
| 664 |
+
}
|
| 665 |
+
}
|
| 666 |
+
|
| 667 |
+
/**
|
| 668 |
+
* rest方法定义和修改
|
| 669 |
+
* @access public
|
| 670 |
+
* @param string|array $name 方法名称
|
| 671 |
+
* @param array|bool $resource 资源
|
| 672 |
+
* @return void
|
| 673 |
+
*/
|
| 674 |
+
public static function rest($name, $resource = [])
|
| 675 |
+
{
|
| 676 |
+
if (is_array($name)) {
|
| 677 |
+
self::$rest = $resource ? $name : array_merge(self::$rest, $name);
|
| 678 |
+
} else {
|
| 679 |
+
self::$rest[$name] = $resource;
|
| 680 |
+
}
|
| 681 |
+
}
|
| 682 |
+
|
| 683 |
+
/**
|
| 684 |
+
* 注册未匹配路由规则后的处理
|
| 685 |
+
* @access public
|
| 686 |
+
* @param string $route 路由地址
|
| 687 |
+
* @param string $method 请求类型
|
| 688 |
+
* @param array $option 路由参数
|
| 689 |
+
* @return void
|
| 690 |
+
*/
|
| 691 |
+
public static function miss($route, $method = '*', $option = [])
|
| 692 |
+
{
|
| 693 |
+
self::rule('__miss__', $route, $method, $option, []);
|
| 694 |
+
}
|
| 695 |
+
|
| 696 |
+
/**
|
| 697 |
+
* 注册一个自动解析的URL路由
|
| 698 |
+
* @access public
|
| 699 |
+
* @param string $route 路由地址
|
| 700 |
+
* @return void
|
| 701 |
+
*/
|
| 702 |
+
public static function auto($route)
|
| 703 |
+
{
|
| 704 |
+
self::rule('__auto__', $route, '*', [], []);
|
| 705 |
+
}
|
| 706 |
+
|
| 707 |
+
/**
|
| 708 |
+
* 获取或者批量设置路由定义
|
| 709 |
+
* @access public
|
| 710 |
+
* @param mixed $rules 请求类型或者路由定义数组
|
| 711 |
+
* @return array
|
| 712 |
+
*/
|
| 713 |
+
public static function rules($rules = '')
|
| 714 |
+
{
|
| 715 |
+
if (is_array($rules)) {
|
| 716 |
+
self::$rules = $rules;
|
| 717 |
+
} elseif ($rules) {
|
| 718 |
+
return true === $rules ? self::$rules : self::$rules[strtolower($rules)];
|
| 719 |
+
} else {
|
| 720 |
+
$rules = self::$rules;
|
| 721 |
+
unset($rules['pattern'], $rules['alias'], $rules['domain'], $rules['name']);
|
| 722 |
+
return $rules;
|
| 723 |
+
}
|
| 724 |
+
}
|
| 725 |
+
|
| 726 |
+
/**
|
| 727 |
+
* 检测子域名部署
|
| 728 |
+
* @access public
|
| 729 |
+
* @param Request $request Request请求对象
|
| 730 |
+
* @param array $currentRules 当前路由规则
|
| 731 |
+
* @param string $method 请求类型
|
| 732 |
+
* @return void
|
| 733 |
+
*/
|
| 734 |
+
public static function checkDomain($request, &$currentRules, $method = 'get')
|
| 735 |
+
{
|
| 736 |
+
// 域名规则
|
| 737 |
+
$rules = self::$rules['domain'];
|
| 738 |
+
// 开启子域名部署 支持二级和三级域名
|
| 739 |
+
if (!empty($rules)) {
|
| 740 |
+
$host = $request->host(true);
|
| 741 |
+
if (isset($rules[$host])) {
|
| 742 |
+
// 完整域名或者IP配置
|
| 743 |
+
$item = $rules[$host];
|
| 744 |
+
} else {
|
| 745 |
+
$rootDomain = Config::get('url_domain_root');
|
| 746 |
+
if ($rootDomain) {
|
| 747 |
+
// 配置域名根 例如 thinkphp.cn 163.com.cn 如果是国家级域名 com.cn net.cn 之类的域名需要配置
|
| 748 |
+
$domain = explode('.', rtrim(stristr($host, $rootDomain, true), '.'));
|
| 749 |
+
} else {
|
| 750 |
+
$domain = explode('.', $host, -2);
|
| 751 |
+
}
|
| 752 |
+
// 子域名配置
|
| 753 |
+
if (!empty($domain)) {
|
| 754 |
+
// 当前子域名
|
| 755 |
+
$subDomain = implode('.', $domain);
|
| 756 |
+
self::$subDomain = $subDomain;
|
| 757 |
+
$domain2 = array_pop($domain);
|
| 758 |
+
if ($domain) {
|
| 759 |
+
// 存在三级域名
|
| 760 |
+
$domain3 = array_pop($domain);
|
| 761 |
+
}
|
| 762 |
+
if ($subDomain && isset($rules[$subDomain])) {
|
| 763 |
+
// 子域名配置
|
| 764 |
+
$item = $rules[$subDomain];
|
| 765 |
+
} elseif (isset($rules['*.' . $domain2]) && !empty($domain3)) {
|
| 766 |
+
// 泛三级域名
|
| 767 |
+
$item = $rules['*.' . $domain2];
|
| 768 |
+
$panDomain = $domain3;
|
| 769 |
+
} elseif (isset($rules['*']) && !empty($domain2)) {
|
| 770 |
+
// 泛二级域名
|
| 771 |
+
if ('www' != $domain2) {
|
| 772 |
+
$item = $rules['*'];
|
| 773 |
+
$panDomain = $domain2;
|
| 774 |
+
}
|
| 775 |
+
}
|
| 776 |
+
}
|
| 777 |
+
}
|
| 778 |
+
if (!empty($item)) {
|
| 779 |
+
if (isset($panDomain)) {
|
| 780 |
+
// 保存当前泛域名
|
| 781 |
+
$request->route(['__domain__' => $panDomain]);
|
| 782 |
+
}
|
| 783 |
+
if (isset($item['[bind]'])) {
|
| 784 |
+
// 解析子域名部署规则
|
| 785 |
+
list($rule, $option, $pattern) = $item['[bind]'];
|
| 786 |
+
if (!empty($option['https']) && !$request->isSsl()) {
|
| 787 |
+
// https检测
|
| 788 |
+
throw new HttpException(404, 'must use https request:' . $host);
|
| 789 |
+
}
|
| 790 |
+
|
| 791 |
+
if (strpos($rule, '?')) {
|
| 792 |
+
// 传入其它参数
|
| 793 |
+
$array = parse_url($rule);
|
| 794 |
+
$result = $array['path'];
|
| 795 |
+
parse_str($array['query'], $params);
|
| 796 |
+
if (isset($panDomain)) {
|
| 797 |
+
$pos = array_search('*', $params);
|
| 798 |
+
if (false !== $pos) {
|
| 799 |
+
// 泛域名作为参数
|
| 800 |
+
$params[$pos] = $panDomain;
|
| 801 |
+
}
|
| 802 |
+
}
|
| 803 |
+
$_GET = array_merge($_GET, $params);
|
| 804 |
+
} else {
|
| 805 |
+
$result = $rule;
|
| 806 |
+
}
|
| 807 |
+
|
| 808 |
+
if (0 === strpos($result, '\\')) {
|
| 809 |
+
// 绑定到命名空间 例如 \app\index\behavior
|
| 810 |
+
self::$bind = ['type' => 'namespace', 'namespace' => $result];
|
| 811 |
+
} elseif (0 === strpos($result, '@')) {
|
| 812 |
+
// 绑定到类 例如 @app\index\controller\User
|
| 813 |
+
self::$bind = ['type' => 'class', 'class' => substr($result, 1)];
|
| 814 |
+
} else {
|
| 815 |
+
// 绑定到模块/控制器 例如 index/user
|
| 816 |
+
self::$bind = ['type' => 'module', 'module' => $result];
|
| 817 |
+
}
|
| 818 |
+
self::$domainBind = true;
|
| 819 |
+
} else {
|
| 820 |
+
self::$domainRule = $item;
|
| 821 |
+
$currentRules = isset($item[$method]) ? $item[$method] : $item['*'];
|
| 822 |
+
}
|
| 823 |
+
}
|
| 824 |
+
}
|
| 825 |
+
}
|
| 826 |
+
|
| 827 |
+
/**
|
| 828 |
+
* 检测URL路由
|
| 829 |
+
* @access public
|
| 830 |
+
* @param Request $request Request请求对象
|
| 831 |
+
* @param string $url URL地址
|
| 832 |
+
* @param string $depr URL分隔符
|
| 833 |
+
* @param bool $checkDomain 是否检测域名规则
|
| 834 |
+
* @return false|array
|
| 835 |
+
*/
|
| 836 |
+
public static function check($request, $url, $depr = '/', $checkDomain = false)
|
| 837 |
+
{
|
| 838 |
+
//检查解析缓存
|
| 839 |
+
if (!App::$debug && Config::get('route_check_cache')) {
|
| 840 |
+
$key = self::getCheckCacheKey($request);
|
| 841 |
+
if (Cache::has($key)) {
|
| 842 |
+
list($rule, $route, $pathinfo, $option, $matches) = Cache::get($key);
|
| 843 |
+
return self::parseRule($rule, $route, $pathinfo, $option, $matches, true);
|
| 844 |
+
}
|
| 845 |
+
}
|
| 846 |
+
|
| 847 |
+
// 分隔符替换 确保路由定义使用统一的分隔符
|
| 848 |
+
$url = str_replace($depr, '|', $url);
|
| 849 |
+
|
| 850 |
+
if (isset(self::$rules['alias'][$url]) || isset(self::$rules['alias'][strstr($url, '|', true)])) {
|
| 851 |
+
// 检测路由别名
|
| 852 |
+
$result = self::checkRouteAlias($request, $url, $depr);
|
| 853 |
+
if (false !== $result) {
|
| 854 |
+
return $result;
|
| 855 |
+
}
|
| 856 |
+
}
|
| 857 |
+
$method = strtolower($request->method());
|
| 858 |
+
// 获取当前请求类型的路由规则
|
| 859 |
+
$rules = isset(self::$rules[$method]) ? self::$rules[$method] : [];
|
| 860 |
+
// 检测域名部署
|
| 861 |
+
if ($checkDomain) {
|
| 862 |
+
self::checkDomain($request, $rules, $method);
|
| 863 |
+
}
|
| 864 |
+
// 检测URL绑定
|
| 865 |
+
$return = self::checkUrlBind($url, $rules, $depr);
|
| 866 |
+
if (false !== $return) {
|
| 867 |
+
return $return;
|
| 868 |
+
}
|
| 869 |
+
if ('|' != $url) {
|
| 870 |
+
$url = rtrim($url, '|');
|
| 871 |
+
}
|
| 872 |
+
$item = str_replace('|', '/', $url);
|
| 873 |
+
if (isset($rules[$item])) {
|
| 874 |
+
// 静态路由规则检测
|
| 875 |
+
$rule = $rules[$item];
|
| 876 |
+
if (true === $rule) {
|
| 877 |
+
$rule = self::getRouteExpress($item);
|
| 878 |
+
}
|
| 879 |
+
if (!empty($rule['route']) && self::checkOption($rule['option'], $request)) {
|
| 880 |
+
self::setOption($rule['option']);
|
| 881 |
+
return self::parseRule($item, $rule['route'], $url, $rule['option']);
|
| 882 |
+
}
|
| 883 |
+
}
|
| 884 |
+
|
| 885 |
+
// 路由规则检测
|
| 886 |
+
if (!empty($rules)) {
|
| 887 |
+
return self::checkRoute($request, $rules, $url, $depr);
|
| 888 |
+
}
|
| 889 |
+
return false;
|
| 890 |
+
}
|
| 891 |
+
|
| 892 |
+
private static function getRouteExpress($key)
|
| 893 |
+
{
|
| 894 |
+
return self::$domainRule ? self::$domainRule['*'][$key] : self::$rules['*'][$key];
|
| 895 |
+
}
|
| 896 |
+
|
| 897 |
+
/**
|
| 898 |
+
* 检测路由规则
|
| 899 |
+
* @access private
|
| 900 |
+
* @param Request $request
|
| 901 |
+
* @param array $rules 路由规则
|
| 902 |
+
* @param string $url URL地址
|
| 903 |
+
* @param string $depr URL分割符
|
| 904 |
+
* @param string $group 路由分组名
|
| 905 |
+
* @param array $options 路由参数(分组)
|
| 906 |
+
* @return mixed
|
| 907 |
+
*/
|
| 908 |
+
private static function checkRoute($request, $rules, $url, $depr = '/', $group = '', $options = [])
|
| 909 |
+
{
|
| 910 |
+
foreach ($rules as $key => $item) {
|
| 911 |
+
if (true === $item) {
|
| 912 |
+
$item = self::getRouteExpress($key);
|
| 913 |
+
}
|
| 914 |
+
if (!isset($item['rule'])) {
|
| 915 |
+
continue;
|
| 916 |
+
}
|
| 917 |
+
$rule = $item['rule'];
|
| 918 |
+
$route = $item['route'];
|
| 919 |
+
$vars = $item['var'];
|
| 920 |
+
$option = $item['option'];
|
| 921 |
+
$pattern = $item['pattern'];
|
| 922 |
+
|
| 923 |
+
// 检查参数有效性
|
| 924 |
+
if (!self::checkOption($option, $request)) {
|
| 925 |
+
continue;
|
| 926 |
+
}
|
| 927 |
+
|
| 928 |
+
if (isset($option['ext'])) {
|
| 929 |
+
// 路由ext参数 优先于系统配置的URL伪静态后缀参数
|
| 930 |
+
$url = preg_replace('/\.' . $request->ext() . '$/i', '', $url);
|
| 931 |
+
}
|
| 932 |
+
|
| 933 |
+
if (is_array($rule)) {
|
| 934 |
+
// 分组路由
|
| 935 |
+
$pos = strpos(str_replace('<', ':', $key), ':');
|
| 936 |
+
if (false !== $pos) {
|
| 937 |
+
$str = substr($key, 0, $pos);
|
| 938 |
+
} else {
|
| 939 |
+
$str = $key;
|
| 940 |
+
}
|
| 941 |
+
if (is_string($str) && $str && 0 !== stripos(str_replace('|', '/', $url), $str)) {
|
| 942 |
+
continue;
|
| 943 |
+
}
|
| 944 |
+
self::setOption($option);
|
| 945 |
+
$result = self::checkRoute($request, $rule, $url, $depr, $key, $option);
|
| 946 |
+
if (false !== $result) {
|
| 947 |
+
return $result;
|
| 948 |
+
}
|
| 949 |
+
} elseif ($route) {
|
| 950 |
+
if ('__miss__' == $rule || '__auto__' == $rule) {
|
| 951 |
+
// 指定特殊路由
|
| 952 |
+
$var = trim($rule, '__');
|
| 953 |
+
${$var} = $item;
|
| 954 |
+
continue;
|
| 955 |
+
}
|
| 956 |
+
if ($group) {
|
| 957 |
+
$rule = $group . ($rule ? '/' . ltrim($rule, '/') : '');
|
| 958 |
+
}
|
| 959 |
+
|
| 960 |
+
self::setOption($option);
|
| 961 |
+
if (isset($options['bind_model']) && isset($option['bind_model'])) {
|
| 962 |
+
$option['bind_model'] = array_merge($options['bind_model'], $option['bind_model']);
|
| 963 |
+
}
|
| 964 |
+
$result = self::checkRule($rule, $route, $url, $pattern, $option, $depr);
|
| 965 |
+
if (false !== $result) {
|
| 966 |
+
return $result;
|
| 967 |
+
}
|
| 968 |
+
}
|
| 969 |
+
}
|
| 970 |
+
if (isset($auto)) {
|
| 971 |
+
// 自动解析URL地址
|
| 972 |
+
return self::parseUrl($auto['route'] . '/' . $url, $depr);
|
| 973 |
+
} elseif (isset($miss)) {
|
| 974 |
+
// 未匹配所有路由的路由规则处理
|
| 975 |
+
return self::parseRule('', $miss['route'], $url, $miss['option']);
|
| 976 |
+
}
|
| 977 |
+
return false;
|
| 978 |
+
}
|
| 979 |
+
|
| 980 |
+
/**
|
| 981 |
+
* 检测路由别名
|
| 982 |
+
* @access private
|
| 983 |
+
* @param Request $request
|
| 984 |
+
* @param string $url URL地址
|
| 985 |
+
* @param string $depr URL分隔符
|
| 986 |
+
* @return mixed
|
| 987 |
+
*/
|
| 988 |
+
private static function checkRouteAlias($request, $url, $depr)
|
| 989 |
+
{
|
| 990 |
+
$array = explode('|', $url);
|
| 991 |
+
$alias = array_shift($array);
|
| 992 |
+
$item = self::$rules['alias'][$alias];
|
| 993 |
+
|
| 994 |
+
if (is_array($item)) {
|
| 995 |
+
list($rule, $option) = $item;
|
| 996 |
+
$action = $array[0];
|
| 997 |
+
if (isset($option['allow']) && !in_array($action, explode(',', $option['allow']))) {
|
| 998 |
+
// 允许操作
|
| 999 |
+
return false;
|
| 1000 |
+
} elseif (isset($option['except']) && in_array($action, explode(',', $option['except']))) {
|
| 1001 |
+
// 排除操作
|
| 1002 |
+
return false;
|
| 1003 |
+
}
|
| 1004 |
+
if (isset($option['method'][$action])) {
|
| 1005 |
+
$option['method'] = $option['method'][$action];
|
| 1006 |
+
}
|
| 1007 |
+
} else {
|
| 1008 |
+
$rule = $item;
|
| 1009 |
+
}
|
| 1010 |
+
$bind = implode('|', $array);
|
| 1011 |
+
// 参数有效性检查
|
| 1012 |
+
if (isset($option) && !self::checkOption($option, $request)) {
|
| 1013 |
+
// 路由不匹配
|
| 1014 |
+
return false;
|
| 1015 |
+
} elseif (0 === strpos($rule, '\\')) {
|
| 1016 |
+
// 路由到类
|
| 1017 |
+
return self::bindToClass($bind, substr($rule, 1), $depr);
|
| 1018 |
+
} elseif (0 === strpos($rule, '@')) {
|
| 1019 |
+
// 路由到控制器类
|
| 1020 |
+
return self::bindToController($bind, substr($rule, 1), $depr);
|
| 1021 |
+
} else {
|
| 1022 |
+
// 路由到模块/控制器
|
| 1023 |
+
return self::bindToModule($bind, $rule, $depr);
|
| 1024 |
+
}
|
| 1025 |
+
}
|
| 1026 |
+
|
| 1027 |
+
/**
|
| 1028 |
+
* 检测URL绑定
|
| 1029 |
+
* @access private
|
| 1030 |
+
* @param string $url URL地址
|
| 1031 |
+
* @param array $rules 路由规则
|
| 1032 |
+
* @param string $depr URL分隔符
|
| 1033 |
+
* @return mixed
|
| 1034 |
+
*/
|
| 1035 |
+
private static function checkUrlBind(&$url, &$rules, $depr = '/')
|
| 1036 |
+
{
|
| 1037 |
+
if (!empty(self::$bind)) {
|
| 1038 |
+
$type = self::$bind['type'];
|
| 1039 |
+
$bind = self::$bind[$type];
|
| 1040 |
+
// 记录绑定信息
|
| 1041 |
+
App::$debug && Log::record('[ BIND ] ' . var_export($bind, true), 'info');
|
| 1042 |
+
// 如果有URL绑定 则进行绑定检测
|
| 1043 |
+
switch ($type) {
|
| 1044 |
+
case 'class':
|
| 1045 |
+
// 绑定到类
|
| 1046 |
+
return self::bindToClass($url, $bind, $depr);
|
| 1047 |
+
case 'controller':
|
| 1048 |
+
// 绑定到控制器类
|
| 1049 |
+
return self::bindToController($url, $bind, $depr);
|
| 1050 |
+
case 'namespace':
|
| 1051 |
+
// 绑定到命名空间
|
| 1052 |
+
return self::bindToNamespace($url, $bind, $depr);
|
| 1053 |
+
}
|
| 1054 |
+
}
|
| 1055 |
+
return false;
|
| 1056 |
+
}
|
| 1057 |
+
|
| 1058 |
+
/**
|
| 1059 |
+
* 绑定到类
|
| 1060 |
+
* @access public
|
| 1061 |
+
* @param string $url URL地址
|
| 1062 |
+
* @param string $class 类名(带命名空间)
|
| 1063 |
+
* @param string $depr URL分隔符
|
| 1064 |
+
* @return array
|
| 1065 |
+
*/
|
| 1066 |
+
public static function bindToClass($url, $class, $depr = '/')
|
| 1067 |
+
{
|
| 1068 |
+
$url = str_replace($depr, '|', $url);
|
| 1069 |
+
$array = explode('|', $url, 2);
|
| 1070 |
+
$action = !empty($array[0]) ? $array[0] : Config::get('default_action');
|
| 1071 |
+
if (!empty($array[1])) {
|
| 1072 |
+
self::parseUrlParams($array[1]);
|
| 1073 |
+
}
|
| 1074 |
+
return ['type' => 'method', 'method' => [$class, $action], 'var' => []];
|
| 1075 |
+
}
|
| 1076 |
+
|
| 1077 |
+
/**
|
| 1078 |
+
* 绑定到命名空间
|
| 1079 |
+
* @access public
|
| 1080 |
+
* @param string $url URL地址
|
| 1081 |
+
* @param string $namespace 命名空间
|
| 1082 |
+
* @param string $depr URL分隔符
|
| 1083 |
+
* @return array
|
| 1084 |
+
*/
|
| 1085 |
+
public static function bindToNamespace($url, $namespace, $depr = '/')
|
| 1086 |
+
{
|
| 1087 |
+
$url = str_replace($depr, '|', $url);
|
| 1088 |
+
$array = explode('|', $url, 3);
|
| 1089 |
+
$class = !empty($array[0]) ? $array[0] : Config::get('default_controller');
|
| 1090 |
+
$method = !empty($array[1]) ? $array[1] : Config::get('default_action');
|
| 1091 |
+
if (!empty($array[2])) {
|
| 1092 |
+
self::parseUrlParams($array[2]);
|
| 1093 |
+
}
|
| 1094 |
+
return ['type' => 'method', 'method' => [$namespace . '\\' . Loader::parseName($class, 1), $method], 'var' => []];
|
| 1095 |
+
}
|
| 1096 |
+
|
| 1097 |
+
/**
|
| 1098 |
+
* 绑定到控制器类
|
| 1099 |
+
* @access public
|
| 1100 |
+
* @param string $url URL地址
|
| 1101 |
+
* @param string $controller 控制器名 (支持带模块名 index/user )
|
| 1102 |
+
* @param string $depr URL分隔符
|
| 1103 |
+
* @return array
|
| 1104 |
+
*/
|
| 1105 |
+
public static function bindToController($url, $controller, $depr = '/')
|
| 1106 |
+
{
|
| 1107 |
+
$url = str_replace($depr, '|', $url);
|
| 1108 |
+
$array = explode('|', $url, 2);
|
| 1109 |
+
$action = !empty($array[0]) ? $array[0] : Config::get('default_action');
|
| 1110 |
+
if (!empty($array[1])) {
|
| 1111 |
+
self::parseUrlParams($array[1]);
|
| 1112 |
+
}
|
| 1113 |
+
return ['type' => 'controller', 'controller' => $controller . '/' . $action, 'var' => []];
|
| 1114 |
+
}
|
| 1115 |
+
|
| 1116 |
+
/**
|
| 1117 |
+
* 绑定到模块/控制器
|
| 1118 |
+
* @access public
|
| 1119 |
+
* @param string $url URL地址
|
| 1120 |
+
* @param string $controller 控制器类名(带命名空间)
|
| 1121 |
+
* @param string $depr URL分隔符
|
| 1122 |
+
* @return array
|
| 1123 |
+
*/
|
| 1124 |
+
public static function bindToModule($url, $controller, $depr = '/')
|
| 1125 |
+
{
|
| 1126 |
+
$url = str_replace($depr, '|', $url);
|
| 1127 |
+
$array = explode('|', $url, 2);
|
| 1128 |
+
$action = !empty($array[0]) ? $array[0] : Config::get('default_action');
|
| 1129 |
+
if (!empty($array[1])) {
|
| 1130 |
+
self::parseUrlParams($array[1]);
|
| 1131 |
+
}
|
| 1132 |
+
return ['type' => 'module', 'module' => $controller . '/' . $action];
|
| 1133 |
+
}
|
| 1134 |
+
|
| 1135 |
+
/**
|
| 1136 |
+
* 路由参数有效性检查
|
| 1137 |
+
* @access private
|
| 1138 |
+
* @param array $option 路由参数
|
| 1139 |
+
* @param Request $request Request对象
|
| 1140 |
+
* @return bool
|
| 1141 |
+
*/
|
| 1142 |
+
private static function checkOption($option, $request)
|
| 1143 |
+
{
|
| 1144 |
+
if ((isset($option['method']) && is_string($option['method']) && false === stripos($option['method'], $request->method()))
|
| 1145 |
+
|| (isset($option['ajax']) && $option['ajax'] && !$request->isAjax()) // Ajax检测
|
| 1146 |
+
|| (isset($option['ajax']) && !$option['ajax'] && $request->isAjax()) // 非Ajax检测
|
| 1147 |
+
|| (isset($option['pjax']) && $option['pjax'] && !$request->isPjax()) // Pjax检测
|
| 1148 |
+
|| (isset($option['pjax']) && !$option['pjax'] && $request->isPjax()) // 非Pjax检测
|
| 1149 |
+
|| (isset($option['ext']) && false === stripos('|' . $option['ext'] . '|', '|' . $request->ext() . '|')) // 伪静态后缀检测
|
| 1150 |
+
|| (isset($option['deny_ext']) && false !== stripos('|' . $option['deny_ext'] . '|', '|' . $request->ext() . '|'))
|
| 1151 |
+
|| (isset($option['domain']) && !in_array($option['domain'], [$_SERVER['HTTP_HOST'], self::$subDomain])) // 域名检测
|
| 1152 |
+
|| (isset($option['https']) && $option['https'] && !$request->isSsl()) // https检测
|
| 1153 |
+
|| (isset($option['https']) && !$option['https'] && $request->isSsl()) // https检测
|
| 1154 |
+
|| (!empty($option['before_behavior']) && false === Hook::exec($option['before_behavior'])) // 行为检测
|
| 1155 |
+
|| (!empty($option['callback']) && is_callable($option['callback']) && false === call_user_func($option['callback'])) // 自定义检测
|
| 1156 |
+
) {
|
| 1157 |
+
return false;
|
| 1158 |
+
}
|
| 1159 |
+
return true;
|
| 1160 |
+
}
|
| 1161 |
+
|
| 1162 |
+
/**
|
| 1163 |
+
* 检测路由规则
|
| 1164 |
+
* @access private
|
| 1165 |
+
* @param string $rule 路由规则
|
| 1166 |
+
* @param string $route 路由地址
|
| 1167 |
+
* @param string $url URL地址
|
| 1168 |
+
* @param array $pattern 变量规则
|
| 1169 |
+
* @param array $option 路由参数
|
| 1170 |
+
* @param string $depr URL分隔符(全局)
|
| 1171 |
+
* @return array|false
|
| 1172 |
+
*/
|
| 1173 |
+
private static function checkRule($rule, $route, $url, $pattern, $option, $depr)
|
| 1174 |
+
{
|
| 1175 |
+
// 检查完整规则定义
|
| 1176 |
+
if (isset($pattern['__url__']) && !preg_match(0 === strpos($pattern['__url__'], '/') ? $pattern['__url__'] : '/^' . $pattern['__url__'] . '/', str_replace('|', $depr, $url))) {
|
| 1177 |
+
return false;
|
| 1178 |
+
}
|
| 1179 |
+
// 检查路由的参数分隔符
|
| 1180 |
+
if (isset($option['param_depr'])) {
|
| 1181 |
+
$url = str_replace(['|', $option['param_depr']], [$depr, '|'], $url);
|
| 1182 |
+
}
|
| 1183 |
+
|
| 1184 |
+
$len1 = substr_count($url, '|');
|
| 1185 |
+
$len2 = substr_count($rule, '/');
|
| 1186 |
+
// 多余参数是否合并
|
| 1187 |
+
$merge = !empty($option['merge_extra_vars']);
|
| 1188 |
+
if ($merge && $len1 > $len2) {
|
| 1189 |
+
$url = str_replace('|', $depr, $url);
|
| 1190 |
+
$url = implode('|', explode($depr, $url, $len2 + 1));
|
| 1191 |
+
}
|
| 1192 |
+
|
| 1193 |
+
if ($len1 >= $len2 || strpos($rule, '[')) {
|
| 1194 |
+
if (!empty($option['complete_match'])) {
|
| 1195 |
+
// 完整匹配
|
| 1196 |
+
if (!$merge && $len1 != $len2 && (false === strpos($rule, '[') || $len1 > $len2 || $len1 < $len2 - substr_count($rule, '['))) {
|
| 1197 |
+
return false;
|
| 1198 |
+
}
|
| 1199 |
+
}
|
| 1200 |
+
$pattern = array_merge(self::$rules['pattern'], $pattern);
|
| 1201 |
+
if (false !== $match = self::match($url, $rule, $pattern)) {
|
| 1202 |
+
// 匹配到路由规则
|
| 1203 |
+
return self::parseRule($rule, $route, $url, $option, $match);
|
| 1204 |
+
}
|
| 1205 |
+
}
|
| 1206 |
+
return false;
|
| 1207 |
+
}
|
| 1208 |
+
|
| 1209 |
+
/**
|
| 1210 |
+
* 解析模块的URL地址 [模块/控制器/操作?]参数1=值1&参数2=值2...
|
| 1211 |
+
* @access public
|
| 1212 |
+
* @param string $url URL地址
|
| 1213 |
+
* @param string $depr URL分隔符
|
| 1214 |
+
* @param bool $autoSearch 是否自动深度搜索控制器
|
| 1215 |
+
* @return array
|
| 1216 |
+
*/
|
| 1217 |
+
public static function parseUrl($url, $depr = '/', $autoSearch = false)
|
| 1218 |
+
{
|
| 1219 |
+
|
| 1220 |
+
if (isset(self::$bind['module'])) {
|
| 1221 |
+
$bind = str_replace('/', $depr, self::$bind['module']);
|
| 1222 |
+
// 如果有模块/控制器绑定
|
| 1223 |
+
$url = $bind . ('.' != substr($bind, -1) ? $depr : '') . ltrim($url, $depr);
|
| 1224 |
+
}
|
| 1225 |
+
$url = str_replace($depr, '|', $url);
|
| 1226 |
+
list($path, $var) = self::parseUrlPath($url);
|
| 1227 |
+
$route = [null, null, null];
|
| 1228 |
+
if (isset($path)) {
|
| 1229 |
+
// 解析模块
|
| 1230 |
+
$module = Config::get('app_multi_module') ? array_shift($path) : null;
|
| 1231 |
+
if ($autoSearch) {
|
| 1232 |
+
// 自动搜索控制器
|
| 1233 |
+
$dir = APP_PATH . ($module ? $module . DS : '') . Config::get('url_controller_layer');
|
| 1234 |
+
$suffix = App::$suffix || Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : '';
|
| 1235 |
+
$item = [];
|
| 1236 |
+
$find = false;
|
| 1237 |
+
foreach ($path as $val) {
|
| 1238 |
+
$item[] = $val;
|
| 1239 |
+
$file = $dir . DS . str_replace('.', DS, $val) . $suffix . EXT;
|
| 1240 |
+
$file = pathinfo($file, PATHINFO_DIRNAME) . DS . Loader::parseName(pathinfo($file, PATHINFO_FILENAME), 1) . EXT;
|
| 1241 |
+
if (is_file($file)) {
|
| 1242 |
+
$find = true;
|
| 1243 |
+
break;
|
| 1244 |
+
} else {
|
| 1245 |
+
$dir .= DS . Loader::parseName($val);
|
| 1246 |
+
}
|
| 1247 |
+
}
|
| 1248 |
+
if ($find) {
|
| 1249 |
+
$controller = implode('.', $item);
|
| 1250 |
+
$path = array_slice($path, count($item));
|
| 1251 |
+
} else {
|
| 1252 |
+
$controller = array_shift($path);
|
| 1253 |
+
}
|
| 1254 |
+
} else {
|
| 1255 |
+
// 解析控制器
|
| 1256 |
+
$controller = !empty($path) ? array_shift($path) : null;
|
| 1257 |
+
}
|
| 1258 |
+
// 解析操作
|
| 1259 |
+
$action = !empty($path) ? array_shift($path) : null;
|
| 1260 |
+
// 解析额外参数
|
| 1261 |
+
self::parseUrlParams(empty($path) ? '' : implode('|', $path));
|
| 1262 |
+
// 封装路由
|
| 1263 |
+
$route = [$module, $controller, $action];
|
| 1264 |
+
// 检查地址是否被定义过路由
|
| 1265 |
+
$name = strtolower($module . '/' . Loader::parseName($controller, 1) . '/' . $action);
|
| 1266 |
+
$name2 = '';
|
| 1267 |
+
if (empty($module) || isset($bind) && $module == $bind) {
|
| 1268 |
+
$name2 = strtolower(Loader::parseName($controller, 1) . '/' . $action);
|
| 1269 |
+
}
|
| 1270 |
+
|
| 1271 |
+
if (isset(self::$rules['name'][$name]) || isset(self::$rules['name'][$name2])) {
|
| 1272 |
+
throw new HttpException(404, 'invalid request:' . str_replace('|', $depr, $url));
|
| 1273 |
+
}
|
| 1274 |
+
}
|
| 1275 |
+
return ['type' => 'module', 'module' => $route];
|
| 1276 |
+
}
|
| 1277 |
+
|
| 1278 |
+
/**
|
| 1279 |
+
* 解析URL的pathinfo参数和变量
|
| 1280 |
+
* @access private
|
| 1281 |
+
* @param string $url URL地址
|
| 1282 |
+
* @return array
|
| 1283 |
+
*/
|
| 1284 |
+
private static function parseUrlPath($url)
|
| 1285 |
+
{
|
| 1286 |
+
// 分隔符替换 确保路由定义使用统一的分隔符
|
| 1287 |
+
$url = str_replace('|', '/', $url);
|
| 1288 |
+
$url = trim($url, '/');
|
| 1289 |
+
$var = [];
|
| 1290 |
+
if (false !== strpos($url, '?')) {
|
| 1291 |
+
// [模块/控制器/操作?]参数1=值1&参数2=值2...
|
| 1292 |
+
$info = parse_url($url);
|
| 1293 |
+
$path = explode('/', $info['path']);
|
| 1294 |
+
parse_str($info['query'], $var);
|
| 1295 |
+
} elseif (strpos($url, '/')) {
|
| 1296 |
+
// [模块/控制器/操作]
|
| 1297 |
+
$path = explode('/', $url);
|
| 1298 |
+
} else {
|
| 1299 |
+
$path = [$url];
|
| 1300 |
+
}
|
| 1301 |
+
return [$path, $var];
|
| 1302 |
+
}
|
| 1303 |
+
|
| 1304 |
+
/**
|
| 1305 |
+
* 检测URL和规则路由是否匹配
|
| 1306 |
+
* @access private
|
| 1307 |
+
* @param string $url URL地址
|
| 1308 |
+
* @param string $rule 路由规则
|
| 1309 |
+
* @param array $pattern 变量规则
|
| 1310 |
+
* @return array|false
|
| 1311 |
+
*/
|
| 1312 |
+
private static function match($url, $rule, $pattern)
|
| 1313 |
+
{
|
| 1314 |
+
$m2 = explode('/', $rule);
|
| 1315 |
+
$m1 = explode('|', $url);
|
| 1316 |
+
|
| 1317 |
+
$var = [];
|
| 1318 |
+
foreach ($m2 as $key => $val) {
|
| 1319 |
+
// val中定义了多个变量 <id><name>
|
| 1320 |
+
if (false !== strpos($val, '<') && preg_match_all('/<(\w+(\??))>/', $val, $matches)) {
|
| 1321 |
+
$value = [];
|
| 1322 |
+
$replace = [];
|
| 1323 |
+
foreach ($matches[1] as $name) {
|
| 1324 |
+
if (strpos($name, '?')) {
|
| 1325 |
+
$name = substr($name, 0, -1);
|
| 1326 |
+
$replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '\w+') . ')?';
|
| 1327 |
+
} else {
|
| 1328 |
+
$replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '\w+') . ')';
|
| 1329 |
+
}
|
| 1330 |
+
$value[] = $name;
|
| 1331 |
+
}
|
| 1332 |
+
$val = str_replace($matches[0], $replace, $val);
|
| 1333 |
+
if (preg_match('/^' . $val . '$/', isset($m1[$key]) ? $m1[$key] : '', $match)) {
|
| 1334 |
+
array_shift($match);
|
| 1335 |
+
foreach ($value as $k => $name) {
|
| 1336 |
+
if (isset($match[$k])) {
|
| 1337 |
+
$var[$name] = $match[$k];
|
| 1338 |
+
}
|
| 1339 |
+
}
|
| 1340 |
+
continue;
|
| 1341 |
+
} else {
|
| 1342 |
+
return false;
|
| 1343 |
+
}
|
| 1344 |
+
}
|
| 1345 |
+
|
| 1346 |
+
if (0 === strpos($val, '[:')) {
|
| 1347 |
+
// 可选参数
|
| 1348 |
+
$val = substr($val, 1, -1);
|
| 1349 |
+
$optional = true;
|
| 1350 |
+
} else {
|
| 1351 |
+
$optional = false;
|
| 1352 |
+
}
|
| 1353 |
+
if (0 === strpos($val, ':')) {
|
| 1354 |
+
// URL变量
|
| 1355 |
+
$name = substr($val, 1);
|
| 1356 |
+
if (!$optional && !isset($m1[$key])) {
|
| 1357 |
+
return false;
|
| 1358 |
+
}
|
| 1359 |
+
if (isset($m1[$key]) && isset($pattern[$name])) {
|
| 1360 |
+
// 检查变量规则
|
| 1361 |
+
if ($pattern[$name] instanceof \Closure) {
|
| 1362 |
+
$result = call_user_func_array($pattern[$name], [$m1[$key]]);
|
| 1363 |
+
if (false === $result) {
|
| 1364 |
+
return false;
|
| 1365 |
+
}
|
| 1366 |
+
} elseif (!preg_match(0 === strpos($pattern[$name], '/') ? $pattern[$name] : '/^' . $pattern[$name] . '$/', $m1[$key])) {
|
| 1367 |
+
return false;
|
| 1368 |
+
}
|
| 1369 |
+
}
|
| 1370 |
+
$var[$name] = isset($m1[$key]) ? $m1[$key] : '';
|
| 1371 |
+
} elseif (!isset($m1[$key]) || 0 !== strcasecmp($val, $m1[$key])) {
|
| 1372 |
+
return false;
|
| 1373 |
+
}
|
| 1374 |
+
}
|
| 1375 |
+
// 成功匹配后返回URL中的动态变量数组
|
| 1376 |
+
return $var;
|
| 1377 |
+
}
|
| 1378 |
+
|
| 1379 |
+
/**
|
| 1380 |
+
* 解析规则路由
|
| 1381 |
+
* @access private
|
| 1382 |
+
* @param string $rule 路由规则
|
| 1383 |
+
* @param string $route 路由地址
|
| 1384 |
+
* @param string $pathinfo URL地址
|
| 1385 |
+
* @param array $option 路由参数
|
| 1386 |
+
* @param array $matches 匹配的变量
|
| 1387 |
+
* @param bool $fromCache 通过缓存解析
|
| 1388 |
+
* @return array
|
| 1389 |
+
*/
|
| 1390 |
+
private static function parseRule($rule, $route, $pathinfo, $option = [], $matches = [], $fromCache = false)
|
| 1391 |
+
{
|
| 1392 |
+
$request = Request::instance();
|
| 1393 |
+
|
| 1394 |
+
//保存解析缓存
|
| 1395 |
+
if (Config::get('route_check_cache') && !$fromCache) {
|
| 1396 |
+
try {
|
| 1397 |
+
$key = self::getCheckCacheKey($request);
|
| 1398 |
+
Cache::tag('route_check')->set($key, [$rule, $route, $pathinfo, $option, $matches]);
|
| 1399 |
+
} catch (\Exception $e) {
|
| 1400 |
+
|
| 1401 |
+
}
|
| 1402 |
+
}
|
| 1403 |
+
|
| 1404 |
+
// 解析路由规则
|
| 1405 |
+
if ($rule) {
|
| 1406 |
+
$rule = explode('/', $rule);
|
| 1407 |
+
// 获取URL地址中的参数
|
| 1408 |
+
$paths = explode('|', $pathinfo);
|
| 1409 |
+
foreach ($rule as $item) {
|
| 1410 |
+
$fun = '';
|
| 1411 |
+
if (0 === strpos($item, '[:')) {
|
| 1412 |
+
$item = substr($item, 1, -1);
|
| 1413 |
+
}
|
| 1414 |
+
if (0 === strpos($item, ':')) {
|
| 1415 |
+
$var = substr($item, 1);
|
| 1416 |
+
$matches[$var] = array_shift($paths);
|
| 1417 |
+
} else {
|
| 1418 |
+
// 过滤URL中的静态变量
|
| 1419 |
+
array_shift($paths);
|
| 1420 |
+
}
|
| 1421 |
+
}
|
| 1422 |
+
} else {
|
| 1423 |
+
$paths = explode('|', $pathinfo);
|
| 1424 |
+
}
|
| 1425 |
+
|
| 1426 |
+
// 获取路由地址规则
|
| 1427 |
+
if (is_string($route) && isset($option['prefix'])) {
|
| 1428 |
+
// 路由地址前缀
|
| 1429 |
+
$route = $option['prefix'] . $route;
|
| 1430 |
+
}
|
| 1431 |
+
// 替换路由地址中的变量
|
| 1432 |
+
if (is_string($route) && !empty($matches)) {
|
| 1433 |
+
foreach ($matches as $key => $val) {
|
| 1434 |
+
if (false !== strpos($route, ':' . $key)) {
|
| 1435 |
+
$route = str_replace(':' . $key, $val, $route);
|
| 1436 |
+
}
|
| 1437 |
+
}
|
| 1438 |
+
}
|
| 1439 |
+
|
| 1440 |
+
// 绑定模型数据
|
| 1441 |
+
if (isset($option['bind_model'])) {
|
| 1442 |
+
$bind = [];
|
| 1443 |
+
foreach ($option['bind_model'] as $key => $val) {
|
| 1444 |
+
if ($val instanceof \Closure) {
|
| 1445 |
+
$result = call_user_func_array($val, [$matches]);
|
| 1446 |
+
} else {
|
| 1447 |
+
if (is_array($val)) {
|
| 1448 |
+
$fields = explode('&', $val[1]);
|
| 1449 |
+
$model = $val[0];
|
| 1450 |
+
$exception = isset($val[2]) ? $val[2] : true;
|
| 1451 |
+
} else {
|
| 1452 |
+
$fields = ['id'];
|
| 1453 |
+
$model = $val;
|
| 1454 |
+
$exception = true;
|
| 1455 |
+
}
|
| 1456 |
+
$where = [];
|
| 1457 |
+
$match = true;
|
| 1458 |
+
foreach ($fields as $field) {
|
| 1459 |
+
if (!isset($matches[$field])) {
|
| 1460 |
+
$match = false;
|
| 1461 |
+
break;
|
| 1462 |
+
} else {
|
| 1463 |
+
$where[$field] = $matches[$field];
|
| 1464 |
+
}
|
| 1465 |
+
}
|
| 1466 |
+
if ($match) {
|
| 1467 |
+
$query = strpos($model, '\\') ? $model::where($where) : Loader::model($model)->where($where);
|
| 1468 |
+
$result = $query->failException($exception)->find();
|
| 1469 |
+
}
|
| 1470 |
+
}
|
| 1471 |
+
if (!empty($result)) {
|
| 1472 |
+
$bind[$key] = $result;
|
| 1473 |
+
}
|
| 1474 |
+
}
|
| 1475 |
+
$request->bind($bind);
|
| 1476 |
+
}
|
| 1477 |
+
|
| 1478 |
+
if (!empty($option['response'])) {
|
| 1479 |
+
Hook::add('response_send', $option['response']);
|
| 1480 |
+
}
|
| 1481 |
+
|
| 1482 |
+
// 解析额外参数
|
| 1483 |
+
self::parseUrlParams(empty($paths) ? '' : implode('|', $paths), $matches);
|
| 1484 |
+
// 记录匹配的路由信息
|
| 1485 |
+
$request->routeInfo(['rule' => $rule, 'route' => $route, 'option' => $option, 'var' => $matches]);
|
| 1486 |
+
|
| 1487 |
+
// 检测路由after行为
|
| 1488 |
+
if (!empty($option['after_behavior'])) {
|
| 1489 |
+
if ($option['after_behavior'] instanceof \Closure) {
|
| 1490 |
+
$result = call_user_func_array($option['after_behavior'], []);
|
| 1491 |
+
} else {
|
| 1492 |
+
foreach ((array) $option['after_behavior'] as $behavior) {
|
| 1493 |
+
$result = Hook::exec($behavior, '');
|
| 1494 |
+
if (!is_null($result)) {
|
| 1495 |
+
break;
|
| 1496 |
+
}
|
| 1497 |
+
}
|
| 1498 |
+
}
|
| 1499 |
+
// 路由规则重定向
|
| 1500 |
+
if ($result instanceof Response) {
|
| 1501 |
+
return ['type' => 'response', 'response' => $result];
|
| 1502 |
+
} elseif (is_array($result)) {
|
| 1503 |
+
return $result;
|
| 1504 |
+
}
|
| 1505 |
+
}
|
| 1506 |
+
|
| 1507 |
+
if ($route instanceof \Closure) {
|
| 1508 |
+
// 执行闭包
|
| 1509 |
+
$result = ['type' => 'function', 'function' => $route];
|
| 1510 |
+
} elseif (0 === strpos($route, '/') || strpos($route, '://')) {
|
| 1511 |
+
// 路由到重定向地址
|
| 1512 |
+
$result = ['type' => 'redirect', 'url' => $route, 'status' => isset($option['status']) ? $option['status'] : 301];
|
| 1513 |
+
} elseif (false !== strpos($route, '\\')) {
|
| 1514 |
+
// 路由到方法
|
| 1515 |
+
list($path, $var) = self::parseUrlPath($route);
|
| 1516 |
+
$route = str_replace('/', '@', implode('/', $path));
|
| 1517 |
+
$method = strpos($route, '@') ? explode('@', $route) : $route;
|
| 1518 |
+
$result = ['type' => 'method', 'method' => $method, 'var' => $var];
|
| 1519 |
+
} elseif (0 === strpos($route, '@')) {
|
| 1520 |
+
// 路由到控制器
|
| 1521 |
+
$route = substr($route, 1);
|
| 1522 |
+
list($route, $var) = self::parseUrlPath($route);
|
| 1523 |
+
$result = ['type' => 'controller', 'controller' => implode('/', $route), 'var' => $var];
|
| 1524 |
+
$request->action(array_pop($route));
|
| 1525 |
+
$request->controller($route ? array_pop($route) : Config::get('default_controller'));
|
| 1526 |
+
$request->module($route ? array_pop($route) : Config::get('default_module'));
|
| 1527 |
+
App::$modulePath = APP_PATH . (Config::get('app_multi_module') ? $request->module() . DS : '');
|
| 1528 |
+
} else {
|
| 1529 |
+
// 路由到模块/控制器/操作
|
| 1530 |
+
$result = self::parseModule($route, isset($option['convert']) ? $option['convert'] : false);
|
| 1531 |
+
}
|
| 1532 |
+
// 开启请求缓存
|
| 1533 |
+
if ($request->isGet() && isset($option['cache'])) {
|
| 1534 |
+
$cache = $option['cache'];
|
| 1535 |
+
if (is_array($cache)) {
|
| 1536 |
+
list($key, $expire, $tag) = array_pad($cache, 3, null);
|
| 1537 |
+
} else {
|
| 1538 |
+
$key = str_replace('|', '/', $pathinfo);
|
| 1539 |
+
$expire = $cache;
|
| 1540 |
+
$tag = null;
|
| 1541 |
+
}
|
| 1542 |
+
$request->cache($key, $expire, $tag);
|
| 1543 |
+
}
|
| 1544 |
+
return $result;
|
| 1545 |
+
}
|
| 1546 |
+
|
| 1547 |
+
/**
|
| 1548 |
+
* 解析URL地址为 模块/控制器/操作
|
| 1549 |
+
* @access private
|
| 1550 |
+
* @param string $url URL地址
|
| 1551 |
+
* @param bool $convert 是否自动转换URL地址
|
| 1552 |
+
* @return array
|
| 1553 |
+
*/
|
| 1554 |
+
private static function parseModule($url, $convert = false)
|
| 1555 |
+
{
|
| 1556 |
+
list($path, $var) = self::parseUrlPath($url);
|
| 1557 |
+
$action = array_pop($path);
|
| 1558 |
+
$controller = !empty($path) ? array_pop($path) : null;
|
| 1559 |
+
$module = Config::get('app_multi_module') && !empty($path) ? array_pop($path) : null;
|
| 1560 |
+
$method = Request::instance()->method();
|
| 1561 |
+
if (Config::get('use_action_prefix') && !empty(self::$methodPrefix[$method])) {
|
| 1562 |
+
// 操作方法前缀支持
|
| 1563 |
+
$action = 0 !== strpos($action, self::$methodPrefix[$method]) ? self::$methodPrefix[$method] . $action : $action;
|
| 1564 |
+
}
|
| 1565 |
+
// 设置当前请求的路由变量
|
| 1566 |
+
Request::instance()->route($var);
|
| 1567 |
+
// 路由到模块/控制器/操作
|
| 1568 |
+
return ['type' => 'module', 'module' => [$module, $controller, $action], 'convert' => $convert];
|
| 1569 |
+
}
|
| 1570 |
+
|
| 1571 |
+
/**
|
| 1572 |
+
* 解析URL地址中的参数Request对象
|
| 1573 |
+
* @access private
|
| 1574 |
+
* @param string $url 路由规则
|
| 1575 |
+
* @param array $var 变量
|
| 1576 |
+
* @return void
|
| 1577 |
+
*/
|
| 1578 |
+
private static function parseUrlParams($url, &$var = [])
|
| 1579 |
+
{
|
| 1580 |
+
if ($url) {
|
| 1581 |
+
if (Config::get('url_param_type')) {
|
| 1582 |
+
$var += explode('|', $url);
|
| 1583 |
+
} else {
|
| 1584 |
+
preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) {
|
| 1585 |
+
$var[$match[1]] = strip_tags($match[2]);
|
| 1586 |
+
}, $url);
|
| 1587 |
+
}
|
| 1588 |
+
}
|
| 1589 |
+
// 设置当前请求的参数
|
| 1590 |
+
Request::instance()->route($var);
|
| 1591 |
+
}
|
| 1592 |
+
|
| 1593 |
+
// 分析路由规则中的变量
|
| 1594 |
+
private static function parseVar($rule)
|
| 1595 |
+
{
|
| 1596 |
+
// 提取路由规则中的变量
|
| 1597 |
+
$var = [];
|
| 1598 |
+
foreach (explode('/', $rule) as $val) {
|
| 1599 |
+
$optional = false;
|
| 1600 |
+
if (false !== strpos($val, '<') && preg_match_all('/<(\w+(\??))>/', $val, $matches)) {
|
| 1601 |
+
foreach ($matches[1] as $name) {
|
| 1602 |
+
if (strpos($name, '?')) {
|
| 1603 |
+
$name = substr($name, 0, -1);
|
| 1604 |
+
$optional = true;
|
| 1605 |
+
} else {
|
| 1606 |
+
$optional = false;
|
| 1607 |
+
}
|
| 1608 |
+
$var[$name] = $optional ? 2 : 1;
|
| 1609 |
+
}
|
| 1610 |
+
}
|
| 1611 |
+
|
| 1612 |
+
if (0 === strpos($val, '[:')) {
|
| 1613 |
+
// 可选参数
|
| 1614 |
+
$optional = true;
|
| 1615 |
+
$val = substr($val, 1, -1);
|
| 1616 |
+
}
|
| 1617 |
+
if (0 === strpos($val, ':')) {
|
| 1618 |
+
// URL变量
|
| 1619 |
+
$name = substr($val, 1);
|
| 1620 |
+
$var[$name] = $optional ? 2 : 1;
|
| 1621 |
+
}
|
| 1622 |
+
}
|
| 1623 |
+
return $var;
|
| 1624 |
+
}
|
| 1625 |
+
|
| 1626 |
+
/**
|
| 1627 |
+
* 获取路由解析缓存的key
|
| 1628 |
+
* @param Request $request
|
| 1629 |
+
* @return string
|
| 1630 |
+
*/
|
| 1631 |
+
private static function getCheckCacheKey(Request $request)
|
| 1632 |
+
{
|
| 1633 |
+
static $key;
|
| 1634 |
+
|
| 1635 |
+
if (empty($key)) {
|
| 1636 |
+
if ($callback = Config::get('route_check_cache_key')) {
|
| 1637 |
+
$key = call_user_func($callback, $request);
|
| 1638 |
+
} else {
|
| 1639 |
+
$key = "{$request->host(true)}|{$request->method()}|{$request->path()}";
|
| 1640 |
+
}
|
| 1641 |
+
}
|
| 1642 |
+
|
| 1643 |
+
return $key;
|
| 1644 |
+
}
|
| 1645 |
+
}
|
thinkphp/library/think/Session.php
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use think\exception\ClassNotFoundException;
|
| 15 |
+
|
| 16 |
+
class Session
|
| 17 |
+
{
|
| 18 |
+
protected static $prefix = '';
|
| 19 |
+
protected static $init = null;
|
| 20 |
+
|
| 21 |
+
/**
|
| 22 |
+
* 设置或者获取session作用域(前缀)
|
| 23 |
+
* @param string $prefix
|
| 24 |
+
* @return string|void
|
| 25 |
+
*/
|
| 26 |
+
public static function prefix($prefix = '')
|
| 27 |
+
{
|
| 28 |
+
empty(self::$init) && self::boot();
|
| 29 |
+
if (empty($prefix) && null !== $prefix) {
|
| 30 |
+
return self::$prefix;
|
| 31 |
+
} else {
|
| 32 |
+
self::$prefix = $prefix;
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
/**
|
| 37 |
+
* session初始化
|
| 38 |
+
* @param array $config
|
| 39 |
+
* @return void
|
| 40 |
+
* @throws \think\Exception
|
| 41 |
+
*/
|
| 42 |
+
public static function init(array $config = [])
|
| 43 |
+
{
|
| 44 |
+
if (empty($config)) {
|
| 45 |
+
$config = Config::get('session');
|
| 46 |
+
}
|
| 47 |
+
// 记录初始化信息
|
| 48 |
+
App::$debug && Log::record('[ SESSION ] INIT ' . var_export($config, true), 'info');
|
| 49 |
+
$isDoStart = false;
|
| 50 |
+
if (isset($config['use_trans_sid'])) {
|
| 51 |
+
ini_set('session.use_trans_sid', $config['use_trans_sid'] ? 1 : 0);
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
// 启动session
|
| 55 |
+
if (!empty($config['auto_start']) && PHP_SESSION_ACTIVE != session_status()) {
|
| 56 |
+
ini_set('session.auto_start', 0);
|
| 57 |
+
$isDoStart = true;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
if (isset($config['prefix']) && ('' === self::$prefix || null === self::$prefix)) {
|
| 61 |
+
self::$prefix = $config['prefix'];
|
| 62 |
+
}
|
| 63 |
+
if (isset($config['var_session_id']) && isset($_REQUEST[$config['var_session_id']])) {
|
| 64 |
+
session_id($_REQUEST[$config['var_session_id']]);
|
| 65 |
+
} elseif (isset($config['id']) && !empty($config['id'])) {
|
| 66 |
+
session_id($config['id']);
|
| 67 |
+
}
|
| 68 |
+
if (isset($config['name'])) {
|
| 69 |
+
session_name($config['name']);
|
| 70 |
+
}
|
| 71 |
+
if (isset($config['path'])) {
|
| 72 |
+
session_save_path($config['path']);
|
| 73 |
+
}
|
| 74 |
+
if (isset($config['domain'])) {
|
| 75 |
+
ini_set('session.cookie_domain', $config['domain']);
|
| 76 |
+
}
|
| 77 |
+
if (isset($config['expire'])) {
|
| 78 |
+
ini_set('session.gc_maxlifetime', $config['expire']);
|
| 79 |
+
ini_set('session.cookie_lifetime', $config['expire']);
|
| 80 |
+
}
|
| 81 |
+
if (isset($config['secure'])) {
|
| 82 |
+
ini_set('session.cookie_secure', $config['secure']);
|
| 83 |
+
}
|
| 84 |
+
if (isset($config['httponly'])) {
|
| 85 |
+
ini_set('session.cookie_httponly', $config['httponly']);
|
| 86 |
+
}
|
| 87 |
+
if (isset($config['use_cookies'])) {
|
| 88 |
+
ini_set('session.use_cookies', $config['use_cookies'] ? 1 : 0);
|
| 89 |
+
}
|
| 90 |
+
if (isset($config['cache_limiter'])) {
|
| 91 |
+
session_cache_limiter($config['cache_limiter']);
|
| 92 |
+
}
|
| 93 |
+
if (isset($config['cache_expire'])) {
|
| 94 |
+
session_cache_expire($config['cache_expire']);
|
| 95 |
+
}
|
| 96 |
+
if (!empty($config['type'])) {
|
| 97 |
+
// 读取session驱动
|
| 98 |
+
$class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\session\\driver\\' . ucwords($config['type']);
|
| 99 |
+
|
| 100 |
+
// 检查驱动类
|
| 101 |
+
if (!class_exists($class) || !session_set_save_handler(new $class($config))) {
|
| 102 |
+
throw new ClassNotFoundException('error session handler:' . $class, $class);
|
| 103 |
+
}
|
| 104 |
+
}
|
| 105 |
+
if ($isDoStart) {
|
| 106 |
+
session_start();
|
| 107 |
+
self::$init = true;
|
| 108 |
+
} else {
|
| 109 |
+
self::$init = false;
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
/**
|
| 114 |
+
* session自动启动或者初始化
|
| 115 |
+
* @return void
|
| 116 |
+
*/
|
| 117 |
+
public static function boot()
|
| 118 |
+
{
|
| 119 |
+
if (is_null(self::$init)) {
|
| 120 |
+
self::init();
|
| 121 |
+
} elseif (false === self::$init) {
|
| 122 |
+
if (PHP_SESSION_ACTIVE != session_status()) {
|
| 123 |
+
session_start();
|
| 124 |
+
}
|
| 125 |
+
self::$init = true;
|
| 126 |
+
}
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
/**
|
| 130 |
+
* session设置
|
| 131 |
+
* @param string $name session名称
|
| 132 |
+
* @param mixed $value session值
|
| 133 |
+
* @param string|null $prefix 作用域(前缀)
|
| 134 |
+
* @return void
|
| 135 |
+
*/
|
| 136 |
+
public static function set($name, $value = '', $prefix = null)
|
| 137 |
+
{
|
| 138 |
+
empty(self::$init) && self::boot();
|
| 139 |
+
|
| 140 |
+
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
|
| 141 |
+
if (strpos($name, '.')) {
|
| 142 |
+
// 二维数组赋值
|
| 143 |
+
list($name1, $name2) = explode('.', $name);
|
| 144 |
+
if ($prefix) {
|
| 145 |
+
$_SESSION[$prefix][$name1][$name2] = $value;
|
| 146 |
+
} else {
|
| 147 |
+
$_SESSION[$name1][$name2] = $value;
|
| 148 |
+
}
|
| 149 |
+
} elseif ($prefix) {
|
| 150 |
+
$_SESSION[$prefix][$name] = $value;
|
| 151 |
+
} else {
|
| 152 |
+
$_SESSION[$name] = $value;
|
| 153 |
+
}
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
/**
|
| 157 |
+
* session获取
|
| 158 |
+
* @param string $name session名称
|
| 159 |
+
* @param string|null $prefix 作用域(前缀)
|
| 160 |
+
* @return mixed
|
| 161 |
+
*/
|
| 162 |
+
public static function get($name = '', $prefix = null)
|
| 163 |
+
{
|
| 164 |
+
empty(self::$init) && self::boot();
|
| 165 |
+
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
|
| 166 |
+
if ('' == $name) {
|
| 167 |
+
// 获取全部的session
|
| 168 |
+
$value = $prefix ? (!empty($_SESSION[$prefix]) ? $_SESSION[$prefix] : []) : $_SESSION;
|
| 169 |
+
} elseif ($prefix) {
|
| 170 |
+
// 获取session
|
| 171 |
+
if (strpos($name, '.')) {
|
| 172 |
+
list($name1, $name2) = explode('.', $name);
|
| 173 |
+
$value = isset($_SESSION[$prefix][$name1][$name2]) ? $_SESSION[$prefix][$name1][$name2] : null;
|
| 174 |
+
} else {
|
| 175 |
+
$value = isset($_SESSION[$prefix][$name]) ? $_SESSION[$prefix][$name] : null;
|
| 176 |
+
}
|
| 177 |
+
} else {
|
| 178 |
+
if (strpos($name, '.')) {
|
| 179 |
+
list($name1, $name2) = explode('.', $name);
|
| 180 |
+
$value = isset($_SESSION[$name1][$name2]) ? $_SESSION[$name1][$name2] : null;
|
| 181 |
+
} else {
|
| 182 |
+
$value = isset($_SESSION[$name]) ? $_SESSION[$name] : null;
|
| 183 |
+
}
|
| 184 |
+
}
|
| 185 |
+
return $value;
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
/**
|
| 189 |
+
* session获取并删除
|
| 190 |
+
* @param string $name session名称
|
| 191 |
+
* @param string|null $prefix 作用域(前缀)
|
| 192 |
+
* @return mixed
|
| 193 |
+
*/
|
| 194 |
+
public static function pull($name, $prefix = null)
|
| 195 |
+
{
|
| 196 |
+
$result = self::get($name, $prefix);
|
| 197 |
+
if ($result) {
|
| 198 |
+
self::delete($name, $prefix);
|
| 199 |
+
return $result;
|
| 200 |
+
} else {
|
| 201 |
+
return;
|
| 202 |
+
}
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
/**
|
| 206 |
+
* session设置 下一次请求有效
|
| 207 |
+
* @param string $name session名称
|
| 208 |
+
* @param mixed $value session值
|
| 209 |
+
* @param string|null $prefix 作用域(前缀)
|
| 210 |
+
* @return void
|
| 211 |
+
*/
|
| 212 |
+
public static function flash($name, $value)
|
| 213 |
+
{
|
| 214 |
+
self::set($name, $value);
|
| 215 |
+
if (!self::has('__flash__.__time__')) {
|
| 216 |
+
self::set('__flash__.__time__', $_SERVER['REQUEST_TIME_FLOAT']);
|
| 217 |
+
}
|
| 218 |
+
self::push('__flash__', $name);
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
/**
|
| 222 |
+
* 清空当前请求的session数据
|
| 223 |
+
* @return void
|
| 224 |
+
*/
|
| 225 |
+
public static function flush()
|
| 226 |
+
{
|
| 227 |
+
if (self::$init) {
|
| 228 |
+
$item = self::get('__flash__');
|
| 229 |
+
|
| 230 |
+
if (!empty($item)) {
|
| 231 |
+
$time = $item['__time__'];
|
| 232 |
+
if ($_SERVER['REQUEST_TIME_FLOAT'] > $time) {
|
| 233 |
+
unset($item['__time__']);
|
| 234 |
+
self::delete($item);
|
| 235 |
+
self::set('__flash__', []);
|
| 236 |
+
}
|
| 237 |
+
}
|
| 238 |
+
}
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
/**
|
| 242 |
+
* 删除session数据
|
| 243 |
+
* @param string|array $name session名称
|
| 244 |
+
* @param string|null $prefix 作用域(前缀)
|
| 245 |
+
* @return void
|
| 246 |
+
*/
|
| 247 |
+
public static function delete($name, $prefix = null)
|
| 248 |
+
{
|
| 249 |
+
empty(self::$init) && self::boot();
|
| 250 |
+
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
|
| 251 |
+
if (is_array($name)) {
|
| 252 |
+
foreach ($name as $key) {
|
| 253 |
+
self::delete($key, $prefix);
|
| 254 |
+
}
|
| 255 |
+
} elseif (strpos($name, '.')) {
|
| 256 |
+
list($name1, $name2) = explode('.', $name);
|
| 257 |
+
if ($prefix) {
|
| 258 |
+
unset($_SESSION[$prefix][$name1][$name2]);
|
| 259 |
+
} else {
|
| 260 |
+
unset($_SESSION[$name1][$name2]);
|
| 261 |
+
}
|
| 262 |
+
} else {
|
| 263 |
+
if ($prefix) {
|
| 264 |
+
unset($_SESSION[$prefix][$name]);
|
| 265 |
+
} else {
|
| 266 |
+
unset($_SESSION[$name]);
|
| 267 |
+
}
|
| 268 |
+
}
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
/**
|
| 272 |
+
* 清空session数据
|
| 273 |
+
* @param string|null $prefix 作用域(前缀)
|
| 274 |
+
* @return void
|
| 275 |
+
*/
|
| 276 |
+
public static function clear($prefix = null)
|
| 277 |
+
{
|
| 278 |
+
empty(self::$init) && self::boot();
|
| 279 |
+
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
|
| 280 |
+
if ($prefix) {
|
| 281 |
+
unset($_SESSION[$prefix]);
|
| 282 |
+
} else {
|
| 283 |
+
$_SESSION = [];
|
| 284 |
+
}
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
/**
|
| 288 |
+
* 判断session数据
|
| 289 |
+
* @param string $name session名称
|
| 290 |
+
* @param string|null $prefix
|
| 291 |
+
* @return bool
|
| 292 |
+
*/
|
| 293 |
+
public static function has($name, $prefix = null)
|
| 294 |
+
{
|
| 295 |
+
empty(self::$init) && self::boot();
|
| 296 |
+
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
|
| 297 |
+
if (strpos($name, '.')) {
|
| 298 |
+
// 支持数组
|
| 299 |
+
list($name1, $name2) = explode('.', $name);
|
| 300 |
+
return $prefix ? isset($_SESSION[$prefix][$name1][$name2]) : isset($_SESSION[$name1][$name2]);
|
| 301 |
+
} else {
|
| 302 |
+
return $prefix ? isset($_SESSION[$prefix][$name]) : isset($_SESSION[$name]);
|
| 303 |
+
}
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
/**
|
| 307 |
+
* 添加数据到一个session数组
|
| 308 |
+
* @param string $key
|
| 309 |
+
* @param mixed $value
|
| 310 |
+
* @return void
|
| 311 |
+
*/
|
| 312 |
+
public static function push($key, $value)
|
| 313 |
+
{
|
| 314 |
+
$array = self::get($key);
|
| 315 |
+
if (is_null($array)) {
|
| 316 |
+
$array = [];
|
| 317 |
+
}
|
| 318 |
+
$array[] = $value;
|
| 319 |
+
self::set($key, $array);
|
| 320 |
+
}
|
| 321 |
+
|
| 322 |
+
/**
|
| 323 |
+
* 启动session
|
| 324 |
+
* @return void
|
| 325 |
+
*/
|
| 326 |
+
public static function start()
|
| 327 |
+
{
|
| 328 |
+
session_start();
|
| 329 |
+
self::$init = true;
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
+
/**
|
| 333 |
+
* 销毁session
|
| 334 |
+
* @return void
|
| 335 |
+
*/
|
| 336 |
+
public static function destroy()
|
| 337 |
+
{
|
| 338 |
+
if (!empty($_SESSION)) {
|
| 339 |
+
$_SESSION = [];
|
| 340 |
+
}
|
| 341 |
+
session_unset();
|
| 342 |
+
session_destroy();
|
| 343 |
+
self::$init = null;
|
| 344 |
+
}
|
| 345 |
+
|
| 346 |
+
/**
|
| 347 |
+
* 重新生成session_id
|
| 348 |
+
* @param bool $delete 是否删除关联会话文件
|
| 349 |
+
* @return void
|
| 350 |
+
*/
|
| 351 |
+
public static function regenerate($delete = false)
|
| 352 |
+
{
|
| 353 |
+
session_regenerate_id($delete);
|
| 354 |
+
}
|
| 355 |
+
|
| 356 |
+
/**
|
| 357 |
+
* 暂停session
|
| 358 |
+
* @return void
|
| 359 |
+
*/
|
| 360 |
+
public static function pause()
|
| 361 |
+
{
|
| 362 |
+
// 暂停session
|
| 363 |
+
session_write_close();
|
| 364 |
+
self::$init = false;
|
| 365 |
+
}
|
| 366 |
+
}
|
thinkphp/library/think/Template.php
ADDED
|
@@ -0,0 +1,1139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use think\exception\TemplateNotFoundException;
|
| 15 |
+
use think\template\TagLib;
|
| 16 |
+
|
| 17 |
+
/**
|
| 18 |
+
* ThinkPHP分离出来的模板引擎
|
| 19 |
+
* 支持XML标签和普通标签的模板解析
|
| 20 |
+
* 编译型模板引擎 支持动态缓存
|
| 21 |
+
*/
|
| 22 |
+
class Template
|
| 23 |
+
{
|
| 24 |
+
// 模板变量
|
| 25 |
+
protected $data = [];
|
| 26 |
+
// 引擎配置
|
| 27 |
+
protected $config = [
|
| 28 |
+
'view_path' => '', // 模板路径
|
| 29 |
+
'view_base' => '',
|
| 30 |
+
'view_suffix' => 'html', // 默认模板文件后缀
|
| 31 |
+
'view_depr' => DS,
|
| 32 |
+
'cache_suffix' => 'php', // 默认模板缓存后缀
|
| 33 |
+
'tpl_deny_func_list' => 'echo,exit', // 模板引擎禁用函数
|
| 34 |
+
'tpl_deny_php' => false, // 默认模板引擎是否禁用PHP原生代码
|
| 35 |
+
'tpl_begin' => '{', // 模板引擎普通标签开始标记
|
| 36 |
+
'tpl_end' => '}', // 模板引擎普通标签结束标记
|
| 37 |
+
'strip_space' => false, // 是否去除模板文件里面的html空格与换行
|
| 38 |
+
'tpl_cache' => true, // 是否开启模板编译缓存,设为false则每次都会重新编译
|
| 39 |
+
'compile_type' => 'file', // 模板编译类型
|
| 40 |
+
'cache_prefix' => '', // 模板缓存前缀标识,可以动态改变
|
| 41 |
+
'cache_time' => 0, // 模板缓存有效期 0 为永久,(以数字为值,单位:秒)
|
| 42 |
+
'layout_on' => false, // 布局模板开关
|
| 43 |
+
'layout_name' => 'layout', // 布局模板入口文件
|
| 44 |
+
'layout_item' => '{__CONTENT__}', // 布局模板的内容替换标识
|
| 45 |
+
'taglib_begin' => '{', // 标签库标签开始标记
|
| 46 |
+
'taglib_end' => '}', // 标签库标签结束标记
|
| 47 |
+
'taglib_load' => true, // 是否使用内置标签库之外的其它标签库,默认自动检测
|
| 48 |
+
'taglib_build_in' => 'cx', // 内置标签库名称(标签使用不必指定标签库名称),以逗号分隔 注意解析顺序
|
| 49 |
+
'taglib_pre_load' => '', // 需要额外加载的标签库(须指定标签库名称),多个以逗号分隔
|
| 50 |
+
'display_cache' => false, // 模板渲染缓存
|
| 51 |
+
'cache_id' => '', // 模板缓存ID
|
| 52 |
+
'tpl_replace_string' => [],
|
| 53 |
+
'tpl_var_identify' => 'array', // .语法变量识别,array|object|'', 为空时自动识别
|
| 54 |
+
];
|
| 55 |
+
|
| 56 |
+
private $literal = [];
|
| 57 |
+
private $includeFile = []; // 记录所有模板包含的文件路径及更新时间
|
| 58 |
+
protected $storage;
|
| 59 |
+
|
| 60 |
+
/**
|
| 61 |
+
* 构造函数
|
| 62 |
+
* @access public
|
| 63 |
+
* @param array $config
|
| 64 |
+
*/
|
| 65 |
+
public function __construct(array $config = [])
|
| 66 |
+
{
|
| 67 |
+
$this->config['cache_path'] = TEMP_PATH;
|
| 68 |
+
$this->config = array_merge($this->config, $config);
|
| 69 |
+
|
| 70 |
+
$this->config['taglib_begin_origin'] = $this->config['taglib_begin'];
|
| 71 |
+
$this->config['taglib_end_origin'] = $this->config['taglib_end'];
|
| 72 |
+
|
| 73 |
+
$this->config['taglib_begin'] = preg_quote($this->config['taglib_begin'], '/');
|
| 74 |
+
$this->config['taglib_end'] = preg_quote($this->config['taglib_end'], '/');
|
| 75 |
+
$this->config['tpl_begin'] = preg_quote($this->config['tpl_begin'], '/');
|
| 76 |
+
$this->config['tpl_end'] = preg_quote($this->config['tpl_end'], '/');
|
| 77 |
+
|
| 78 |
+
// 初始化模板编译存储器
|
| 79 |
+
$type = $this->config['compile_type'] ? $this->config['compile_type'] : 'File';
|
| 80 |
+
$class = false !== strpos($type, '\\') ? $type : '\\think\\template\\driver\\' . ucwords($type);
|
| 81 |
+
$this->storage = new $class();
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
/**
|
| 85 |
+
* 模板变量赋值
|
| 86 |
+
* @access public
|
| 87 |
+
* @param mixed $name
|
| 88 |
+
* @param mixed $value
|
| 89 |
+
* @return void
|
| 90 |
+
*/
|
| 91 |
+
public function assign($name, $value = '')
|
| 92 |
+
{
|
| 93 |
+
if (is_array($name)) {
|
| 94 |
+
$this->data = array_merge($this->data, $name);
|
| 95 |
+
} else {
|
| 96 |
+
$this->data[$name] = $value;
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
/**
|
| 101 |
+
* 模板引擎参数赋值
|
| 102 |
+
* @access public
|
| 103 |
+
* @param mixed $name
|
| 104 |
+
* @param mixed $value
|
| 105 |
+
*/
|
| 106 |
+
public function __set($name, $value)
|
| 107 |
+
{
|
| 108 |
+
$this->config[$name] = $value;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
/**
|
| 112 |
+
* 模板引擎配置项
|
| 113 |
+
* @access public
|
| 114 |
+
* @param array|string $config
|
| 115 |
+
* @return string|void|array
|
| 116 |
+
*/
|
| 117 |
+
public function config($config)
|
| 118 |
+
{
|
| 119 |
+
if (is_array($config)) {
|
| 120 |
+
$this->config = array_merge($this->config, $config);
|
| 121 |
+
} elseif (isset($this->config[$config])) {
|
| 122 |
+
return $this->config[$config];
|
| 123 |
+
} else {
|
| 124 |
+
return;
|
| 125 |
+
}
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
/**
|
| 129 |
+
* 模板变量获取
|
| 130 |
+
* @access public
|
| 131 |
+
* @param string $name 变量名
|
| 132 |
+
* @return mixed
|
| 133 |
+
*/
|
| 134 |
+
public function get($name = '')
|
| 135 |
+
{
|
| 136 |
+
if ('' == $name) {
|
| 137 |
+
return $this->data;
|
| 138 |
+
} else {
|
| 139 |
+
$data = $this->data;
|
| 140 |
+
foreach (explode('.', $name) as $key => $val) {
|
| 141 |
+
if (isset($data[$val])) {
|
| 142 |
+
$data = $data[$val];
|
| 143 |
+
} else {
|
| 144 |
+
$data = null;
|
| 145 |
+
break;
|
| 146 |
+
}
|
| 147 |
+
}
|
| 148 |
+
return $data;
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
/**
|
| 153 |
+
* 渲染模板文件
|
| 154 |
+
* @access public
|
| 155 |
+
* @param string $template 模板文件
|
| 156 |
+
* @param array $vars 模板变量
|
| 157 |
+
* @param array $config 模板参数
|
| 158 |
+
* @return void
|
| 159 |
+
*/
|
| 160 |
+
public function fetch($template, $vars = [], $config = [])
|
| 161 |
+
{
|
| 162 |
+
if ($vars) {
|
| 163 |
+
$this->data = $vars;
|
| 164 |
+
}
|
| 165 |
+
if ($config) {
|
| 166 |
+
$this->config($config);
|
| 167 |
+
}
|
| 168 |
+
if (!empty($this->config['cache_id']) && $this->config['display_cache']) {
|
| 169 |
+
// 读取渲染缓存
|
| 170 |
+
$cacheContent = Cache::get($this->config['cache_id']);
|
| 171 |
+
if (false !== $cacheContent) {
|
| 172 |
+
echo $cacheContent;
|
| 173 |
+
return;
|
| 174 |
+
}
|
| 175 |
+
}
|
| 176 |
+
$template = $this->parseTemplateFile($template);
|
| 177 |
+
if ($template) {
|
| 178 |
+
$cacheFile = $this->config['cache_path'] . $this->config['cache_prefix'] . md5($this->config['layout_name'] . $template) . '.' . ltrim($this->config['cache_suffix'], '.');
|
| 179 |
+
if (!$this->checkCache($cacheFile)) {
|
| 180 |
+
// 缓存无效 重新模板编译
|
| 181 |
+
$content = file_get_contents($template);
|
| 182 |
+
$this->compiler($content, $cacheFile);
|
| 183 |
+
}
|
| 184 |
+
// 页面缓存
|
| 185 |
+
ob_start();
|
| 186 |
+
ob_implicit_flush(0);
|
| 187 |
+
// 读取编译存储
|
| 188 |
+
$this->storage->read($cacheFile, $this->data);
|
| 189 |
+
// 获取并清空缓存
|
| 190 |
+
$content = ob_get_clean();
|
| 191 |
+
if (!empty($this->config['cache_id']) && $this->config['display_cache']) {
|
| 192 |
+
// 缓存页面输出
|
| 193 |
+
Cache::set($this->config['cache_id'], $content, $this->config['cache_time']);
|
| 194 |
+
}
|
| 195 |
+
echo $content;
|
| 196 |
+
}
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
/**
|
| 200 |
+
* 渲染模板内容
|
| 201 |
+
* @access public
|
| 202 |
+
* @param string $content 模板内容
|
| 203 |
+
* @param array $vars 模板变量
|
| 204 |
+
* @param array $config 模板参数
|
| 205 |
+
* @return void
|
| 206 |
+
*/
|
| 207 |
+
public function display($content, $vars = [], $config = [])
|
| 208 |
+
{
|
| 209 |
+
if ($vars) {
|
| 210 |
+
$this->data = $vars;
|
| 211 |
+
}
|
| 212 |
+
if ($config) {
|
| 213 |
+
$this->config($config);
|
| 214 |
+
}
|
| 215 |
+
$cacheFile = $this->config['cache_path'] . $this->config['cache_prefix'] . md5($content) . '.' . ltrim($this->config['cache_suffix'], '.');
|
| 216 |
+
if (!$this->checkCache($cacheFile)) {
|
| 217 |
+
// 缓存无效 模板编译
|
| 218 |
+
$this->compiler($content, $cacheFile);
|
| 219 |
+
}
|
| 220 |
+
// 读取编译存储
|
| 221 |
+
$this->storage->read($cacheFile, $this->data);
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
/**
|
| 225 |
+
* 设置布局
|
| 226 |
+
* @access public
|
| 227 |
+
* @param mixed $name 布局模板名称 false 则关闭布局
|
| 228 |
+
* @param string $replace 布局模板内容替换标识
|
| 229 |
+
* @return Template
|
| 230 |
+
*/
|
| 231 |
+
public function layout($name, $replace = '')
|
| 232 |
+
{
|
| 233 |
+
if (false === $name) {
|
| 234 |
+
// 关闭布局
|
| 235 |
+
$this->config['layout_on'] = false;
|
| 236 |
+
} else {
|
| 237 |
+
// 开启布局
|
| 238 |
+
$this->config['layout_on'] = true;
|
| 239 |
+
// 名称必须为字符串
|
| 240 |
+
if (is_string($name)) {
|
| 241 |
+
$this->config['layout_name'] = $name;
|
| 242 |
+
}
|
| 243 |
+
if (!empty($replace)) {
|
| 244 |
+
$this->config['layout_item'] = $replace;
|
| 245 |
+
}
|
| 246 |
+
}
|
| 247 |
+
return $this;
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
/**
|
| 251 |
+
* 检查编译缓存是否有效
|
| 252 |
+
* 如果无效则需要重新编译
|
| 253 |
+
* @access private
|
| 254 |
+
* @param string $cacheFile 缓存文件名
|
| 255 |
+
* @return boolean
|
| 256 |
+
*/
|
| 257 |
+
private function checkCache($cacheFile)
|
| 258 |
+
{
|
| 259 |
+
// 未开启缓存功能
|
| 260 |
+
if (!$this->config['tpl_cache']) {
|
| 261 |
+
return false;
|
| 262 |
+
}
|
| 263 |
+
// 缓存文件不存在
|
| 264 |
+
if (!is_file($cacheFile)) {
|
| 265 |
+
return false;
|
| 266 |
+
}
|
| 267 |
+
// 读取缓存文件失败
|
| 268 |
+
if (!$handle = @fopen($cacheFile, "r")) {
|
| 269 |
+
return false;
|
| 270 |
+
}
|
| 271 |
+
// 读取第一行
|
| 272 |
+
preg_match('/\/\*(.+?)\*\//', fgets($handle), $matches);
|
| 273 |
+
if (!isset($matches[1])) {
|
| 274 |
+
return false;
|
| 275 |
+
}
|
| 276 |
+
$includeFile = unserialize($matches[1]);
|
| 277 |
+
if (!is_array($includeFile)) {
|
| 278 |
+
return false;
|
| 279 |
+
}
|
| 280 |
+
// 检查模板文件是否有更新
|
| 281 |
+
foreach ($includeFile as $path => $time) {
|
| 282 |
+
if (is_file($path) && filemtime($path) > $time) {
|
| 283 |
+
// 模板文件如果有更新则缓存需要更新
|
| 284 |
+
return false;
|
| 285 |
+
}
|
| 286 |
+
}
|
| 287 |
+
// 检查编译存储是否有效
|
| 288 |
+
return $this->storage->check($cacheFile, $this->config['cache_time']);
|
| 289 |
+
}
|
| 290 |
+
|
| 291 |
+
/**
|
| 292 |
+
* 检查编译缓存是否存在
|
| 293 |
+
* @access public
|
| 294 |
+
* @param string $cacheId 缓存的id
|
| 295 |
+
* @return boolean
|
| 296 |
+
*/
|
| 297 |
+
public function isCache($cacheId)
|
| 298 |
+
{
|
| 299 |
+
if ($cacheId && $this->config['display_cache']) {
|
| 300 |
+
// 缓存页面输出
|
| 301 |
+
return Cache::has($cacheId);
|
| 302 |
+
}
|
| 303 |
+
return false;
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
/**
|
| 307 |
+
* 编译模板文件内容
|
| 308 |
+
* @access private
|
| 309 |
+
* @param string $content 模板内容
|
| 310 |
+
* @param string $cacheFile 缓存文件名
|
| 311 |
+
* @return void
|
| 312 |
+
*/
|
| 313 |
+
private function compiler(&$content, $cacheFile)
|
| 314 |
+
{
|
| 315 |
+
// 判断是否启用布局
|
| 316 |
+
if ($this->config['layout_on']) {
|
| 317 |
+
if (false !== strpos($content, '{__NOLAYOUT__}')) {
|
| 318 |
+
// 可以单独定义不使用布局
|
| 319 |
+
$content = str_replace('{__NOLAYOUT__}', '', $content);
|
| 320 |
+
} else {
|
| 321 |
+
// 读取布局模板
|
| 322 |
+
$layoutFile = $this->parseTemplateFile($this->config['layout_name']);
|
| 323 |
+
if ($layoutFile) {
|
| 324 |
+
// 替换布局的主体内容
|
| 325 |
+
$content = str_replace($this->config['layout_item'], $content, file_get_contents($layoutFile));
|
| 326 |
+
}
|
| 327 |
+
}
|
| 328 |
+
} else {
|
| 329 |
+
$content = str_replace('{__NOLAYOUT__}', '', $content);
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
+
// 模板解析
|
| 333 |
+
$this->parse($content);
|
| 334 |
+
if ($this->config['strip_space']) {
|
| 335 |
+
/* 去除html空格与换行 */
|
| 336 |
+
$find = ['~>\s+<~', '~>(\s+\n|\r)~'];
|
| 337 |
+
$replace = ['><', '>'];
|
| 338 |
+
$content = preg_replace($find, $replace, $content);
|
| 339 |
+
}
|
| 340 |
+
// 优化生成的php代码
|
| 341 |
+
$content = preg_replace('/\?>\s*<\?php\s(?!echo\b)/s', '', $content);
|
| 342 |
+
// 模板过滤输出
|
| 343 |
+
$replace = $this->config['tpl_replace_string'];
|
| 344 |
+
$content = str_replace(array_keys($replace), array_values($replace), $content);
|
| 345 |
+
// 添加安全代码及模板引用记录
|
| 346 |
+
$content = '<?php if (!defined(\'THINK_PATH\')) exit(); /*' . serialize($this->includeFile) . '*/ ?>' . "\n" . $content;
|
| 347 |
+
// 编译存储
|
| 348 |
+
$this->storage->write($cacheFile, $content);
|
| 349 |
+
$this->includeFile = [];
|
| 350 |
+
return;
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
/**
|
| 354 |
+
* 模板解析入口
|
| 355 |
+
* 支持普通标签和TagLib解析 支持自定义标签库
|
| 356 |
+
* @access public
|
| 357 |
+
* @param string $content 要解析的模板内容
|
| 358 |
+
* @return void
|
| 359 |
+
*/
|
| 360 |
+
public function parse(&$content)
|
| 361 |
+
{
|
| 362 |
+
// 内容为空不解析
|
| 363 |
+
if (empty($content)) {
|
| 364 |
+
return;
|
| 365 |
+
}
|
| 366 |
+
// 替换literal标签内容
|
| 367 |
+
$this->parseLiteral($content);
|
| 368 |
+
// 解析继承
|
| 369 |
+
$this->parseExtend($content);
|
| 370 |
+
// 解析布局
|
| 371 |
+
$this->parseLayout($content);
|
| 372 |
+
// 检查include语法
|
| 373 |
+
$this->parseInclude($content);
|
| 374 |
+
// 替换包含文件中literal标签内容
|
| 375 |
+
$this->parseLiteral($content);
|
| 376 |
+
// 检查PHP语法
|
| 377 |
+
$this->parsePhp($content);
|
| 378 |
+
|
| 379 |
+
// 获取需要引入的标签库列表
|
| 380 |
+
// 标签库只需要定义一次,允许引入多个一次
|
| 381 |
+
// 一般放在文件的最前面
|
| 382 |
+
// 格式:<taglib name="html,mytag..." />
|
| 383 |
+
// 当TAGLIB_LOAD配置为true时才会进行检测
|
| 384 |
+
if ($this->config['taglib_load']) {
|
| 385 |
+
$tagLibs = $this->getIncludeTagLib($content);
|
| 386 |
+
if (!empty($tagLibs)) {
|
| 387 |
+
// 对导入的TagLib进行解析
|
| 388 |
+
foreach ($tagLibs as $tagLibName) {
|
| 389 |
+
$this->parseTagLib($tagLibName, $content);
|
| 390 |
+
}
|
| 391 |
+
}
|
| 392 |
+
}
|
| 393 |
+
// 预先加载的标签库 无需在每个模板中使用taglib标签加载 但必须使用标签库XML前缀
|
| 394 |
+
if ($this->config['taglib_pre_load']) {
|
| 395 |
+
$tagLibs = explode(',', $this->config['taglib_pre_load']);
|
| 396 |
+
foreach ($tagLibs as $tag) {
|
| 397 |
+
$this->parseTagLib($tag, $content);
|
| 398 |
+
}
|
| 399 |
+
}
|
| 400 |
+
// 内置标签库 无需使用taglib标签导入就可以使用 并且不需使用标签库XML前缀
|
| 401 |
+
$tagLibs = explode(',', $this->config['taglib_build_in']);
|
| 402 |
+
foreach ($tagLibs as $tag) {
|
| 403 |
+
$this->parseTagLib($tag, $content, true);
|
| 404 |
+
}
|
| 405 |
+
// 解析普通模板标签 {$tagName}
|
| 406 |
+
$this->parseTag($content);
|
| 407 |
+
|
| 408 |
+
// 还原被替换的Literal标签
|
| 409 |
+
$this->parseLiteral($content, true);
|
| 410 |
+
return;
|
| 411 |
+
}
|
| 412 |
+
|
| 413 |
+
/**
|
| 414 |
+
* 检查PHP语法
|
| 415 |
+
* @access private
|
| 416 |
+
* @param string $content 要解析的模板内容
|
| 417 |
+
* @return void
|
| 418 |
+
* @throws \think\Exception
|
| 419 |
+
*/
|
| 420 |
+
private function parsePhp(&$content)
|
| 421 |
+
{
|
| 422 |
+
// 短标签的情况要将<?标签用echo方式输出 否则无法正常输出xml标识
|
| 423 |
+
$content = preg_replace('/(<\?(?!php|=|$))/i', '<?php echo \'\\1\'; ?>' . "\n", $content);
|
| 424 |
+
// PHP语法检查
|
| 425 |
+
if ($this->config['tpl_deny_php'] && false !== strpos($content, '<?php')) {
|
| 426 |
+
throw new Exception('not allow php tag', 11600);
|
| 427 |
+
}
|
| 428 |
+
return;
|
| 429 |
+
}
|
| 430 |
+
|
| 431 |
+
/**
|
| 432 |
+
* 解析模板中的布局标签
|
| 433 |
+
* @access private
|
| 434 |
+
* @param string $content 要解析的模板内容
|
| 435 |
+
* @return void
|
| 436 |
+
*/
|
| 437 |
+
private function parseLayout(&$content)
|
| 438 |
+
{
|
| 439 |
+
// 读取模板中的布局标签
|
| 440 |
+
if (preg_match($this->getRegex('layout'), $content, $matches)) {
|
| 441 |
+
// 替换Layout标签
|
| 442 |
+
$content = str_replace($matches[0], '', $content);
|
| 443 |
+
// 解析Layout标签
|
| 444 |
+
$array = $this->parseAttr($matches[0]);
|
| 445 |
+
if (!$this->config['layout_on'] || $this->config['layout_name'] != $array['name']) {
|
| 446 |
+
// 读取布局模板
|
| 447 |
+
$layoutFile = $this->parseTemplateFile($array['name']);
|
| 448 |
+
if ($layoutFile) {
|
| 449 |
+
$replace = isset($array['replace']) ? $array['replace'] : $this->config['layout_item'];
|
| 450 |
+
// 替换布局的主体内容
|
| 451 |
+
$content = str_replace($replace, $content, file_get_contents($layoutFile));
|
| 452 |
+
}
|
| 453 |
+
}
|
| 454 |
+
} else {
|
| 455 |
+
$content = str_replace('{__NOLAYOUT__}', '', $content);
|
| 456 |
+
}
|
| 457 |
+
return;
|
| 458 |
+
}
|
| 459 |
+
|
| 460 |
+
/**
|
| 461 |
+
* 解析模板中的include标签
|
| 462 |
+
* @access private
|
| 463 |
+
* @param string $content 要解析的模板内容
|
| 464 |
+
* @return void
|
| 465 |
+
*/
|
| 466 |
+
private function parseInclude(&$content)
|
| 467 |
+
{
|
| 468 |
+
$regex = $this->getRegex('include');
|
| 469 |
+
$func = function ($template) use (&$func, &$regex, &$content) {
|
| 470 |
+
if (preg_match_all($regex, $template, $matches, PREG_SET_ORDER)) {
|
| 471 |
+
foreach ($matches as $match) {
|
| 472 |
+
$array = $this->parseAttr($match[0]);
|
| 473 |
+
$file = $array['file'];
|
| 474 |
+
unset($array['file']);
|
| 475 |
+
// 分析模板文件名并读取内容
|
| 476 |
+
$parseStr = $this->parseTemplateName($file);
|
| 477 |
+
foreach ($array as $k => $v) {
|
| 478 |
+
// 以$开头字符串转换成模板变量
|
| 479 |
+
if (0 === strpos($v, '$')) {
|
| 480 |
+
$v = $this->get(substr($v, 1));
|
| 481 |
+
}
|
| 482 |
+
$parseStr = str_replace('[' . $k . ']', $v, $parseStr);
|
| 483 |
+
}
|
| 484 |
+
$content = str_replace($match[0], $parseStr, $content);
|
| 485 |
+
// 再次对包含文件进行模板分析
|
| 486 |
+
$func($parseStr);
|
| 487 |
+
}
|
| 488 |
+
unset($matches);
|
| 489 |
+
}
|
| 490 |
+
};
|
| 491 |
+
// 替换模板中的include标签
|
| 492 |
+
$func($content);
|
| 493 |
+
return;
|
| 494 |
+
}
|
| 495 |
+
|
| 496 |
+
/**
|
| 497 |
+
* 解析模板中的extend标签
|
| 498 |
+
* @access private
|
| 499 |
+
* @param string $content 要解析的模板内容
|
| 500 |
+
* @return void
|
| 501 |
+
*/
|
| 502 |
+
private function parseExtend(&$content)
|
| 503 |
+
{
|
| 504 |
+
$regex = $this->getRegex('extend');
|
| 505 |
+
$array = $blocks = $baseBlocks = [];
|
| 506 |
+
$extend = '';
|
| 507 |
+
$func = function ($template) use (&$func, &$regex, &$array, &$extend, &$blocks, &$baseBlocks) {
|
| 508 |
+
if (preg_match($regex, $template, $matches)) {
|
| 509 |
+
if (!isset($array[$matches['name']])) {
|
| 510 |
+
$array[$matches['name']] = 1;
|
| 511 |
+
// 读取继承模板
|
| 512 |
+
$extend = $this->parseTemplateName($matches['name']);
|
| 513 |
+
// 递归检查继承
|
| 514 |
+
$func($extend);
|
| 515 |
+
// 取得block标签内容
|
| 516 |
+
$blocks = array_merge($blocks, $this->parseBlock($template));
|
| 517 |
+
return;
|
| 518 |
+
}
|
| 519 |
+
} else {
|
| 520 |
+
// 取得顶层模板block标签内容
|
| 521 |
+
$baseBlocks = $this->parseBlock($template, true);
|
| 522 |
+
if (empty($extend)) {
|
| 523 |
+
// 无extend标签但有block标签的情况
|
| 524 |
+
$extend = $template;
|
| 525 |
+
}
|
| 526 |
+
}
|
| 527 |
+
};
|
| 528 |
+
|
| 529 |
+
$func($content);
|
| 530 |
+
if (!empty($extend)) {
|
| 531 |
+
if ($baseBlocks) {
|
| 532 |
+
$children = [];
|
| 533 |
+
foreach ($baseBlocks as $name => $val) {
|
| 534 |
+
$replace = $val['content'];
|
| 535 |
+
if (!empty($children[$name])) {
|
| 536 |
+
// 如果包含有子block标签
|
| 537 |
+
foreach ($children[$name] as $key) {
|
| 538 |
+
$replace = str_replace($baseBlocks[$key]['begin'] . $baseBlocks[$key]['content'] . $baseBlocks[$key]['end'], $blocks[$key]['content'], $replace);
|
| 539 |
+
}
|
| 540 |
+
}
|
| 541 |
+
if (isset($blocks[$name])) {
|
| 542 |
+
// 带有{__block__}表示与所继承模板的相应标签合并,而不是覆盖
|
| 543 |
+
$replace = str_replace(['{__BLOCK__}', '{__block__}'], $replace, $blocks[$name]['content']);
|
| 544 |
+
if (!empty($val['parent'])) {
|
| 545 |
+
// 如果不是最顶层的block标签
|
| 546 |
+
$parent = $val['parent'];
|
| 547 |
+
if (isset($blocks[$parent])) {
|
| 548 |
+
$blocks[$parent]['content'] = str_replace($blocks[$name]['begin'] . $blocks[$name]['content'] . $blocks[$name]['end'], $replace, $blocks[$parent]['content']);
|
| 549 |
+
}
|
| 550 |
+
$blocks[$name]['content'] = $replace;
|
| 551 |
+
$children[$parent][] = $name;
|
| 552 |
+
continue;
|
| 553 |
+
}
|
| 554 |
+
} elseif (!empty($val['parent'])) {
|
| 555 |
+
// 如果子标签没有被继承则用原值
|
| 556 |
+
$children[$val['parent']][] = $name;
|
| 557 |
+
$blocks[$name] = $val;
|
| 558 |
+
}
|
| 559 |
+
if (!$val['parent']) {
|
| 560 |
+
// 替换模板中的顶级block标签
|
| 561 |
+
$extend = str_replace($val['begin'] . $val['content'] . $val['end'], $replace, $extend);
|
| 562 |
+
}
|
| 563 |
+
}
|
| 564 |
+
}
|
| 565 |
+
$content = $extend;
|
| 566 |
+
unset($blocks, $baseBlocks);
|
| 567 |
+
}
|
| 568 |
+
return;
|
| 569 |
+
}
|
| 570 |
+
|
| 571 |
+
/**
|
| 572 |
+
* 替换页面中的literal标签
|
| 573 |
+
* @access private
|
| 574 |
+
* @param string $content 模板内容
|
| 575 |
+
* @param boolean $restore 是否为还原
|
| 576 |
+
* @return void
|
| 577 |
+
*/
|
| 578 |
+
private function parseLiteral(&$content, $restore = false)
|
| 579 |
+
{
|
| 580 |
+
$regex = $this->getRegex($restore ? 'restoreliteral' : 'literal');
|
| 581 |
+
if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
|
| 582 |
+
if (!$restore) {
|
| 583 |
+
$count = count($this->literal);
|
| 584 |
+
// 替换literal标签
|
| 585 |
+
foreach ($matches as $match) {
|
| 586 |
+
$this->literal[] = substr($match[0], strlen($match[1]), -strlen($match[2]));
|
| 587 |
+
$content = str_replace($match[0], "<!--###literal{$count}###-->", $content);
|
| 588 |
+
$count++;
|
| 589 |
+
}
|
| 590 |
+
} else {
|
| 591 |
+
// 还原literal标签
|
| 592 |
+
foreach ($matches as $match) {
|
| 593 |
+
$content = str_replace($match[0], $this->literal[$match[1]], $content);
|
| 594 |
+
}
|
| 595 |
+
// 清空literal记录
|
| 596 |
+
$this->literal = [];
|
| 597 |
+
}
|
| 598 |
+
unset($matches);
|
| 599 |
+
}
|
| 600 |
+
return;
|
| 601 |
+
}
|
| 602 |
+
|
| 603 |
+
/**
|
| 604 |
+
* 获取模板中的block标签
|
| 605 |
+
* @access private
|
| 606 |
+
* @param string $content 模板内容
|
| 607 |
+
* @param boolean $sort 是否排序
|
| 608 |
+
* @return array
|
| 609 |
+
*/
|
| 610 |
+
private function parseBlock(&$content, $sort = false)
|
| 611 |
+
{
|
| 612 |
+
$regex = $this->getRegex('block');
|
| 613 |
+
$result = [];
|
| 614 |
+
if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
|
| 615 |
+
$right = $keys = [];
|
| 616 |
+
foreach ($matches as $match) {
|
| 617 |
+
if (empty($match['name'][0])) {
|
| 618 |
+
if (count($right) > 0) {
|
| 619 |
+
$tag = array_pop($right);
|
| 620 |
+
$start = $tag['offset'] + strlen($tag['tag']);
|
| 621 |
+
$length = $match[0][1] - $start;
|
| 622 |
+
$result[$tag['name']] = [
|
| 623 |
+
'begin' => $tag['tag'],
|
| 624 |
+
'content' => substr($content, $start, $length),
|
| 625 |
+
'end' => $match[0][0],
|
| 626 |
+
'parent' => count($right) ? end($right)['name'] : '',
|
| 627 |
+
];
|
| 628 |
+
$keys[$tag['name']] = $match[0][1];
|
| 629 |
+
}
|
| 630 |
+
} else {
|
| 631 |
+
// 标签头压入栈
|
| 632 |
+
$right[] = [
|
| 633 |
+
'name' => $match[2][0],
|
| 634 |
+
'offset' => $match[0][1],
|
| 635 |
+
'tag' => $match[0][0],
|
| 636 |
+
];
|
| 637 |
+
}
|
| 638 |
+
}
|
| 639 |
+
unset($right, $matches);
|
| 640 |
+
if ($sort) {
|
| 641 |
+
// 按block标签结束符在模板中的位置排序
|
| 642 |
+
array_multisort($keys, $result);
|
| 643 |
+
}
|
| 644 |
+
}
|
| 645 |
+
return $result;
|
| 646 |
+
}
|
| 647 |
+
|
| 648 |
+
/**
|
| 649 |
+
* 搜索模板页面中包含的TagLib库
|
| 650 |
+
* 并返回列表
|
| 651 |
+
* @access private
|
| 652 |
+
* @param string $content 模板内容
|
| 653 |
+
* @return array|null
|
| 654 |
+
*/
|
| 655 |
+
private function getIncludeTagLib(&$content)
|
| 656 |
+
{
|
| 657 |
+
// 搜索是否有TagLib标签
|
| 658 |
+
if (preg_match($this->getRegex('taglib'), $content, $matches)) {
|
| 659 |
+
// 替换TagLib标签
|
| 660 |
+
$content = str_replace($matches[0], '', $content);
|
| 661 |
+
return explode(',', $matches['name']);
|
| 662 |
+
}
|
| 663 |
+
return;
|
| 664 |
+
}
|
| 665 |
+
|
| 666 |
+
/**
|
| 667 |
+
* TagLib库解析
|
| 668 |
+
* @access public
|
| 669 |
+
* @param string $tagLib 要解析的标签库
|
| 670 |
+
* @param string $content 要解析的模板内容
|
| 671 |
+
* @param boolean $hide 是否隐藏标签库前缀
|
| 672 |
+
* @return void
|
| 673 |
+
*/
|
| 674 |
+
public function parseTagLib($tagLib, &$content, $hide = false)
|
| 675 |
+
{
|
| 676 |
+
if (false !== strpos($tagLib, '\\')) {
|
| 677 |
+
// 支持指定标签库的命名空间
|
| 678 |
+
$className = $tagLib;
|
| 679 |
+
$tagLib = substr($tagLib, strrpos($tagLib, '\\') + 1);
|
| 680 |
+
} else {
|
| 681 |
+
$className = '\\think\\template\\taglib\\' . ucwords($tagLib);
|
| 682 |
+
}
|
| 683 |
+
/** @var Taglib $tLib */
|
| 684 |
+
$tLib = new $className($this);
|
| 685 |
+
$tLib->parseTag($content, $hide ? '' : $tagLib);
|
| 686 |
+
return;
|
| 687 |
+
}
|
| 688 |
+
|
| 689 |
+
/**
|
| 690 |
+
* 分析标签属性
|
| 691 |
+
* @access public
|
| 692 |
+
* @param string $str 属性字符串
|
| 693 |
+
* @param string $name 不为空时返回指定的属性名
|
| 694 |
+
* @return array
|
| 695 |
+
*/
|
| 696 |
+
public function parseAttr($str, $name = null)
|
| 697 |
+
{
|
| 698 |
+
$regex = '/\s+(?>(?P<name>[\w-]+)\s*)=(?>\s*)([\"\'])(?P<value>(?:(?!\\2).)*)\\2/is';
|
| 699 |
+
$array = [];
|
| 700 |
+
if (preg_match_all($regex, $str, $matches, PREG_SET_ORDER)) {
|
| 701 |
+
foreach ($matches as $match) {
|
| 702 |
+
$array[$match['name']] = $match['value'];
|
| 703 |
+
}
|
| 704 |
+
unset($matches);
|
| 705 |
+
}
|
| 706 |
+
if (!empty($name) && isset($array[$name])) {
|
| 707 |
+
return $array[$name];
|
| 708 |
+
} else {
|
| 709 |
+
return $array;
|
| 710 |
+
}
|
| 711 |
+
}
|
| 712 |
+
|
| 713 |
+
/**
|
| 714 |
+
* 模板标签解析
|
| 715 |
+
* 格式: {TagName:args [|content] }
|
| 716 |
+
* @access private
|
| 717 |
+
* @param string $content 要解析的模板内容
|
| 718 |
+
* @return void
|
| 719 |
+
*/
|
| 720 |
+
private function parseTag(&$content)
|
| 721 |
+
{
|
| 722 |
+
$regex = $this->getRegex('tag');
|
| 723 |
+
if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
|
| 724 |
+
foreach ($matches as $match) {
|
| 725 |
+
$str = stripslashes($match[1]);
|
| 726 |
+
$flag = substr($str, 0, 1);
|
| 727 |
+
switch ($flag) {
|
| 728 |
+
case '$':
|
| 729 |
+
// 解析模板变量 格式 {$varName}
|
| 730 |
+
// 是否带有?号
|
| 731 |
+
if (false !== $pos = strpos($str, '?')) {
|
| 732 |
+
$array = preg_split('/([!=]={1,2}|(?<!-)[><]={0,1})/', substr($str, 0, $pos), 2, PREG_SPLIT_DELIM_CAPTURE);
|
| 733 |
+
$name = $array[0];
|
| 734 |
+
$this->parseVar($name);
|
| 735 |
+
$this->parseVarFunction($name);
|
| 736 |
+
|
| 737 |
+
$str = trim(substr($str, $pos + 1));
|
| 738 |
+
$this->parseVar($str);
|
| 739 |
+
$first = substr($str, 0, 1);
|
| 740 |
+
if (strpos($name, ')')) {
|
| 741 |
+
// $name为对象或是自动识别,或者含有函数
|
| 742 |
+
if (isset($array[1])) {
|
| 743 |
+
$this->parseVar($array[2]);
|
| 744 |
+
$name .= $array[1] . $array[2];
|
| 745 |
+
}
|
| 746 |
+
switch ($first) {
|
| 747 |
+
case '?':
|
| 748 |
+
$str = '<?php echo (' . $name . ') ? ' . $name . ' : ' . substr($str, 1) . '; ?>';
|
| 749 |
+
break;
|
| 750 |
+
case '=':
|
| 751 |
+
$str = '<?php if(' . $name . ') echo ' . substr($str, 1) . '; ?>';
|
| 752 |
+
break;
|
| 753 |
+
default:
|
| 754 |
+
$str = '<?php echo ' . $name . '?' . $str . '; ?>';
|
| 755 |
+
}
|
| 756 |
+
} else {
|
| 757 |
+
if (isset($array[1])) {
|
| 758 |
+
$this->parseVar($array[2]);
|
| 759 |
+
$express = $name . $array[1] . $array[2];
|
| 760 |
+
} else {
|
| 761 |
+
$express = false;
|
| 762 |
+
}
|
| 763 |
+
// $name为数组
|
| 764 |
+
switch ($first) {
|
| 765 |
+
case '?':
|
| 766 |
+
// {$varname??'xxx'} $varname有定义则输出$varname,否则输出xxx
|
| 767 |
+
$str = '<?php echo ' . ($express ?: 'isset(' . $name . ')') . '?' . $name . ':' . substr($str, 1) . '; ?>';
|
| 768 |
+
break;
|
| 769 |
+
case '=':
|
| 770 |
+
// {$varname?='xxx'} $varname为真时才输出xxx
|
| 771 |
+
$str = '<?php if(' . ($express ?: '!empty(' . $name . ')') . ') echo ' . substr($str, 1) . '; ?>';
|
| 772 |
+
break;
|
| 773 |
+
case ':':
|
| 774 |
+
// {$varname?:'xxx'} $varname为真时输出$varname,否则输出xxx
|
| 775 |
+
$str = '<?php echo ' . ($express ?: '!empty(' . $name . ')') . '?' . $name . $str . '; ?>';
|
| 776 |
+
break;
|
| 777 |
+
default:
|
| 778 |
+
$str = '<?php echo ' . ($express ?: '!empty(' . $name . ')') . '?' . $str . '; ?>';
|
| 779 |
+
}
|
| 780 |
+
}
|
| 781 |
+
} else {
|
| 782 |
+
$this->parseVar($str);
|
| 783 |
+
$this->parseVarFunction($str);
|
| 784 |
+
$str = '<?php echo ' . $str . '; ?>';
|
| 785 |
+
}
|
| 786 |
+
break;
|
| 787 |
+
case ':':
|
| 788 |
+
// 输出某个函数的结果
|
| 789 |
+
$str = substr($str, 1);
|
| 790 |
+
$this->parseVar($str);
|
| 791 |
+
$str = '<?php echo ' . $str . '; ?>';
|
| 792 |
+
break;
|
| 793 |
+
case '~':
|
| 794 |
+
// 执行某个函数
|
| 795 |
+
$str = substr($str, 1);
|
| 796 |
+
$this->parseVar($str);
|
| 797 |
+
$str = '<?php ' . $str . '; ?>';
|
| 798 |
+
break;
|
| 799 |
+
case '-':
|
| 800 |
+
case '+':
|
| 801 |
+
// 输出计算
|
| 802 |
+
$this->parseVar($str);
|
| 803 |
+
$str = '<?php echo ' . $str . '; ?>';
|
| 804 |
+
break;
|
| 805 |
+
case '/':
|
| 806 |
+
// 注释标签
|
| 807 |
+
$flag2 = substr($str, 1, 1);
|
| 808 |
+
if ('/' == $flag2 || ('*' == $flag2 && substr(rtrim($str), -2) == '*/')) {
|
| 809 |
+
$str = '';
|
| 810 |
+
}
|
| 811 |
+
break;
|
| 812 |
+
default:
|
| 813 |
+
// 未识别的标签直接返回
|
| 814 |
+
$str = $this->config['tpl_begin'] . $str . $this->config['tpl_end'];
|
| 815 |
+
break;
|
| 816 |
+
}
|
| 817 |
+
$content = str_replace($match[0], $str, $content);
|
| 818 |
+
}
|
| 819 |
+
unset($matches);
|
| 820 |
+
}
|
| 821 |
+
return;
|
| 822 |
+
}
|
| 823 |
+
|
| 824 |
+
/**
|
| 825 |
+
* 模板变量解析,支持使用函数
|
| 826 |
+
* 格式: {$varname|function1|function2=arg1,arg2}
|
| 827 |
+
* @access public
|
| 828 |
+
* @param string $varStr 变量数据
|
| 829 |
+
* @return void
|
| 830 |
+
*/
|
| 831 |
+
public function parseVar(&$varStr)
|
| 832 |
+
{
|
| 833 |
+
$varStr = trim($varStr);
|
| 834 |
+
if (preg_match_all('/\$[a-zA-Z_](?>\w*)(?:[:\.][0-9a-zA-Z_](?>\w*))+/', $varStr, $matches, PREG_OFFSET_CAPTURE)) {
|
| 835 |
+
static $_varParseList = [];
|
| 836 |
+
while ($matches[0]) {
|
| 837 |
+
$match = array_pop($matches[0]);
|
| 838 |
+
//如果已经解析过该变量字串,则直接返回变量值
|
| 839 |
+
if (isset($_varParseList[$match[0]])) {
|
| 840 |
+
$parseStr = $_varParseList[$match[0]];
|
| 841 |
+
} else {
|
| 842 |
+
if (strpos($match[0], '.')) {
|
| 843 |
+
$vars = explode('.', $match[0]);
|
| 844 |
+
$first = array_shift($vars);
|
| 845 |
+
if ('$Think' == $first) {
|
| 846 |
+
// 所有以Think.打头的以特殊变量对待 无需模板赋值就可以输出
|
| 847 |
+
$parseStr = $this->parseThinkVar($vars);
|
| 848 |
+
} elseif ('$Request' == $first) {
|
| 849 |
+
// 获取Request请求对象参数
|
| 850 |
+
$method = array_shift($vars);
|
| 851 |
+
if (!empty($vars)) {
|
| 852 |
+
$params = implode('.', $vars);
|
| 853 |
+
if ('true' != $params) {
|
| 854 |
+
$params = '\'' . $params . '\'';
|
| 855 |
+
}
|
| 856 |
+
} else {
|
| 857 |
+
$params = '';
|
| 858 |
+
}
|
| 859 |
+
$parseStr = '\think\Request::instance()->' . $method . '(' . $params . ')';
|
| 860 |
+
} else {
|
| 861 |
+
switch ($this->config['tpl_var_identify']) {
|
| 862 |
+
case 'array': // 识别为数组
|
| 863 |
+
$parseStr = $first . '[\'' . implode('\'][\'', $vars) . '\']';
|
| 864 |
+
break;
|
| 865 |
+
case 'obj': // 识别为对象
|
| 866 |
+
$parseStr = $first . '->' . implode('->', $vars);
|
| 867 |
+
break;
|
| 868 |
+
default: // 自动判断数组或对象
|
| 869 |
+
$parseStr = '(is_array(' . $first . ')?' . $first . '[\'' . implode('\'][\'', $vars) . '\']:' . $first . '->' . implode('->', $vars) . ')';
|
| 870 |
+
}
|
| 871 |
+
}
|
| 872 |
+
} else {
|
| 873 |
+
$parseStr = str_replace(':', '->', $match[0]);
|
| 874 |
+
}
|
| 875 |
+
$_varParseList[$match[0]] = $parseStr;
|
| 876 |
+
}
|
| 877 |
+
$varStr = substr_replace($varStr, $parseStr, $match[1], strlen($match[0]));
|
| 878 |
+
}
|
| 879 |
+
unset($matches);
|
| 880 |
+
}
|
| 881 |
+
return;
|
| 882 |
+
}
|
| 883 |
+
|
| 884 |
+
/**
|
| 885 |
+
* 对模板中使用了函数的变量进行解析
|
| 886 |
+
* 格式 {$varname|function1|function2=arg1,arg2}
|
| 887 |
+
* @access public
|
| 888 |
+
* @param string $varStr 变量字符串
|
| 889 |
+
* @return void
|
| 890 |
+
*/
|
| 891 |
+
public function parseVarFunction(&$varStr)
|
| 892 |
+
{
|
| 893 |
+
if (false == strpos($varStr, '|')) {
|
| 894 |
+
return;
|
| 895 |
+
}
|
| 896 |
+
static $_varFunctionList = [];
|
| 897 |
+
$_key = md5($varStr);
|
| 898 |
+
//如果已经解析过该变量字串,则直接返回变量值
|
| 899 |
+
if (isset($_varFunctionList[$_key])) {
|
| 900 |
+
$varStr = $_varFunctionList[$_key];
|
| 901 |
+
} else {
|
| 902 |
+
$varArray = explode('|', $varStr);
|
| 903 |
+
// 取得变量名称
|
| 904 |
+
$name = array_shift($varArray);
|
| 905 |
+
// 对变量使用函数
|
| 906 |
+
$length = count($varArray);
|
| 907 |
+
// 取得模板禁止使用函数列表
|
| 908 |
+
$template_deny_funs = explode(',', $this->config['tpl_deny_func_list']);
|
| 909 |
+
for ($i = 0; $i < $length; $i++) {
|
| 910 |
+
$args = explode('=', $varArray[$i], 2);
|
| 911 |
+
// 模板函数过滤
|
| 912 |
+
$fun = trim($args[0]);
|
| 913 |
+
switch ($fun) {
|
| 914 |
+
case 'default': // 特殊模板函数
|
| 915 |
+
if (false === strpos($name, '(')) {
|
| 916 |
+
$name = '(isset(' . $name . ') && (' . $name . ' !== \'\')?' . $name . ':' . $args[1] . ')';
|
| 917 |
+
} else {
|
| 918 |
+
$name = '(' . $name . ' ?: ' . $args[1] . ')';
|
| 919 |
+
}
|
| 920 |
+
break;
|
| 921 |
+
default: // 通用模板函数
|
| 922 |
+
if (!in_array($fun, $template_deny_funs)) {
|
| 923 |
+
if (isset($args[1])) {
|
| 924 |
+
if (strstr($args[1], '###')) {
|
| 925 |
+
$args[1] = str_replace('###', $name, $args[1]);
|
| 926 |
+
$name = "$fun($args[1])";
|
| 927 |
+
} else {
|
| 928 |
+
$name = "$fun($name,$args[1])";
|
| 929 |
+
}
|
| 930 |
+
} else {
|
| 931 |
+
if (!empty($args[0])) {
|
| 932 |
+
$name = "$fun($name)";
|
| 933 |
+
}
|
| 934 |
+
}
|
| 935 |
+
}
|
| 936 |
+
}
|
| 937 |
+
}
|
| 938 |
+
$_varFunctionList[$_key] = $name;
|
| 939 |
+
$varStr = $name;
|
| 940 |
+
}
|
| 941 |
+
return;
|
| 942 |
+
}
|
| 943 |
+
|
| 944 |
+
/**
|
| 945 |
+
* 特殊模板变量解析
|
| 946 |
+
* 格式 以 $Think. 打头的变量属于特殊模板变量
|
| 947 |
+
* @access public
|
| 948 |
+
* @param array $vars 变量数组
|
| 949 |
+
* @return string
|
| 950 |
+
*/
|
| 951 |
+
public function parseThinkVar($vars)
|
| 952 |
+
{
|
| 953 |
+
$type = strtoupper(trim(array_shift($vars)));
|
| 954 |
+
$param = implode('.', $vars);
|
| 955 |
+
if ($vars) {
|
| 956 |
+
switch ($type) {
|
| 957 |
+
case 'SERVER':
|
| 958 |
+
$parseStr = '\\think\\Request::instance()->server(\'' . $param . '\')';
|
| 959 |
+
break;
|
| 960 |
+
case 'GET':
|
| 961 |
+
$parseStr = '\\think\\Request::instance()->get(\'' . $param . '\')';
|
| 962 |
+
break;
|
| 963 |
+
case 'POST':
|
| 964 |
+
$parseStr = '\\think\\Request::instance()->post(\'' . $param . '\')';
|
| 965 |
+
break;
|
| 966 |
+
case 'COOKIE':
|
| 967 |
+
$parseStr = '\\think\\Cookie::get(\'' . $param . '\')';
|
| 968 |
+
break;
|
| 969 |
+
case 'SESSION':
|
| 970 |
+
$parseStr = '\\think\\Session::get(\'' . $param . '\')';
|
| 971 |
+
break;
|
| 972 |
+
case 'ENV':
|
| 973 |
+
$parseStr = '\\think\\Request::instance()->env(\'' . $param . '\')';
|
| 974 |
+
break;
|
| 975 |
+
case 'REQUEST':
|
| 976 |
+
$parseStr = '\\think\\Request::instance()->request(\'' . $param . '\')';
|
| 977 |
+
break;
|
| 978 |
+
case 'CONST':
|
| 979 |
+
$parseStr = strtoupper($param);
|
| 980 |
+
break;
|
| 981 |
+
case 'LANG':
|
| 982 |
+
$parseStr = '\\think\\Lang::get(\'' . $param . '\')';
|
| 983 |
+
break;
|
| 984 |
+
case 'CONFIG':
|
| 985 |
+
$parseStr = '\\think\\Config::get(\'' . $param . '\')';
|
| 986 |
+
break;
|
| 987 |
+
default:
|
| 988 |
+
$parseStr = '\'\'';
|
| 989 |
+
break;
|
| 990 |
+
}
|
| 991 |
+
} else {
|
| 992 |
+
switch ($type) {
|
| 993 |
+
case 'NOW':
|
| 994 |
+
$parseStr = "date('Y-m-d g:i a',time())";
|
| 995 |
+
break;
|
| 996 |
+
case 'VERSION':
|
| 997 |
+
$parseStr = 'THINK_VERSION';
|
| 998 |
+
break;
|
| 999 |
+
case 'LDELIM':
|
| 1000 |
+
$parseStr = '\'' . ltrim($this->config['tpl_begin'], '\\') . '\'';
|
| 1001 |
+
break;
|
| 1002 |
+
case 'RDELIM':
|
| 1003 |
+
$parseStr = '\'' . ltrim($this->config['tpl_end'], '\\') . '\'';
|
| 1004 |
+
break;
|
| 1005 |
+
default:
|
| 1006 |
+
if (defined($type)) {
|
| 1007 |
+
$parseStr = $type;
|
| 1008 |
+
} else {
|
| 1009 |
+
$parseStr = '';
|
| 1010 |
+
}
|
| 1011 |
+
}
|
| 1012 |
+
}
|
| 1013 |
+
return $parseStr;
|
| 1014 |
+
}
|
| 1015 |
+
|
| 1016 |
+
/**
|
| 1017 |
+
* 分析加载的模板文件并读取内容 支持多个模板文件读取
|
| 1018 |
+
* @access private
|
| 1019 |
+
* @param string $templateName 模板文件名
|
| 1020 |
+
* @return string
|
| 1021 |
+
*/
|
| 1022 |
+
private function parseTemplateName($templateName)
|
| 1023 |
+
{
|
| 1024 |
+
$array = explode(',', $templateName);
|
| 1025 |
+
$parseStr = '';
|
| 1026 |
+
foreach ($array as $templateName) {
|
| 1027 |
+
if (empty($templateName)) {
|
| 1028 |
+
continue;
|
| 1029 |
+
}
|
| 1030 |
+
if (0 === strpos($templateName, '$')) {
|
| 1031 |
+
//支持加载变量文件名
|
| 1032 |
+
$templateName = $this->get(substr($templateName, 1));
|
| 1033 |
+
}
|
| 1034 |
+
$template = $this->parseTemplateFile($templateName);
|
| 1035 |
+
if ($template) {
|
| 1036 |
+
// 获取模板文件内容
|
| 1037 |
+
$parseStr .= file_get_contents($template);
|
| 1038 |
+
}
|
| 1039 |
+
}
|
| 1040 |
+
return $parseStr;
|
| 1041 |
+
}
|
| 1042 |
+
|
| 1043 |
+
/**
|
| 1044 |
+
* 解析模板文件名
|
| 1045 |
+
* @access private
|
| 1046 |
+
* @param string $template 文件名
|
| 1047 |
+
* @return string|false
|
| 1048 |
+
*/
|
| 1049 |
+
private function parseTemplateFile($template)
|
| 1050 |
+
{
|
| 1051 |
+
if ('' == pathinfo($template, PATHINFO_EXTENSION)) {
|
| 1052 |
+
if (strpos($template, '@')) {
|
| 1053 |
+
list($module, $template) = explode('@', $template);
|
| 1054 |
+
}
|
| 1055 |
+
if (0 !== strpos($template, '/')) {
|
| 1056 |
+
$template = str_replace(['/', ':'], $this->config['view_depr'], $template);
|
| 1057 |
+
} else {
|
| 1058 |
+
$template = str_replace(['/', ':'], $this->config['view_depr'], substr($template, 1));
|
| 1059 |
+
}
|
| 1060 |
+
if ($this->config['view_base']) {
|
| 1061 |
+
$module = isset($module) ? $module : Request::instance()->module();
|
| 1062 |
+
$path = $this->config['view_base'] . ($module ? $module . DS : '');
|
| 1063 |
+
} else {
|
| 1064 |
+
$path = isset($module) ? APP_PATH . $module . DS . basename($this->config['view_path']) . DS : $this->config['view_path'];
|
| 1065 |
+
}
|
| 1066 |
+
$template = realpath($path . $template . '.' . ltrim($this->config['view_suffix'], '.'));
|
| 1067 |
+
}
|
| 1068 |
+
|
| 1069 |
+
if (is_file($template)) {
|
| 1070 |
+
// 记录模板文件的更新时间
|
| 1071 |
+
$this->includeFile[$template] = filemtime($template);
|
| 1072 |
+
return $template;
|
| 1073 |
+
} else {
|
| 1074 |
+
throw new TemplateNotFoundException('template not exists:' . $template, $template);
|
| 1075 |
+
}
|
| 1076 |
+
}
|
| 1077 |
+
|
| 1078 |
+
/**
|
| 1079 |
+
* 按标签生成正则
|
| 1080 |
+
* @access private
|
| 1081 |
+
* @param string $tagName 标签名
|
| 1082 |
+
* @return string
|
| 1083 |
+
*/
|
| 1084 |
+
private function getRegex($tagName)
|
| 1085 |
+
{
|
| 1086 |
+
$regex = '';
|
| 1087 |
+
if ('tag' == $tagName) {
|
| 1088 |
+
$begin = $this->config['tpl_begin'];
|
| 1089 |
+
$end = $this->config['tpl_end'];
|
| 1090 |
+
if (strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1) {
|
| 1091 |
+
$regex = $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>[^' . $end . ']*))' . $end;
|
| 1092 |
+
} else {
|
| 1093 |
+
$regex = $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>(?:(?!' . $end . ').)*))' . $end;
|
| 1094 |
+
}
|
| 1095 |
+
} else {
|
| 1096 |
+
$begin = $this->config['taglib_begin'];
|
| 1097 |
+
$end = $this->config['taglib_end'];
|
| 1098 |
+
$single = strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1 ? true : false;
|
| 1099 |
+
switch ($tagName) {
|
| 1100 |
+
case 'block':
|
| 1101 |
+
if ($single) {
|
| 1102 |
+
$regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?P<name>[\$\w\-\/\.]+)\\1(?>[^' . $end . ']*)|\/' . $tagName . ')' . $end;
|
| 1103 |
+
} else {
|
| 1104 |
+
$regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?P<name>[\$\w\-\/\.]+)\\1(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end;
|
| 1105 |
+
}
|
| 1106 |
+
break;
|
| 1107 |
+
case 'literal':
|
| 1108 |
+
if ($single) {
|
| 1109 |
+
$regex = '(' . $begin . $tagName . '\b(?>[^' . $end . ']*)' . $end . ')';
|
| 1110 |
+
$regex .= '(?:(?>[^' . $begin . ']*)(?>(?!' . $begin . '(?>' . $tagName . '\b[^' . $end . ']*|\/' . $tagName . ')' . $end . ')' . $begin . '[^' . $begin . ']*)*)';
|
| 1111 |
+
$regex .= '(' . $begin . '\/' . $tagName . $end . ')';
|
| 1112 |
+
} else {
|
| 1113 |
+
$regex = '(' . $begin . $tagName . '\b(?>(?:(?!' . $end . ').)*)' . $end . ')';
|
| 1114 |
+
$regex .= '(?:(?>(?:(?!' . $begin . ').)*)(?>(?!' . $begin . '(?>' . $tagName . '\b(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end . ')' . $begin . '(?>(?:(?!' . $begin . ').)*))*)';
|
| 1115 |
+
$regex .= '(' . $begin . '\/' . $tagName . $end . ')';
|
| 1116 |
+
}
|
| 1117 |
+
break;
|
| 1118 |
+
case 'restoreliteral':
|
| 1119 |
+
$regex = '<!--###literal(\d+)###-->';
|
| 1120 |
+
break;
|
| 1121 |
+
case 'include':
|
| 1122 |
+
$name = 'file';
|
| 1123 |
+
case 'taglib':
|
| 1124 |
+
case 'layout':
|
| 1125 |
+
case 'extend':
|
| 1126 |
+
if (empty($name)) {
|
| 1127 |
+
$name = 'name';
|
| 1128 |
+
}
|
| 1129 |
+
if ($single) {
|
| 1130 |
+
$regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P<name>[\$\w\-\/\.\:@,\\\\]+)\\1(?>[^' . $end . ']*)' . $end;
|
| 1131 |
+
} else {
|
| 1132 |
+
$regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P<name>[\$\w\-\/\.\:@,\\\\]+)\\1(?>(?:(?!' . $end . ').)*)' . $end;
|
| 1133 |
+
}
|
| 1134 |
+
break;
|
| 1135 |
+
}
|
| 1136 |
+
}
|
| 1137 |
+
return '/' . $regex . '/is';
|
| 1138 |
+
}
|
| 1139 |
+
}
|
thinkphp/library/think/Url.php
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
class Url
|
| 15 |
+
{
|
| 16 |
+
// 生成URL地址的root
|
| 17 |
+
protected static $root;
|
| 18 |
+
protected static $bindCheck;
|
| 19 |
+
|
| 20 |
+
/**
|
| 21 |
+
* URL生成 支持路由反射
|
| 22 |
+
* @param string $url 路由地址
|
| 23 |
+
* @param string|array $vars 参数(支持数组和字符串)a=val&b=val2... ['a'=>'val1', 'b'=>'val2']
|
| 24 |
+
* @param string|bool $suffix 伪静态后缀,默认为true表示获取配置值
|
| 25 |
+
* @param boolean|string $domain 是否显示域名 或者直接传入域名
|
| 26 |
+
* @return string
|
| 27 |
+
*/
|
| 28 |
+
public static function build($url = '', $vars = '', $suffix = true, $domain = false)
|
| 29 |
+
{
|
| 30 |
+
if (false === $domain && Route::rules('domain')) {
|
| 31 |
+
$domain = true;
|
| 32 |
+
}
|
| 33 |
+
// 解析URL
|
| 34 |
+
if (0 === strpos($url, '[') && $pos = strpos($url, ']')) {
|
| 35 |
+
// [name] 表示使用路由命名标识生成URL
|
| 36 |
+
$name = substr($url, 1, $pos - 1);
|
| 37 |
+
$url = 'name' . substr($url, $pos + 1);
|
| 38 |
+
}
|
| 39 |
+
if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
|
| 40 |
+
$info = parse_url($url);
|
| 41 |
+
$url = !empty($info['path']) ? $info['path'] : '';
|
| 42 |
+
if (isset($info['fragment'])) {
|
| 43 |
+
// 解析锚点
|
| 44 |
+
$anchor = $info['fragment'];
|
| 45 |
+
if (false !== strpos($anchor, '?')) {
|
| 46 |
+
// 解析参数
|
| 47 |
+
list($anchor, $info['query']) = explode('?', $anchor, 2);
|
| 48 |
+
}
|
| 49 |
+
if (false !== strpos($anchor, '@')) {
|
| 50 |
+
// 解析域名
|
| 51 |
+
list($anchor, $domain) = explode('@', $anchor, 2);
|
| 52 |
+
}
|
| 53 |
+
} elseif (strpos($url, '@') && false === strpos($url, '\\')) {
|
| 54 |
+
// 解析域名
|
| 55 |
+
list($url, $domain) = explode('@', $url, 2);
|
| 56 |
+
}
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
// 解析参数
|
| 60 |
+
if (is_string($vars)) {
|
| 61 |
+
// aaa=1&bbb=2 转换成数组
|
| 62 |
+
parse_str($vars, $vars);
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
if ($url) {
|
| 66 |
+
$rule = Route::name(isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : ''));
|
| 67 |
+
if (is_null($rule) && isset($info['query'])) {
|
| 68 |
+
$rule = Route::name($url);
|
| 69 |
+
// 解析地址里面参数 合并到vars
|
| 70 |
+
parse_str($info['query'], $params);
|
| 71 |
+
$vars = array_merge($params, $vars);
|
| 72 |
+
unset($info['query']);
|
| 73 |
+
}
|
| 74 |
+
}
|
| 75 |
+
if (!empty($rule) && $match = self::getRuleUrl($rule, $vars)) {
|
| 76 |
+
// 匹配路由命名标识
|
| 77 |
+
$url = $match[0];
|
| 78 |
+
// 替换可选分隔符
|
| 79 |
+
$url = preg_replace(['/(\W)\?$/', '/(\W)\?/'], ['', '\1'], $url);
|
| 80 |
+
if (!empty($match[1])) {
|
| 81 |
+
$domain = $match[1];
|
| 82 |
+
}
|
| 83 |
+
if (!is_null($match[2])) {
|
| 84 |
+
$suffix = $match[2];
|
| 85 |
+
}
|
| 86 |
+
} elseif (!empty($rule) && isset($name)) {
|
| 87 |
+
throw new \InvalidArgumentException('route name not exists:' . $name);
|
| 88 |
+
} else {
|
| 89 |
+
// 检查别名路由
|
| 90 |
+
$alias = Route::rules('alias');
|
| 91 |
+
$matchAlias = false;
|
| 92 |
+
if ($alias) {
|
| 93 |
+
// 别名路由解析
|
| 94 |
+
foreach ($alias as $key => $val) {
|
| 95 |
+
if (is_array($val)) {
|
| 96 |
+
$val = $val[0];
|
| 97 |
+
}
|
| 98 |
+
if (0 === strpos($url, $val)) {
|
| 99 |
+
$url = $key . substr($url, strlen($val));
|
| 100 |
+
$matchAlias = true;
|
| 101 |
+
break;
|
| 102 |
+
}
|
| 103 |
+
}
|
| 104 |
+
}
|
| 105 |
+
if (!$matchAlias) {
|
| 106 |
+
// 路由标识不存在 直接解析
|
| 107 |
+
$url = self::parseUrl($url, $domain);
|
| 108 |
+
}
|
| 109 |
+
if (isset($info['query'])) {
|
| 110 |
+
// 解析地址里面参数 合并到vars
|
| 111 |
+
parse_str($info['query'], $params);
|
| 112 |
+
$vars = array_merge($params, $vars);
|
| 113 |
+
}
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
// 检测URL绑定
|
| 117 |
+
if (!self::$bindCheck) {
|
| 118 |
+
$type = Route::getBind('type');
|
| 119 |
+
if ($type) {
|
| 120 |
+
$bind = Route::getBind($type);
|
| 121 |
+
if ($bind && 0 === strpos($url, $bind)) {
|
| 122 |
+
$url = substr($url, strlen($bind) + 1);
|
| 123 |
+
}
|
| 124 |
+
}
|
| 125 |
+
}
|
| 126 |
+
// 还原URL分隔符
|
| 127 |
+
$depr = Config::get('pathinfo_depr');
|
| 128 |
+
$url = str_replace('/', $depr, $url);
|
| 129 |
+
|
| 130 |
+
// URL后缀
|
| 131 |
+
$suffix = in_array($url, ['/', '']) ? '' : self::parseSuffix($suffix);
|
| 132 |
+
// 锚点
|
| 133 |
+
$anchor = !empty($anchor) ? '#' . $anchor : '';
|
| 134 |
+
// 参数组装
|
| 135 |
+
if (!empty($vars)) {
|
| 136 |
+
// 添加参数
|
| 137 |
+
if (Config::get('url_common_param')) {
|
| 138 |
+
$vars = http_build_query($vars);
|
| 139 |
+
$url .= $suffix . '?' . $vars . $anchor;
|
| 140 |
+
} else {
|
| 141 |
+
$paramType = Config::get('url_param_type');
|
| 142 |
+
foreach ($vars as $var => $val) {
|
| 143 |
+
if ('' !== trim($val)) {
|
| 144 |
+
if ($paramType) {
|
| 145 |
+
$url .= $depr . urlencode($val);
|
| 146 |
+
} else {
|
| 147 |
+
$url .= $depr . $var . $depr . urlencode($val);
|
| 148 |
+
}
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
$url .= $suffix . $anchor;
|
| 152 |
+
}
|
| 153 |
+
} else {
|
| 154 |
+
$url .= $suffix . $anchor;
|
| 155 |
+
}
|
| 156 |
+
// 检测域名
|
| 157 |
+
$domain = self::parseDomain($url, $domain);
|
| 158 |
+
// URL组装
|
| 159 |
+
$url = $domain . rtrim(self::$root ?: Request::instance()->root(), '/') . '/' . ltrim($url, '/');
|
| 160 |
+
|
| 161 |
+
self::$bindCheck = false;
|
| 162 |
+
return $url;
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
// 直接解析URL地址
|
| 166 |
+
protected static function parseUrl($url, &$domain)
|
| 167 |
+
{
|
| 168 |
+
$request = Request::instance();
|
| 169 |
+
if (0 === strpos($url, '/')) {
|
| 170 |
+
// 直接作为路由地址解析
|
| 171 |
+
$url = substr($url, 1);
|
| 172 |
+
} elseif (false !== strpos($url, '\\')) {
|
| 173 |
+
// 解析到类
|
| 174 |
+
$url = ltrim(str_replace('\\', '/', $url), '/');
|
| 175 |
+
} elseif (0 === strpos($url, '@')) {
|
| 176 |
+
// 解析到控制器
|
| 177 |
+
$url = substr($url, 1);
|
| 178 |
+
} else {
|
| 179 |
+
// 解析到 模块/控制器/操作
|
| 180 |
+
$module = $request->module();
|
| 181 |
+
$domains = Route::rules('domain');
|
| 182 |
+
if (true === $domain && 2 == substr_count($url, '/')) {
|
| 183 |
+
$current = $request->host();
|
| 184 |
+
$match = [];
|
| 185 |
+
$pos = [];
|
| 186 |
+
foreach ($domains as $key => $item) {
|
| 187 |
+
if (isset($item['[bind]']) && 0 === strpos($url, $item['[bind]'][0])) {
|
| 188 |
+
$pos[$key] = strlen($item['[bind]'][0]) + 1;
|
| 189 |
+
$match[] = $key;
|
| 190 |
+
$module = '';
|
| 191 |
+
}
|
| 192 |
+
}
|
| 193 |
+
if ($match) {
|
| 194 |
+
$domain = current($match);
|
| 195 |
+
foreach ($match as $item) {
|
| 196 |
+
if (0 === strpos($current, $item)) {
|
| 197 |
+
$domain = $item;
|
| 198 |
+
}
|
| 199 |
+
}
|
| 200 |
+
self::$bindCheck = true;
|
| 201 |
+
$url = substr($url, $pos[$domain]);
|
| 202 |
+
}
|
| 203 |
+
} elseif ($domain) {
|
| 204 |
+
if (isset($domains[$domain]['[bind]'][0])) {
|
| 205 |
+
$bindModule = $domains[$domain]['[bind]'][0];
|
| 206 |
+
if ($bindModule && !in_array($bindModule[0], ['\\', '@'])) {
|
| 207 |
+
$module = '';
|
| 208 |
+
}
|
| 209 |
+
}
|
| 210 |
+
}
|
| 211 |
+
$module = $module ? $module . '/' : '';
|
| 212 |
+
|
| 213 |
+
$controller = $request->controller();
|
| 214 |
+
if ('' == $url) {
|
| 215 |
+
// 空字符串输出当前的 模块/控制器/操作
|
| 216 |
+
$action = $request->action();
|
| 217 |
+
} else {
|
| 218 |
+
$path = explode('/', $url);
|
| 219 |
+
$action = array_pop($path);
|
| 220 |
+
$controller = empty($path) ? $controller : array_pop($path);
|
| 221 |
+
$module = empty($path) ? $module : array_pop($path) . '/';
|
| 222 |
+
}
|
| 223 |
+
if (Config::get('url_convert')) {
|
| 224 |
+
$action = strtolower($action);
|
| 225 |
+
$controller = Loader::parseName($controller);
|
| 226 |
+
}
|
| 227 |
+
$url = $module . $controller . '/' . $action;
|
| 228 |
+
}
|
| 229 |
+
return $url;
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
// 检测域名
|
| 233 |
+
protected static function parseDomain(&$url, $domain)
|
| 234 |
+
{
|
| 235 |
+
if (!$domain) {
|
| 236 |
+
return '';
|
| 237 |
+
}
|
| 238 |
+
$request = Request::instance();
|
| 239 |
+
$rootDomain = Config::get('url_domain_root');
|
| 240 |
+
if (true === $domain) {
|
| 241 |
+
// 自动判断域名
|
| 242 |
+
$domain = Config::get('app_host') ?: $request->host();
|
| 243 |
+
|
| 244 |
+
$domains = Route::rules('domain');
|
| 245 |
+
if ($domains) {
|
| 246 |
+
$route_domain = array_keys($domains);
|
| 247 |
+
foreach ($route_domain as $domain_prefix) {
|
| 248 |
+
if (0 === strpos($domain_prefix, '*.') && strpos($domain, ltrim($domain_prefix, '*.')) !== false) {
|
| 249 |
+
foreach ($domains as $key => $rule) {
|
| 250 |
+
$rule = is_array($rule) ? $rule[0] : $rule;
|
| 251 |
+
if (is_string($rule) && false === strpos($key, '*') && 0 === strpos($url, $rule)) {
|
| 252 |
+
$url = ltrim($url, $rule);
|
| 253 |
+
$domain = $key;
|
| 254 |
+
// 生成对应子域名
|
| 255 |
+
if (!empty($rootDomain)) {
|
| 256 |
+
$domain .= $rootDomain;
|
| 257 |
+
}
|
| 258 |
+
break;
|
| 259 |
+
} elseif (false !== strpos($key, '*')) {
|
| 260 |
+
if (!empty($rootDomain)) {
|
| 261 |
+
$domain .= $rootDomain;
|
| 262 |
+
}
|
| 263 |
+
break;
|
| 264 |
+
}
|
| 265 |
+
}
|
| 266 |
+
}
|
| 267 |
+
}
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
} else {
|
| 271 |
+
if (empty($rootDomain)) {
|
| 272 |
+
$host = Config::get('app_host') ?: $request->host();
|
| 273 |
+
$rootDomain = substr_count($host, '.') > 1 ? substr(strstr($host, '.'), 1) : $host;
|
| 274 |
+
}
|
| 275 |
+
if (substr_count($domain, '.') < 2 && !strpos($domain, $rootDomain)) {
|
| 276 |
+
$domain .= '.' . $rootDomain;
|
| 277 |
+
}
|
| 278 |
+
}
|
| 279 |
+
if (false !== strpos($domain, '://')) {
|
| 280 |
+
$scheme = '';
|
| 281 |
+
} else {
|
| 282 |
+
$scheme = $request->isSsl() || Config::get('is_https') ? 'https://' : 'http://';
|
| 283 |
+
}
|
| 284 |
+
return $scheme . $domain;
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
// 解析URL后缀
|
| 288 |
+
protected static function parseSuffix($suffix)
|
| 289 |
+
{
|
| 290 |
+
if ($suffix) {
|
| 291 |
+
$suffix = true === $suffix ? Config::get('url_html_suffix') : $suffix;
|
| 292 |
+
if ($pos = strpos($suffix, '|')) {
|
| 293 |
+
$suffix = substr($suffix, 0, $pos);
|
| 294 |
+
}
|
| 295 |
+
}
|
| 296 |
+
return (empty($suffix) || 0 === strpos($suffix, '.')) ? $suffix : '.' . $suffix;
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
// 匹配路由地址
|
| 300 |
+
public static function getRuleUrl($rule, &$vars = [])
|
| 301 |
+
{
|
| 302 |
+
foreach ($rule as $item) {
|
| 303 |
+
list($url, $pattern, $domain, $suffix) = $item;
|
| 304 |
+
if (empty($pattern)) {
|
| 305 |
+
return [rtrim($url, '$'), $domain, $suffix];
|
| 306 |
+
}
|
| 307 |
+
$type = Config::get('url_common_param');
|
| 308 |
+
foreach ($pattern as $key => $val) {
|
| 309 |
+
if (isset($vars[$key])) {
|
| 310 |
+
$url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key . '', '<' . $key . '>'], $type ? $vars[$key] : urlencode($vars[$key]), $url);
|
| 311 |
+
unset($vars[$key]);
|
| 312 |
+
$result = [$url, $domain, $suffix];
|
| 313 |
+
} elseif (2 == $val) {
|
| 314 |
+
$url = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url);
|
| 315 |
+
$result = [$url, $domain, $suffix];
|
| 316 |
+
} else {
|
| 317 |
+
break;
|
| 318 |
+
}
|
| 319 |
+
}
|
| 320 |
+
if (isset($result)) {
|
| 321 |
+
return $result;
|
| 322 |
+
}
|
| 323 |
+
}
|
| 324 |
+
return false;
|
| 325 |
+
}
|
| 326 |
+
|
| 327 |
+
// 指定当前生成URL地址的root
|
| 328 |
+
public static function root($root)
|
| 329 |
+
{
|
| 330 |
+
self::$root = $root;
|
| 331 |
+
Request::instance()->root($root);
|
| 332 |
+
}
|
| 333 |
+
}
|
thinkphp/library/think/Validate.php
ADDED
|
@@ -0,0 +1,1371 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
use think\exception\ClassNotFoundException;
|
| 15 |
+
|
| 16 |
+
class Validate
|
| 17 |
+
{
|
| 18 |
+
// 实例
|
| 19 |
+
protected static $instance;
|
| 20 |
+
|
| 21 |
+
// 自定义的验证类型
|
| 22 |
+
protected static $type = [];
|
| 23 |
+
|
| 24 |
+
// 验证类型别名
|
| 25 |
+
protected $alias = [
|
| 26 |
+
'>' => 'gt', '>=' => 'egt', '<' => 'lt', '<=' => 'elt', '=' => 'eq', 'same' => 'eq',
|
| 27 |
+
];
|
| 28 |
+
|
| 29 |
+
// 当前验证的规则
|
| 30 |
+
protected $rule = [];
|
| 31 |
+
|
| 32 |
+
// 验证提示信息
|
| 33 |
+
protected $message = [];
|
| 34 |
+
// 验证字段描述
|
| 35 |
+
protected $field = [];
|
| 36 |
+
|
| 37 |
+
// 验证规则默认提示信息
|
| 38 |
+
protected static $typeMsg = [
|
| 39 |
+
'require' => ':attribute require',
|
| 40 |
+
'number' => ':attribute must be numeric',
|
| 41 |
+
'integer' => ':attribute must be integer',
|
| 42 |
+
'float' => ':attribute must be float',
|
| 43 |
+
'boolean' => ':attribute must be bool',
|
| 44 |
+
'email' => ':attribute not a valid email address',
|
| 45 |
+
'array' => ':attribute must be a array',
|
| 46 |
+
'accepted' => ':attribute must be yes,on or 1',
|
| 47 |
+
'date' => ':attribute not a valid datetime',
|
| 48 |
+
'file' => ':attribute not a valid file',
|
| 49 |
+
'image' => ':attribute not a valid image',
|
| 50 |
+
'alpha' => ':attribute must be alpha',
|
| 51 |
+
'alphaNum' => ':attribute must be alpha-numeric',
|
| 52 |
+
'alphaDash' => ':attribute must be alpha-numeric, dash, underscore',
|
| 53 |
+
'activeUrl' => ':attribute not a valid domain or ip',
|
| 54 |
+
'chs' => ':attribute must be chinese',
|
| 55 |
+
'chsAlpha' => ':attribute must be chinese or alpha',
|
| 56 |
+
'chsAlphaNum' => ':attribute must be chinese,alpha-numeric',
|
| 57 |
+
'chsDash' => ':attribute must be chinese,alpha-numeric,underscore, dash',
|
| 58 |
+
'url' => ':attribute not a valid url',
|
| 59 |
+
'ip' => ':attribute not a valid ip',
|
| 60 |
+
'dateFormat' => ':attribute must be dateFormat of :rule',
|
| 61 |
+
'in' => ':attribute must be in :rule',
|
| 62 |
+
'notIn' => ':attribute be notin :rule',
|
| 63 |
+
'between' => ':attribute must between :1 - :2',
|
| 64 |
+
'notBetween' => ':attribute not between :1 - :2',
|
| 65 |
+
'length' => 'size of :attribute must be :rule',
|
| 66 |
+
'max' => 'max size of :attribute must be :rule',
|
| 67 |
+
'min' => 'min size of :attribute must be :rule',
|
| 68 |
+
'after' => ':attribute cannot be less than :rule',
|
| 69 |
+
'before' => ':attribute cannot exceed :rule',
|
| 70 |
+
'afterWith' => ':attribute cannot be less than :rule',
|
| 71 |
+
'beforeWith' => ':attribute cannot exceed :rule',
|
| 72 |
+
'expire' => ':attribute not within :rule',
|
| 73 |
+
'allowIp' => 'access IP is not allowed',
|
| 74 |
+
'denyIp' => 'access IP denied',
|
| 75 |
+
'confirm' => ':attribute out of accord with :2',
|
| 76 |
+
'different' => ':attribute cannot be same with :2',
|
| 77 |
+
'egt' => ':attribute must greater than or equal :rule',
|
| 78 |
+
'gt' => ':attribute must greater than :rule',
|
| 79 |
+
'elt' => ':attribute must less than or equal :rule',
|
| 80 |
+
'lt' => ':attribute must less than :rule',
|
| 81 |
+
'eq' => ':attribute must equal :rule',
|
| 82 |
+
'unique' => ':attribute has exists',
|
| 83 |
+
'regex' => ':attribute not conform to the rules',
|
| 84 |
+
'method' => 'invalid Request method',
|
| 85 |
+
'token' => 'invalid token',
|
| 86 |
+
'fileSize' => 'filesize not match',
|
| 87 |
+
'fileExt' => 'extensions to upload is not allowed',
|
| 88 |
+
'fileMime' => 'mimetype to upload is not allowed',
|
| 89 |
+
];
|
| 90 |
+
|
| 91 |
+
// 当前验证场景
|
| 92 |
+
protected $currentScene = null;
|
| 93 |
+
|
| 94 |
+
// 正则表达式 regex = ['zip'=>'\d{6}',...]
|
| 95 |
+
protected $regex = [];
|
| 96 |
+
|
| 97 |
+
// 验证场景 scene = ['edit'=>'name1,name2,...']
|
| 98 |
+
protected $scene = [];
|
| 99 |
+
|
| 100 |
+
// 验证失败错误信息
|
| 101 |
+
protected $error = [];
|
| 102 |
+
|
| 103 |
+
// 批量验证
|
| 104 |
+
protected $batch = false;
|
| 105 |
+
|
| 106 |
+
/**
|
| 107 |
+
* 构造函数
|
| 108 |
+
* @access public
|
| 109 |
+
* @param array $rules 验证规则
|
| 110 |
+
* @param array $message 验证提示信息
|
| 111 |
+
* @param array $field 验证字段描述信息
|
| 112 |
+
*/
|
| 113 |
+
public function __construct(array $rules = [], $message = [], $field = [])
|
| 114 |
+
{
|
| 115 |
+
$this->rule = array_merge($this->rule, $rules);
|
| 116 |
+
$this->message = array_merge($this->message, $message);
|
| 117 |
+
$this->field = array_merge($this->field, $field);
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
/**
|
| 121 |
+
* 实例化验证
|
| 122 |
+
* @access public
|
| 123 |
+
* @param array $rules 验证规则
|
| 124 |
+
* @param array $message 验证提示信息
|
| 125 |
+
* @param array $field 验证字段描述信息
|
| 126 |
+
* @return Validate
|
| 127 |
+
*/
|
| 128 |
+
public static function make($rules = [], $message = [], $field = [])
|
| 129 |
+
{
|
| 130 |
+
if (is_null(self::$instance)) {
|
| 131 |
+
self::$instance = new self($rules, $message, $field);
|
| 132 |
+
}
|
| 133 |
+
return self::$instance;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
/**
|
| 137 |
+
* 添加字段验证规则
|
| 138 |
+
* @access protected
|
| 139 |
+
* @param string|array $name 字段名称或者规则数组
|
| 140 |
+
* @param mixed $rule 验证规则
|
| 141 |
+
* @return Validate
|
| 142 |
+
*/
|
| 143 |
+
public function rule($name, $rule = '')
|
| 144 |
+
{
|
| 145 |
+
if (is_array($name)) {
|
| 146 |
+
$this->rule = array_merge($this->rule, $name);
|
| 147 |
+
} else {
|
| 148 |
+
$this->rule[$name] = $rule;
|
| 149 |
+
}
|
| 150 |
+
return $this;
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
/**
|
| 154 |
+
* 注册验证(类型)规则
|
| 155 |
+
* @access public
|
| 156 |
+
* @param string $type 验证规则类型
|
| 157 |
+
* @param mixed $callback callback方法(或闭包)
|
| 158 |
+
* @return void
|
| 159 |
+
*/
|
| 160 |
+
public static function extend($type, $callback = null)
|
| 161 |
+
{
|
| 162 |
+
if (is_array($type)) {
|
| 163 |
+
self::$type = array_merge(self::$type, $type);
|
| 164 |
+
} else {
|
| 165 |
+
self::$type[$type] = $callback;
|
| 166 |
+
}
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
/**
|
| 170 |
+
* 设置验证规则的默认提示信息
|
| 171 |
+
* @access protected
|
| 172 |
+
* @param string|array $type 验证规则类型名称或者数组
|
| 173 |
+
* @param string $msg 验证提示信息
|
| 174 |
+
* @return void
|
| 175 |
+
*/
|
| 176 |
+
public static function setTypeMsg($type, $msg = null)
|
| 177 |
+
{
|
| 178 |
+
if (is_array($type)) {
|
| 179 |
+
self::$typeMsg = array_merge(self::$typeMsg, $type);
|
| 180 |
+
} else {
|
| 181 |
+
self::$typeMsg[$type] = $msg;
|
| 182 |
+
}
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
/**
|
| 186 |
+
* 设置提示信息
|
| 187 |
+
* @access public
|
| 188 |
+
* @param string|array $name 字段名称
|
| 189 |
+
* @param string $message 提示信息
|
| 190 |
+
* @return Validate
|
| 191 |
+
*/
|
| 192 |
+
public function message($name, $message = '')
|
| 193 |
+
{
|
| 194 |
+
if (is_array($name)) {
|
| 195 |
+
$this->message = array_merge($this->message, $name);
|
| 196 |
+
} else {
|
| 197 |
+
$this->message[$name] = $message;
|
| 198 |
+
}
|
| 199 |
+
return $this;
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
/**
|
| 203 |
+
* 设置验证场景
|
| 204 |
+
* @access public
|
| 205 |
+
* @param string|array $name 场景名或者场景设置数组
|
| 206 |
+
* @param mixed $fields 要验证的字段
|
| 207 |
+
* @return Validate
|
| 208 |
+
*/
|
| 209 |
+
public function scene($name, $fields = null)
|
| 210 |
+
{
|
| 211 |
+
if (is_array($name)) {
|
| 212 |
+
$this->scene = array_merge($this->scene, $name);
|
| 213 |
+
}if (is_null($fields)) {
|
| 214 |
+
// 设置当前场景
|
| 215 |
+
$this->currentScene = $name;
|
| 216 |
+
} else {
|
| 217 |
+
// 设置验证场景
|
| 218 |
+
$this->scene[$name] = $fields;
|
| 219 |
+
}
|
| 220 |
+
return $this;
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
/**
|
| 224 |
+
* 判断是否存在某个验证场景
|
| 225 |
+
* @access public
|
| 226 |
+
* @param string $name 场景名
|
| 227 |
+
* @return bool
|
| 228 |
+
*/
|
| 229 |
+
public function hasScene($name)
|
| 230 |
+
{
|
| 231 |
+
return isset($this->scene[$name]);
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
/**
|
| 235 |
+
* 设置批量验证
|
| 236 |
+
* @access public
|
| 237 |
+
* @param bool $batch 是否批量验证
|
| 238 |
+
* @return Validate
|
| 239 |
+
*/
|
| 240 |
+
public function batch($batch = true)
|
| 241 |
+
{
|
| 242 |
+
$this->batch = $batch;
|
| 243 |
+
return $this;
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
/**
|
| 247 |
+
* 数据自动验证
|
| 248 |
+
* @access public
|
| 249 |
+
* @param array $data 数据
|
| 250 |
+
* @param mixed $rules 验证规则
|
| 251 |
+
* @param string $scene 验证场景
|
| 252 |
+
* @return bool
|
| 253 |
+
*/
|
| 254 |
+
public function check($data, $rules = [], $scene = '')
|
| 255 |
+
{
|
| 256 |
+
$this->error = [];
|
| 257 |
+
|
| 258 |
+
if (empty($rules)) {
|
| 259 |
+
// 读取验证规则
|
| 260 |
+
$rules = $this->rule;
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
// 分析验证规则
|
| 264 |
+
$scene = $this->getScene($scene);
|
| 265 |
+
if (is_array($scene)) {
|
| 266 |
+
// 处理场景验证字段
|
| 267 |
+
$change = [];
|
| 268 |
+
$array = [];
|
| 269 |
+
foreach ($scene as $k => $val) {
|
| 270 |
+
if (is_numeric($k)) {
|
| 271 |
+
$array[] = $val;
|
| 272 |
+
} else {
|
| 273 |
+
$array[] = $k;
|
| 274 |
+
$change[$k] = $val;
|
| 275 |
+
}
|
| 276 |
+
}
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
foreach ($rules as $key => $item) {
|
| 280 |
+
// field => rule1|rule2... field=>['rule1','rule2',...]
|
| 281 |
+
if (is_numeric($key)) {
|
| 282 |
+
// [field,rule1|rule2,msg1|msg2]
|
| 283 |
+
$key = $item[0];
|
| 284 |
+
$rule = $item[1];
|
| 285 |
+
if (isset($item[2])) {
|
| 286 |
+
$msg = is_string($item[2]) ? explode('|', $item[2]) : $item[2];
|
| 287 |
+
} else {
|
| 288 |
+
$msg = [];
|
| 289 |
+
}
|
| 290 |
+
} else {
|
| 291 |
+
$rule = $item;
|
| 292 |
+
$msg = [];
|
| 293 |
+
}
|
| 294 |
+
if (strpos($key, '|')) {
|
| 295 |
+
// 字段|描述 用于指定属性名称
|
| 296 |
+
list($key, $title) = explode('|', $key);
|
| 297 |
+
} else {
|
| 298 |
+
$title = isset($this->field[$key]) ? $this->field[$key] : $key;
|
| 299 |
+
}
|
| 300 |
+
|
| 301 |
+
// 场景检测
|
| 302 |
+
if (!empty($scene)) {
|
| 303 |
+
if ($scene instanceof \Closure && !call_user_func_array($scene, [$key, $data])) {
|
| 304 |
+
continue;
|
| 305 |
+
} elseif (is_array($scene)) {
|
| 306 |
+
if (!in_array($key, $array)) {
|
| 307 |
+
continue;
|
| 308 |
+
} elseif (isset($change[$key])) {
|
| 309 |
+
// 重载某个验证规则
|
| 310 |
+
$rule = $change[$key];
|
| 311 |
+
}
|
| 312 |
+
}
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
// 获取数据 支持二维数组
|
| 316 |
+
$value = $this->getDataValue($data, $key);
|
| 317 |
+
|
| 318 |
+
// 字段验证
|
| 319 |
+
if ($rule instanceof \Closure) {
|
| 320 |
+
// 匿名函数验证 支持传入当前字段和所有字段两个数据
|
| 321 |
+
$result = call_user_func_array($rule, [$value, $data]);
|
| 322 |
+
} else {
|
| 323 |
+
$result = $this->checkItem($key, $value, $rule, $data, $title, $msg);
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
if (true !== $result) {
|
| 327 |
+
// 没有返回true 则表示验证失败
|
| 328 |
+
if (!empty($this->batch)) {
|
| 329 |
+
// 批量验证
|
| 330 |
+
if (is_array($result)) {
|
| 331 |
+
$this->error = array_merge($this->error, $result);
|
| 332 |
+
} else {
|
| 333 |
+
$this->error[$key] = $result;
|
| 334 |
+
}
|
| 335 |
+
} else {
|
| 336 |
+
$this->error = $result;
|
| 337 |
+
return false;
|
| 338 |
+
}
|
| 339 |
+
}
|
| 340 |
+
}
|
| 341 |
+
return !empty($this->error) ? false : true;
|
| 342 |
+
}
|
| 343 |
+
|
| 344 |
+
/**
|
| 345 |
+
* 根据验证规则验证数据
|
| 346 |
+
* @access protected
|
| 347 |
+
* @param mixed $value 字段值
|
| 348 |
+
* @param mixed $rules 验证规则
|
| 349 |
+
* @return bool
|
| 350 |
+
*/
|
| 351 |
+
protected function checkRule($value, $rules)
|
| 352 |
+
{
|
| 353 |
+
if ($rules instanceof \Closure) {
|
| 354 |
+
return call_user_func_array($rules, [$value]);
|
| 355 |
+
} elseif (is_string($rules)) {
|
| 356 |
+
$rules = explode('|', $rules);
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
foreach ($rules as $key => $rule) {
|
| 360 |
+
if ($rule instanceof \Closure) {
|
| 361 |
+
$result = call_user_func_array($rule, [$value]);
|
| 362 |
+
} else {
|
| 363 |
+
// 判断验证类型
|
| 364 |
+
list($type, $rule) = $this->getValidateType($key, $rule);
|
| 365 |
+
|
| 366 |
+
$callback = isset(self::$type[$type]) ? self::$type[$type] : [$this, $type];
|
| 367 |
+
|
| 368 |
+
$result = call_user_func_array($callback, [$value, $rule]);
|
| 369 |
+
}
|
| 370 |
+
|
| 371 |
+
if (true !== $result) {
|
| 372 |
+
return $result;
|
| 373 |
+
}
|
| 374 |
+
}
|
| 375 |
+
|
| 376 |
+
return true;
|
| 377 |
+
}
|
| 378 |
+
|
| 379 |
+
/**
|
| 380 |
+
* 验证单个字段规则
|
| 381 |
+
* @access protected
|
| 382 |
+
* @param string $field 字段名
|
| 383 |
+
* @param mixed $value 字段值
|
| 384 |
+
* @param mixed $rules 验证规则
|
| 385 |
+
* @param array $data 数据
|
| 386 |
+
* @param string $title 字段描述
|
| 387 |
+
* @param array $msg 提示信息
|
| 388 |
+
* @return mixed
|
| 389 |
+
*/
|
| 390 |
+
protected function checkItem($field, $value, $rules, $data, $title = '', $msg = [])
|
| 391 |
+
{
|
| 392 |
+
// 支持多规则验证 require|in:a,b,c|... 或者 ['require','in'=>'a,b,c',...]
|
| 393 |
+
if (is_string($rules)) {
|
| 394 |
+
$rules = explode('|', $rules);
|
| 395 |
+
}
|
| 396 |
+
$i = 0;
|
| 397 |
+
foreach ($rules as $key => $rule) {
|
| 398 |
+
if ($rule instanceof \Closure) {
|
| 399 |
+
$result = call_user_func_array($rule, [$value, $data]);
|
| 400 |
+
$info = is_numeric($key) ? '' : $key;
|
| 401 |
+
} else {
|
| 402 |
+
// 判断验证类型
|
| 403 |
+
list($type, $rule, $info) = $this->getValidateType($key, $rule);
|
| 404 |
+
|
| 405 |
+
// 如果不是require 有数据才会行验证
|
| 406 |
+
if (0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) {
|
| 407 |
+
// 验证类型
|
| 408 |
+
$callback = isset(self::$type[$type]) ? self::$type[$type] : [$this, $type];
|
| 409 |
+
// 验证数据
|
| 410 |
+
$result = call_user_func_array($callback, [$value, $rule, $data, $field, $title]);
|
| 411 |
+
} else {
|
| 412 |
+
$result = true;
|
| 413 |
+
}
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
if (false === $result) {
|
| 417 |
+
// 验证失败 返回错误信息
|
| 418 |
+
if (isset($msg[$i])) {
|
| 419 |
+
$message = $msg[$i];
|
| 420 |
+
if (is_string($message) && strpos($message, '{%') === 0) {
|
| 421 |
+
$message = Lang::get(substr($message, 2, -1));
|
| 422 |
+
}
|
| 423 |
+
} else {
|
| 424 |
+
$message = $this->getRuleMsg($field, $title, $info, $rule);
|
| 425 |
+
}
|
| 426 |
+
return $message;
|
| 427 |
+
} elseif (true !== $result) {
|
| 428 |
+
// 返回自定义错误信息
|
| 429 |
+
if (is_string($result) && false !== strpos($result, ':')) {
|
| 430 |
+
$result = str_replace([':attribute', ':rule'], [$title, (string) $rule], $result);
|
| 431 |
+
}
|
| 432 |
+
return $result;
|
| 433 |
+
}
|
| 434 |
+
$i++;
|
| 435 |
+
}
|
| 436 |
+
return $result;
|
| 437 |
+
}
|
| 438 |
+
|
| 439 |
+
/**
|
| 440 |
+
* 获取当前验证类型及规则
|
| 441 |
+
* @access public
|
| 442 |
+
* @param mixed $key
|
| 443 |
+
* @param mixed $rule
|
| 444 |
+
* @return array
|
| 445 |
+
*/
|
| 446 |
+
protected function getValidateType($key, $rule)
|
| 447 |
+
{
|
| 448 |
+
// 判断验证类型
|
| 449 |
+
if (!is_numeric($key)) {
|
| 450 |
+
return [$key, $rule, $key];
|
| 451 |
+
}
|
| 452 |
+
|
| 453 |
+
if (strpos($rule, ':')) {
|
| 454 |
+
list($type, $rule) = explode(':', $rule, 2);
|
| 455 |
+
if (isset($this->alias[$type])) {
|
| 456 |
+
// 判断别名
|
| 457 |
+
$type = $this->alias[$type];
|
| 458 |
+
}
|
| 459 |
+
$info = $type;
|
| 460 |
+
} elseif (method_exists($this, $rule)) {
|
| 461 |
+
$type = $rule;
|
| 462 |
+
$info = $rule;
|
| 463 |
+
$rule = '';
|
| 464 |
+
} else {
|
| 465 |
+
$type = 'is';
|
| 466 |
+
$info = $rule;
|
| 467 |
+
}
|
| 468 |
+
|
| 469 |
+
return [$type, $rule, $info];
|
| 470 |
+
}
|
| 471 |
+
|
| 472 |
+
/**
|
| 473 |
+
* 验证是否和某个字段的值一致
|
| 474 |
+
* @access protected
|
| 475 |
+
* @param mixed $value 字段值
|
| 476 |
+
* @param mixed $rule 验证规则
|
| 477 |
+
* @param array $data 数据
|
| 478 |
+
* @param string $field 字段名
|
| 479 |
+
* @return bool
|
| 480 |
+
*/
|
| 481 |
+
protected function confirm($value, $rule, $data, $field = '')
|
| 482 |
+
{
|
| 483 |
+
if ('' == $rule) {
|
| 484 |
+
if (strpos($field, '_confirm')) {
|
| 485 |
+
$rule = strstr($field, '_confirm', true);
|
| 486 |
+
} else {
|
| 487 |
+
$rule = $field . '_confirm';
|
| 488 |
+
}
|
| 489 |
+
}
|
| 490 |
+
return $this->getDataValue($data, $rule) === $value;
|
| 491 |
+
}
|
| 492 |
+
|
| 493 |
+
/**
|
| 494 |
+
* 验证是否和某个字段的值是否不同
|
| 495 |
+
* @access protected
|
| 496 |
+
* @param mixed $value 字段值
|
| 497 |
+
* @param mixed $rule 验证规则
|
| 498 |
+
* @param array $data 数据
|
| 499 |
+
* @return bool
|
| 500 |
+
*/
|
| 501 |
+
protected function different($value, $rule, $data)
|
| 502 |
+
{
|
| 503 |
+
return $this->getDataValue($data, $rule) != $value;
|
| 504 |
+
}
|
| 505 |
+
|
| 506 |
+
/**
|
| 507 |
+
* 验证是否大于等于某个值
|
| 508 |
+
* @access protected
|
| 509 |
+
* @param mixed $value 字段值
|
| 510 |
+
* @param mixed $rule 验证规则
|
| 511 |
+
* @param array $data 数据
|
| 512 |
+
* @return bool
|
| 513 |
+
*/
|
| 514 |
+
protected function egt($value, $rule, $data)
|
| 515 |
+
{
|
| 516 |
+
$val = $this->getDataValue($data, $rule);
|
| 517 |
+
return !is_null($val) && $value >= $val;
|
| 518 |
+
}
|
| 519 |
+
|
| 520 |
+
/**
|
| 521 |
+
* 验证是否大于某个值
|
| 522 |
+
* @access protected
|
| 523 |
+
* @param mixed $value 字段值
|
| 524 |
+
* @param mixed $rule 验证规则
|
| 525 |
+
* @param array $data 数据
|
| 526 |
+
* @return bool
|
| 527 |
+
*/
|
| 528 |
+
protected function gt($value, $rule, $data)
|
| 529 |
+
{
|
| 530 |
+
$val = $this->getDataValue($data, $rule);
|
| 531 |
+
return !is_null($val) && $value > $val;
|
| 532 |
+
}
|
| 533 |
+
|
| 534 |
+
/**
|
| 535 |
+
* 验证是否小于等于某个值
|
| 536 |
+
* @access protected
|
| 537 |
+
* @param mixed $value 字段值
|
| 538 |
+
* @param mixed $rule 验证规则
|
| 539 |
+
* @param array $data 数据
|
| 540 |
+
* @return bool
|
| 541 |
+
*/
|
| 542 |
+
protected function elt($value, $rule, $data)
|
| 543 |
+
{
|
| 544 |
+
$val = $this->getDataValue($data, $rule);
|
| 545 |
+
return !is_null($val) && $value <= $val;
|
| 546 |
+
}
|
| 547 |
+
|
| 548 |
+
/**
|
| 549 |
+
* 验证是否小于某个值
|
| 550 |
+
* @access protected
|
| 551 |
+
* @param mixed $value 字段值
|
| 552 |
+
* @param mixed $rule 验证规则
|
| 553 |
+
* @param array $data 数据
|
| 554 |
+
* @return bool
|
| 555 |
+
*/
|
| 556 |
+
protected function lt($value, $rule, $data)
|
| 557 |
+
{
|
| 558 |
+
$val = $this->getDataValue($data, $rule);
|
| 559 |
+
return !is_null($val) && $value < $val;
|
| 560 |
+
}
|
| 561 |
+
|
| 562 |
+
/**
|
| 563 |
+
* 验证是否等于某个值
|
| 564 |
+
* @access protected
|
| 565 |
+
* @param mixed $value 字段值
|
| 566 |
+
* @param mixed $rule 验证规则
|
| 567 |
+
* @return bool
|
| 568 |
+
*/
|
| 569 |
+
protected function eq($value, $rule)
|
| 570 |
+
{
|
| 571 |
+
return $value == $rule;
|
| 572 |
+
}
|
| 573 |
+
|
| 574 |
+
/**
|
| 575 |
+
* 验证字段值是否为有效格式
|
| 576 |
+
* @access protected
|
| 577 |
+
* @param mixed $value 字段值
|
| 578 |
+
* @param string $rule 验证规则
|
| 579 |
+
* @param array $data 验证数据
|
| 580 |
+
* @return bool
|
| 581 |
+
*/
|
| 582 |
+
protected function is($value, $rule, $data = [])
|
| 583 |
+
{
|
| 584 |
+
switch ($rule) {
|
| 585 |
+
case 'require':
|
| 586 |
+
// 必须
|
| 587 |
+
$result = !empty($value) || '0' == $value;
|
| 588 |
+
break;
|
| 589 |
+
case 'accepted':
|
| 590 |
+
// 接受
|
| 591 |
+
$result = in_array($value, ['1', 'on', 'yes']);
|
| 592 |
+
break;
|
| 593 |
+
case 'date':
|
| 594 |
+
// 是否是一个有效日期
|
| 595 |
+
$result = false !== strtotime($value);
|
| 596 |
+
break;
|
| 597 |
+
case 'alpha':
|
| 598 |
+
// 只允许字母
|
| 599 |
+
$result = $this->regex($value, '/^[A-Za-z]+$/');
|
| 600 |
+
break;
|
| 601 |
+
case 'alphaNum':
|
| 602 |
+
// 只允许字母和数字
|
| 603 |
+
$result = $this->regex($value, '/^[A-Za-z0-9]+$/');
|
| 604 |
+
break;
|
| 605 |
+
case 'alphaDash':
|
| 606 |
+
// 只允许字母、数字和下划线 破折号
|
| 607 |
+
$result = $this->regex($value, '/^[A-Za-z0-9\-\_]+$/');
|
| 608 |
+
break;
|
| 609 |
+
case 'chs':
|
| 610 |
+
// 只允许汉字
|
| 611 |
+
$result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}]+$/u');
|
| 612 |
+
break;
|
| 613 |
+
case 'chsAlpha':
|
| 614 |
+
// 只允许汉字、字母
|
| 615 |
+
$result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}a-zA-Z]+$/u');
|
| 616 |
+
break;
|
| 617 |
+
case 'chsAlphaNum':
|
| 618 |
+
// 只允许汉字、字母和数字
|
| 619 |
+
$result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}a-zA-Z0-9]+$/u');
|
| 620 |
+
break;
|
| 621 |
+
case 'chsDash':
|
| 622 |
+
// 只允许汉字、字母、数字和下划线_及破折号-
|
| 623 |
+
$result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}a-zA-Z0-9\_\-]+$/u');
|
| 624 |
+
break;
|
| 625 |
+
case 'activeUrl':
|
| 626 |
+
// 是否为有效的网址
|
| 627 |
+
$result = checkdnsrr($value);
|
| 628 |
+
break;
|
| 629 |
+
case 'ip':
|
| 630 |
+
// 是否为IP地址
|
| 631 |
+
$result = $this->filter($value, [FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6]);
|
| 632 |
+
break;
|
| 633 |
+
case 'url':
|
| 634 |
+
// 是否为一个URL地址
|
| 635 |
+
$result = $this->filter($value, FILTER_VALIDATE_URL);
|
| 636 |
+
break;
|
| 637 |
+
case 'float':
|
| 638 |
+
// 是否为float
|
| 639 |
+
$result = $this->filter($value, FILTER_VALIDATE_FLOAT);
|
| 640 |
+
break;
|
| 641 |
+
case 'number':
|
| 642 |
+
$result = is_numeric($value);
|
| 643 |
+
break;
|
| 644 |
+
case 'integer':
|
| 645 |
+
// 是否为整型
|
| 646 |
+
$result = $this->filter($value, FILTER_VALIDATE_INT);
|
| 647 |
+
break;
|
| 648 |
+
case 'email':
|
| 649 |
+
// 是否为邮箱地址
|
| 650 |
+
$result = $this->filter($value, FILTER_VALIDATE_EMAIL);
|
| 651 |
+
break;
|
| 652 |
+
case 'boolean':
|
| 653 |
+
// 是否为布尔值
|
| 654 |
+
$result = in_array($value, [true, false, 0, 1, '0', '1'], true);
|
| 655 |
+
break;
|
| 656 |
+
case 'array':
|
| 657 |
+
// 是否为数组
|
| 658 |
+
$result = is_array($value);
|
| 659 |
+
break;
|
| 660 |
+
case 'file':
|
| 661 |
+
$result = $value instanceof File;
|
| 662 |
+
break;
|
| 663 |
+
case 'image':
|
| 664 |
+
$result = $value instanceof File && in_array($this->getImageType($value->getRealPath()), [1, 2, 3, 6]);
|
| 665 |
+
break;
|
| 666 |
+
case 'token':
|
| 667 |
+
$result = $this->token($value, '__token__', $data);
|
| 668 |
+
break;
|
| 669 |
+
default:
|
| 670 |
+
if (isset(self::$type[$rule])) {
|
| 671 |
+
// 注册的验证规则
|
| 672 |
+
$result = call_user_func_array(self::$type[$rule], [$value]);
|
| 673 |
+
} else {
|
| 674 |
+
// 正则验证
|
| 675 |
+
$result = $this->regex($value, $rule);
|
| 676 |
+
}
|
| 677 |
+
}
|
| 678 |
+
return $result;
|
| 679 |
+
}
|
| 680 |
+
|
| 681 |
+
// 判断图像类型
|
| 682 |
+
protected function getImageType($image)
|
| 683 |
+
{
|
| 684 |
+
if (function_exists('exif_imagetype')) {
|
| 685 |
+
return exif_imagetype($image);
|
| 686 |
+
} else {
|
| 687 |
+
try {
|
| 688 |
+
$info = getimagesize($image);
|
| 689 |
+
return $info ? $info[2] : false;
|
| 690 |
+
} catch (\Exception $e) {
|
| 691 |
+
return false;
|
| 692 |
+
}
|
| 693 |
+
}
|
| 694 |
+
}
|
| 695 |
+
|
| 696 |
+
/**
|
| 697 |
+
* 验证是否为合格的域名或者IP 支持A,MX,NS,SOA,PTR,CNAME,AAAA,A6, SRV,NAPTR,TXT 或者 ANY类型
|
| 698 |
+
* @access protected
|
| 699 |
+
* @param mixed $value 字段值
|
| 700 |
+
* @param mixed $rule 验证规则
|
| 701 |
+
* @return bool
|
| 702 |
+
*/
|
| 703 |
+
protected function activeUrl($value, $rule)
|
| 704 |
+
{
|
| 705 |
+
if (!in_array($rule, ['A', 'MX', 'NS', 'SOA', 'PTR', 'CNAME', 'AAAA', 'A6', 'SRV', 'NAPTR', 'TXT', 'ANY'])) {
|
| 706 |
+
$rule = 'MX';
|
| 707 |
+
}
|
| 708 |
+
return checkdnsrr($value, $rule);
|
| 709 |
+
}
|
| 710 |
+
|
| 711 |
+
/**
|
| 712 |
+
* 验证是否有效IP
|
| 713 |
+
* @access protected
|
| 714 |
+
* @param mixed $value 字段值
|
| 715 |
+
* @param mixed $rule 验证规则 ipv4 ipv6
|
| 716 |
+
* @return bool
|
| 717 |
+
*/
|
| 718 |
+
protected function ip($value, $rule)
|
| 719 |
+
{
|
| 720 |
+
if (!in_array($rule, ['ipv4', 'ipv6'])) {
|
| 721 |
+
$rule = 'ipv4';
|
| 722 |
+
}
|
| 723 |
+
return $this->filter($value, [FILTER_VALIDATE_IP, 'ipv6' == $rule ? FILTER_FLAG_IPV6 : FILTER_FLAG_IPV4]);
|
| 724 |
+
}
|
| 725 |
+
|
| 726 |
+
/**
|
| 727 |
+
* 验证上传文件后缀
|
| 728 |
+
* @access protected
|
| 729 |
+
* @param mixed $file 上传文件
|
| 730 |
+
* @param mixed $rule 验证规则
|
| 731 |
+
* @return bool
|
| 732 |
+
*/
|
| 733 |
+
protected function fileExt($file, $rule)
|
| 734 |
+
{
|
| 735 |
+
if (is_array($file)) {
|
| 736 |
+
foreach ($file as $item) {
|
| 737 |
+
if (!($item instanceof File) || !$item->checkExt($rule)) {
|
| 738 |
+
return false;
|
| 739 |
+
}
|
| 740 |
+
}
|
| 741 |
+
return true;
|
| 742 |
+
} elseif ($file instanceof File) {
|
| 743 |
+
return $file->checkExt($rule);
|
| 744 |
+
} else {
|
| 745 |
+
return false;
|
| 746 |
+
}
|
| 747 |
+
}
|
| 748 |
+
|
| 749 |
+
/**
|
| 750 |
+
* 验证上传文件类型
|
| 751 |
+
* @access protected
|
| 752 |
+
* @param mixed $file 上传文件
|
| 753 |
+
* @param mixed $rule 验证规则
|
| 754 |
+
* @return bool
|
| 755 |
+
*/
|
| 756 |
+
protected function fileMime($file, $rule)
|
| 757 |
+
{
|
| 758 |
+
if (is_array($file)) {
|
| 759 |
+
foreach ($file as $item) {
|
| 760 |
+
if (!($item instanceof File) || !$item->checkMime($rule)) {
|
| 761 |
+
return false;
|
| 762 |
+
}
|
| 763 |
+
}
|
| 764 |
+
return true;
|
| 765 |
+
} elseif ($file instanceof File) {
|
| 766 |
+
return $file->checkMime($rule);
|
| 767 |
+
} else {
|
| 768 |
+
return false;
|
| 769 |
+
}
|
| 770 |
+
}
|
| 771 |
+
|
| 772 |
+
/**
|
| 773 |
+
* 验证上传文件大小
|
| 774 |
+
* @access protected
|
| 775 |
+
* @param mixed $file 上传文件
|
| 776 |
+
* @param mixed $rule 验证规则
|
| 777 |
+
* @return bool
|
| 778 |
+
*/
|
| 779 |
+
protected function fileSize($file, $rule)
|
| 780 |
+
{
|
| 781 |
+
if (is_array($file)) {
|
| 782 |
+
foreach ($file as $item) {
|
| 783 |
+
if (!($item instanceof File) || !$item->checkSize($rule)) {
|
| 784 |
+
return false;
|
| 785 |
+
}
|
| 786 |
+
}
|
| 787 |
+
return true;
|
| 788 |
+
} elseif ($file instanceof File) {
|
| 789 |
+
return $file->checkSize($rule);
|
| 790 |
+
} else {
|
| 791 |
+
return false;
|
| 792 |
+
}
|
| 793 |
+
}
|
| 794 |
+
|
| 795 |
+
/**
|
| 796 |
+
* 验证图片的宽高及类型
|
| 797 |
+
* @access protected
|
| 798 |
+
* @param mixed $file 上传文件
|
| 799 |
+
* @param mixed $rule 验证规则
|
| 800 |
+
* @return bool
|
| 801 |
+
*/
|
| 802 |
+
protected function image($file, $rule)
|
| 803 |
+
{
|
| 804 |
+
if (!($file instanceof File)) {
|
| 805 |
+
return false;
|
| 806 |
+
}
|
| 807 |
+
if ($rule) {
|
| 808 |
+
$rule = explode(',', $rule);
|
| 809 |
+
list($width, $height, $type) = getimagesize($file->getRealPath());
|
| 810 |
+
if (isset($rule[2])) {
|
| 811 |
+
$imageType = strtolower($rule[2]);
|
| 812 |
+
if ('jpeg' == $imageType) {
|
| 813 |
+
$imageType = 'jpg';
|
| 814 |
+
}
|
| 815 |
+
if (image_type_to_extension($type, false) != $imageType) {
|
| 816 |
+
return false;
|
| 817 |
+
}
|
| 818 |
+
}
|
| 819 |
+
|
| 820 |
+
list($w, $h) = $rule;
|
| 821 |
+
return $w == $width && $h == $height;
|
| 822 |
+
} else {
|
| 823 |
+
return in_array($this->getImageType($file->getRealPath()), [1, 2, 3, 6]);
|
| 824 |
+
}
|
| 825 |
+
}
|
| 826 |
+
|
| 827 |
+
/**
|
| 828 |
+
* 验证请求类型
|
| 829 |
+
* @access protected
|
| 830 |
+
* @param mixed $value 字段值
|
| 831 |
+
* @param mixed $rule 验证规则
|
| 832 |
+
* @return bool
|
| 833 |
+
*/
|
| 834 |
+
protected function method($value, $rule)
|
| 835 |
+
{
|
| 836 |
+
$method = Request::instance()->method();
|
| 837 |
+
return strtoupper($rule) == $method;
|
| 838 |
+
}
|
| 839 |
+
|
| 840 |
+
/**
|
| 841 |
+
* 验证时间和日期是否符合指定格式
|
| 842 |
+
* @access protected
|
| 843 |
+
* @param mixed $value 字段值
|
| 844 |
+
* @param mixed $rule 验证规则
|
| 845 |
+
* @return bool
|
| 846 |
+
*/
|
| 847 |
+
protected function dateFormat($value, $rule)
|
| 848 |
+
{
|
| 849 |
+
$info = date_parse_from_format($rule, $value);
|
| 850 |
+
return 0 == $info['warning_count'] && 0 == $info['error_count'];
|
| 851 |
+
}
|
| 852 |
+
|
| 853 |
+
/**
|
| 854 |
+
* 验证是否唯一
|
| 855 |
+
* @access protected
|
| 856 |
+
* @param mixed $value 字段值
|
| 857 |
+
* @param mixed $rule 验证规则 格式:数据表,字段名,排除ID,主键名
|
| 858 |
+
* @param array $data 数据
|
| 859 |
+
* @param string $field 验证字段名
|
| 860 |
+
* @return bool
|
| 861 |
+
*/
|
| 862 |
+
protected function unique($value, $rule, $data, $field)
|
| 863 |
+
{
|
| 864 |
+
if (is_string($rule)) {
|
| 865 |
+
$rule = explode(',', $rule);
|
| 866 |
+
}
|
| 867 |
+
if (false !== strpos($rule[0], '\\')) {
|
| 868 |
+
// 指定模型类
|
| 869 |
+
$db = new $rule[0];
|
| 870 |
+
} else {
|
| 871 |
+
try {
|
| 872 |
+
$db = Loader::model($rule[0]);
|
| 873 |
+
} catch (ClassNotFoundException $e) {
|
| 874 |
+
$db = Db::name($rule[0]);
|
| 875 |
+
}
|
| 876 |
+
}
|
| 877 |
+
$key = isset($rule[1]) ? $rule[1] : $field;
|
| 878 |
+
|
| 879 |
+
if (strpos($key, '^')) {
|
| 880 |
+
// 支持多个字段验证
|
| 881 |
+
$fields = explode('^', $key);
|
| 882 |
+
foreach ($fields as $key) {
|
| 883 |
+
if (isset($data[$key])) {
|
| 884 |
+
$map[$key] = $data[$key];
|
| 885 |
+
}
|
| 886 |
+
}
|
| 887 |
+
} elseif (strpos($key, '=')) {
|
| 888 |
+
parse_str($key, $map);
|
| 889 |
+
} elseif (isset($data[$field])) {
|
| 890 |
+
$map[$key] = $data[$field];
|
| 891 |
+
} else {
|
| 892 |
+
$map = [];
|
| 893 |
+
}
|
| 894 |
+
|
| 895 |
+
$pk = isset($rule[3]) ? $rule[3] : $db->getPk();
|
| 896 |
+
if (is_string($pk)) {
|
| 897 |
+
if (isset($rule[2])) {
|
| 898 |
+
$map[$pk] = ['neq', $rule[2]];
|
| 899 |
+
} elseif (isset($data[$pk])) {
|
| 900 |
+
$map[$pk] = ['neq', $data[$pk]];
|
| 901 |
+
}
|
| 902 |
+
}
|
| 903 |
+
if ($db->where($map)->field($pk)->find()) {
|
| 904 |
+
return false;
|
| 905 |
+
}
|
| 906 |
+
return true;
|
| 907 |
+
}
|
| 908 |
+
|
| 909 |
+
/**
|
| 910 |
+
* 使用行为类验证
|
| 911 |
+
* @access protected
|
| 912 |
+
* @param mixed $value 字段值
|
| 913 |
+
* @param mixed $rule 验证规则
|
| 914 |
+
* @param array $data 数据
|
| 915 |
+
* @return mixed
|
| 916 |
+
*/
|
| 917 |
+
protected function behavior($value, $rule, $data)
|
| 918 |
+
{
|
| 919 |
+
return Hook::exec($rule, '', $data);
|
| 920 |
+
}
|
| 921 |
+
|
| 922 |
+
/**
|
| 923 |
+
* 使用filter_var方式验证
|
| 924 |
+
* @access protected
|
| 925 |
+
* @param mixed $value 字段值
|
| 926 |
+
* @param mixed $rule 验证规则
|
| 927 |
+
* @return bool
|
| 928 |
+
*/
|
| 929 |
+
protected function filter($value, $rule)
|
| 930 |
+
{
|
| 931 |
+
if (is_string($rule) && strpos($rule, ',')) {
|
| 932 |
+
list($rule, $param) = explode(',', $rule);
|
| 933 |
+
} elseif (is_array($rule)) {
|
| 934 |
+
$param = isset($rule[1]) ? $rule[1] : null;
|
| 935 |
+
$rule = $rule[0];
|
| 936 |
+
} else {
|
| 937 |
+
$param = null;
|
| 938 |
+
}
|
| 939 |
+
return false !== filter_var($value, is_int($rule) ? $rule : filter_id($rule), $param);
|
| 940 |
+
}
|
| 941 |
+
|
| 942 |
+
/**
|
| 943 |
+
* 验证某个字段等于某个值的时候必须
|
| 944 |
+
* @access protected
|
| 945 |
+
* @param mixed $value 字段值
|
| 946 |
+
* @param mixed $rule 验证规则
|
| 947 |
+
* @param array $data 数据
|
| 948 |
+
* @return bool
|
| 949 |
+
*/
|
| 950 |
+
protected function requireIf($value, $rule, $data)
|
| 951 |
+
{
|
| 952 |
+
list($field, $val) = explode(',', $rule);
|
| 953 |
+
if ($this->getDataValue($data, $field) == $val) {
|
| 954 |
+
return !empty($value) || '0' == $value;
|
| 955 |
+
} else {
|
| 956 |
+
return true;
|
| 957 |
+
}
|
| 958 |
+
}
|
| 959 |
+
|
| 960 |
+
/**
|
| 961 |
+
* 通过回调方法验证某个字段是否必须
|
| 962 |
+
* @access protected
|
| 963 |
+
* @param mixed $value 字段值
|
| 964 |
+
* @param mixed $rule 验证规则
|
| 965 |
+
* @param array $data 数据
|
| 966 |
+
* @return bool
|
| 967 |
+
*/
|
| 968 |
+
protected function requireCallback($value, $rule, $data)
|
| 969 |
+
{
|
| 970 |
+
$result = call_user_func_array($rule, [$value, $data]);
|
| 971 |
+
if ($result) {
|
| 972 |
+
return !empty($value) || '0' == $value;
|
| 973 |
+
} else {
|
| 974 |
+
return true;
|
| 975 |
+
}
|
| 976 |
+
}
|
| 977 |
+
|
| 978 |
+
/**
|
| 979 |
+
* 验证某个字段有值的情况下必须
|
| 980 |
+
* @access protected
|
| 981 |
+
* @param mixed $value 字段值
|
| 982 |
+
* @param mixed $rule 验证规则
|
| 983 |
+
* @param array $data 数据
|
| 984 |
+
* @return bool
|
| 985 |
+
*/
|
| 986 |
+
protected function requireWith($value, $rule, $data)
|
| 987 |
+
{
|
| 988 |
+
$val = $this->getDataValue($data, $rule);
|
| 989 |
+
if (!empty($val)) {
|
| 990 |
+
return !empty($value) || '0' == $value;
|
| 991 |
+
} else {
|
| 992 |
+
return true;
|
| 993 |
+
}
|
| 994 |
+
}
|
| 995 |
+
|
| 996 |
+
/**
|
| 997 |
+
* 验证是否在范围内
|
| 998 |
+
* @access protected
|
| 999 |
+
* @param mixed $value 字段值
|
| 1000 |
+
* @param mixed $rule 验证规则
|
| 1001 |
+
* @return bool
|
| 1002 |
+
*/
|
| 1003 |
+
protected function in($value, $rule)
|
| 1004 |
+
{
|
| 1005 |
+
return in_array($value, is_array($rule) ? $rule : explode(',', $rule));
|
| 1006 |
+
}
|
| 1007 |
+
|
| 1008 |
+
/**
|
| 1009 |
+
* 验证是否不在某个范围
|
| 1010 |
+
* @access protected
|
| 1011 |
+
* @param mixed $value 字段值
|
| 1012 |
+
* @param mixed $rule 验证规则
|
| 1013 |
+
* @return bool
|
| 1014 |
+
*/
|
| 1015 |
+
protected function notIn($value, $rule)
|
| 1016 |
+
{
|
| 1017 |
+
return !in_array($value, is_array($rule) ? $rule : explode(',', $rule));
|
| 1018 |
+
}
|
| 1019 |
+
|
| 1020 |
+
/**
|
| 1021 |
+
* between验证数据
|
| 1022 |
+
* @access protected
|
| 1023 |
+
* @param mixed $value 字段值
|
| 1024 |
+
* @param mixed $rule 验证规则
|
| 1025 |
+
* @return bool
|
| 1026 |
+
*/
|
| 1027 |
+
protected function between($value, $rule)
|
| 1028 |
+
{
|
| 1029 |
+
if (is_string($rule)) {
|
| 1030 |
+
$rule = explode(',', $rule);
|
| 1031 |
+
}
|
| 1032 |
+
list($min, $max) = $rule;
|
| 1033 |
+
return $value >= $min && $value <= $max;
|
| 1034 |
+
}
|
| 1035 |
+
|
| 1036 |
+
/**
|
| 1037 |
+
* 使用notbetween验证数据
|
| 1038 |
+
* @access protected
|
| 1039 |
+
* @param mixed $value 字段值
|
| 1040 |
+
* @param mixed $rule 验证规则
|
| 1041 |
+
* @return bool
|
| 1042 |
+
*/
|
| 1043 |
+
protected function notBetween($value, $rule)
|
| 1044 |
+
{
|
| 1045 |
+
if (is_string($rule)) {
|
| 1046 |
+
$rule = explode(',', $rule);
|
| 1047 |
+
}
|
| 1048 |
+
list($min, $max) = $rule;
|
| 1049 |
+
return $value < $min || $value > $max;
|
| 1050 |
+
}
|
| 1051 |
+
|
| 1052 |
+
/**
|
| 1053 |
+
* 验证数据长度
|
| 1054 |
+
* @access protected
|
| 1055 |
+
* @param mixed $value 字段值
|
| 1056 |
+
* @param mixed $rule 验证规则
|
| 1057 |
+
* @return bool
|
| 1058 |
+
*/
|
| 1059 |
+
protected function length($value, $rule)
|
| 1060 |
+
{
|
| 1061 |
+
if (is_array($value)) {
|
| 1062 |
+
$length = count($value);
|
| 1063 |
+
} elseif ($value instanceof File) {
|
| 1064 |
+
$length = $value->getSize();
|
| 1065 |
+
} else {
|
| 1066 |
+
$length = mb_strlen((string) $value);
|
| 1067 |
+
}
|
| 1068 |
+
|
| 1069 |
+
if (strpos($rule, ',')) {
|
| 1070 |
+
// 长度区间
|
| 1071 |
+
list($min, $max) = explode(',', $rule);
|
| 1072 |
+
return $length >= $min && $length <= $max;
|
| 1073 |
+
} else {
|
| 1074 |
+
// 指定长度
|
| 1075 |
+
return $length == $rule;
|
| 1076 |
+
}
|
| 1077 |
+
}
|
| 1078 |
+
|
| 1079 |
+
/**
|
| 1080 |
+
* 验证数据最大长度
|
| 1081 |
+
* @access protected
|
| 1082 |
+
* @param mixed $value 字段值
|
| 1083 |
+
* @param mixed $rule 验证规则
|
| 1084 |
+
* @return bool
|
| 1085 |
+
*/
|
| 1086 |
+
protected function max($value, $rule)
|
| 1087 |
+
{
|
| 1088 |
+
if (is_array($value)) {
|
| 1089 |
+
$length = count($value);
|
| 1090 |
+
} elseif ($value instanceof File) {
|
| 1091 |
+
$length = $value->getSize();
|
| 1092 |
+
} else {
|
| 1093 |
+
$length = mb_strlen((string) $value);
|
| 1094 |
+
}
|
| 1095 |
+
return $length <= $rule;
|
| 1096 |
+
}
|
| 1097 |
+
|
| 1098 |
+
/**
|
| 1099 |
+
* 验证数据最小长度
|
| 1100 |
+
* @access protected
|
| 1101 |
+
* @param mixed $value 字段值
|
| 1102 |
+
* @param mixed $rule 验证规则
|
| 1103 |
+
* @return bool
|
| 1104 |
+
*/
|
| 1105 |
+
protected function min($value, $rule)
|
| 1106 |
+
{
|
| 1107 |
+
if (is_array($value)) {
|
| 1108 |
+
$length = count($value);
|
| 1109 |
+
} elseif ($value instanceof File) {
|
| 1110 |
+
$length = $value->getSize();
|
| 1111 |
+
} else {
|
| 1112 |
+
$length = mb_strlen((string) $value);
|
| 1113 |
+
}
|
| 1114 |
+
return $length >= $rule;
|
| 1115 |
+
}
|
| 1116 |
+
|
| 1117 |
+
/**
|
| 1118 |
+
* 验证日期
|
| 1119 |
+
* @access protected
|
| 1120 |
+
* @param mixed $value 字段值
|
| 1121 |
+
* @param mixed $rule 验证规则
|
| 1122 |
+
* @param array $data 数据
|
| 1123 |
+
* @return bool
|
| 1124 |
+
*/
|
| 1125 |
+
protected function after($value, $rule, $data)
|
| 1126 |
+
{
|
| 1127 |
+
return strtotime($value) >= strtotime($rule);
|
| 1128 |
+
}
|
| 1129 |
+
|
| 1130 |
+
/**
|
| 1131 |
+
* 验证日期
|
| 1132 |
+
* @access protected
|
| 1133 |
+
* @param mixed $value 字段值
|
| 1134 |
+
* @param mixed $rule 验证规则
|
| 1135 |
+
* @param array $data 数据
|
| 1136 |
+
* @return bool
|
| 1137 |
+
*/
|
| 1138 |
+
protected function before($value, $rule, $data)
|
| 1139 |
+
{
|
| 1140 |
+
return strtotime($value) <= strtotime($rule);
|
| 1141 |
+
}
|
| 1142 |
+
|
| 1143 |
+
/**
|
| 1144 |
+
* 验证日期字段
|
| 1145 |
+
* @access protected
|
| 1146 |
+
* @param mixed $value 字段值
|
| 1147 |
+
* @param mixed $rule 验证规则
|
| 1148 |
+
* @param array $data 数据
|
| 1149 |
+
* @return bool
|
| 1150 |
+
*/
|
| 1151 |
+
protected function afterWith($value, $rule, $data)
|
| 1152 |
+
{
|
| 1153 |
+
$rule = $this->getDataValue($data, $rule);
|
| 1154 |
+
return !is_null($rule) && strtotime($value) >= strtotime($rule);
|
| 1155 |
+
}
|
| 1156 |
+
|
| 1157 |
+
/**
|
| 1158 |
+
* 验证日期字段
|
| 1159 |
+
* @access protected
|
| 1160 |
+
* @param mixed $value 字段值
|
| 1161 |
+
* @param mixed $rule 验证规则
|
| 1162 |
+
* @param array $data 数据
|
| 1163 |
+
* @return bool
|
| 1164 |
+
*/
|
| 1165 |
+
protected function beforeWith($value, $rule, $data)
|
| 1166 |
+
{
|
| 1167 |
+
$rule = $this->getDataValue($data, $rule);
|
| 1168 |
+
return !is_null($rule) && strtotime($value) <= strtotime($rule);
|
| 1169 |
+
}
|
| 1170 |
+
|
| 1171 |
+
/**
|
| 1172 |
+
* 验证有效期
|
| 1173 |
+
* @access protected
|
| 1174 |
+
* @param mixed $value 字段值
|
| 1175 |
+
* @param mixed $rule 验证规则
|
| 1176 |
+
* @return bool
|
| 1177 |
+
*/
|
| 1178 |
+
protected function expire($value, $rule)
|
| 1179 |
+
{
|
| 1180 |
+
if (is_string($rule)) {
|
| 1181 |
+
$rule = explode(',', $rule);
|
| 1182 |
+
}
|
| 1183 |
+
list($start, $end) = $rule;
|
| 1184 |
+
if (!is_numeric($start)) {
|
| 1185 |
+
$start = strtotime($start);
|
| 1186 |
+
}
|
| 1187 |
+
|
| 1188 |
+
if (!is_numeric($end)) {
|
| 1189 |
+
$end = strtotime($end);
|
| 1190 |
+
}
|
| 1191 |
+
return $_SERVER['REQUEST_TIME'] >= $start && $_SERVER['REQUEST_TIME'] <= $end;
|
| 1192 |
+
}
|
| 1193 |
+
|
| 1194 |
+
/**
|
| 1195 |
+
* 验证IP许可
|
| 1196 |
+
* @access protected
|
| 1197 |
+
* @param string $value 字段值
|
| 1198 |
+
* @param mixed $rule 验证规则
|
| 1199 |
+
* @return mixed
|
| 1200 |
+
*/
|
| 1201 |
+
protected function allowIp($value, $rule)
|
| 1202 |
+
{
|
| 1203 |
+
return in_array($_SERVER['REMOTE_ADDR'], is_array($rule) ? $rule : explode(',', $rule));
|
| 1204 |
+
}
|
| 1205 |
+
|
| 1206 |
+
/**
|
| 1207 |
+
* 验证IP禁用
|
| 1208 |
+
* @access protected
|
| 1209 |
+
* @param string $value 字段值
|
| 1210 |
+
* @param mixed $rule 验证规则
|
| 1211 |
+
* @return mixed
|
| 1212 |
+
*/
|
| 1213 |
+
protected function denyIp($value, $rule)
|
| 1214 |
+
{
|
| 1215 |
+
return !in_array($_SERVER['REMOTE_ADDR'], is_array($rule) ? $rule : explode(',', $rule));
|
| 1216 |
+
}
|
| 1217 |
+
|
| 1218 |
+
/**
|
| 1219 |
+
* 使用正则验证数据
|
| 1220 |
+
* @access protected
|
| 1221 |
+
* @param mixed $value 字段值
|
| 1222 |
+
* @param mixed $rule 验证规则 正则规则或者预定义正则名
|
| 1223 |
+
* @return mixed
|
| 1224 |
+
*/
|
| 1225 |
+
protected function regex($value, $rule)
|
| 1226 |
+
{
|
| 1227 |
+
if (isset($this->regex[$rule])) {
|
| 1228 |
+
$rule = $this->regex[$rule];
|
| 1229 |
+
}
|
| 1230 |
+
if (0 !== strpos($rule, '/') && !preg_match('/\/[imsU]{0,4}$/', $rule)) {
|
| 1231 |
+
// 不是正则表达式则两端补上/
|
| 1232 |
+
$rule = '/^' . $rule . '$/';
|
| 1233 |
+
}
|
| 1234 |
+
return is_scalar($value) && 1 === preg_match($rule, (string) $value);
|
| 1235 |
+
}
|
| 1236 |
+
|
| 1237 |
+
/**
|
| 1238 |
+
* 验证表单令牌
|
| 1239 |
+
* @access protected
|
| 1240 |
+
* @param mixed $value 字段值
|
| 1241 |
+
* @param mixed $rule 验证规则
|
| 1242 |
+
* @param array $data 数据
|
| 1243 |
+
* @return bool
|
| 1244 |
+
*/
|
| 1245 |
+
protected function token($value, $rule, $data)
|
| 1246 |
+
{
|
| 1247 |
+
$rule = !empty($rule) ? $rule : '__token__';
|
| 1248 |
+
if (!isset($data[$rule]) || !Session::has($rule)) {
|
| 1249 |
+
// 令牌数据无效
|
| 1250 |
+
return false;
|
| 1251 |
+
}
|
| 1252 |
+
|
| 1253 |
+
// 令牌验证
|
| 1254 |
+
if (isset($data[$rule]) && Session::get($rule) === $data[$rule]) {
|
| 1255 |
+
// 防止重复提交
|
| 1256 |
+
Session::delete($rule); // 验证完成销毁session
|
| 1257 |
+
return true;
|
| 1258 |
+
}
|
| 1259 |
+
// 开启TOKEN重置
|
| 1260 |
+
Session::delete($rule);
|
| 1261 |
+
return false;
|
| 1262 |
+
}
|
| 1263 |
+
|
| 1264 |
+
// 获取错误信息
|
| 1265 |
+
public function getError()
|
| 1266 |
+
{
|
| 1267 |
+
return $this->error;
|
| 1268 |
+
}
|
| 1269 |
+
|
| 1270 |
+
/**
|
| 1271 |
+
* 获取数据值
|
| 1272 |
+
* @access protected
|
| 1273 |
+
* @param array $data 数据
|
| 1274 |
+
* @param string $key 数据标识 支持二维
|
| 1275 |
+
* @return mixed
|
| 1276 |
+
*/
|
| 1277 |
+
protected function getDataValue($data, $key)
|
| 1278 |
+
{
|
| 1279 |
+
if (is_numeric($key)) {
|
| 1280 |
+
$value = $key;
|
| 1281 |
+
} elseif (strpos($key, '.')) {
|
| 1282 |
+
// 支持二维数组验证
|
| 1283 |
+
list($name1, $name2) = explode('.', $key);
|
| 1284 |
+
$value = isset($data[$name1][$name2]) ? $data[$name1][$name2] : null;
|
| 1285 |
+
} else {
|
| 1286 |
+
$value = isset($data[$key]) ? $data[$key] : null;
|
| 1287 |
+
}
|
| 1288 |
+
return $value;
|
| 1289 |
+
}
|
| 1290 |
+
|
| 1291 |
+
/**
|
| 1292 |
+
* 获取验证规则的错误提示信息
|
| 1293 |
+
* @access protected
|
| 1294 |
+
* @param string $attribute 字段英文名
|
| 1295 |
+
* @param string $title 字段描述名
|
| 1296 |
+
* @param string $type 验证规则名称
|
| 1297 |
+
* @param mixed $rule 验证规则数据
|
| 1298 |
+
* @return string
|
| 1299 |
+
*/
|
| 1300 |
+
protected function getRuleMsg($attribute, $title, $type, $rule)
|
| 1301 |
+
{
|
| 1302 |
+
if (isset($this->message[$attribute . '.' . $type])) {
|
| 1303 |
+
$msg = $this->message[$attribute . '.' . $type];
|
| 1304 |
+
} elseif (isset($this->message[$attribute][$type])) {
|
| 1305 |
+
$msg = $this->message[$attribute][$type];
|
| 1306 |
+
} elseif (isset($this->message[$attribute])) {
|
| 1307 |
+
$msg = $this->message[$attribute];
|
| 1308 |
+
} elseif (isset(self::$typeMsg[$type])) {
|
| 1309 |
+
$msg = self::$typeMsg[$type];
|
| 1310 |
+
} elseif (0 === strpos($type, 'require')) {
|
| 1311 |
+
$msg = self::$typeMsg['require'];
|
| 1312 |
+
} else {
|
| 1313 |
+
$msg = $title . Lang::get('not conform to the rules');
|
| 1314 |
+
}
|
| 1315 |
+
|
| 1316 |
+
if (is_string($msg) && 0 === strpos($msg, '{%')) {
|
| 1317 |
+
$msg = Lang::get(substr($msg, 2, -1));
|
| 1318 |
+
} elseif (Lang::has($msg)) {
|
| 1319 |
+
$msg = Lang::get($msg);
|
| 1320 |
+
}
|
| 1321 |
+
|
| 1322 |
+
if (is_string($msg) && is_scalar($rule) && false !== strpos($msg, ':')) {
|
| 1323 |
+
// 变量替换
|
| 1324 |
+
if (is_string($rule) && strpos($rule, ',')) {
|
| 1325 |
+
$array = array_pad(explode(',', $rule), 3, '');
|
| 1326 |
+
} else {
|
| 1327 |
+
$array = array_pad([], 3, '');
|
| 1328 |
+
}
|
| 1329 |
+
$msg = str_replace(
|
| 1330 |
+
[':attribute', ':rule', ':1', ':2', ':3'],
|
| 1331 |
+
[$title, (string) $rule, $array[0], $array[1], $array[2]],
|
| 1332 |
+
$msg);
|
| 1333 |
+
}
|
| 1334 |
+
return $msg;
|
| 1335 |
+
}
|
| 1336 |
+
|
| 1337 |
+
/**
|
| 1338 |
+
* 获取数据验证的场景
|
| 1339 |
+
* @access protected
|
| 1340 |
+
* @param string $scene 验证场景
|
| 1341 |
+
* @return array
|
| 1342 |
+
*/
|
| 1343 |
+
protected function getScene($scene = '')
|
| 1344 |
+
{
|
| 1345 |
+
if (empty($scene)) {
|
| 1346 |
+
// 读取指定场景
|
| 1347 |
+
$scene = $this->currentScene;
|
| 1348 |
+
}
|
| 1349 |
+
|
| 1350 |
+
if (!empty($scene) && isset($this->scene[$scene])) {
|
| 1351 |
+
// 如果设置了验证适用场景
|
| 1352 |
+
$scene = $this->scene[$scene];
|
| 1353 |
+
if (is_string($scene)) {
|
| 1354 |
+
$scene = explode(',', $scene);
|
| 1355 |
+
}
|
| 1356 |
+
} else {
|
| 1357 |
+
$scene = [];
|
| 1358 |
+
}
|
| 1359 |
+
return $scene;
|
| 1360 |
+
}
|
| 1361 |
+
|
| 1362 |
+
public static function __callStatic($method, $params)
|
| 1363 |
+
{
|
| 1364 |
+
$class = self::make();
|
| 1365 |
+
if (method_exists($class, $method)) {
|
| 1366 |
+
return call_user_func_array([$class, $method], $params);
|
| 1367 |
+
} else {
|
| 1368 |
+
throw new \BadMethodCallException('method not exists:' . __CLASS__ . '->' . $method);
|
| 1369 |
+
}
|
| 1370 |
+
}
|
| 1371 |
+
}
|
thinkphp/library/think/View.php
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think;
|
| 13 |
+
|
| 14 |
+
class View
|
| 15 |
+
{
|
| 16 |
+
// 视图实例
|
| 17 |
+
protected static $instance;
|
| 18 |
+
// 模板引擎实例
|
| 19 |
+
public $engine;
|
| 20 |
+
// 模板变量
|
| 21 |
+
protected $data = [];
|
| 22 |
+
// 用于静态赋值的模板变量
|
| 23 |
+
protected static $var = [];
|
| 24 |
+
// 视图输出替换
|
| 25 |
+
protected $replace = [];
|
| 26 |
+
|
| 27 |
+
/**
|
| 28 |
+
* 构造函数
|
| 29 |
+
* @access public
|
| 30 |
+
* @param array $engine 模板引擎参数
|
| 31 |
+
* @param array $replace 字符串替换参数
|
| 32 |
+
*/
|
| 33 |
+
public function __construct($engine = [], $replace = [])
|
| 34 |
+
{
|
| 35 |
+
// 初始化模板引擎
|
| 36 |
+
$this->engine($engine);
|
| 37 |
+
// 基础替换字符串
|
| 38 |
+
$request = Request::instance();
|
| 39 |
+
$base = $request->root();
|
| 40 |
+
$root = strpos($base, '.') ? ltrim(dirname($base), DS) : $base;
|
| 41 |
+
if ('' != $root) {
|
| 42 |
+
$root = '/' . ltrim($root, '/');
|
| 43 |
+
}
|
| 44 |
+
// 如果 new_version 为 1 或者 new_version 不存在或者为null,则使用新版模板
|
| 45 |
+
if($GLOBALS['config']['site']['new_version'] == 1 || !isset($GLOBALS['config']['site']['new_version']) || (empty($GLOBALS['config']['site']['new_version']) && $GLOBALS['config']['site']['new_version'] != 0)){
|
| 46 |
+
$root . $static_path = '/static_new/';
|
| 47 |
+
}else{
|
| 48 |
+
$root . $static_path = '/static/';
|
| 49 |
+
}
|
| 50 |
+
$baseReplace = [
|
| 51 |
+
'__ROOT__' => $root,
|
| 52 |
+
'MAC_BASE_PATH' => $root,
|
| 53 |
+
'__URL__' => $base . '/' . $request->module() . '/' . Loader::parseName($request->controller()),
|
| 54 |
+
'__STATIC__' => $root . $static_path,
|
| 55 |
+
'__CSS__' => $root . $static_path . '/css',
|
| 56 |
+
'__JS__' => $root . $static_path .'/js',
|
| 57 |
+
];
|
| 58 |
+
$this->assign('MAC_BASE_PATH', $this->mac_base_path());
|
| 59 |
+
$this->replace = array_merge($baseReplace, (array) $replace);
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
public function mac_base_path()
|
| 63 |
+
{
|
| 64 |
+
return $GLOBALS['rootpath'];
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
/**
|
| 69 |
+
* 初始化视图
|
| 70 |
+
* @access public
|
| 71 |
+
* @param array $engine 模板引擎参数
|
| 72 |
+
* @param array $replace 字符串替换参数
|
| 73 |
+
* @return object
|
| 74 |
+
*/
|
| 75 |
+
public static function instance($engine = [], $replace = [])
|
| 76 |
+
{
|
| 77 |
+
if (is_null(self::$instance)) {
|
| 78 |
+
self::$instance = new self($engine, $replace);
|
| 79 |
+
}
|
| 80 |
+
return self::$instance;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
/**
|
| 84 |
+
* 模板变量静态赋值
|
| 85 |
+
* @access public
|
| 86 |
+
* @param mixed $name 变量名
|
| 87 |
+
* @param mixed $value 变量值
|
| 88 |
+
* @return void
|
| 89 |
+
*/
|
| 90 |
+
public static function share($name, $value = '')
|
| 91 |
+
{
|
| 92 |
+
if (is_array($name)) {
|
| 93 |
+
self::$var = array_merge(self::$var, $name);
|
| 94 |
+
} else {
|
| 95 |
+
self::$var[$name] = $value;
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
/**
|
| 100 |
+
* 模板变量赋值
|
| 101 |
+
* @access public
|
| 102 |
+
* @param mixed $name 变量名
|
| 103 |
+
* @param mixed $value 变量值
|
| 104 |
+
* @return $this
|
| 105 |
+
*/
|
| 106 |
+
public function assign($name, $value = '')
|
| 107 |
+
{
|
| 108 |
+
if (is_array($name)) {
|
| 109 |
+
$this->data = array_merge($this->data, $name);
|
| 110 |
+
} else {
|
| 111 |
+
$this->data[$name] = $value;
|
| 112 |
+
}
|
| 113 |
+
return $this;
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
/**
|
| 117 |
+
* 设置当前模板解析的引擎
|
| 118 |
+
* @access public
|
| 119 |
+
* @param array|string $options 引擎参数
|
| 120 |
+
* @return $this
|
| 121 |
+
*/
|
| 122 |
+
public function engine($options = [])
|
| 123 |
+
{
|
| 124 |
+
if (is_string($options)) {
|
| 125 |
+
$type = $options;
|
| 126 |
+
$options = [];
|
| 127 |
+
} else {
|
| 128 |
+
$type = !empty($options['type']) ? $options['type'] : 'Think';
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
$class = false !== strpos($type, '\\') ? $type : '\\think\\view\\driver\\' . ucfirst($type);
|
| 132 |
+
if (isset($options['type'])) {
|
| 133 |
+
unset($options['type']);
|
| 134 |
+
}
|
| 135 |
+
$this->engine = new $class($options);
|
| 136 |
+
return $this;
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
/**
|
| 140 |
+
* 配置模板引擎
|
| 141 |
+
* @access private
|
| 142 |
+
* @param string|array $name 参数名
|
| 143 |
+
* @param mixed $value 参数值
|
| 144 |
+
* @return $this
|
| 145 |
+
*/
|
| 146 |
+
public function config($name, $value = null)
|
| 147 |
+
{
|
| 148 |
+
$this->engine->config($name, $value);
|
| 149 |
+
return $this;
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
/**
|
| 153 |
+
* 解析和获取模板内容 用于输出
|
| 154 |
+
* @param string $template 模板文件名或者内容
|
| 155 |
+
* @param array $vars 模板输出变量
|
| 156 |
+
* @param array $replace 替换内容
|
| 157 |
+
* @param array $config 模板参数
|
| 158 |
+
* @param bool $renderContent 是否渲染内容
|
| 159 |
+
* @return string
|
| 160 |
+
* @throws Exception
|
| 161 |
+
*/
|
| 162 |
+
public function fetch($template = '', $vars = [], $replace = [], $config = [], $renderContent = false)
|
| 163 |
+
{
|
| 164 |
+
// 模板变量
|
| 165 |
+
$vars = array_merge(self::$var, $this->data, $vars);
|
| 166 |
+
|
| 167 |
+
// 页面缓存
|
| 168 |
+
ob_start();
|
| 169 |
+
ob_implicit_flush(0);
|
| 170 |
+
|
| 171 |
+
// 渲染输出
|
| 172 |
+
try {
|
| 173 |
+
$method = $renderContent ? 'display' : 'fetch';
|
| 174 |
+
// 允许用户自定义模板的字符串替换
|
| 175 |
+
$replace = array_merge($this->replace, $replace, (array) $this->engine->config('tpl_replace_string'));
|
| 176 |
+
$this->engine->config('tpl_replace_string', $replace);
|
| 177 |
+
$this->engine->$method($template, $vars, $config);
|
| 178 |
+
} catch (\Exception $e) {
|
| 179 |
+
ob_end_clean();
|
| 180 |
+
throw $e;
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
// 获取并清空缓存
|
| 184 |
+
$content = ob_get_clean();
|
| 185 |
+
// 内容过滤标签
|
| 186 |
+
Hook::listen('view_filter', $content);
|
| 187 |
+
return $content;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
/**
|
| 191 |
+
* 视图内容替换
|
| 192 |
+
* @access public
|
| 193 |
+
* @param string|array $content 被替换内容(支持批量替换)
|
| 194 |
+
* @param string $replace 替换内容
|
| 195 |
+
* @return $this
|
| 196 |
+
*/
|
| 197 |
+
public function replace($content, $replace = '')
|
| 198 |
+
{
|
| 199 |
+
if (is_array($content)) {
|
| 200 |
+
$this->replace = array_merge($this->replace, $content);
|
| 201 |
+
} else {
|
| 202 |
+
$this->replace[$content] = $replace;
|
| 203 |
+
}
|
| 204 |
+
return $this;
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
/**
|
| 208 |
+
* 渲染内容输出
|
| 209 |
+
* @access public
|
| 210 |
+
* @param string $content 内容
|
| 211 |
+
* @param array $vars 模板输出变量
|
| 212 |
+
* @param array $replace 替换内容
|
| 213 |
+
* @param array $config 模板参数
|
| 214 |
+
* @return mixed
|
| 215 |
+
*/
|
| 216 |
+
public function display($content, $vars = [], $replace = [], $config = [])
|
| 217 |
+
{
|
| 218 |
+
return $this->fetch($content, $vars, $replace, $config, true);
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
/**
|
| 222 |
+
* 模板变量赋值
|
| 223 |
+
* @access public
|
| 224 |
+
* @param string $name 变量名
|
| 225 |
+
* @param mixed $value 变量值
|
| 226 |
+
*/
|
| 227 |
+
public function __set($name, $value)
|
| 228 |
+
{
|
| 229 |
+
$this->data[$name] = $value;
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
/**
|
| 233 |
+
* 取得模板显示变量的值
|
| 234 |
+
* @access protected
|
| 235 |
+
* @param string $name 模板变量
|
| 236 |
+
* @return mixed
|
| 237 |
+
*/
|
| 238 |
+
public function __get($name)
|
| 239 |
+
{
|
| 240 |
+
return $this->data[$name];
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
/**
|
| 244 |
+
* 检测模板变量是否设置
|
| 245 |
+
* @access public
|
| 246 |
+
* @param string $name 模板变量名
|
| 247 |
+
* @return bool
|
| 248 |
+
*/
|
| 249 |
+
public function __isset($name)
|
| 250 |
+
{
|
| 251 |
+
return isset($this->data[$name]);
|
| 252 |
+
}
|
| 253 |
+
}
|
thinkphp/library/think/cache/Driver.php
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think\cache;
|
| 13 |
+
|
| 14 |
+
/**
|
| 15 |
+
* 缓存基础类
|
| 16 |
+
*/
|
| 17 |
+
abstract class Driver
|
| 18 |
+
{
|
| 19 |
+
protected $handler = null;
|
| 20 |
+
protected $options = [];
|
| 21 |
+
protected $tag;
|
| 22 |
+
|
| 23 |
+
/**
|
| 24 |
+
* 判断缓存是否存在
|
| 25 |
+
* @access public
|
| 26 |
+
* @param string $name 缓存变量名
|
| 27 |
+
* @return bool
|
| 28 |
+
*/
|
| 29 |
+
abstract public function has($name);
|
| 30 |
+
|
| 31 |
+
/**
|
| 32 |
+
* 读取缓存
|
| 33 |
+
* @access public
|
| 34 |
+
* @param string $name 缓存变量名
|
| 35 |
+
* @param mixed $default 默认值
|
| 36 |
+
* @return mixed
|
| 37 |
+
*/
|
| 38 |
+
abstract public function get($name, $default = false);
|
| 39 |
+
|
| 40 |
+
/**
|
| 41 |
+
* 写入缓存
|
| 42 |
+
* @access public
|
| 43 |
+
* @param string $name 缓存变量名
|
| 44 |
+
* @param mixed $value 存储数据
|
| 45 |
+
* @param int $expire 有效时间 0为永久
|
| 46 |
+
* @return boolean
|
| 47 |
+
*/
|
| 48 |
+
abstract public function set($name, $value, $expire = null);
|
| 49 |
+
|
| 50 |
+
/**
|
| 51 |
+
* 自增缓存(针对数值缓存)
|
| 52 |
+
* @access public
|
| 53 |
+
* @param string $name 缓存变量名
|
| 54 |
+
* @param int $step 步长
|
| 55 |
+
* @return false|int
|
| 56 |
+
*/
|
| 57 |
+
abstract public function inc($name, $step = 1);
|
| 58 |
+
|
| 59 |
+
/**
|
| 60 |
+
* 自减缓存(针对数值缓存)
|
| 61 |
+
* @access public
|
| 62 |
+
* @param string $name 缓存变量名
|
| 63 |
+
* @param int $step 步长
|
| 64 |
+
* @return false|int
|
| 65 |
+
*/
|
| 66 |
+
abstract public function dec($name, $step = 1);
|
| 67 |
+
|
| 68 |
+
/**
|
| 69 |
+
* 删除缓存
|
| 70 |
+
* @access public
|
| 71 |
+
* @param string $name 缓存变量名
|
| 72 |
+
* @return boolean
|
| 73 |
+
*/
|
| 74 |
+
abstract public function rm($name);
|
| 75 |
+
|
| 76 |
+
/**
|
| 77 |
+
* 清除缓存
|
| 78 |
+
* @access public
|
| 79 |
+
* @param string $tag 标签名
|
| 80 |
+
* @return boolean
|
| 81 |
+
*/
|
| 82 |
+
abstract public function clear($tag = null);
|
| 83 |
+
|
| 84 |
+
/**
|
| 85 |
+
* 获取实际的缓存标识
|
| 86 |
+
* @access public
|
| 87 |
+
* @param string $name 缓存名
|
| 88 |
+
* @return string
|
| 89 |
+
*/
|
| 90 |
+
protected function getCacheKey($name)
|
| 91 |
+
{
|
| 92 |
+
return $this->options['prefix'] . $name;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
/**
|
| 96 |
+
* 读取缓存并删除
|
| 97 |
+
* @access public
|
| 98 |
+
* @param string $name 缓存变量名
|
| 99 |
+
* @return mixed
|
| 100 |
+
*/
|
| 101 |
+
public function pull($name)
|
| 102 |
+
{
|
| 103 |
+
$result = $this->get($name, false);
|
| 104 |
+
if ($result) {
|
| 105 |
+
$this->rm($name);
|
| 106 |
+
return $result;
|
| 107 |
+
} else {
|
| 108 |
+
return;
|
| 109 |
+
}
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
/**
|
| 113 |
+
* 如果不存在则写入缓存
|
| 114 |
+
* @access public
|
| 115 |
+
* @param string $name 缓存变量名
|
| 116 |
+
* @param mixed $value 存储数据
|
| 117 |
+
* @param int $expire 有效时间 0为永久
|
| 118 |
+
* @return mixed
|
| 119 |
+
*/
|
| 120 |
+
public function remember($name, $value, $expire = null)
|
| 121 |
+
{
|
| 122 |
+
if (!$this->has($name)) {
|
| 123 |
+
$time = time();
|
| 124 |
+
while ($time + 5 > time() && $this->has($name . '_lock')) {
|
| 125 |
+
// 存在锁定则等待
|
| 126 |
+
usleep(200000);
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
try {
|
| 130 |
+
// 锁定
|
| 131 |
+
$this->set($name . '_lock', true);
|
| 132 |
+
if ($value instanceof \Closure) {
|
| 133 |
+
$value = call_user_func($value);
|
| 134 |
+
}
|
| 135 |
+
$this->set($name, $value, $expire);
|
| 136 |
+
// 解锁
|
| 137 |
+
$this->rm($name . '_lock');
|
| 138 |
+
} catch (\Exception $e) {
|
| 139 |
+
// 解锁
|
| 140 |
+
$this->rm($name . '_lock');
|
| 141 |
+
throw $e;
|
| 142 |
+
} catch (\throwable $e) {
|
| 143 |
+
$this->rm($name . '_lock');
|
| 144 |
+
throw $e;
|
| 145 |
+
}
|
| 146 |
+
} else {
|
| 147 |
+
$value = $this->get($name);
|
| 148 |
+
}
|
| 149 |
+
return $value;
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
/**
|
| 153 |
+
* 缓存标签
|
| 154 |
+
* @access public
|
| 155 |
+
* @param string $name 标签名
|
| 156 |
+
* @param string|array $keys 缓存标识
|
| 157 |
+
* @param bool $overlay 是否覆盖
|
| 158 |
+
* @return $this
|
| 159 |
+
*/
|
| 160 |
+
public function tag($name, $keys = null, $overlay = false)
|
| 161 |
+
{
|
| 162 |
+
if (is_null($name)) {
|
| 163 |
+
|
| 164 |
+
} elseif (is_null($keys)) {
|
| 165 |
+
$this->tag = $name;
|
| 166 |
+
} else {
|
| 167 |
+
$key = 'tag_' . md5($name);
|
| 168 |
+
if (is_string($keys)) {
|
| 169 |
+
$keys = explode(',', $keys);
|
| 170 |
+
}
|
| 171 |
+
$keys = array_map([$this, 'getCacheKey'], $keys);
|
| 172 |
+
if ($overlay) {
|
| 173 |
+
$value = $keys;
|
| 174 |
+
} else {
|
| 175 |
+
$value = array_unique(array_merge($this->getTagItem($name), $keys));
|
| 176 |
+
}
|
| 177 |
+
$this->set($key, implode(',', $value), 0);
|
| 178 |
+
}
|
| 179 |
+
return $this;
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
/**
|
| 183 |
+
* 更新标签
|
| 184 |
+
* @access public
|
| 185 |
+
* @param string $name 缓存标识
|
| 186 |
+
* @return void
|
| 187 |
+
*/
|
| 188 |
+
protected function setTagItem($name)
|
| 189 |
+
{
|
| 190 |
+
if ($this->tag) {
|
| 191 |
+
$key = 'tag_' . md5($this->tag);
|
| 192 |
+
$this->tag = null;
|
| 193 |
+
if ($this->has($key)) {
|
| 194 |
+
$value = explode(',', $this->get($key));
|
| 195 |
+
$value[] = $name;
|
| 196 |
+
$value = implode(',', array_unique($value));
|
| 197 |
+
} else {
|
| 198 |
+
$value = $name;
|
| 199 |
+
}
|
| 200 |
+
$this->set($key, $value, 0);
|
| 201 |
+
}
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
/**
|
| 205 |
+
* 获取标签包含的缓存标识
|
| 206 |
+
* @access public
|
| 207 |
+
* @param string $tag 缓存标签
|
| 208 |
+
* @return array
|
| 209 |
+
*/
|
| 210 |
+
protected function getTagItem($tag)
|
| 211 |
+
{
|
| 212 |
+
$key = 'tag_' . md5($tag);
|
| 213 |
+
$value = $this->get($key);
|
| 214 |
+
if ($value) {
|
| 215 |
+
return array_filter(explode(',', $value));
|
| 216 |
+
} else {
|
| 217 |
+
return [];
|
| 218 |
+
}
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
/**
|
| 222 |
+
* 返回句柄对象,可执行其它高级方法
|
| 223 |
+
*
|
| 224 |
+
* @access public
|
| 225 |
+
* @return object
|
| 226 |
+
*/
|
| 227 |
+
public function handler()
|
| 228 |
+
{
|
| 229 |
+
return $this->handler;
|
| 230 |
+
}
|
| 231 |
+
}
|
thinkphp/library/think/cache/driver/File.php
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think\cache\driver;
|
| 13 |
+
|
| 14 |
+
use think\cache\Driver;
|
| 15 |
+
|
| 16 |
+
/**
|
| 17 |
+
* 文件类型缓存类
|
| 18 |
+
* @author liu21st <liu21st@gmail.com>
|
| 19 |
+
*/
|
| 20 |
+
class File extends Driver
|
| 21 |
+
{
|
| 22 |
+
protected $options = [
|
| 23 |
+
'expire' => 0,
|
| 24 |
+
'cache_subdir' => true,
|
| 25 |
+
'prefix' => '',
|
| 26 |
+
'path' => CACHE_PATH,
|
| 27 |
+
'data_compress' => false,
|
| 28 |
+
];
|
| 29 |
+
|
| 30 |
+
protected $expire;
|
| 31 |
+
|
| 32 |
+
/**
|
| 33 |
+
* 构造函数
|
| 34 |
+
* @param array $options
|
| 35 |
+
*/
|
| 36 |
+
public function __construct($options = [])
|
| 37 |
+
{
|
| 38 |
+
if (!empty($options)) {
|
| 39 |
+
$this->options = array_merge($this->options, $options);
|
| 40 |
+
}
|
| 41 |
+
if (substr($this->options['path'], -1) != DS) {
|
| 42 |
+
$this->options['path'] .= DS;
|
| 43 |
+
}
|
| 44 |
+
$this->init();
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
/**
|
| 48 |
+
* 初始化检查
|
| 49 |
+
* @access private
|
| 50 |
+
* @return boolean
|
| 51 |
+
*/
|
| 52 |
+
private function init()
|
| 53 |
+
{
|
| 54 |
+
// 创建项目缓存目录
|
| 55 |
+
if (!is_dir($this->options['path'])) {
|
| 56 |
+
if (mkdir($this->options['path'], 0755, true)) {
|
| 57 |
+
return true;
|
| 58 |
+
}
|
| 59 |
+
}
|
| 60 |
+
return false;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
/**
|
| 64 |
+
* 取得变量的存储文件名
|
| 65 |
+
* @access protected
|
| 66 |
+
* @param string $name 缓存变量名
|
| 67 |
+
* @param bool $auto 是否自动创建目录
|
| 68 |
+
* @return string
|
| 69 |
+
*/
|
| 70 |
+
protected function getCacheKey($name, $auto = false)
|
| 71 |
+
{
|
| 72 |
+
$name = md5($name);
|
| 73 |
+
if ($this->options['cache_subdir']) {
|
| 74 |
+
// 使用子目录
|
| 75 |
+
$name = substr($name, 0, 2) . DS . substr($name, 2);
|
| 76 |
+
}
|
| 77 |
+
if ($this->options['prefix']) {
|
| 78 |
+
$name = $this->options['prefix'] . DS . $name;
|
| 79 |
+
}
|
| 80 |
+
$filename = $this->options['path'] . $name . '.php';
|
| 81 |
+
$dir = dirname($filename);
|
| 82 |
+
|
| 83 |
+
if ($auto && !is_dir($dir)) {
|
| 84 |
+
mkdir($dir, 0755, true);
|
| 85 |
+
}
|
| 86 |
+
return $filename;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
/**
|
| 90 |
+
* 判断缓存是否存在
|
| 91 |
+
* @access public
|
| 92 |
+
* @param string $name 缓存变量名
|
| 93 |
+
* @return bool
|
| 94 |
+
*/
|
| 95 |
+
public function has($name)
|
| 96 |
+
{
|
| 97 |
+
return $this->get($name) ? true : false;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
/**
|
| 101 |
+
* 读取缓存
|
| 102 |
+
* @access public
|
| 103 |
+
* @param string $name 缓存变量名
|
| 104 |
+
* @param mixed $default 默认值
|
| 105 |
+
* @return mixed
|
| 106 |
+
*/
|
| 107 |
+
public function get($name, $default = false)
|
| 108 |
+
{
|
| 109 |
+
$filename = $this->getCacheKey($name);
|
| 110 |
+
if (!is_file($filename)) {
|
| 111 |
+
return $default;
|
| 112 |
+
}
|
| 113 |
+
$content = file_get_contents($filename);
|
| 114 |
+
$this->expire = null;
|
| 115 |
+
if (false !== $content) {
|
| 116 |
+
$expire = (int) substr($content, 8, 12);
|
| 117 |
+
if (0 != $expire && time() > filemtime($filename) + $expire) {
|
| 118 |
+
return $default;
|
| 119 |
+
}
|
| 120 |
+
$this->expire = $expire;
|
| 121 |
+
$content = substr($content, 32);
|
| 122 |
+
if ($this->options['data_compress'] && function_exists('gzcompress')) {
|
| 123 |
+
//启用数据压缩
|
| 124 |
+
$content = gzuncompress($content);
|
| 125 |
+
}
|
| 126 |
+
$content = unserialize($content);
|
| 127 |
+
return $content;
|
| 128 |
+
} else {
|
| 129 |
+
return $default;
|
| 130 |
+
}
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
/**
|
| 134 |
+
* 写入缓存
|
| 135 |
+
* @access public
|
| 136 |
+
* @param string $name 缓存变量名
|
| 137 |
+
* @param mixed $value 存储数据
|
| 138 |
+
* @param integer|\DateTime $expire 有效时间(秒)
|
| 139 |
+
* @return boolean
|
| 140 |
+
*/
|
| 141 |
+
public function set($name, $value, $expire = null)
|
| 142 |
+
{
|
| 143 |
+
if (is_null($expire)) {
|
| 144 |
+
$expire = $this->options['expire'];
|
| 145 |
+
}
|
| 146 |
+
if ($expire instanceof \DateTime) {
|
| 147 |
+
$expire = $expire->getTimestamp() - time();
|
| 148 |
+
}
|
| 149 |
+
$filename = $this->getCacheKey($name, true);
|
| 150 |
+
if ($this->tag && !is_file($filename)) {
|
| 151 |
+
$first = true;
|
| 152 |
+
}
|
| 153 |
+
$data = serialize($value);
|
| 154 |
+
if ($this->options['data_compress'] && function_exists('gzcompress')) {
|
| 155 |
+
//数据压缩
|
| 156 |
+
$data = gzcompress($data, 3);
|
| 157 |
+
}
|
| 158 |
+
$data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
|
| 159 |
+
$result = file_put_contents($filename, $data);
|
| 160 |
+
if ($result) {
|
| 161 |
+
isset($first) && $this->setTagItem($filename);
|
| 162 |
+
clearstatcache();
|
| 163 |
+
return true;
|
| 164 |
+
} else {
|
| 165 |
+
return false;
|
| 166 |
+
}
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
/**
|
| 170 |
+
* 自��缓存(针对数值缓存)
|
| 171 |
+
* @access public
|
| 172 |
+
* @param string $name 缓存变量名
|
| 173 |
+
* @param int $step 步长
|
| 174 |
+
* @return false|int
|
| 175 |
+
*/
|
| 176 |
+
public function inc($name, $step = 1)
|
| 177 |
+
{
|
| 178 |
+
if ($this->has($name)) {
|
| 179 |
+
$value = $this->get($name) + $step;
|
| 180 |
+
$expire = $this->expire;
|
| 181 |
+
} else {
|
| 182 |
+
$value = $step;
|
| 183 |
+
$expire = 0;
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
return $this->set($name, $value, $expire) ? $value : false;
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
/**
|
| 190 |
+
* 自减缓存(针对数值缓存)
|
| 191 |
+
* @access public
|
| 192 |
+
* @param string $name 缓存变量名
|
| 193 |
+
* @param int $step 步长
|
| 194 |
+
* @return false|int
|
| 195 |
+
*/
|
| 196 |
+
public function dec($name, $step = 1)
|
| 197 |
+
{
|
| 198 |
+
if ($this->has($name)) {
|
| 199 |
+
$value = $this->get($name) - $step;
|
| 200 |
+
$expire = $this->expire;
|
| 201 |
+
} else {
|
| 202 |
+
$value = -$step;
|
| 203 |
+
$expire = 0;
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
return $this->set($name, $value, $expire) ? $value : false;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
/**
|
| 210 |
+
* 删除缓存
|
| 211 |
+
* @access public
|
| 212 |
+
* @param string $name 缓存变量名
|
| 213 |
+
* @return boolean
|
| 214 |
+
*/
|
| 215 |
+
public function rm($name)
|
| 216 |
+
{
|
| 217 |
+
$filename = $this->getCacheKey($name);
|
| 218 |
+
try {
|
| 219 |
+
return $this->unlink($filename);
|
| 220 |
+
} catch (\Exception $e) {
|
| 221 |
+
}
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
/**
|
| 225 |
+
* 清除缓存
|
| 226 |
+
* @access public
|
| 227 |
+
* @param string $tag 标签名
|
| 228 |
+
* @return boolean
|
| 229 |
+
*/
|
| 230 |
+
public function clear($tag = null)
|
| 231 |
+
{
|
| 232 |
+
if ($tag) {
|
| 233 |
+
// 指定标签清除
|
| 234 |
+
$keys = $this->getTagItem($tag);
|
| 235 |
+
foreach ($keys as $key) {
|
| 236 |
+
$this->unlink($key);
|
| 237 |
+
}
|
| 238 |
+
$this->rm('tag_' . md5($tag));
|
| 239 |
+
return true;
|
| 240 |
+
}
|
| 241 |
+
$files = (array) glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DS : '') . '*');
|
| 242 |
+
foreach ($files as $path) {
|
| 243 |
+
if (is_dir($path)) {
|
| 244 |
+
$matches = glob($path . '/*.php');
|
| 245 |
+
if (is_array($matches)) {
|
| 246 |
+
array_map('unlink', $matches);
|
| 247 |
+
}
|
| 248 |
+
rmdir($path);
|
| 249 |
+
} else {
|
| 250 |
+
unlink($path);
|
| 251 |
+
}
|
| 252 |
+
}
|
| 253 |
+
return true;
|
| 254 |
+
}
|
| 255 |
+
|
| 256 |
+
/**
|
| 257 |
+
* 判断文件是否存在后,删除
|
| 258 |
+
* @param $path
|
| 259 |
+
* @return bool
|
| 260 |
+
* @author byron sampson <xiaobo.sun@qq.com>
|
| 261 |
+
* @return boolean
|
| 262 |
+
*/
|
| 263 |
+
private function unlink($path)
|
| 264 |
+
{
|
| 265 |
+
return is_file($path) && unlink($path);
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
}
|
thinkphp/library/think/cache/driver/Lite.php
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think\cache\driver;
|
| 13 |
+
|
| 14 |
+
use think\cache\Driver;
|
| 15 |
+
|
| 16 |
+
/**
|
| 17 |
+
* 文件类型缓存类
|
| 18 |
+
* @author liu21st <liu21st@gmail.com>
|
| 19 |
+
*/
|
| 20 |
+
class Lite extends Driver
|
| 21 |
+
{
|
| 22 |
+
protected $options = [
|
| 23 |
+
'prefix' => '',
|
| 24 |
+
'path' => '',
|
| 25 |
+
'expire' => 0, // 等于 10*365*24*3600(10年)
|
| 26 |
+
];
|
| 27 |
+
|
| 28 |
+
/**
|
| 29 |
+
* 构造函数
|
| 30 |
+
* @access public
|
| 31 |
+
*
|
| 32 |
+
* @param array $options
|
| 33 |
+
*/
|
| 34 |
+
public function __construct($options = [])
|
| 35 |
+
{
|
| 36 |
+
if (!empty($options)) {
|
| 37 |
+
$this->options = array_merge($this->options, $options);
|
| 38 |
+
}
|
| 39 |
+
if (substr($this->options['path'], -1) != DS) {
|
| 40 |
+
$this->options['path'] .= DS;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
/**
|
| 46 |
+
* 取得变量的存储文件名
|
| 47 |
+
* @access protected
|
| 48 |
+
* @param string $name 缓存变量名
|
| 49 |
+
* @return string
|
| 50 |
+
*/
|
| 51 |
+
protected function getCacheKey($name)
|
| 52 |
+
{
|
| 53 |
+
return $this->options['path'] . $this->options['prefix'] . md5($name) . '.php';
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
/**
|
| 57 |
+
* 判断缓存是否存在
|
| 58 |
+
* @access public
|
| 59 |
+
* @param string $name 缓存变量名
|
| 60 |
+
* @return mixed
|
| 61 |
+
*/
|
| 62 |
+
public function has($name)
|
| 63 |
+
{
|
| 64 |
+
return $this->get($name) ? true : false;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
/**
|
| 68 |
+
* 读取缓存
|
| 69 |
+
* @access public
|
| 70 |
+
* @param string $name 缓存变量名
|
| 71 |
+
* @param mixed $default 默认值
|
| 72 |
+
* @return mixed
|
| 73 |
+
*/
|
| 74 |
+
public function get($name, $default = false)
|
| 75 |
+
{
|
| 76 |
+
$filename = $this->getCacheKey($name);
|
| 77 |
+
if (is_file($filename)) {
|
| 78 |
+
// 判断是否过期
|
| 79 |
+
$mtime = filemtime($filename);
|
| 80 |
+
if ($mtime < time()) {
|
| 81 |
+
// 清除已经过期的文件
|
| 82 |
+
unlink($filename);
|
| 83 |
+
return $default;
|
| 84 |
+
}
|
| 85 |
+
return include $filename;
|
| 86 |
+
} else {
|
| 87 |
+
return $default;
|
| 88 |
+
}
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
/**
|
| 92 |
+
* 写入缓存
|
| 93 |
+
* @access public
|
| 94 |
+
* @param string $name 缓存变量名
|
| 95 |
+
* @param mixed $value 存储数据
|
| 96 |
+
* @param integer|\DateTime $expire 有效时间(秒)
|
| 97 |
+
* @return bool
|
| 98 |
+
*/
|
| 99 |
+
public function set($name, $value, $expire = null)
|
| 100 |
+
{
|
| 101 |
+
if (is_null($expire)) {
|
| 102 |
+
$expire = $this->options['expire'];
|
| 103 |
+
}
|
| 104 |
+
if ($expire instanceof \DateTime) {
|
| 105 |
+
$expire = $expire->getTimestamp();
|
| 106 |
+
} else {
|
| 107 |
+
$expire = 0 === $expire ? 10 * 365 * 24 * 3600 : $expire;
|
| 108 |
+
$expire = time() + $expire;
|
| 109 |
+
}
|
| 110 |
+
$filename = $this->getCacheKey($name);
|
| 111 |
+
if ($this->tag && !is_file($filename)) {
|
| 112 |
+
$first = true;
|
| 113 |
+
}
|
| 114 |
+
$ret = file_put_contents($filename, ("<?php return " . var_export($value, true) . ";"));
|
| 115 |
+
// 通过设置修改时间实现有效期
|
| 116 |
+
if ($ret) {
|
| 117 |
+
isset($first) && $this->setTagItem($filename);
|
| 118 |
+
touch($filename, $expire);
|
| 119 |
+
}
|
| 120 |
+
return $ret;
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
/**
|
| 124 |
+
* 自增缓存(针对数值缓存)
|
| 125 |
+
* @access public
|
| 126 |
+
* @param string $name 缓存变量名
|
| 127 |
+
* @param int $step 步长
|
| 128 |
+
* @return false|int
|
| 129 |
+
*/
|
| 130 |
+
public function inc($name, $step = 1)
|
| 131 |
+
{
|
| 132 |
+
if ($this->has($name)) {
|
| 133 |
+
$value = $this->get($name) + $step;
|
| 134 |
+
} else {
|
| 135 |
+
$value = $step;
|
| 136 |
+
}
|
| 137 |
+
return $this->set($name, $value, 0) ? $value : false;
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
/**
|
| 141 |
+
* 自减缓存(针对数值缓存)
|
| 142 |
+
* @access public
|
| 143 |
+
* @param string $name 缓存变量名
|
| 144 |
+
* @param int $step 步长
|
| 145 |
+
* @return false|int
|
| 146 |
+
*/
|
| 147 |
+
public function dec($name, $step = 1)
|
| 148 |
+
{
|
| 149 |
+
if ($this->has($name)) {
|
| 150 |
+
$value = $this->get($name) - $step;
|
| 151 |
+
} else {
|
| 152 |
+
$value = -$step;
|
| 153 |
+
}
|
| 154 |
+
return $this->set($name, $value, 0) ? $value : false;
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
/**
|
| 158 |
+
* 删除缓存
|
| 159 |
+
* @access public
|
| 160 |
+
* @param string $name 缓存变量名
|
| 161 |
+
* @return boolean
|
| 162 |
+
*/
|
| 163 |
+
public function rm($name)
|
| 164 |
+
{
|
| 165 |
+
return unlink($this->getCacheKey($name));
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
/**
|
| 169 |
+
* 清除缓存
|
| 170 |
+
* @access public
|
| 171 |
+
* @param string $tag 标签名
|
| 172 |
+
* @return bool
|
| 173 |
+
*/
|
| 174 |
+
public function clear($tag = null)
|
| 175 |
+
{
|
| 176 |
+
if ($tag) {
|
| 177 |
+
// 指定标签清除
|
| 178 |
+
$keys = $this->getTagItem($tag);
|
| 179 |
+
foreach ($keys as $key) {
|
| 180 |
+
unlink($key);
|
| 181 |
+
}
|
| 182 |
+
$this->rm('tag_' . md5($tag));
|
| 183 |
+
return true;
|
| 184 |
+
}
|
| 185 |
+
array_map("unlink", glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DS : '') . '*.php'));
|
| 186 |
+
}
|
| 187 |
+
}
|
thinkphp/library/think/cache/driver/Memcache.php
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think\cache\driver;
|
| 13 |
+
|
| 14 |
+
use think\cache\Driver;
|
| 15 |
+
|
| 16 |
+
class Memcache extends Driver
|
| 17 |
+
{
|
| 18 |
+
protected $options = [
|
| 19 |
+
'host' => '127.0.0.1',
|
| 20 |
+
'port' => 11211,
|
| 21 |
+
'expire' => 0,
|
| 22 |
+
'timeout' => 0, // 超时时间(单位:毫秒)
|
| 23 |
+
'persistent' => true,
|
| 24 |
+
'prefix' => '',
|
| 25 |
+
];
|
| 26 |
+
|
| 27 |
+
/**
|
| 28 |
+
* 构造函数
|
| 29 |
+
* @param array $options 缓存参数
|
| 30 |
+
* @access public
|
| 31 |
+
* @throws \BadFunctionCallException
|
| 32 |
+
*/
|
| 33 |
+
public function __construct($options = [])
|
| 34 |
+
{
|
| 35 |
+
if (!extension_loaded('memcache')) {
|
| 36 |
+
throw new \BadFunctionCallException('not support: memcache');
|
| 37 |
+
}
|
| 38 |
+
if (!empty($options)) {
|
| 39 |
+
$this->options = array_merge($this->options, $options);
|
| 40 |
+
}
|
| 41 |
+
$this->handler = new \Memcache;
|
| 42 |
+
// 支持集群
|
| 43 |
+
$hosts = explode(',', $this->options['host']);
|
| 44 |
+
$ports = explode(',', $this->options['port']);
|
| 45 |
+
if (empty($ports[0])) {
|
| 46 |
+
$ports[0] = 11211;
|
| 47 |
+
}
|
| 48 |
+
// 建立连接
|
| 49 |
+
foreach ((array) $hosts as $i => $host) {
|
| 50 |
+
$port = isset($ports[$i]) ? $ports[$i] : $ports[0];
|
| 51 |
+
$this->options['timeout'] > 0 ?
|
| 52 |
+
$this->handler->addServer($host, $port, $this->options['persistent'], 1, $this->options['timeout']) :
|
| 53 |
+
$this->handler->addServer($host, $port, $this->options['persistent'], 1);
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
/**
|
| 58 |
+
* 判断缓存
|
| 59 |
+
* @access public
|
| 60 |
+
* @param string $name 缓存变量名
|
| 61 |
+
* @return bool
|
| 62 |
+
*/
|
| 63 |
+
public function has($name)
|
| 64 |
+
{
|
| 65 |
+
$key = $this->getCacheKey($name);
|
| 66 |
+
return false !== $this->handler->get($key);
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
/**
|
| 70 |
+
* 读取缓存
|
| 71 |
+
* @access public
|
| 72 |
+
* @param string $name 缓存变量名
|
| 73 |
+
* @param mixed $default 默认值
|
| 74 |
+
* @return mixed
|
| 75 |
+
*/
|
| 76 |
+
public function get($name, $default = false)
|
| 77 |
+
{
|
| 78 |
+
$result = $this->handler->get($this->getCacheKey($name));
|
| 79 |
+
return false !== $result ? $result : $default;
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
/**
|
| 83 |
+
* 写入缓存
|
| 84 |
+
* @access public
|
| 85 |
+
* @param string $name 缓存变量名
|
| 86 |
+
* @param mixed $value 存储数据
|
| 87 |
+
* @param integer|\DateTime $expire 有效时间(秒)
|
| 88 |
+
* @return bool
|
| 89 |
+
*/
|
| 90 |
+
public function set($name, $value, $expire = null)
|
| 91 |
+
{
|
| 92 |
+
if (is_null($expire)) {
|
| 93 |
+
$expire = $this->options['expire'];
|
| 94 |
+
}
|
| 95 |
+
if ($expire instanceof \DateTime) {
|
| 96 |
+
$expire = $expire->getTimestamp() - time();
|
| 97 |
+
}
|
| 98 |
+
if ($this->tag && !$this->has($name)) {
|
| 99 |
+
$first = true;
|
| 100 |
+
}
|
| 101 |
+
$key = $this->getCacheKey($name);
|
| 102 |
+
if ($this->handler->set($key, $value, 0, $expire)) {
|
| 103 |
+
isset($first) && $this->setTagItem($key);
|
| 104 |
+
return true;
|
| 105 |
+
}
|
| 106 |
+
return false;
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
/**
|
| 110 |
+
* 自增缓存(针对数值缓存)
|
| 111 |
+
* @access public
|
| 112 |
+
* @param string $name 缓存变量名
|
| 113 |
+
* @param int $step 步长
|
| 114 |
+
* @return false|int
|
| 115 |
+
*/
|
| 116 |
+
public function inc($name, $step = 1)
|
| 117 |
+
{
|
| 118 |
+
$key = $this->getCacheKey($name);
|
| 119 |
+
if ($this->handler->get($key)) {
|
| 120 |
+
return $this->handler->increment($key, $step);
|
| 121 |
+
}
|
| 122 |
+
return $this->handler->set($key, $step);
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
/**
|
| 126 |
+
* 自减缓存(针对数值缓存)
|
| 127 |
+
* @access public
|
| 128 |
+
* @param string $name 缓存变量名
|
| 129 |
+
* @param int $step 步长
|
| 130 |
+
* @return false|int
|
| 131 |
+
*/
|
| 132 |
+
public function dec($name, $step = 1)
|
| 133 |
+
{
|
| 134 |
+
$key = $this->getCacheKey($name);
|
| 135 |
+
$value = $this->handler->get($key) - $step;
|
| 136 |
+
$res = $this->handler->set($key, $value);
|
| 137 |
+
if (!$res) {
|
| 138 |
+
return false;
|
| 139 |
+
} else {
|
| 140 |
+
return $value;
|
| 141 |
+
}
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
/**
|
| 145 |
+
* 删除缓存
|
| 146 |
+
* @param string $name 缓存变量名
|
| 147 |
+
* @param bool|false $ttl
|
| 148 |
+
* @return bool
|
| 149 |
+
*/
|
| 150 |
+
public function rm($name, $ttl = false)
|
| 151 |
+
{
|
| 152 |
+
$key = $this->getCacheKey($name);
|
| 153 |
+
return false === $ttl ?
|
| 154 |
+
$this->handler->delete($key) :
|
| 155 |
+
$this->handler->delete($key, $ttl);
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
/**
|
| 159 |
+
* 清除缓存
|
| 160 |
+
* @access public
|
| 161 |
+
* @param string $tag 标签名
|
| 162 |
+
* @return bool
|
| 163 |
+
*/
|
| 164 |
+
public function clear($tag = null)
|
| 165 |
+
{
|
| 166 |
+
if ($tag) {
|
| 167 |
+
// 指定标签清除
|
| 168 |
+
$keys = $this->getTagItem($tag);
|
| 169 |
+
foreach ($keys as $key) {
|
| 170 |
+
$this->handler->delete($key);
|
| 171 |
+
}
|
| 172 |
+
$this->rm('tag_' . md5($tag));
|
| 173 |
+
return true;
|
| 174 |
+
}
|
| 175 |
+
return $this->handler->flush();
|
| 176 |
+
}
|
| 177 |
+
}
|
thinkphp/library/think/cache/driver/Memcached.php
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think\cache\driver;
|
| 13 |
+
|
| 14 |
+
use think\cache\Driver;
|
| 15 |
+
|
| 16 |
+
class Memcached extends Driver
|
| 17 |
+
{
|
| 18 |
+
protected $options = [
|
| 19 |
+
'host' => '127.0.0.1',
|
| 20 |
+
'port' => 11211,
|
| 21 |
+
'expire' => 0,
|
| 22 |
+
'timeout' => 0, // 超时时间(单位:毫秒)
|
| 23 |
+
'prefix' => '',
|
| 24 |
+
'username' => '', //账号
|
| 25 |
+
'password' => '', //密码
|
| 26 |
+
'option' => [],
|
| 27 |
+
];
|
| 28 |
+
|
| 29 |
+
/**
|
| 30 |
+
* 构造函数
|
| 31 |
+
* @param array $options 缓存参数
|
| 32 |
+
* @access public
|
| 33 |
+
*/
|
| 34 |
+
public function __construct($options = [])
|
| 35 |
+
{
|
| 36 |
+
if (!extension_loaded('memcached')) {
|
| 37 |
+
throw new \BadFunctionCallException('not support: memcached');
|
| 38 |
+
}
|
| 39 |
+
if (!empty($options)) {
|
| 40 |
+
$this->options = array_merge($this->options, $options);
|
| 41 |
+
}
|
| 42 |
+
$this->handler = new \Memcached;
|
| 43 |
+
if (!empty($this->options['option'])) {
|
| 44 |
+
$this->handler->setOptions($this->options['option']);
|
| 45 |
+
}
|
| 46 |
+
// 设置连接超时时间(单位:毫秒)
|
| 47 |
+
if ($this->options['timeout'] > 0) {
|
| 48 |
+
$this->handler->setOption(\Memcached::OPT_CONNECT_TIMEOUT, $this->options['timeout']);
|
| 49 |
+
}
|
| 50 |
+
// 支持集群
|
| 51 |
+
$hosts = explode(',', $this->options['host']);
|
| 52 |
+
$ports = explode(',', $this->options['port']);
|
| 53 |
+
if (empty($ports[0])) {
|
| 54 |
+
$ports[0] = 11211;
|
| 55 |
+
}
|
| 56 |
+
// 建立连接
|
| 57 |
+
$servers = [];
|
| 58 |
+
foreach ((array) $hosts as $i => $host) {
|
| 59 |
+
$servers[] = [$host, (isset($ports[$i]) ? $ports[$i] : $ports[0]), 1];
|
| 60 |
+
}
|
| 61 |
+
$this->handler->addServers($servers);
|
| 62 |
+
if ('' != $this->options['username']) {
|
| 63 |
+
$this->handler->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
|
| 64 |
+
$this->handler->setSaslAuthData($this->options['username'], $this->options['password']);
|
| 65 |
+
}
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
/**
|
| 69 |
+
* 判断缓存
|
| 70 |
+
* @access public
|
| 71 |
+
* @param string $name 缓存变量名
|
| 72 |
+
* @return bool
|
| 73 |
+
*/
|
| 74 |
+
public function has($name)
|
| 75 |
+
{
|
| 76 |
+
$key = $this->getCacheKey($name);
|
| 77 |
+
return $this->handler->get($key) ? true : false;
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
/**
|
| 81 |
+
* 读取缓存
|
| 82 |
+
* @access public
|
| 83 |
+
* @param string $name 缓存变量名
|
| 84 |
+
* @param mixed $default 默认值
|
| 85 |
+
* @return mixed
|
| 86 |
+
*/
|
| 87 |
+
public function get($name, $default = false)
|
| 88 |
+
{
|
| 89 |
+
$result = $this->handler->get($this->getCacheKey($name));
|
| 90 |
+
return false !== $result ? $result : $default;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
/**
|
| 94 |
+
* 写入缓存
|
| 95 |
+
* @access public
|
| 96 |
+
* @param string $name 缓存变量名
|
| 97 |
+
* @param mixed $value 存储数据
|
| 98 |
+
* @param integer|\DateTime $expire 有效时间(秒)
|
| 99 |
+
* @return bool
|
| 100 |
+
*/
|
| 101 |
+
public function set($name, $value, $expire = null)
|
| 102 |
+
{
|
| 103 |
+
if (is_null($expire)) {
|
| 104 |
+
$expire = $this->options['expire'];
|
| 105 |
+
}
|
| 106 |
+
if ($expire instanceof \DateTime) {
|
| 107 |
+
$expire = $expire->getTimestamp() - time();
|
| 108 |
+
}
|
| 109 |
+
if ($this->tag && !$this->has($name)) {
|
| 110 |
+
$first = true;
|
| 111 |
+
}
|
| 112 |
+
$key = $this->getCacheKey($name);
|
| 113 |
+
$expire = 0 == $expire ? 0 : $_SERVER['REQUEST_TIME'] + $expire;
|
| 114 |
+
if ($this->handler->set($key, $value, $expire)) {
|
| 115 |
+
isset($first) && $this->setTagItem($key);
|
| 116 |
+
return true;
|
| 117 |
+
}
|
| 118 |
+
return false;
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
/**
|
| 122 |
+
* 自增缓存(针对数值缓存)
|
| 123 |
+
* @access public
|
| 124 |
+
* @param string $name 缓存变量名
|
| 125 |
+
* @param int $step 步长
|
| 126 |
+
* @return false|int
|
| 127 |
+
*/
|
| 128 |
+
public function inc($name, $step = 1)
|
| 129 |
+
{
|
| 130 |
+
$key = $this->getCacheKey($name);
|
| 131 |
+
if ($this->handler->get($key)) {
|
| 132 |
+
return $this->handler->increment($key, $step);
|
| 133 |
+
}
|
| 134 |
+
return $this->handler->set($key, $step);
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
/**
|
| 138 |
+
* 自减缓存(针对数值缓存)
|
| 139 |
+
* @access public
|
| 140 |
+
* @param string $name 缓存变量名
|
| 141 |
+
* @param int $step 步长
|
| 142 |
+
* @return false|int
|
| 143 |
+
*/
|
| 144 |
+
public function dec($name, $step = 1)
|
| 145 |
+
{
|
| 146 |
+
$key = $this->getCacheKey($name);
|
| 147 |
+
$value = $this->handler->get($key) - $step;
|
| 148 |
+
$res = $this->handler->set($key, $value);
|
| 149 |
+
if (!$res) {
|
| 150 |
+
return false;
|
| 151 |
+
} else {
|
| 152 |
+
return $value;
|
| 153 |
+
}
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
/**
|
| 157 |
+
* 删除缓存
|
| 158 |
+
* @param string $name 缓存变量名
|
| 159 |
+
* @param bool|false $ttl
|
| 160 |
+
* @return bool
|
| 161 |
+
*/
|
| 162 |
+
public function rm($name, $ttl = false)
|
| 163 |
+
{
|
| 164 |
+
$key = $this->getCacheKey($name);
|
| 165 |
+
return false === $ttl ?
|
| 166 |
+
$this->handler->delete($key) :
|
| 167 |
+
$this->handler->delete($key, $ttl);
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
/**
|
| 171 |
+
* 清除缓存
|
| 172 |
+
* @access public
|
| 173 |
+
* @param string $tag 标签名
|
| 174 |
+
* @return bool
|
| 175 |
+
*/
|
| 176 |
+
public function clear($tag = null)
|
| 177 |
+
{
|
| 178 |
+
if ($tag) {
|
| 179 |
+
// 指定标签清除
|
| 180 |
+
$keys = $this->getTagItem($tag);
|
| 181 |
+
$this->handler->deleteMulti($keys);
|
| 182 |
+
$this->rm('tag_' . md5($tag));
|
| 183 |
+
return true;
|
| 184 |
+
}
|
| 185 |
+
return $this->handler->flush();
|
| 186 |
+
}
|
| 187 |
+
}
|
thinkphp/library/think/cache/driver/Redis.php
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think\cache\driver;
|
| 13 |
+
|
| 14 |
+
use think\cache\Driver;
|
| 15 |
+
|
| 16 |
+
/**
|
| 17 |
+
* Redis缓存驱动,适合单机部署、有前端代理实现高可用的场景,性能最好
|
| 18 |
+
* 有需要在业务层实现读写分离、或者使用RedisCluster的需求,请使用Redisd驱动
|
| 19 |
+
*
|
| 20 |
+
* 要求安装phpredis扩展:https://github.com/nicolasff/phpredis
|
| 21 |
+
* @author 尘缘 <130775@qq.com>
|
| 22 |
+
*/
|
| 23 |
+
class Redis extends Driver
|
| 24 |
+
{
|
| 25 |
+
protected $options = [
|
| 26 |
+
'host' => '127.0.0.1',
|
| 27 |
+
'port' => 6379,
|
| 28 |
+
'password' => '',
|
| 29 |
+
'select' => 0,
|
| 30 |
+
'timeout' => 0,
|
| 31 |
+
'expire' => 0,
|
| 32 |
+
'persistent' => false,
|
| 33 |
+
'prefix' => '',
|
| 34 |
+
];
|
| 35 |
+
|
| 36 |
+
/**
|
| 37 |
+
* 构造函数
|
| 38 |
+
* @param array $options 缓存参数
|
| 39 |
+
* @access public
|
| 40 |
+
*/
|
| 41 |
+
public function __construct($options = [])
|
| 42 |
+
{
|
| 43 |
+
if (!extension_loaded('redis')) {
|
| 44 |
+
throw new \BadFunctionCallException('not support: redis');
|
| 45 |
+
}
|
| 46 |
+
if (!empty($options)) {
|
| 47 |
+
$this->options = array_merge($this->options, $options);
|
| 48 |
+
}
|
| 49 |
+
$this->handler = new \Redis;
|
| 50 |
+
if ($this->options['persistent']) {
|
| 51 |
+
$this->handler->pconnect($this->options['host'], $this->options['port'], $this->options['timeout'], 'persistent_id_' . $this->options['select']);
|
| 52 |
+
} else {
|
| 53 |
+
$this->handler->connect($this->options['host'], $this->options['port'], $this->options['timeout']);
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
if ('' != $this->options['password']) {
|
| 57 |
+
$this->handler->auth($this->options['password']);
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
if (0 != $this->options['select']) {
|
| 61 |
+
$this->handler->select($this->options['select']);
|
| 62 |
+
}
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
/**
|
| 66 |
+
* 判断缓存
|
| 67 |
+
* @access public
|
| 68 |
+
* @param string $name 缓存变量名
|
| 69 |
+
* @return bool
|
| 70 |
+
*/
|
| 71 |
+
public function has($name)
|
| 72 |
+
{
|
| 73 |
+
return $this->handler->exists($this->getCacheKey($name));
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
/**
|
| 77 |
+
* 读取缓存
|
| 78 |
+
* @access public
|
| 79 |
+
* @param string $name 缓存变量名
|
| 80 |
+
* @param mixed $default 默认值
|
| 81 |
+
* @return mixed
|
| 82 |
+
*/
|
| 83 |
+
public function get($name, $default = false)
|
| 84 |
+
{
|
| 85 |
+
$value = $this->handler->get($this->getCacheKey($name));
|
| 86 |
+
if (is_null($value) || false === $value) {
|
| 87 |
+
return $default;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
try {
|
| 91 |
+
$result = 0 === strpos($value, 'think_serialize:') ? unserialize(substr($value, 16)) : $value;
|
| 92 |
+
} catch (\Exception $e) {
|
| 93 |
+
$result = $default;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
return $result;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
/**
|
| 100 |
+
* 写入缓存
|
| 101 |
+
* @access public
|
| 102 |
+
* @param string $name 缓存变量名
|
| 103 |
+
* @param mixed $value 存储数据
|
| 104 |
+
* @param integer|\DateTime $expire 有效时间(秒)
|
| 105 |
+
* @return boolean
|
| 106 |
+
*/
|
| 107 |
+
public function set($name, $value, $expire = null)
|
| 108 |
+
{
|
| 109 |
+
if (is_null($expire)) {
|
| 110 |
+
$expire = $this->options['expire'];
|
| 111 |
+
}
|
| 112 |
+
if ($expire instanceof \DateTime) {
|
| 113 |
+
$expire = $expire->getTimestamp() - time();
|
| 114 |
+
}
|
| 115 |
+
if ($this->tag && !$this->has($name)) {
|
| 116 |
+
$first = true;
|
| 117 |
+
}
|
| 118 |
+
$key = $this->getCacheKey($name);
|
| 119 |
+
$value = is_scalar($value) ? $value : 'think_serialize:' . serialize($value);
|
| 120 |
+
if ($expire) {
|
| 121 |
+
$result = $this->handler->setex($key, $expire, $value);
|
| 122 |
+
} else {
|
| 123 |
+
$result = $this->handler->set($key, $value);
|
| 124 |
+
}
|
| 125 |
+
isset($first) && $this->setTagItem($key);
|
| 126 |
+
return $result;
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
/**
|
| 130 |
+
* 自增缓存(针对数值缓存)
|
| 131 |
+
* @access public
|
| 132 |
+
* @param string $name 缓存变量名
|
| 133 |
+
* @param int $step 步长
|
| 134 |
+
* @return false|int
|
| 135 |
+
*/
|
| 136 |
+
public function inc($name, $step = 1)
|
| 137 |
+
{
|
| 138 |
+
$key = $this->getCacheKey($name);
|
| 139 |
+
|
| 140 |
+
return $this->handler->incrby($key, $step);
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
/**
|
| 144 |
+
* 自减缓存(针对数值缓存)
|
| 145 |
+
* @access public
|
| 146 |
+
* @param string $name 缓存变量名
|
| 147 |
+
* @param int $step 步长
|
| 148 |
+
* @return false|int
|
| 149 |
+
*/
|
| 150 |
+
public function dec($name, $step = 1)
|
| 151 |
+
{
|
| 152 |
+
$key = $this->getCacheKey($name);
|
| 153 |
+
|
| 154 |
+
return $this->handler->decrby($key, $step);
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
/**
|
| 158 |
+
* 删除缓存
|
| 159 |
+
* @access public
|
| 160 |
+
* @param string $name 缓存变量名
|
| 161 |
+
* @return boolean
|
| 162 |
+
*/
|
| 163 |
+
public function rm($name)
|
| 164 |
+
{
|
| 165 |
+
return $this->handler->delete($this->getCacheKey($name));
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
/**
|
| 169 |
+
* 清除缓存
|
| 170 |
+
* @access public
|
| 171 |
+
* @param string $tag 标签名
|
| 172 |
+
* @return boolean
|
| 173 |
+
*/
|
| 174 |
+
public function clear($tag = null)
|
| 175 |
+
{
|
| 176 |
+
if ($tag) {
|
| 177 |
+
// 指定标签清除
|
| 178 |
+
$keys = $this->getTagItem($tag);
|
| 179 |
+
foreach ($keys as $key) {
|
| 180 |
+
$this->handler->delete($key);
|
| 181 |
+
}
|
| 182 |
+
$this->rm('tag_' . md5($tag));
|
| 183 |
+
return true;
|
| 184 |
+
}
|
| 185 |
+
return $this->handler->flushDB();
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
}
|
thinkphp/library/think/cache/driver/Sqlite.php
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think\cache\driver;
|
| 13 |
+
|
| 14 |
+
use think\cache\Driver;
|
| 15 |
+
|
| 16 |
+
/**
|
| 17 |
+
* Sqlite缓存驱动
|
| 18 |
+
* @author liu21st <liu21st@gmail.com>
|
| 19 |
+
*/
|
| 20 |
+
class Sqlite extends Driver
|
| 21 |
+
{
|
| 22 |
+
protected $options = [
|
| 23 |
+
'db' => ':memory:',
|
| 24 |
+
'table' => 'sharedmemory',
|
| 25 |
+
'prefix' => '',
|
| 26 |
+
'expire' => 0,
|
| 27 |
+
'persistent' => false,
|
| 28 |
+
];
|
| 29 |
+
|
| 30 |
+
/**
|
| 31 |
+
* 构造函数
|
| 32 |
+
* @param array $options 缓存参数
|
| 33 |
+
* @throws \BadFunctionCallException
|
| 34 |
+
* @access public
|
| 35 |
+
*/
|
| 36 |
+
public function __construct($options = [])
|
| 37 |
+
{
|
| 38 |
+
if (!extension_loaded('sqlite')) {
|
| 39 |
+
throw new \BadFunctionCallException('not support: sqlite');
|
| 40 |
+
}
|
| 41 |
+
if (!empty($options)) {
|
| 42 |
+
$this->options = array_merge($this->options, $options);
|
| 43 |
+
}
|
| 44 |
+
$func = $this->options['persistent'] ? 'sqlite_popen' : 'sqlite_open';
|
| 45 |
+
$this->handler = $func($this->options['db']);
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
/**
|
| 49 |
+
* 获取实际的缓存标识
|
| 50 |
+
* @access public
|
| 51 |
+
* @param string $name 缓存名
|
| 52 |
+
* @return string
|
| 53 |
+
*/
|
| 54 |
+
protected function getCacheKey($name)
|
| 55 |
+
{
|
| 56 |
+
return $this->options['prefix'] . sqlite_escape_string($name);
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
/**
|
| 60 |
+
* 判断缓存
|
| 61 |
+
* @access public
|
| 62 |
+
* @param string $name 缓存变量名
|
| 63 |
+
* @return bool
|
| 64 |
+
*/
|
| 65 |
+
public function has($name)
|
| 66 |
+
{
|
| 67 |
+
$name = $this->getCacheKey($name);
|
| 68 |
+
$sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . $_SERVER['REQUEST_TIME'] . ') LIMIT 1';
|
| 69 |
+
$result = sqlite_query($this->handler, $sql);
|
| 70 |
+
return sqlite_num_rows($result);
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
/**
|
| 74 |
+
* 读取缓存
|
| 75 |
+
* @access public
|
| 76 |
+
* @param string $name 缓存变量名
|
| 77 |
+
* @param mixed $default 默认值
|
| 78 |
+
* @return mixed
|
| 79 |
+
*/
|
| 80 |
+
public function get($name, $default = false)
|
| 81 |
+
{
|
| 82 |
+
$name = $this->getCacheKey($name);
|
| 83 |
+
$sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . $_SERVER['REQUEST_TIME'] . ') LIMIT 1';
|
| 84 |
+
$result = sqlite_query($this->handler, $sql);
|
| 85 |
+
if (sqlite_num_rows($result)) {
|
| 86 |
+
$content = sqlite_fetch_single($result);
|
| 87 |
+
if (function_exists('gzcompress')) {
|
| 88 |
+
//启用数据压缩
|
| 89 |
+
$content = gzuncompress($content);
|
| 90 |
+
}
|
| 91 |
+
return unserialize($content);
|
| 92 |
+
}
|
| 93 |
+
return $default;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
/**
|
| 97 |
+
* 写入缓存
|
| 98 |
+
* @access public
|
| 99 |
+
* @param string $name 缓存变量名
|
| 100 |
+
* @param mixed $value 存储数据
|
| 101 |
+
* @param integer|\DateTime $expire 有效时间(秒)
|
| 102 |
+
* @return boolean
|
| 103 |
+
*/
|
| 104 |
+
public function set($name, $value, $expire = null)
|
| 105 |
+
{
|
| 106 |
+
$name = $this->getCacheKey($name);
|
| 107 |
+
$value = sqlite_escape_string(serialize($value));
|
| 108 |
+
if (is_null($expire)) {
|
| 109 |
+
$expire = $this->options['expire'];
|
| 110 |
+
}
|
| 111 |
+
if ($expire instanceof \DateTime) {
|
| 112 |
+
$expire = $expire->getTimestamp();
|
| 113 |
+
} else {
|
| 114 |
+
$expire = (0 == $expire) ? 0 : (time() + $expire); //缓存有效期为0表示永久缓存
|
| 115 |
+
}
|
| 116 |
+
if (function_exists('gzcompress')) {
|
| 117 |
+
//数据压缩
|
| 118 |
+
$value = gzcompress($value, 3);
|
| 119 |
+
}
|
| 120 |
+
if ($this->tag) {
|
| 121 |
+
$tag = $this->tag;
|
| 122 |
+
$this->tag = null;
|
| 123 |
+
} else {
|
| 124 |
+
$tag = '';
|
| 125 |
+
}
|
| 126 |
+
$sql = 'REPLACE INTO ' . $this->options['table'] . ' (var, value, expire, tag) VALUES (\'' . $name . '\', \'' . $value . '\', \'' . $expire . '\', \'' . $tag . '\')';
|
| 127 |
+
if (sqlite_query($this->handler, $sql)) {
|
| 128 |
+
return true;
|
| 129 |
+
}
|
| 130 |
+
return false;
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
/**
|
| 134 |
+
* 自增缓存(针对数值缓存)
|
| 135 |
+
* @access public
|
| 136 |
+
* @param string $name 缓存变量名
|
| 137 |
+
* @param int $step 步长
|
| 138 |
+
* @return false|int
|
| 139 |
+
*/
|
| 140 |
+
public function inc($name, $step = 1)
|
| 141 |
+
{
|
| 142 |
+
if ($this->has($name)) {
|
| 143 |
+
$value = $this->get($name) + $step;
|
| 144 |
+
} else {
|
| 145 |
+
$value = $step;
|
| 146 |
+
}
|
| 147 |
+
return $this->set($name, $value, 0) ? $value : false;
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
/**
|
| 151 |
+
* 自减缓存(针对数值缓存)
|
| 152 |
+
* @access public
|
| 153 |
+
* @param string $name 缓存变量名
|
| 154 |
+
* @param int $step 步���
|
| 155 |
+
* @return false|int
|
| 156 |
+
*/
|
| 157 |
+
public function dec($name, $step = 1)
|
| 158 |
+
{
|
| 159 |
+
if ($this->has($name)) {
|
| 160 |
+
$value = $this->get($name) - $step;
|
| 161 |
+
} else {
|
| 162 |
+
$value = -$step;
|
| 163 |
+
}
|
| 164 |
+
return $this->set($name, $value, 0) ? $value : false;
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
/**
|
| 168 |
+
* 删除缓存
|
| 169 |
+
* @access public
|
| 170 |
+
* @param string $name 缓存变量名
|
| 171 |
+
* @return boolean
|
| 172 |
+
*/
|
| 173 |
+
public function rm($name)
|
| 174 |
+
{
|
| 175 |
+
$name = $this->getCacheKey($name);
|
| 176 |
+
$sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\'';
|
| 177 |
+
sqlite_query($this->handler, $sql);
|
| 178 |
+
return true;
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
/**
|
| 182 |
+
* 清除缓存
|
| 183 |
+
* @access public
|
| 184 |
+
* @param string $tag 标签名
|
| 185 |
+
* @return boolean
|
| 186 |
+
*/
|
| 187 |
+
public function clear($tag = null)
|
| 188 |
+
{
|
| 189 |
+
if ($tag) {
|
| 190 |
+
$name = sqlite_escape_string($tag);
|
| 191 |
+
$sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE tag=\'' . $name . '\'';
|
| 192 |
+
sqlite_query($this->handler, $sql);
|
| 193 |
+
return true;
|
| 194 |
+
}
|
| 195 |
+
$sql = 'DELETE FROM ' . $this->options['table'];
|
| 196 |
+
sqlite_query($this->handler, $sql);
|
| 197 |
+
return true;
|
| 198 |
+
}
|
| 199 |
+
}
|
thinkphp/library/think/cache/driver/Wincache.php
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
// +----------------------------------------------------------------------
|
| 3 |
+
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
| 4 |
+
// +----------------------------------------------------------------------
|
| 5 |
+
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
| 6 |
+
// +----------------------------------------------------------------------
|
| 7 |
+
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
| 8 |
+
// +----------------------------------------------------------------------
|
| 9 |
+
// | Author: liu21st <liu21st@gmail.com>
|
| 10 |
+
// +----------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
namespace think\cache\driver;
|
| 13 |
+
|
| 14 |
+
use think\cache\Driver;
|
| 15 |
+
|
| 16 |
+
/**
|
| 17 |
+
* Wincache缓存驱动
|
| 18 |
+
* @author liu21st <liu21st@gmail.com>
|
| 19 |
+
*/
|
| 20 |
+
class Wincache extends Driver
|
| 21 |
+
{
|
| 22 |
+
protected $options = [
|
| 23 |
+
'prefix' => '',
|
| 24 |
+
'expire' => 0,
|
| 25 |
+
];
|
| 26 |
+
|
| 27 |
+
/**
|
| 28 |
+
* 构造函数
|
| 29 |
+
* @param array $options 缓存参数
|
| 30 |
+
* @throws \BadFunctionCallException
|
| 31 |
+
* @access public
|
| 32 |
+
*/
|
| 33 |
+
public function __construct($options = [])
|
| 34 |
+
{
|
| 35 |
+
if (!function_exists('wincache_ucache_info')) {
|
| 36 |
+
throw new \BadFunctionCallException('not support: WinCache');
|
| 37 |
+
}
|
| 38 |
+
if (!empty($options)) {
|
| 39 |
+
$this->options = array_merge($this->options, $options);
|
| 40 |
+
}
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
/**
|
| 44 |
+
* 判断缓存
|
| 45 |
+
* @access public
|
| 46 |
+
* @param string $name 缓存变量名
|
| 47 |
+
* @return bool
|
| 48 |
+
*/
|
| 49 |
+
public function has($name)
|
| 50 |
+
{
|
| 51 |
+
$key = $this->getCacheKey($name);
|
| 52 |
+
return wincache_ucache_exists($key);
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
/**
|
| 56 |
+
* 读取缓存
|
| 57 |
+
* @access public
|
| 58 |
+
* @param string $name 缓存变量名
|
| 59 |
+
* @param mixed $default 默认值
|
| 60 |
+
* @return mixed
|
| 61 |
+
*/
|
| 62 |
+
public function get($name, $default = false)
|
| 63 |
+
{
|
| 64 |
+
$key = $this->getCacheKey($name);
|
| 65 |
+
return wincache_ucache_exists($key) ? wincache_ucache_get($key) : $default;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
/**
|
| 69 |
+
* 写入缓存
|
| 70 |
+
* @access public
|
| 71 |
+
* @param string $name 缓存变量名
|
| 72 |
+
* @param mixed $value 存储数据
|
| 73 |
+
* @param integer|\DateTime $expire 有效时间(秒)
|
| 74 |
+
* @return boolean
|
| 75 |
+
*/
|
| 76 |
+
public function set($name, $value, $expire = null)
|
| 77 |
+
{
|
| 78 |
+
if (is_null($expire)) {
|
| 79 |
+
$expire = $this->options['expire'];
|
| 80 |
+
}
|
| 81 |
+
if ($expire instanceof \DateTime) {
|
| 82 |
+
$expire = $expire->getTimestamp() - time();
|
| 83 |
+
}
|
| 84 |
+
$key = $this->getCacheKey($name);
|
| 85 |
+
if ($this->tag && !$this->has($name)) {
|
| 86 |
+
$first = true;
|
| 87 |
+
}
|
| 88 |
+
if (wincache_ucache_set($key, $value, $expire)) {
|
| 89 |
+
isset($first) && $this->setTagItem($key);
|
| 90 |
+
return true;
|
| 91 |
+
}
|
| 92 |
+
return false;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
/**
|
| 96 |
+
* 自增缓存(针对数值缓存)
|
| 97 |
+
* @access public
|
| 98 |
+
* @param string $name 缓存变量名
|
| 99 |
+
* @param int $step 步长
|
| 100 |
+
* @return false|int
|
| 101 |
+
*/
|
| 102 |
+
public function inc($name, $step = 1)
|
| 103 |
+
{
|
| 104 |
+
$key = $this->getCacheKey($name);
|
| 105 |
+
return wincache_ucache_inc($key, $step);
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
/**
|
| 109 |
+
* 自减缓存(针对数值缓存)
|
| 110 |
+
* @access public
|
| 111 |
+
* @param string $name 缓存变量名
|
| 112 |
+
* @param int $step 步长
|
| 113 |
+
* @return false|int
|
| 114 |
+
*/
|
| 115 |
+
public function dec($name, $step = 1)
|
| 116 |
+
{
|
| 117 |
+
$key = $this->getCacheKey($name);
|
| 118 |
+
return wincache_ucache_dec($key, $step);
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
/**
|
| 122 |
+
* 删除缓存
|
| 123 |
+
* @access public
|
| 124 |
+
* @param string $name 缓存变量名
|
| 125 |
+
* @return boolean
|
| 126 |
+
*/
|
| 127 |
+
public function rm($name)
|
| 128 |
+
{
|
| 129 |
+
return wincache_ucache_delete($this->getCacheKey($name));
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
/**
|
| 133 |
+
* 清除缓存
|
| 134 |
+
* @access public
|
| 135 |
+
* @param string $tag 标签名
|
| 136 |
+
* @return boolean
|
| 137 |
+
*/
|
| 138 |
+
public function clear($tag = null)
|
| 139 |
+
{
|
| 140 |
+
if ($tag) {
|
| 141 |
+
$keys = $this->getTagItem($tag);
|
| 142 |
+
foreach ($keys as $key) {
|
| 143 |
+
wincache_ucache_delete($key);
|
| 144 |
+
}
|
| 145 |
+
$this->rm('tag_' . md5($tag));
|
| 146 |
+
return true;
|
| 147 |
+
} else {
|
| 148 |
+
return wincache_ucache_clear();
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
}
|