diff --git a/application/api/controller/Api.php b/application/api/controller/Api.php
index dde56e2a995099b8168c99f168ab6014acf8992a..2ab1ea156fc615055067fd988cdf534b139dd2d1 100644
--- a/application/api/controller/Api.php
+++ b/application/api/controller/Api.php
@@ -32,7 +32,7 @@ class Api
{
$this->request = $request;
$this->init();
- $this->uid = $this->clientInfo['uid'];
+ //$this->uid = $this->clientInfo['uid'];
}
@@ -44,14 +44,12 @@ class Api
{
//所有ajax请求的options预请求都会直接返回200,如果需要单独针对某个类中的方法,可以在路由规则中进行配置
if($this->request->isOptions()){
-
return self::returnMsg(200,'success');
}
if(!Oauth::match($this->noAuth)){ //请求方法白名单
$oauth = app('app\sns\controller\Oauth'); //tp5.1容器,直接绑定类到容器进行实例化
return $this->clientInfo = $oauth->authenticate();;
}
-
}
/**
diff --git a/application/api/controller/v1/User.php b/application/api/controller/v1/User.php
index d846d7c0f4e14112120210aacdcd66f2c378db52..1568112ee9621d7778b956875fa0d9013211b625 100644
--- a/application/api/controller/v1/User.php
+++ b/application/api/controller/v1/User.php
@@ -2,14 +2,14 @@
namespace app\api\controller\v1;
-use think\Controller;
use think\Request;
use app\api\controller\Api;
use app\api\common\Page;
-use app\api\validate\ValidataCommon;
+use app\sns\validate\ValidataCommon;
+use Volc\Service\Visual;
class User extends Api
-{
+{
/**
* 不需要鉴权方法
* index、save不需要鉴权
@@ -17,20 +17,39 @@ class User extends Api
* 所有方法都不需要鉴权
* [*]
*/
- protected $noAuth = ['create'];
-
- /**
+ protected $noAuth = ['index'];
+
+ /**
* 显示资源列表
*
* @return \think\Response
*/
- public function index()
- {
+ public function index(Request $request)
+ {
+
+
+ $image_base64 = $request->post('base64',"");
+ if(empty($image_base64)){
+ return self::returnMsg(401,'请上传图片');
+ }
+
+
+
+ $client = Visual::getInstance();
+ $client->setAccessKey("AKLTNGJkMmQ3N2MyYjgxNDQ2YWEwOWFlZmNhOGQ0NDQ4MGE");
+ $client->setSecretKey("Wmpaa01qQXlaREZtWkdNeE5EazVZemhoWXpKbFpESXdPVGhrTWpBNU1EZw==");
+ $response = $client->JPCartoon(['form_params' => ['image_base64' => $image_base64]]);
+ $res = json_decode($response,1);
+ p($res);
+ if($res['code'] == "10000"){
+ echo '';
+ }
+ //echo $response;
//通用参数验证
- ValidataCommon::validateCheck(['lng' => 'require', 'lat' => 'require'], $this->request->param('')); //参数验证
+ // ValidataCommon::validateCheck(['lng' => 'require', 'lat' => 'require'], $this->request->param('')); //参数验证
//通用分页
- list($page, $size) = Page::getPage($this->request->param(''));
- echo($this->uid);
+ // list($page, $size) = Page::getPage($this->request->param(''));
+ // echo($this->uid);
}
/**
@@ -38,8 +57,7 @@ class User extends Api
*
* @return \think\Response
*/
- public function create()
- {
+ public function create() {
echo "create";
}
diff --git a/application/common.php b/application/common.php
index 55d22f266ef5664a8d25635cafe58acba2ed13d2..20341a5bfb0c3b35cbb37f06b09cde54456518ae 100644
--- a/application/common.php
+++ b/application/common.php
@@ -10,3 +10,26 @@
// +----------------------------------------------------------------------
// 应用公共文件
+/**
+ * 打印输出
+ * @param array $obj
+ * @param int $exit
+ */
+function p($obj = array(),$exit = 0,$format=1){
+ if($format){
+ echo "
";
+ print_r($obj);
+ echo "
";
+ } else {
+ print_r($obj);
+ }
+ $exit && exit;
+}
+function base64EncodeImage ($image_file) {
+ $base64_image = '';
+ $image_info = getimagesize($image_file);
+ $image_data = fread(fopen($image_file, 'r'), filesize($image_file));
+ // $base64_image = 'data:' . $image_info['mime'] . ';base64,' . chunk_split(base64_encode($image_data));
+ $base64_image = chunk_split(base64_encode($image_data));
+ return $base64_image;
+}
diff --git a/application/sns/controller/Oauth.php b/application/sns/controller/Oauth.php
index ab3a6ff1af804ea8fbff80cd00848448283db76f..2c83995c8f8223ded6d9ac10c3c61bee879d117e 100644
--- a/application/sns/controller/Oauth.php
+++ b/application/sns/controller/Oauth.php
@@ -48,15 +48,14 @@ class Oauth
{
//获取头部信息
try {
- $authorization = Request::header('authorization'); //获取请求中的authorization字段,值形式为USERID asdsajh..这种形式
- $authorization = explode(" ", $authorization); //explode分割,获取后面一窜base64加密数据
- $authorizationInfo = explode(":", base64_decode($authorization[1])); //对base_64解密,获取到用:拼接的自字符串,然后分割,可获取appid、accesstoken、uid这三个参数
+ $authorization = Request::header('authorization'); //获取请求中的authorization字段
+ $authorizationInfo = explode(":", base64_decode($authorization)); //对base_64解密,获取到用:拼接的自字符串,然后分割,可获取appid、accesstoken,uid这三个参数
$clientInfo['uid'] = $authorizationInfo[2];
$clientInfo['appid'] = $authorizationInfo[0];
$clientInfo['access_token'] = $authorizationInfo[1];
return $clientInfo;
} catch (Exception $e) {
- return self::returnMsg(401,'Invalid authorization credentials',Request::header(''));
+ return self::returnMsg(401,'Invalid authorization credentials',[]);
}
}
@@ -67,13 +66,14 @@ class Oauth
public static function certification($data = []){
$getCacheAccessToken = Cache::get(self::$accessTokenPrefix . $data['access_token']); //获取缓存access_token
+ //p($getCacheAccessToken,1);
if(!$getCacheAccessToken){
return self::returnMsg(401,'fail',"access_token不存在或为空");
}
- if($getCacheAccessToken['client']['appid'] !== $data['appid']){
-
- return self::returnMsg(401,'fail',"appid错误"); //appid与缓存中的appid不匹配
- }
+// if($getCacheAccessToken['client']['appid'] !== $data['appid']){
+//
+// return self::returnMsg(401,'fail',"appid错误"); //appid与缓存中的appid不匹配
+// }
return $data;
}
diff --git a/application/sns/controller/Token.php b/application/sns/controller/Token.php
index 921df0b2e9b942c64e874dcc47e44afcb844e3e4..a59fff882b971427c7fba9db0d3ebe9929bc7054 100644
--- a/application/sns/controller/Token.php
+++ b/application/sns/controller/Token.php
@@ -23,11 +23,18 @@ class Token
/**
* 测试appid,正式请数据库进行相关验证
*/
- public static $appid = 'tp5restfultest';
+ public static $appId = 'tp5restfultest';
/**
* appsercet
*/
- public static $appsercet = '123456';
+ public static $appSercet = '123456';
+
+ /*
+ *
+ */
+ public function login(){
+
+ }
/**
* 生成token
@@ -40,13 +47,9 @@ class Token
return self::returnMsg(401,$validate->getError());
}
self::checkParams(input('')); //参数校验
- //数据库已经有一个用户,这里需要根据input('mobile')去数据库查找有没有这个用户
- $userInfo = [
- 'uid' => 1,
- 'mobile'=> input('mobile')
- ]; //虚拟一个uid返回给调用方
+
try {
- $accessToken = self::setAccessToken(array_merge($userInfo,input(''))); //传入参数应该是根据手机号查询改用户的数据
+ $accessToken = self::setAccessToken(input('')); //传入参数应该是根据手机号查询改用户的数据
return self::returnMsg(200,'success',$accessToken);
} catch (Exception $e) {
return self::returnMsg(500,'fail',$e);
@@ -77,22 +80,20 @@ class Token
*/
public static function checkParams($params = [])
{
- //时间戳校验
- if(abs($params['timestamp'] - time()) > self::$timeDif){
-
- return self::returnMsg(401,'请求时间戳与服务器时间戳异常','timestamp:'.time());
- }
//appid检测,这里是在本地进行测试,正式的应该是查找数据库或者redis进行验证
- if($params['appid'] !== self::$appid){
+ if($params['appId'] !== self::$appId){
return self::returnMsg(401,'appid 错误');
}
+ if($params['appSercet'] !== self::$appSercet){
+ return self::returnMsg(401,'appSercet错误');
+ }
//签名检测
- $sign = Oauth::makeSign($params,self::$appsercet);
- if($sign !== $params['sign']){
- return self::returnMsg(401,'sign错误','sign:'.$sign);
- }
+// $sign = Oauth::makeSign($params,self::$appsercet);
+// if($sign !== $params['sign']){
+// return self::returnMsg(401,'sign错误','sign:'.$sign);
+// }
}
/**
@@ -104,17 +105,16 @@ class Token
{
//生成令牌
$accessToken = self::buildAccessToken();
- $refresh_token = self::getRefreshToken($clientInfo['appid']);
+ $refresh_token = self::getRefreshToken($clientInfo['appId']);
$accessTokenInfo = [
'access_token' => $accessToken,//访问令牌
'expires_time' => time() + self::$expires, //过期时间时间戳
'refresh_token' => $refresh_token,//刷新的token
'refresh_expires_time' => time() + self::$refreshExpires, //过期时间时间戳
- 'client' => $clientInfo,//用户信息
];
self::saveAccessToken($accessToken, $accessTokenInfo); //保存本次token
- self::saveRefreshToken($refresh_token,$clientInfo['appid']);
+ self::saveRefreshToken($refresh_token,$clientInfo['appId']);
return $accessTokenInfo;
}
@@ -134,7 +134,7 @@ class Token
{
//生成AccessToken
$str_pol = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz";
- return substr(str_shuffle($str_pol), 0, $lenght);
+ return 'at.'.substr(str_shuffle($str_pol), 0, $lenght).substr(str_shuffle($str_pol), 0, $lenght);
}
diff --git a/application/sns/validate/Token.php b/application/sns/validate/Token.php
index e1cd8f24fa0ed6671c6c18625d5b2341481c30e7..367896c5dde178068f23514a2372207c92608de0 100644
--- a/application/sns/validate/Token.php
+++ b/application/sns/validate/Token.php
@@ -9,18 +9,12 @@ class Token extends Validate
{
protected $rule = [
- 'appid' => 'require',
- 'mobile' => 'mobile|require',
- 'nonce' => 'require',
- 'timestamp' => 'number|require',
- 'sign' => 'require'
+ 'appId' => 'require',
+ 'appSercet' => 'require',
];
protected $message = [
- 'appid.require' => 'appid不能为空',
- 'mobile.mobile' => '手机格式错误',
- 'nonce.require' => '随机数不能为空',
- 'timestamp.number' => '时间戳格式错误',
- 'sign.require' => '签名不能为空',
+ 'appId.require' => 'appId不能为空',
+ 'appSercet.require' => 'appSercet不能为空',
];
}
diff --git a/composer.json b/composer.json
index e71e919c1e7df8750bb0ec2fd1a71a7ab2add4c8..24b18acac4939b1870bf20dd8fc8687c6b306289 100644
--- a/composer.json
+++ b/composer.json
@@ -19,7 +19,8 @@
"php": ">=5.6.0",
"topthink/framework": "5.1.*",
"ext-curl": "*",
- "ext-json": "*"
+ "ext-json": "*",
+ "volcengine/volc-sdk-php": "^1.0"
},
"autoload": {
"psr-4": {
diff --git a/composer.lock b/composer.lock
index 18212114477c682ef2ae2d8076098db4c57724d9..bd0b1d3979345a7060927248105f4add1972ae9d 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1,24 +1,1005 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "296bd8a1d28e39d56dcd80ce7be249f3",
+ "content-hash": "f38d1798da5f06fb1b9a010006d8eaab",
"packages": [
+ {
+ "name": "firebase/php-jwt",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/firebase/php-jwt.git",
+ "reference": "d2113d9b2e0e349796e72d2a63cf9319100382d2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d2113d9b2e0e349796e72d2a63cf9319100382d2",
+ "reference": "d2113d9b2e0e349796e72d2a63cf9319100382d2",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": ">=4.8 <=9"
+ },
+ "suggest": {
+ "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Firebase\\JWT\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Neuman Vong",
+ "email": "neuman+pear@twilio.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Anant Narayanan",
+ "email": "anant@php.net",
+ "role": "Developer"
+ }
+ ],
+ "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
+ "homepage": "https://github.com/firebase/php-jwt",
+ "keywords": [
+ "jwt",
+ "php"
+ ],
+ "time": "2021-06-23T19:00:23+00:00"
+ },
+ {
+ "name": "google/auth",
+ "version": "v1.16.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/googleapis/google-auth-library-php.git",
+ "reference": "c747738d2dd450f541f09f26510198fbedd1c8a0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/googleapis/google-auth-library-php/zipball/c747738d2dd450f541f09f26510198fbedd1c8a0",
+ "reference": "c747738d2dd450f541f09f26510198fbedd1c8a0",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "firebase/php-jwt": "~2.0|~3.0|~4.0|~5.0",
+ "guzzlehttp/guzzle": "^5.3.1|^6.2.1|^7.0",
+ "guzzlehttp/psr7": "^1.2",
+ "php": ">=5.4",
+ "psr/cache": "^1.0|^2.0",
+ "psr/http-message": "^1.0"
+ },
+ "require-dev": {
+ "guzzlehttp/promises": "0.1.1|^1.3",
+ "kelvinmo/simplejwt": "^0.2.5|^0.5.1",
+ "phpseclib/phpseclib": "^2.0.31",
+ "phpunit/phpunit": "^4.8.36|^5.7",
+ "sebastian/comparator": ">=1.2.3",
+ "squizlabs/php_codesniffer": "^3.5"
+ },
+ "suggest": {
+ "phpseclib/phpseclib": "May be used in place of OpenSSL for signing strings or for token management. Please require version ^2."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Google\\Auth\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "description": "Google Auth Library for PHP",
+ "homepage": "http://github.com/google/google-auth-library-php",
+ "keywords": [
+ "Authentication",
+ "google",
+ "oauth2"
+ ],
+ "time": "2021-06-22T18:06:03+00:00"
+ },
+ {
+ "name": "google/common-protos",
+ "version": "1.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/googleapis/common-protos-php.git",
+ "reference": "c348d1545fbeac7df3c101fdc687aba35f49811f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/googleapis/common-protos-php/zipball/c348d1545fbeac7df3c101fdc687aba35f49811f",
+ "reference": "c348d1545fbeac7df3c101fdc687aba35f49811f",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "google/protobuf": "^3.6.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.36",
+ "sami/sami": "*"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Google\\": "src",
+ "GPBMetadata\\Google\\": "metadata"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "description": "Google API Common Protos for PHP",
+ "homepage": "https://github.com/googleapis/common-protos-php",
+ "keywords": [
+ "google"
+ ],
+ "time": "2021-06-29T15:42:12+00:00"
+ },
+ {
+ "name": "google/gax",
+ "version": "v1.7.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/googleapis/gax-php.git",
+ "reference": "48cd41dbea7b8fece8c41100022786d149de64ca"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/googleapis/gax-php/zipball/48cd41dbea7b8fece8c41100022786d149de64ca",
+ "reference": "48cd41dbea7b8fece8c41100022786d149de64ca",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "google/auth": "^1.2.0",
+ "google/common-protos": "^1.0",
+ "google/grpc-gcp": "^0.1.0",
+ "google/protobuf": "^3.12.2",
+ "grpc/grpc": "^1.13",
+ "guzzlehttp/promises": "^1.3",
+ "guzzlehttp/psr7": "^1.2",
+ "php": ">=5.5"
+ },
+ "conflict": {
+ "ext-protobuf": "<3.7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.36",
+ "squizlabs/php_codesniffer": "3.*"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Google\\ApiCore\\": "src",
+ "GPBMetadata\\ApiCore\\": "metadata/ApiCore"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "Google API Core for PHP",
+ "homepage": "https://github.com/googleapis/gax-php",
+ "keywords": [
+ "google"
+ ],
+ "time": "2021-03-22T22:27:35+00:00"
+ },
+ {
+ "name": "google/grpc-gcp",
+ "version": "0.1.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/GoogleCloudPlatform/grpc-gcp-php.git",
+ "reference": "bb9bdbf62f6ae4e73d5209d85b1d0a0b9855ff36"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/GoogleCloudPlatform/grpc-gcp-php/zipball/bb9bdbf62f6ae4e73d5209d85b1d0a0b9855ff36",
+ "reference": "bb9bdbf62f6ae4e73d5209d85b1d0a0b9855ff36",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "google/auth": "^1.3",
+ "google/protobuf": "^v3.3.0",
+ "grpc/grpc": "^v1.13.0",
+ "php": ">=5.5.0",
+ "psr/cache": "^1.0.1"
+ },
+ "require-dev": {
+ "google/cloud-spanner": "^1.7",
+ "phpunit/phpunit": "4.8.36"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Grpc\\Gcp\\": "src/"
+ },
+ "classmap": [
+ "src/generated/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "description": "gRPC GCP library for channel management",
+ "time": "2020-05-26T17:21:09+00:00"
+ },
+ {
+ "name": "google/protobuf",
+ "version": "v3.12.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/protocolbuffers/protobuf-php.git",
+ "reference": "e8316b6aaf62a4dc506b8f88a88e506280c8538d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/e8316b6aaf62a4dc506b8f88a88e506280c8538d",
+ "reference": "e8316b6aaf62a4dc506b8f88a88e506280c8538d",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": ">=4.8.0"
+ },
+ "suggest": {
+ "ext-bcmath": "Need to support JSON deserialization"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Google\\Protobuf\\": "src/Google/Protobuf",
+ "GPBMetadata\\Google\\Protobuf\\": "src/GPBMetadata/Google/Protobuf"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "proto library for PHP",
+ "homepage": "https://developers.google.com/protocol-buffers/",
+ "keywords": [
+ "proto"
+ ],
+ "time": "2020-07-28T18:31:07+00:00"
+ },
+ {
+ "name": "grpc/grpc",
+ "version": "1.38.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/grpc/grpc-php.git",
+ "reference": "e1687963fb0b087d0c70e75d3bfff9062eaeb215"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/grpc/grpc-php/zipball/e1687963fb0b087d0c70e75d3bfff9062eaeb215",
+ "reference": "e1687963fb0b087d0c70e75d3bfff9062eaeb215",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.0.0"
+ },
+ "require-dev": {
+ "google/auth": "^v1.3.0"
+ },
+ "suggest": {
+ "ext-protobuf": "For better performance, install the protobuf C extension.",
+ "google/protobuf": "To get started using grpc quickly, install the native protobuf library."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Grpc\\": "src/lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "description": "gRPC library for PHP",
+ "homepage": "https://grpc.io",
+ "keywords": [
+ "rpc"
+ ],
+ "time": "2021-05-24T20:45:41+00:00"
+ },
+ {
+ "name": "guzzlehttp/guzzle",
+ "version": "6.5.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle.git",
+ "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e",
+ "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-json": "*",
+ "guzzlehttp/promises": "^1.0",
+ "guzzlehttp/psr7": "^1.6.1",
+ "php": ">=5.5",
+ "symfony/polyfill-intl-idn": "^1.17.0"
+ },
+ "require-dev": {
+ "ext-curl": "*",
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
+ "psr/log": "^1.1"
+ },
+ "suggest": {
+ "psr/log": "Required for using the Log middleware"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "6.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Guzzle is a PHP HTTP client library",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "rest",
+ "web service"
+ ],
+ "time": "2020-06-16T21:01:06+00:00"
+ },
+ {
+ "name": "guzzlehttp/promises",
+ "version": "1.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/promises.git",
+ "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d",
+ "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.5"
+ },
+ "require-dev": {
+ "symfony/phpunit-bridge": "^4.4 || ^5.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Guzzle promises library",
+ "keywords": [
+ "promise"
+ ],
+ "time": "2021-03-07T09:25:29+00:00"
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "1.8.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "dc960a912984efb74d0a90222870c72c87f10c91"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91",
+ "reference": "dc960a912984efb74d0a90222870c72c87f10c91",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.4.0",
+ "psr/http-message": "~1.0",
+ "ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
+ },
+ "provide": {
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "ext-zlib": "*",
+ "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
+ },
+ "suggest": {
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Schultze",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ],
+ "time": "2021-04-26T09:17:50+00:00"
+ },
+ {
+ "name": "psr/cache",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/cache.git",
+ "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
+ "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Cache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for caching libraries",
+ "keywords": [
+ "cache",
+ "psr",
+ "psr-6"
+ ],
+ "time": "2016-08-06T20:24:11+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "time": "2016-08-06T14:39:51+00:00"
+ },
+ {
+ "name": "ralouphie/getallheaders",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "^5 || ^6.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/getallheaders.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "description": "A polyfill for getallheaders.",
+ "time": "2019-03-08T08:55:37+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-idn",
+ "version": "v1.23.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-idn.git",
+ "reference": "65bd267525e82759e7d8c4e8ceea44f398838e65"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/65bd267525e82759e7d8c4e8ceea44f398838e65",
+ "reference": "65bd267525e82759e7d8c4e8ceea44f398838e65",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1",
+ "symfony/polyfill-intl-normalizer": "^1.10",
+ "symfony/polyfill-php72": "^1.10"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Idn\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Laurent Bassin",
+ "email": "laurent@bassin.info"
+ },
+ {
+ "name": "Trevor Rowbotham",
+ "email": "trevor.rowbotham@pm.me"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "idn",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-05-27T09:27:20+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.23.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8",
+ "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ],
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-02-19T12:13:01+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php72",
+ "version": "v1.23.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php72.git",
+ "reference": "9a142215a36a3888e30d0a9eeea9766764e96976"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9a142215a36a3888e30d0a9eeea9766764e96976",
+ "reference": "9a142215a36a3888e30d0a9eeea9766764e96976",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php72\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-05-27T09:17:38+00:00"
+ },
{
"name": "topthink/framework",
- "version": "v5.1.19",
+ "version": "v5.1.41",
"source": {
"type": "git",
"url": "https://github.com/top-think/framework.git",
- "reference": "3a0fea90ed2a99b181ce503090e08be1171ed091"
+ "reference": "7137741a323a4a60cfca334507cd1812fac91bb2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/top-think/framework/zipball/3a0fea90ed2a99b181ce503090e08be1171ed091",
- "reference": "3a0fea90ed2a99b181ce503090e08be1171ed091",
- "shasum": ""
+ "url": "https://api.github.com/repos/top-think/framework/zipball/7137741a323a4a60cfca334507cd1812fac91bb2",
+ "reference": "7137741a323a4a60cfca334507cd1812fac91bb2",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
},
"require": {
"php": ">=5.6.0",
@@ -55,27 +1036,33 @@
"orm",
"thinkphp"
],
- "time": "2018-07-13T14:10:28+00:00"
+ "time": "2021-01-11T02:51:29+00:00"
},
{
"name": "topthink/think-installer",
- "version": "v2.0.0",
+ "version": "v2.0.5",
"source": {
"type": "git",
"url": "https://github.com/top-think/think-installer.git",
- "reference": "f5400a12c60e513911aef41fe443fa6920952675"
+ "reference": "38ba647706e35d6704b5d370c06f8a160b635f88"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/top-think/think-installer/zipball/f5400a12c60e513911aef41fe443fa6920952675",
- "reference": "f5400a12c60e513911aef41fe443fa6920952675",
- "shasum": ""
+ "url": "https://api.github.com/repos/top-think/think-installer/zipball/38ba647706e35d6704b5d370c06f8a160b635f88",
+ "reference": "38ba647706e35d6704b5d370c06f8a160b635f88",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
},
"require": {
- "composer-plugin-api": "^1.0"
+ "composer-plugin-api": "^1.0||^2.0"
},
"require-dev": {
- "composer/composer": "1.0.*@dev"
+ "composer/composer": "^1.0||^2.0"
},
"type": "composer-plugin",
"extra": {
@@ -96,7 +1083,53 @@
"email": "448901948@qq.com"
}
],
- "time": "2018-05-11T06:45:42+00:00"
+ "time": "2021-01-14T12:12:14+00:00"
+ },
+ {
+ "name": "volcengine/volc-sdk-php",
+ "version": "v1.0.14",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/volcengine/volc-sdk-php.git",
+ "reference": "6ba954336e4f2e1e838b81c08a429a4ce45b7d18"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/volcengine/volc-sdk-php/zipball/6ba954336e4f2e1e838b81c08a429a4ce45b7d18",
+ "reference": "6ba954336e4f2e1e838b81c08a429a4ce45b7d18",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.cloud.tencent.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-openssl": "*",
+ "google/gax": "*",
+ "google/protobuf": "v3.12.*",
+ "guzzlehttp/guzzle": "~6.2",
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Volc\\": "src/",
+ "Test\\": "tests/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "bytedance"
+ }
+ ],
+ "time": "2021-07-01T02:02:28+00:00"
}
],
"packages-dev": [],
@@ -106,7 +1139,10 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
- "php": ">=5.6.0"
+ "php": ">=5.6.0",
+ "ext-curl": "*",
+ "ext-json": "*"
},
- "platform-dev": []
+ "platform-dev": [],
+ "plugin-api-version": "2.0.0"
}
diff --git a/data/extend/Send.php b/data/extend/Send.php
index b1a32b89a17d4f596fe1d914ec4972019957f9f8..b1c60190f6c12c395681ce02c6546ac77911541e 100644
--- a/data/extend/Send.php
+++ b/data/extend/Send.php
@@ -8,7 +8,7 @@ trait Send
*/
public static function returnMsg($code = 200,$message = '',$data = [],$header = [])
{
- http_response_code($code); //设置返回头部
+ http_response_code(200); //设置返回头部
$return['code'] = (int)$code;
$return['message'] = $message;
$return['data'] = is_array($data) ? $data : ['info'=>$data];
diff --git a/route/route.php b/route/route.php
index ff7c64aff14ceea05f1f701112e712436d2a89d8..2eb995d1491096133ef309646742866f8faa99ff 100644
--- a/route/route.php
+++ b/route/route.php
@@ -1,10 +1,13 @@
ThinkPHP5.1的运行环境要求PHP5.6+。
+> ThinkPHP5.1的运行环境要求PHP5.6+ 兼容PHP8.0。
+
## 安装
@@ -65,9 +70,15 @@ composer update topthink/framework
+ [完全开发手册](https://www.kancloud.cn/manual/thinkphp5_1/content)
+ [升级指导](https://www.kancloud.cn/manual/thinkphp5_1/354155)
+
+## 官方服务
+
++ [应用服务市场](https://market.topthink.com/)
++ [ThinkAPI——统一API服务](https://docs.topthink.com/think-api)
+
## 命名规范
-`ThinkPHP5`遵循PSR-2命名规范和PSR-4自动加载规范。
+`ThinkPHP5.1`遵循PSR-2命名规范和PSR-4自动加载规范。
## 参与开发
diff --git a/thinkphp/base.php b/thinkphp/base.php
index 084dd74dba3d99fdd6ed137d46762eb048eaf526..d7238cc6ac249ca8e4c2d96bb542f0e10581ce65 100644
--- a/thinkphp/base.php
+++ b/thinkphp/base.php
@@ -28,28 +28,6 @@ if (interface_exists('Psr\Log\LoggerInterface')) {
{}
}
-// 注册核心类的静态代理
-Facade::bind([
- facade\App::class => App::class,
- facade\Build::class => Build::class,
- facade\Cache::class => Cache::class,
- facade\Config::class => Config::class,
- facade\Cookie::class => Cookie::class,
- facade\Debug::class => Debug::class,
- facade\Env::class => Env::class,
- facade\Hook::class => Hook::class,
- facade\Lang::class => Lang::class,
- facade\Log::class => Log::class,
- facade\Middleware::class => Middleware::class,
- facade\Request::class => Request::class,
- facade\Response::class => Response::class,
- facade\Route::class => Route::class,
- facade\Session::class => Session::class,
- facade\Url::class => Url::class,
- facade\Validate::class => Validate::class,
- facade\View::class => View::class,
-]);
-
// 注册类库别名
Loader::addClassAlias([
'App' => facade\App::class,
diff --git a/thinkphp/composer.json b/thinkphp/composer.json
index cc4fca9121543c469c53686b5c4d466e48afdf3f..33477b1d7c9f14c82f1cd4276221ce01d50731c3 100644
--- a/thinkphp/composer.json
+++ b/thinkphp/composer.json
@@ -26,7 +26,7 @@
"require-dev": {
"phpunit/phpunit": "^5.0|^6.0",
"johnkary/phpunit-speedtrap": "^1.0",
- "mikey179/vfsStream": "~1.6",
+ "mikey179/vfsstream": "~1.6",
"phploc/phploc": "2.*",
"sebastian/phpcpd": "2.*",
"squizlabs/php_codesniffer": "2.*",
diff --git a/thinkphp/convention.php b/thinkphp/convention.php
index 6675a57a9cc0f2c9fe40845a1b7f1173c8e97534..1d85e56ef18adf7c6a5e664980c4453975421e4d 100644
--- a/thinkphp/convention.php
+++ b/thinkphp/convention.php
@@ -128,6 +128,8 @@ return [
'route_check_cache' => false,
// 路由缓存的Key自定义设置(闭包),默认为当前URL和请求类型的md5
'route_check_cache_key' => '',
+ // 路由缓存的设置
+ 'route_cache_option' => [],
// +----------------------------------------------------------------------
// | 异常及错误设置
@@ -312,9 +314,10 @@ return [
//控制台配置
'console' => [
- 'name' => 'Think Console',
- 'version' => '0.1',
- 'user' => null,
+ 'name' => 'Think Console',
+ 'version' => '0.1',
+ 'user' => null,
+ 'auto_path' => '',
],
// 中间件配置
diff --git a/thinkphp/helper.php b/thinkphp/helper.php
index 372357f1284fe44f7b1d1a3419dc852ae1122a85..72b9e9fde8ee260497b2403f59419c3e37bafaa6 100644
--- a/thinkphp/helper.php
+++ b/thinkphp/helper.php
@@ -130,19 +130,19 @@ if (!function_exists('cache')) {
} elseif (is_null($value)) {
// 删除缓存
return Cache::rm($name);
+ }
+
+ // 缓存数据
+ if (is_array($options)) {
+ $expire = isset($options['expire']) ? $options['expire'] : null; //修复查询缓存无法设置过期时间
} else {
- // 缓存数据
- if (is_array($options)) {
- $expire = isset($options['expire']) ? $options['expire'] : null; //修复查询缓存无法设置过期时间
- } else {
- $expire = is_numeric($options) ? $options : null; //默认快捷缓存设置过期时间
- }
+ $expire = is_numeric($options) ? $options : null; //默认快捷缓存设置过期时间
+ }
- if (is_null($tag)) {
- return Cache::set($name, $value, $expire);
- } else {
- return Cache::tag($tag)->set($name, $value, $expire);
- }
+ if (is_null($tag)) {
+ return Cache::set($name, $value, $expire);
+ } else {
+ return Cache::tag($tag)->set($name, $value, $expire);
}
}
}
@@ -304,6 +304,21 @@ if (!function_exists('debug')) {
}
}
+if (!function_exists('download')) {
+ /**
+ * 获取\think\response\Download对象实例
+ * @param string $filename 要下载的文件
+ * @param string $name 显示文件名
+ * @param bool $content 是否为内容
+ * @param integer $expire 有效期(秒)
+ * @return \think\response\Download
+ */
+ function download($filename, $name = '', $content = false, $expire = 360, $openinBrowser = false)
+ {
+ return Response::create($filename, 'download')->name($name)->isContent($content)->expire($expire)->openinBrowser($openinBrowser);
+ }
+}
+
if (!function_exists('dump')) {
/**
* 浏览器友好的变量输出
@@ -518,7 +533,7 @@ if (!function_exists('response')) {
* @param string $type
* @return Response
*/
- function response($data = [], $code = 200, $header = [], $type = 'html')
+ function response($data = '', $code = 200, $header = [], $type = 'html')
{
return Response::create($data, $type, $code, $header);
}
@@ -671,7 +686,13 @@ if (!function_exists('widget')) {
*/
function widget($name, $data = [])
{
- return app()->action($name, $data, 'widget');
+ $result = app()->action($name, $data, 'widget');
+
+ if (is_object($result)) {
+ $result = $result->getContent();
+ }
+
+ return $result;
}
}
@@ -689,3 +710,17 @@ if (!function_exists('xml')) {
return Response::create($data, 'xml', $code, $header, $options);
}
}
+
+if (!function_exists('yaconf')) {
+ /**
+ * 获取yaconf配置
+ *
+ * @param string $name 配置参数名
+ * @param mixed $default 默认值
+ * @return mixed
+ */
+ function yaconf($name, $default = null)
+ {
+ return Config::yaconf($name, $default);
+ }
+}
diff --git a/thinkphp/lang/zh-cn.php b/thinkphp/lang/zh-cn.php
index d7c82f0c19a913c9e532de64a7a34faf6c5dc8c5..1e050820471872cdaa208f6cec167aa3e612c805 100644
--- a/thinkphp/lang/zh-cn.php
+++ b/thinkphp/lang/zh-cn.php
@@ -25,6 +25,7 @@ return [
'method param miss' => '方法参数错误',
'method not exists' => '方法不存在',
'function not exists' => '函数不存在',
+ 'file not exists' => '文件不存在',
'module not exists' => '模块不存在',
'controller not exists' => '控制器不存在',
'class not exists' => '类不存在',
@@ -49,6 +50,7 @@ return [
'KVDB init error' => '没有初始化KVDB,请在SAE管理平台初始化KVDB服务',
'fields not exists' => '数据表字段不存在',
'where express error' => '查询表达式错误',
+ 'order express error' => '排序表达式错误',
'no data to update' => '没有任何数据需要更新',
'miss data to insert' => '缺少需要写入的数据',
'not support data' => '不支持的数据表达式',
diff --git a/thinkphp/library/think/App.php b/thinkphp/library/think/App.php
index 235c4d856f734a846e0fac4844fd4321a0b02d8a..35924a5bae3df7294a272736d01252610b5c7502 100644
--- a/thinkphp/library/think/App.php
+++ b/thinkphp/library/think/App.php
@@ -20,7 +20,7 @@ use think\route\Dispatch;
*/
class App extends Container
{
- const VERSION = '5.1.19';
+ const VERSION = '5.1.41 LTS';
/**
* 当前模块路径
@@ -126,13 +126,8 @@ class App extends Container
public function __construct($appPath = '')
{
- $this->appPath = $appPath ? realpath($appPath) . DIRECTORY_SEPARATOR : $this->getAppPath();
-
- $this->thinkPath = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
- $this->rootPath = dirname($this->appPath) . DIRECTORY_SEPARATOR;
- $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
- $this->routePath = $this->rootPath . 'route' . DIRECTORY_SEPARATOR;
- $this->configPath = $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
+ $this->thinkPath = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
+ $this->path($appPath);
}
/**
@@ -155,7 +150,8 @@ class App extends Container
*/
public function path($path)
{
- $this->appPath = $path;
+ $this->appPath = $path ? realpath($path) . DIRECTORY_SEPARATOR : $this->getAppPath();
+
return $this;
}
@@ -174,10 +170,22 @@ class App extends Container
$this->beginTime = microtime(true);
$this->beginMem = memory_get_usage();
+ $this->rootPath = dirname($this->appPath) . DIRECTORY_SEPARATOR;
+ $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
+ $this->routePath = $this->rootPath . 'route' . DIRECTORY_SEPARATOR;
+ $this->configPath = $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
+
static::setInstance($this);
$this->instance('app', $this);
+ // 加载环境变量配置文件
+ if (is_file($this->rootPath . '.env')) {
+ $this->env->load($this->rootPath . '.env');
+ }
+
+ $this->configExt = $this->env->get('config_ext', '.php');
+
// 加载惯例配置文件
$this->config->set(include $this->thinkPath . 'convention.php');
@@ -193,19 +201,12 @@ class App extends Container
'vendor_path' => $this->rootPath . 'vendor' . DIRECTORY_SEPARATOR,
]);
- // 加载环境变量配置文件
- if (is_file($this->rootPath . '.env')) {
- $this->env->load($this->rootPath . '.env');
- }
-
$this->namespace = $this->env->get('app_namespace', $this->namespace);
$this->env->set('app_namespace', $this->namespace);
// 注册应用命名空间
Loader::addNamespace($this->namespace, $this->appPath);
- $this->configExt = $this->env->get('config_ext', '.php');
-
// 初始化应用
$this->init();
@@ -312,7 +313,7 @@ class App extends Container
// 自动读取配置文件
if (is_dir($path . 'config')) {
- $dir = $path . 'config';
+ $dir = $path . 'config' . DIRECTORY_SEPARATOR;
} elseif (is_dir($this->configPath . $module)) {
$dir = $this->configPath . $module;
}
@@ -321,8 +322,7 @@ class App extends Container
foreach ($files as $file) {
if ('.' . pathinfo($file, PATHINFO_EXTENSION) === $this->configExt) {
- $filename = $dir . DIRECTORY_SEPARATOR . $file;
- $this->config->load($filename, pathinfo($file, PATHINFO_FILENAME));
+ $this->config->load($dir . $file, pathinfo($file, PATHINFO_FILENAME));
}
}
}
@@ -485,17 +485,23 @@ class App extends Container
$cache = $this->request->cache($key, $expire, $except, $tag);
if ($cache) {
- list($key, $expire, $tag) = $cache;
- if (strtotime($this->request->server('HTTP_IF_MODIFIED_SINCE')) + $expire > $this->request->server('REQUEST_TIME')) {
- // 读取缓存
- $response = Response::create()->code(304);
- throw new HttpResponseException($response);
- } elseif ($this->cache->has($key)) {
- list($content, $header) = $this->cache->get($key);
-
- $response = Response::create($content)->header($header);
- throw new HttpResponseException($response);
- }
+ $this->setResponseCache($cache);
+ }
+ }
+
+ public function setResponseCache($cache)
+ {
+ list($key, $expire, $tag) = $cache;
+
+ if (strtotime($this->request->server('HTTP_IF_MODIFIED_SINCE')) + $expire > $this->request->server('REQUEST_TIME')) {
+ // 读取缓存
+ $response = Response::create()->code(304);
+ throw new HttpResponseException($response);
+ } elseif ($this->cache->has($key)) {
+ list($content, $header) = $this->cache->get($key);
+
+ $response = Response::create($content)->header($header);
+ throw new HttpResponseException($response);
}
}
@@ -542,12 +548,10 @@ class App extends Container
public function routeInit()
{
// 路由检测
- $files = scandir($this->routePath);
- foreach ($files as $file) {
- if (strpos($file, '.php')) {
- $filename = $this->routePath . $file;
- // 导入路由配置
- $rules = include $filename;
+ if (is_dir($this->routePath)) {
+ $files = glob($this->routePath . '*.php');
+ foreach ($files as $file) {
+ $rules = include $file;
if (is_array($rules)) {
$this->route->import($rules);
}
@@ -557,7 +561,8 @@ class App extends Container
if ($this->route->config('route_annotation')) {
// 自动生成路由定义
if ($this->appDebug) {
- $this->build->buildRoute($this->route->config('controller_suffix'));
+ $suffix = $this->route->config('controller_suffix') || $this->route->config('class_suffix');
+ $this->build->buildRoute($suffix);
}
$filename = $this->runtimePath . 'build_route.php';
@@ -578,10 +583,12 @@ class App extends Container
// 检测路由缓存
if (!$this->appDebug && $this->config->get('route_check_cache')) {
$routeKey = $this->getRouteCacheKey();
- $option = $this->config->get('route_cache_option') ?: $this->cache->getConfig();
+ $option = $this->config->get('route_cache_option');
- if ($this->cache->connect($option)->has($routeKey)) {
+ if ($option && $this->cache->connect($option)->has($routeKey)) {
return $this->cache->connect($option)->get($routeKey);
+ } elseif ($this->cache->has($routeKey)) {
+ return $this->cache->get($routeKey);
}
}
@@ -596,10 +603,11 @@ class App extends Container
if (!empty($routeKey)) {
try {
- $this->cache
- ->connect($option)
- ->tag('route_cache')
- ->set($routeKey, $dispatch);
+ if ($option) {
+ $this->cache->connect($option)->tag('route_cache')->set($routeKey, $dispatch);
+ } else {
+ $this->cache->tag('route_cache')->set($routeKey, $dispatch);
+ }
} catch (\Exception $e) {
// 存在闭包的时候缓存无效
}
@@ -712,9 +720,9 @@ class App extends Container
list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);
if (class_exists($class)) {
- return $this->__get($class);
+ return $this->make($class, true);
} elseif ($empty && class_exists($emptyClass = $this->parseClass($module, $layer, $empty, $appendSuffix))) {
- return $this->__get($emptyClass);
+ return $this->make($emptyClass, true);
}
throw new ClassNotFoundException('class not exists:' . $class, $class);
diff --git a/thinkphp/library/think/Build.php b/thinkphp/library/think/Build.php
index f12c79093292754e54b45c66992d2f58bcc9eb28..7a531d74c786563c99da034c131eb99f2b703105 100644
--- a/thinkphp/library/think/Build.php
+++ b/thinkphp/library/think/Build.php
@@ -190,22 +190,28 @@ class Build
public function buildRoute($suffix = false, $layer = '')
{
$namespace = $this->app->getNameSpace();
- $modules = glob($this->basePath . '*', GLOB_ONLYDIR);
$content = 'app->config('app.url_controller_layer');
}
- foreach ($modules as $module) {
- $module = basename($module);
+ if ($this->app->config('app.app_multi_module')) {
+ $modules = glob($this->basePath . '*', GLOB_ONLYDIR);
- if (in_array($module, $this->app->config('app.deny_module_list'))) {
- continue;
- }
+ foreach ($modules as $module) {
+ $module = basename($module);
+
+ if (in_array($module, $this->app->config('app.deny_module_list'))) {
+ continue;
+ }
- $path = $this->basePath . $module . DIRECTORY_SEPARATOR . $layer . DIRECTORY_SEPARATOR;
- $content .= $this->buildDirRoute($path, $namespace, $module, $suffix, $layer);
+ $path = $this->basePath . $module . DIRECTORY_SEPARATOR . $layer . DIRECTORY_SEPARATOR;
+ $content .= $this->buildDirRoute($path, $namespace, $module, $suffix, $layer);
+ }
+ } else {
+ $path = $this->basePath . $layer . DIRECTORY_SEPARATOR;
+ $content .= $this->buildDirRoute($path, $namespace, '', $suffix, $layer);
}
$filename = $this->app->getRuntimePath() . 'build_route.php';
@@ -232,17 +238,19 @@ class Build
foreach ($controllers as $controller) {
$controller = basename($controller, '.php');
- if ($suffix) {
- // 控制器后缀
- $controller = substr($controller, 0, -10);
- }
-
- $class = new \ReflectionClass($namespace . '\\' . $module . '\\' . $layer . '\\' . $controller);
+ $class = new \ReflectionClass($namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $controller);
if (strpos($layer, '\\')) {
// 多级控制器
$level = str_replace(DIRECTORY_SEPARATOR, '.', substr($layer, 11));
$controller = $level . '.' . $controller;
+ $length = strlen(strstr($layer, '\\', true));
+ } else {
+ $length = strlen($layer);
+ }
+
+ if ($suffix) {
+ $controller = substr($controller, 0, -$length);
}
$content .= $this->getControllerRoute($class, $module, $controller);
@@ -272,12 +280,12 @@ class Build
if (false !== strpos($comment, '@route(')) {
$comment = $this->parseRouteComment($comment);
- $route = $module . '/' . $controller;
+ $route = ($module ? $module . '/' : '') . $controller;
$comment = preg_replace('/route\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\)/is', 'Route::resource(\1,\'' . $route . '\')', $comment);
$content .= PHP_EOL . $comment;
} elseif (false !== strpos($comment, '@alias(')) {
$comment = $this->parseRouteComment($comment, '@alias(');
- $route = $module . '/' . $controller;
+ $route = ($module ? $module . '/' : '') . $controller;
$comment = preg_replace('/alias\(\s?([\'\"][\-\_\/\w]+[\'\"])\s?\)/is', 'Route::alias(\1,\'' . $route . '\')', $comment);
$content .= PHP_EOL . $comment;
}
@@ -342,7 +350,7 @@ class Build
$action = substr($action, 0, -strlen($suffix));
}
- $route = $module . '/' . $controller . '/' . $action;
+ $route = ($module ? $module . '/' : '') . $controller . '/' . $action;
$comment = preg_replace('/route\s?\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\,?\s?[\'\"]?(\w+?)[\'\"]?\s?\)/is', 'Route::\2(\1,\'' . $route . '\')', $comment);
$comment = preg_replace('/route\s?\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\)/is', 'Route::rule(\1,\'' . $route . '\')', $comment);
diff --git a/thinkphp/library/think/Collection.php b/thinkphp/library/think/Collection.php
index 929b51939dd73985c592763b6226056f4c9e7559..d7454ec59ac5dcaf109efcf910403659c865bbd3 100644
--- a/thinkphp/library/think/Collection.php
+++ b/thinkphp/library/think/Collection.php
@@ -70,49 +70,118 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
}
/**
- * 比较数组,返回差集
+ * 交换数组中的键和值
*
* @access public
- * @param mixed $items
* @return static
*/
- public function diff($items)
+ public function flip()
{
- return new static(array_diff($this->items, $this->convertToArray($items)));
+ return new static(array_flip($this->items));
}
/**
- * 交换数组中的键和值
+ * 按指定键整理数据
*
* @access public
+ * @param mixed $items 数据
+ * @param string $indexKey 键名
+ * @return array
+ */
+ public function dictionary($items = null, &$indexKey = null)
+ {
+ if ($items instanceof self || $items instanceof Paginator) {
+ $items = $items->all();
+ }
+
+ $items = is_null($items) ? $this->items : $items;
+
+ if ($items && empty($indexKey)) {
+ $indexKey = is_array($items[0]) ? 'id' : $items[0]->getPk();
+ }
+
+ if (isset($indexKey) && is_string($indexKey)) {
+ return array_column($items, null, $indexKey);
+ }
+
+ return $items;
+ }
+
+ /**
+ * 比较数组,返回差集
+ *
+ * @access public
+ * @param mixed $items 数据
+ * @param string $indexKey 指定比较的键名
* @return static
*/
- public function flip()
+ public function diff($items, $indexKey = null)
{
- return new static(array_flip($this->items));
+ if ($this->isEmpty() || is_scalar($this->items[0])) {
+ return new static(array_diff($this->items, $this->convertToArray($items)));
+ }
+
+ $diff = [];
+ $dictionary = $this->dictionary($items, $indexKey);
+
+ if (is_string($indexKey)) {
+ foreach ($this->items as $item) {
+ if (!isset($dictionary[$item[$indexKey]])) {
+ $diff[] = $item;
+ }
+ }
+ }
+
+ return new static($diff);
}
/**
* 比较数组,返回交集
*
* @access public
- * @param mixed $items
+ * @param mixed $items 数据
+ * @param string $indexKey 指定比较的键名
* @return static
*/
- public function intersect($items)
+ public function intersect($items, $indexKey = null)
{
- return new static(array_intersect($this->items, $this->convertToArray($items)));
+ if ($this->isEmpty() || is_scalar($this->items[0])) {
+ return new static(array_diff($this->items, $this->convertToArray($items)));
+ }
+
+ $intersect = [];
+ $dictionary = $this->dictionary($items, $indexKey);
+
+ if (is_string($indexKey)) {
+ foreach ($this->items as $item) {
+ if (isset($dictionary[$item[$indexKey]])) {
+ $intersect[] = $item;
+ }
+ }
+ }
+
+ return new static($intersect);
}
/**
* 返回数组中所有的键名
*
* @access public
- * @return static
+ * @return array
*/
public function keys()
{
- return new static(array_keys($this->items));
+ $current = current($this->items);
+
+ if (is_scalar($current)) {
+ $array = $this->items;
+ } elseif (is_array($current)) {
+ $array = $current;
+ } else {
+ $array = $current->toArray();
+ }
+
+ return array_keys($array);
}
/**
@@ -234,6 +303,17 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
return $this;
}
+ /**
+ * 用回调函数处理数组中的元素
+ * @access public
+ * @param callable|null $callback
+ * @return static
+ */
+ public function map(callable $callback)
+ {
+ return new static(array_map($callback, $this->items));
+ }
+
/**
* 用回调函数过滤数组中的元素
* @access public
@@ -249,6 +329,68 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
return new static(array_filter($this->items));
}
+ /**
+ * 根据字段条件过滤数组中的元素
+ * @access public
+ * @param string $field 字段名
+ * @param mixed $operator 操作符
+ * @param mixed $value 数据
+ * @return static
+ */
+ public function where($field, $operator, $value = null)
+ {
+ if (is_null($value)) {
+ $value = $operator;
+ $operator = '=';
+ }
+
+ return $this->filter(function ($data) use ($field, $operator, $value) {
+ if (strpos($field, '.')) {
+ list($field, $relation) = explode('.', $field);
+
+ $result = isset($data[$field][$relation]) ? $data[$field][$relation] : null;
+ } else {
+ $result = isset($data[$field]) ? $data[$field] : null;
+ }
+
+ switch (strtolower($operator)) {
+ case '===':
+ return $result === $value;
+ case '!==':
+ return $result !== $value;
+ case '!=':
+ case '<>':
+ return $result != $value;
+ case '>':
+ return $result > $value;
+ case '>=':
+ return $result >= $value;
+ case '<':
+ return $result < $value;
+ case '<=':
+ return $result <= $value;
+ case 'like':
+ return is_string($result) && false !== strpos($result, $value);
+ case 'not like':
+ return is_string($result) && false === strpos($result, $value);
+ case 'in':
+ return is_scalar($result) && in_array($result, $value, true);
+ case 'not in':
+ return is_scalar($result) && !in_array($result, $value, true);
+ case 'between':
+ list($min, $max) = is_string($value) ? explode(',', $value) : $value;
+ return is_scalar($result) && $result >= $min && $result <= $max;
+ case 'not between':
+ list($min, $max) = is_string($value) ? explode(',', $value) : $value;
+ return is_scalar($result) && $result > $max || $result < $min;
+ case '==':
+ case '=':
+ default:
+ return $result == $value;
+ }
+ });
+ }
+
/**
* 返回数据中指定的一列
* @access public
@@ -258,7 +400,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
*/
public function column($columnKey, $indexKey = null)
{
- return array_column($this->items, $columnKey, $indexKey);
+ return array_column($this->toArray(), $columnKey, $indexKey);
}
/**
@@ -282,6 +424,28 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
return new static($items);
}
+ /**
+ * 指定字段排序
+ * @access public
+ * @param string $field 排序字段
+ * @param string $order 排序
+ * @param bool $intSort 是否为数字排序
+ * @return $this
+ */
+ public function order($field, $order = null, $intSort = true)
+ {
+ return $this->sort(function ($a, $b) use ($field, $order, $intSort) {
+ $fieldA = isset($a[$field]) ? $a[$field] : null;
+ $fieldB = isset($b[$field]) ? $b[$field] : null;
+
+ if ($intSort) {
+ return 'desc' == strtolower($order) ? $fieldB >= $fieldA : $fieldA >= $fieldB;
+ } else {
+ return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB);
+ }
+ });
+ }
+
/**
* 将数组打乱
*
diff --git a/thinkphp/library/think/Config.php b/thinkphp/library/think/Config.php
index 1e60ac9d0317fc586a4e4baa20b69feb0701c46d..bec6222adbf0a0b94d7df77f2efbbd1dcb162f1c 100644
--- a/thinkphp/library/think/Config.php
+++ b/thinkphp/library/think/Config.php
@@ -11,19 +11,70 @@
namespace think;
+use Yaconf;
+
class Config implements \ArrayAccess
{
/**
* 配置参数
* @var array
*/
- private $config = [];
+ protected $config = [];
/**
* 配置前缀
* @var string
*/
- private $prefix = 'app';
+ protected $prefix = 'app';
+
+ /**
+ * 配置文件目录
+ * @var string
+ */
+ protected $path;
+
+ /**
+ * 配置文件后缀
+ * @var string
+ */
+ protected $ext;
+
+ /**
+ * 是否支持Yaconf
+ * @var bool
+ */
+ protected $yaconf;
+
+ /**
+ * 构造方法
+ * @access public
+ */
+ public function __construct($path = '', $ext = '.php')
+ {
+ $this->path = $path;
+ $this->ext = $ext;
+ $this->yaconf = class_exists('Yaconf');
+ }
+
+ public static function __make(App $app)
+ {
+ $path = $app->getConfigPath();
+ $ext = $app->getConfigExt();
+ return new static($path, $ext);
+ }
+
+ /**
+ * 设置开启Yaconf
+ * @access public
+ * @param bool|string $yaconf 是否使用Yaconf
+ * @return void
+ */
+ public function setYaconf($yaconf)
+ {
+ if ($this->yaconf) {
+ $this->yaconf = $yaconf;
+ }
+ }
/**
* 设置配置参数默认前缀
@@ -65,20 +116,69 @@ class Config implements \ArrayAccess
public function load($file, $name = '')
{
if (is_file($file)) {
- $name = strtolower($name);
- $type = pathinfo($file, PATHINFO_EXTENSION);
+ $filename = $file;
+ } elseif (is_file($this->path . $file . $this->ext)) {
+ $filename = $this->path . $file . $this->ext;
+ }
- if ('php' == $type) {
- return $this->set(include $file, $name);
- } elseif ('yaml' == $type && function_exists('yaml_parse_file')) {
- return $this->set(yaml_parse_file($file), $name);
- }
- return $this->parse($file, $type, $name);
+ if (isset($filename)) {
+ return $this->loadFile($filename, $name);
+ } elseif ($this->yaconf && Yaconf::has($file)) {
+ return $this->set(Yaconf::get($file), $name);
}
return $this->config;
}
+ /**
+ * 获取实际的yaconf配置参数
+ * @access protected
+ * @param string $name 配置参数名
+ * @return string
+ */
+ protected function getYaconfName($name)
+ {
+ if ($this->yaconf && is_string($this->yaconf)) {
+ return $this->yaconf . '.' . $name;
+ }
+
+ return $name;
+ }
+
+ /**
+ * 获取yaconf配置
+ * @access public
+ * @param string $name 配置参数名
+ * @param mixed $default 默认值
+ * @return mixed
+ */
+ public function yaconf($name, $default = null)
+ {
+ if ($this->yaconf) {
+ $yaconfName = $this->getYaconfName($name);
+
+ if (Yaconf::has($yaconfName)) {
+ return Yaconf::get($yaconfName);
+ }
+ }
+
+ return $default;
+ }
+
+ protected function loadFile($file, $name)
+ {
+ $name = strtolower($name);
+ $type = pathinfo($file, PATHINFO_EXTENSION);
+
+ if ('php' == $type) {
+ return $this->set(include $file, $name);
+ } elseif ('yaml' == $type && function_exists('yaml_parse_file')) {
+ return $this->set(yaml_parse_file($file), $name);
+ }
+
+ return $this->parse($file, $type, $name);
+ }
+
/**
* 检测配置是否存在
* @access public
@@ -87,11 +187,11 @@ class Config implements \ArrayAccess
*/
public function has($name)
{
- if (!strpos($name, '.')) {
+ if (false === strpos($name, '.')) {
$name = $this->prefix . '.' . $name;
}
- return !is_null($this->get($name)) ? true : false;
+ return !is_null($this->get($name));
}
/**
@@ -104,6 +204,15 @@ class Config implements \ArrayAccess
{
$name = strtolower($name);
+ if ($this->yaconf) {
+ $yaconfName = $this->getYaconfName($name);
+
+ if (Yaconf::has($yaconfName)) {
+ $config = Yaconf::get($yaconfName);
+ return isset($this->config[$name]) ? array_merge($this->config[$name], $config) : $config;
+ }
+ }
+
return isset($this->config[$name]) ? $this->config[$name] : [];
}
@@ -116,17 +225,27 @@ class Config implements \ArrayAccess
*/
public function get($name = null, $default = null)
{
+ if ($name && false === strpos($name, '.')) {
+ $name = $this->prefix . '.' . $name;
+ }
+
// 无参数时获取所有
if (empty($name)) {
return $this->config;
}
- if (!strpos($name, '.')) {
- $name = $this->prefix . '.' . $name;
- } elseif ('.' == substr($name, -1)) {
+ if ('.' == substr($name, -1)) {
return $this->pull(substr($name, 0, -1));
}
+ if ($this->yaconf) {
+ $yaconfName = $this->getYaconfName($name);
+
+ if (Yaconf::has($yaconfName)) {
+ return Yaconf::get($yaconfName);
+ }
+ }
+
$name = explode('.', $name);
$name[0] = strtolower($name[0]);
$config = $this->config;
@@ -153,7 +272,7 @@ class Config implements \ArrayAccess
public function set($name, $value = null)
{
if (is_string($name)) {
- if (!strpos($name, '.')) {
+ if (false === strpos($name, '.')) {
$name = $this->prefix . '.' . $name;
}
@@ -195,7 +314,7 @@ class Config implements \ArrayAccess
*/
public function remove($name)
{
- if (!strpos($name, '.')) {
+ if (false === strpos($name, '.')) {
$name = $this->prefix . '.' . $name;
}
diff --git a/thinkphp/library/think/Console.php b/thinkphp/library/think/Console.php
index 9757e4766035d491a511d0beb21500e92d4fa8c8..22f3e2c5f512f7bd3e67cb32170efd5f5c4a04be 100644
--- a/thinkphp/library/think/Console.php
+++ b/thinkphp/library/think/Console.php
@@ -35,19 +35,22 @@ class Console
private $defaultCommand;
private static $defaultCommands = [
- "think\\console\\command\\Help",
- "think\\console\\command\\Lists",
- "think\\console\\command\\Build",
- "think\\console\\command\\Clear",
- "think\\console\\command\\make\\Controller",
- "think\\console\\command\\make\\Model",
- "think\\console\\command\\make\\Middleware",
- "think\\console\\command\\make\\Validate",
- "think\\console\\command\\optimize\\Autoload",
- "think\\console\\command\\optimize\\Config",
- "think\\console\\command\\optimize\\Schema",
- "think\\console\\command\\optimize\\Route",
- "think\\console\\command\\RunServer",
+ 'help' => "think\\console\\command\\Help",
+ 'list' => "think\\console\\command\\Lists",
+ 'build' => "think\\console\\command\\Build",
+ 'clear' => "think\\console\\command\\Clear",
+ 'make:command' => "think\\console\\command\\make\\Command",
+ 'make:controller' => "think\\console\\command\\make\\Controller",
+ 'make:model' => "think\\console\\command\\make\\Model",
+ 'make:middleware' => "think\\console\\command\\make\\Middleware",
+ 'make:validate' => "think\\console\\command\\make\\Validate",
+ 'optimize:autoload' => "think\\console\\command\\optimize\\Autoload",
+ 'optimize:config' => "think\\console\\command\\optimize\\Config",
+ 'optimize:schema' => "think\\console\\command\\optimize\\Schema",
+ 'optimize:route' => "think\\console\\command\\optimize\\Route",
+ 'run' => "think\\console\\command\\RunServer",
+ 'version' => "think\\console\\command\\Version",
+ 'route:list' => "think\\console\\command\\RouteList",
];
/**
@@ -68,10 +71,6 @@ class Console
$this->defaultCommand = 'list';
$this->definition = $this->getDefaultInputDefinition();
-
- foreach ($this->getDefaultCommands() as $command) {
- $this->add($command);
- }
}
/**
@@ -80,6 +79,10 @@ class Console
*/
public function setUser($user)
{
+ if (DIRECTORY_SEPARATOR == '\\') {
+ return;
+ }
+
$user = posix_getpwnam($user);
if ($user) {
posix_setuid($user['uid']);
@@ -98,25 +101,13 @@ class Console
static $console;
if (!$console) {
- $config = Container::get('config')->pull('console');
- // 实例化 console
+ $config = Container::get('config')->pull('console');
$console = new self($config['name'], $config['version'], $config['user']);
- // 读取指令集
- $file = Container::get('env')->get('app_path') . 'command.php';
-
- if (is_file($file)) {
- $commands = include $file;
+ $commands = $console->getDefinedCommands($config);
- if (is_array($commands)) {
- foreach ($commands as $command) {
- if (class_exists($command) && is_subclass_of($command, "\\think\\console\\Command")) {
- // 注册指令
- $console->add(new $command());
- }
- }
- }
- }
+ // 添加指令集
+ $console->addCommands($commands);
}
if ($run) {
@@ -127,6 +118,46 @@ class Console
}
}
+ /**
+ * @access public
+ * @param array $config
+ * @return array
+ */
+ public function getDefinedCommands(array $config = [])
+ {
+ $commands = self::$defaultCommands;
+
+ if (!empty($config['auto_path']) && is_dir($config['auto_path'])) {
+ // 自动加载指令类
+ $files = scandir($config['auto_path']);
+
+ if (count($files) > 2) {
+ $beforeClass = get_declared_classes();
+
+ foreach ($files as $file) {
+ if (pathinfo($file, PATHINFO_EXTENSION) == 'php') {
+ include $config['auto_path'] . $file;
+ }
+ }
+
+ $afterClass = get_declared_classes();
+ $commands = array_merge($commands, array_diff($afterClass, $beforeClass));
+ }
+ }
+
+ $file = Container::get('env')->get('app_path') . 'command.php';
+
+ if (is_file($file)) {
+ $appCommands = include $file;
+
+ if (is_array($appCommands)) {
+ $commands = array_merge($commands, $appCommands);
+ }
+ }
+
+ return $commands;
+ }
+
/**
* @access public
* @param string $command
@@ -340,9 +371,9 @@ class Console
}
/**
- * 注册一个指令
+ * 注册一个指令 (便于动态创建指令)
* @access public
- * @param string $name
+ * @param string $name 指令名
* @return Command
*/
public function register($name)
@@ -351,25 +382,38 @@ class Console
}
/**
- * 添加指令
+ * 添加指令集
* @access public
- * @param Command[] $commands
+ * @param array $commands
*/
public function addCommands(array $commands)
{
- foreach ($commands as $command) {
- $this->add($command);
+ foreach ($commands as $key => $command) {
+ if (is_subclass_of($command, "\\think\\console\\Command")) {
+ // 注册指令
+ $this->add($command, is_numeric($key) ? '' : $key);
+ }
}
}
/**
- * 添加一个指令
+ * 注册一个指令(对象)
* @access public
- * @param Command $command
- * @return Command
+ * @param mixed $command 指令对象或者指令类名
+ * @param string $name 指令名 留空则自动获取
+ * @return mixed
*/
- public function add(Command $command)
+ public function add($command, $name)
{
+ if ($name) {
+ $this->commands[$name] = $command;
+ return;
+ }
+
+ if (is_string($command)) {
+ $command = new $command();
+ }
+
$command->setConsole($this);
if (!$command->isEnabled()) {
@@ -405,6 +449,12 @@ class Console
$command = $this->commands[$name];
+ if (is_string($command)) {
+ $command = new $command();
+ }
+
+ $command->setConsole($this);
+
if ($this->wantHelps) {
$this->wantHelps = false;
@@ -437,12 +487,17 @@ class Console
public function getNamespaces()
{
$namespaces = [];
- foreach ($this->commands as $command) {
- $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
+ foreach ($this->commands as $name => $command) {
+ if (is_string($command)) {
+ $namespaces = array_merge($namespaces, $this->extractAllNamespaces($name));
+ } else {
+ $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
- foreach ($command->getAliases() as $alias) {
- $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
+ foreach ($command->getAliases() as $alias) {
+ $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
+ }
}
+
}
return array_values(array_unique(array_filter($namespaces)));
@@ -523,16 +578,6 @@ class Console
throw new \InvalidArgumentException($message);
}
- if (count($commands) > 1) {
- $commandList = $this->commands;
-
- $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) {
- $commandName = $commandList[$nameOrAlias]->getName();
-
- return $commandName === $nameOrAlias || !in_array($commandName, $commands);
- });
- }
-
$exact = in_array($name, $commands, true);
if (count($commands) > 1 && !$exact) {
$suggestions = $this->getAbbreviationSuggestions(array_values($commands));
@@ -660,24 +705,6 @@ class Console
]);
}
- /**
- * 设置默认命令
- * @access protected
- * @return Command[] An array of default Command instances
- */
- protected function getDefaultCommands()
- {
- $defaultCommands = [];
-
- foreach (self::$defaultCommands as $classname) {
- if (class_exists($classname) && is_subclass_of($classname, "think\\console\\Command")) {
- $defaultCommands[] = new $classname();
- }
- }
-
- return $defaultCommands;
- }
-
public static function addDefaultCommands(array $classnames)
{
self::$defaultCommands = array_merge(self::$defaultCommands, $classnames);
@@ -792,4 +819,11 @@ class Console
return $namespaces;
}
+ public function __debugInfo()
+ {
+ $data = get_object_vars($this);
+ unset($data['commands'], $data['definition']);
+
+ return $data;
+ }
}
diff --git a/thinkphp/library/think/Container.php b/thinkphp/library/think/Container.php
index a8af9fa52b2f72017305c2d3e87be0939beed737..91b32aa63e455a971977f369f29554b4ac4a1813 100644
--- a/thinkphp/library/think/Container.php
+++ b/thinkphp/library/think/Container.php
@@ -23,6 +23,28 @@ use ReflectionFunction;
use ReflectionMethod;
use think\exception\ClassNotFoundException;
+/**
+ * @package think
+ * @property Build $build
+ * @property Cache $cache
+ * @property Config $config
+ * @property Cookie $cookie
+ * @property Debug $debug
+ * @property Env $env
+ * @property Hook $hook
+ * @property Lang $lang
+ * @property Middleware $middleware
+ * @property Request $request
+ * @property Response $response
+ * @property Route $route
+ * @property Session $session
+ * @property Template $template
+ * @property Url $url
+ * @property Validate $validate
+ * @property View $view
+ * @property route\RuleName $rule_name
+ * @property Log $log
+ */
class Container implements ArrayAccess, IteratorAggregate, Countable
{
/**
@@ -57,6 +79,7 @@ class Container implements ArrayAccess, IteratorAggregate, Countable
'response' => Response::class,
'route' => Route::class,
'session' => Session::class,
+ 'template' => Template::class,
'url' => Url::class,
'validate' => Validate::class,
'view' => View::class,
@@ -438,9 +461,29 @@ class Container implements ArrayAccess, IteratorAggregate, Countable
$type = key($vars) === 0 ? 1 : 0;
$params = $reflect->getParameters();
+ if (PHP_VERSION > 8.0) {
+ $args = $this->parseParamsForPHP8($params, $vars, $type);
+ } else {
+ $args = $this->parseParams($params, $vars, $type);
+ }
+
+ return $args;
+ }
+
+ /**
+ * 解析参数
+ * @access protected
+ * @param array $params 参数列表
+ * @param array $vars 参数数据
+ * @param int $type 参数类别
+ * @return array
+ */
+ protected function parseParams($params, $vars, $type)
+ {
foreach ($params as $param) {
- $name = $param->getName();
- $class = $param->getClass();
+ $name = $param->getName();
+ $lowerName = Loader::parseName($name);
+ $class = $param->getClass();
if ($class) {
$args[] = $this->getObjectParam($class->getName(), $vars);
@@ -448,13 +491,46 @@ class Container implements ArrayAccess, IteratorAggregate, Countable
$args[] = array_shift($vars);
} elseif (0 == $type && isset($vars[$name])) {
$args[] = $vars[$name];
+ } elseif (0 == $type && isset($vars[$lowerName])) {
+ $args[] = $vars[$lowerName];
} elseif ($param->isDefaultValueAvailable()) {
$args[] = $param->getDefaultValue();
} else {
throw new InvalidArgumentException('method param miss:' . $name);
}
}
+ return $args;
+ }
+ /**
+ * 解析参数
+ * @access protected
+ * @param array $params 参数列表
+ * @param array $vars 参数数据
+ * @param int $type 参数类别
+ * @return array
+ */
+ protected function parseParamsForPHP8($params, $vars, $type)
+ {
+ foreach ($params as $param) {
+ $name = $param->getName();
+ $lowerName = Loader::parseName($name);
+ $reflectionType = $param->getType();
+
+ if ($reflectionType && $reflectionType->isBuiltin() === false) {
+ $args[] = $this->getObjectParam($reflectionType->getName(), $vars);
+ } elseif (1 == $type && !empty($vars)) {
+ $args[] = array_shift($vars);
+ } elseif (0 == $type && array_key_exists($name, $vars)) {
+ $args[] = $vars[$name];
+ } elseif (0 == $type && array_key_exists($lowerName, $vars)) {
+ $args[] = $vars[$lowerName];
+ } elseif ($param->isDefaultValueAvailable()) {
+ $args[] = $param->getDefaultValue();
+ } else {
+ throw new InvalidArgumentException('method param miss:' . $name);
+ }
+ }
return $args;
}
@@ -531,4 +607,12 @@ class Container implements ArrayAccess, IteratorAggregate, Countable
{
return new ArrayIterator($this->instances);
}
+
+ public function __debugInfo()
+ {
+ $data = get_object_vars($this);
+ unset($data['instances'], $data['instance']);
+
+ return $data;
+ }
}
diff --git a/thinkphp/library/think/Controller.php b/thinkphp/library/think/Controller.php
index 19fc8df6f0b98603b60943f61c2016a9941a1460..966eaaa83aa247424b9ade293a56b3386c746559 100644
--- a/thinkphp/library/think/Controller.php
+++ b/thinkphp/library/think/Controller.php
@@ -67,22 +67,7 @@ class Controller
// 控制器初始化
$this->initialize();
- // 控制器中间件
- if ($this->middleware) {
- foreach ($this->middleware as $key => $val) {
- if (!is_int($key)) {
- if (isset($val['only']) && !in_array($this->request->action(), $val['only'])) {
- continue;
- } elseif (isset($val['except']) && in_array($this->request->action(), $val['except'])) {
- continue;
- } else {
- $val = $key;
- }
- }
-
- $this->app['middleware']->controller($val);
- }
- }
+ $this->registerMiddleware();
// 前置操作方法 即将废弃
foreach ((array) $this->beforeActionList as $method => $options) {
@@ -96,6 +81,36 @@ class Controller
protected function initialize()
{}
+ // 注册控制器中间件
+ public function registerMiddleware()
+ {
+ foreach ($this->middleware as $key => $val) {
+ if (!is_int($key)) {
+ $only = $except = null;
+
+ if (isset($val['only'])) {
+ $only = array_map(function ($item) {
+ return strtolower($item);
+ }, $val['only']);
+ } elseif (isset($val['except'])) {
+ $except = array_map(function ($item) {
+ return strtolower($item);
+ }, $val['except']);
+ }
+
+ if (isset($only) && !in_array($this->request->action(), $only)) {
+ continue;
+ } elseif (isset($except) && in_array($this->request->action(), $except)) {
+ continue;
+ } else {
+ $val = $key;
+ }
+ }
+
+ $this->app['middleware']->controller($val);
+ }
+ }
+
/**
* 前置操作
* @access protected
@@ -108,14 +123,24 @@ class Controller
if (is_string($options['only'])) {
$options['only'] = explode(',', $options['only']);
}
- if (!in_array($this->request->action(), $options['only'])) {
+
+ $only = array_map(function ($val) {
+ return strtolower($val);
+ }, $options['only']);
+
+ if (!in_array($this->request->action(), $only)) {
return;
}
} elseif (isset($options['except'])) {
if (is_string($options['except'])) {
$options['except'] = explode(',', $options['except']);
}
- if (in_array($this->request->action(), $options['except'])) {
+
+ $except = array_map(function ($val) {
+ return strtolower($val);
+ }, $options['except']);
+
+ if (in_array($this->request->action(), $except)) {
return;
}
}
@@ -133,7 +158,7 @@ class Controller
*/
protected function fetch($template = '', $vars = [], $config = [])
{
- return $this->view->fetch($template, $vars, $config);
+ return Response::create($template, 'view')->assign($vars)->config($config);
}
/**
@@ -146,7 +171,7 @@ class Controller
*/
protected function display($content = '', $vars = [], $config = [])
{
- return $this->view->display($content, $vars, $config);
+ return Response::create($content, 'view')->assign($vars)->config($config)->isContent(true);
}
/**
@@ -251,4 +276,12 @@ class Controller
return true;
}
+
+ public function __debugInfo()
+ {
+ $data = get_object_vars($this);
+ unset($data['app'], $data['request']);
+
+ return $data;
+ }
}
diff --git a/thinkphp/library/think/Db.php b/thinkphp/library/think/Db.php
index f72c0f1d1e95ad4606af771a1074243b9b888282..9280eac0e35d4a1bb8011a070f7f8f40dfa3d641 100644
--- a/thinkphp/library/think/Db.php
+++ b/thinkphp/library/think/Db.php
@@ -16,7 +16,6 @@ use think\db\Connection;
/**
* Class Db
* @package think
- * @method \think\db\Query connect(array $config =[], mixed $name = false) static 连接/切换数据库连接
* @method \think\db\Query master() static 从主服务器读取数据
* @method \think\db\Query readMaster(bool $all = false) static 后续从主服务器读取数据
* @method \think\db\Query table(string $table) static 指定数据表(含前缀)
@@ -35,6 +34,7 @@ use think\db\Connection;
* @method \think\db\Query order(mixed $field, string $order = null) static 查询ORDER
* @method \think\db\Query orderRaw(string $field, array $bind = []) static 查询ORDER
* @method \think\db\Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存
+ * @method \think\db\Query withAttr(string $name,callable $callback = null) static 使用获取器获取数据
* @method mixed value(string $field) static 获取某个字段的值
* @method array column(string $field, string $key = '') static 获取某个列的值
* @method mixed find(mixed $data = null) static 查询单个记录
@@ -55,7 +55,6 @@ use think\db\Connection;
* @method void rollback() static 事务回滚
* @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句
* @method string getLastInsID(string $sequence = null) static 获取最近插入的ID
- * @method mixed getConfig(string $name = '') static 获取数据库的配置参数
*/
class Db
{
diff --git a/thinkphp/library/think/Debug.php b/thinkphp/library/think/Debug.php
index 8aec3c3b425d511b809e6198becb9b675e42609a..776e178731ef28a9542651ba91dff4bc7c365fd3 100644
--- a/thinkphp/library/think/Debug.php
+++ b/thinkphp/library/think/Debug.php
@@ -267,4 +267,12 @@ class Debug
}
}
}
+
+ public function __debugInfo()
+ {
+ $data = get_object_vars($this);
+ unset($data['app']);
+
+ return $data;
+ }
}
diff --git a/thinkphp/library/think/Env.php b/thinkphp/library/think/Env.php
index ef5d9468d3fc51528c2b9d46ae524d48e32a36ee..eaeee943ef3ce1553a982dfa613410d7f3219bce 100644
--- a/thinkphp/library/think/Env.php
+++ b/thinkphp/library/think/Env.php
@@ -43,7 +43,7 @@ class Env
* @param mixed $default 默认值
* @return mixed
*/
- public function get($name = null, $default = null)
+ public function get($name = null, $default = null, $php_prefix = true)
{
if (is_null($name)) {
return $this->data;
@@ -55,12 +55,16 @@ class Env
return $this->data[$name];
}
- return $this->getEnv($name, $default);
+ return $this->getEnv($name, $default, $php_prefix);
}
- protected function getEnv($name, $default = null)
+ protected function getEnv($name, $default = null, $php_prefix = true)
{
- $result = getenv('PHP_' . $name);
+ if ($php_prefix) {
+ $name = 'PHP_' . $name;
+ }
+
+ $result = getenv($name);
if (false === $result) {
return $default;
diff --git a/thinkphp/library/think/Facade.php b/thinkphp/library/think/Facade.php
index f467e0f81344aa31a028590539140494963846e3..ac5ae28bca826e0cb28bb7d39a1a8491b1567e43 100644
--- a/thinkphp/library/think/Facade.php
+++ b/thinkphp/library/think/Facade.php
@@ -75,7 +75,7 @@ class Facade
}
/**
- * 获取当前Facade对应类名
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
* @access protected
* @return string
*/
diff --git a/thinkphp/library/think/File.php b/thinkphp/library/think/File.php
index e9985690c3dfe697156a8287016650d651716c48..b24b77708ae41f7fa86480cc4cb0d7345476a671 100644
--- a/thinkphp/library/think/File.php
+++ b/thinkphp/library/think/File.php
@@ -300,7 +300,7 @@ class File extends SplFileObject
*/
public function checkSize($size)
{
- if ($this->getSize() > $size) {
+ if ($this->getSize() > (int) $size) {
$this->error = 'filesize not match';
return false;
}
@@ -334,9 +334,10 @@ class File extends SplFileObject
* @param string $path 保存路径
* @param string|bool $savename 保存的文件名 默认自动生成
* @param boolean $replace 同名文件是否覆盖
+ * @param bool $autoAppendExt 自动补充扩展名
* @return false|File false-失败 否则返回File实例
*/
- public function move($path, $savename = true, $replace = true)
+ public function move($path, $savename = true, $replace = true, $autoAppendExt = true)
{
// 文件上传失败,捕获错误代码
if (!empty($this->info['error'])) {
@@ -357,7 +358,7 @@ class File extends SplFileObject
$path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
// 文件保存命名规则
- $saveName = $this->buildSaveName($savename);
+ $saveName = $this->buildSaveName($savename, $autoAppendExt);
$filename = $path . $saveName;
// 检测目录
@@ -391,9 +392,10 @@ class File extends SplFileObject
* 获取保存文件名
* @access protected
* @param string|bool $savename 保存的文件名 默认自动生成
+ * @param bool $autoAppendExt 自动补充扩展名
* @return string
*/
- protected function buildSaveName($savename)
+ protected function buildSaveName($savename, $autoAppendExt = true)
{
if (true === $savename) {
// 自动生成文件名
@@ -403,7 +405,7 @@ class File extends SplFileObject
$savename = $this->getInfo('name');
}
- if (!strpos($savename, '.')) {
+ if ($autoAppendExt && false === strpos($savename, '.')) {
$savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION);
}
diff --git a/thinkphp/library/think/Hook.php b/thinkphp/library/think/Hook.php
index 58791c815ce61b59f789344f6e47824a57de2d3f..1d011410ead3bc912968296bd9932f8dc493bb1d 100644
--- a/thinkphp/library/think/Hook.php
+++ b/thinkphp/library/think/Hook.php
@@ -187,14 +187,12 @@ class Hook
*/
protected function execTag($class, $tag = '', $params = null)
{
- $this->app->isDebug() && $this->app['debug']->remark('behavior_start', 'time');
-
$method = Loader::parseName($tag, 1, false);
if ($class instanceof \Closure) {
$call = $class;
$class = 'Closure';
- } elseif (strpos($class, '::')) {
+ } elseif (is_array($class) || strpos($class, '::')) {
$call = $class;
} else {
$obj = Container::get($class);
@@ -209,13 +207,14 @@ class Hook
$result = $this->app->invoke($call, [$params]);
- if ($this->app->isDebug()) {
- $debug = $this->app['debug'];
- $debug->remark('behavior_end', 'time');
- $this->app->log('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . $debug->getRangeTime('behavior_start', 'behavior_end') . 's ]');
- }
-
return $result;
}
+ public function __debugInfo()
+ {
+ $data = get_object_vars($this);
+ unset($data['app']);
+
+ return $data;
+ }
}
diff --git a/thinkphp/library/think/Lang.php b/thinkphp/library/think/Lang.php
index 5d8d9d99e9885044202668b54eb1c2e066983077..be7979f894e4429ed804599e1b8b64766bf4c979 100644
--- a/thinkphp/library/think/Lang.php
+++ b/thinkphp/library/think/Lang.php
@@ -161,7 +161,7 @@ class Lang
$range = $range ?: $this->range;
// 空参数返回所有定义
- if (empty($name)) {
+ if (is_null($name)) {
return $this->lang[$range];
}
diff --git a/thinkphp/library/think/Log.php b/thinkphp/library/think/Log.php
index b63233cd65c0c602d7890add889ba91d52945a1d..8902e9767f51772043b369d926e0aa452ad0616a 100644
--- a/thinkphp/library/think/Log.php
+++ b/thinkphp/library/think/Log.php
@@ -127,8 +127,10 @@ class Log implements LoggerInterface
}
if (PHP_SAPI == 'cli') {
- // 命令行日志实时写入
- $this->write($msg, $type, true);
+ if (empty($this->config['level']) || in_array($type, $this->config['level'])) {
+ // 命令行日志实时写入
+ $this->write($msg, $type, true);
+ }
} else {
$this->log[$type][] = $msg;
}
@@ -205,19 +207,17 @@ class Log implements LoggerInterface
return false;
}
- if (empty($this->config['level'])) {
- // 获取全部日志
- $log = $this->log;
- if (!$this->app->isDebug() && isset($log['debug'])) {
- unset($log['debug']);
+ $log = [];
+
+ foreach ($this->log as $level => $info) {
+ if (!$this->app->isDebug() && 'debug' == $level) {
+ continue;
}
- } else {
- // 记录允许级别
- $log = [];
- foreach ($this->config['level'] as $level) {
- if (isset($this->log[$level])) {
- $log[$level] = $this->log[$level];
- }
+
+ if (empty($this->config['level']) || in_array($level, $this->config['level'])) {
+ $log[$level] = $info;
+
+ $this->app['hook']->listen('log_level', [$level, $info]);
}
}
@@ -378,4 +378,12 @@ class Log implements LoggerInterface
{
$this->log(__FUNCTION__, $message, $context);
}
+
+ public function __debugInfo()
+ {
+ $data = get_object_vars($this);
+ unset($data['app']);
+
+ return $data;
+ }
}
diff --git a/thinkphp/library/think/Middleware.php b/thinkphp/library/think/Middleware.php
index 4814fdb99b72a9278506db93268dc9b05963c0f6..d3f43606db76a0392f82d9725fc1a110b953329d 100644
--- a/thinkphp/library/think/Middleware.php
+++ b/thinkphp/library/think/Middleware.php
@@ -110,6 +110,15 @@ class Middleware
return $this->queue[$type] ?: [];
}
+ /**
+ * 清除中间件
+ * @access public
+ */
+ public function clear()
+ {
+ $this->queue = [];
+ }
+
/**
* 中间件调度
* @access public
@@ -186,4 +195,11 @@ class Middleware
};
}
+ public function __debugInfo()
+ {
+ $data = get_object_vars($this);
+ unset($data['app']);
+
+ return $data;
+ }
}
diff --git a/thinkphp/library/think/Model.php b/thinkphp/library/think/Model.php
index 5b9ab3df7b03267334f462c81b6b7b747532bc87..4544ab21fe760d88f31d012b7efb6f9cbbd76b5b 100644
--- a/thinkphp/library/think/Model.php
+++ b/thinkphp/library/think/Model.php
@@ -18,6 +18,43 @@ use think\db\Query;
* Class Model
* @package think
* @mixin Query
+ * @method $this scope(string|array $scope) static 查询范围
+ * @method $this where(mixed $field, string $op = null, mixed $condition = null) static 查询条件
+ * @method $this whereRaw(string $where, array $bind = [], string $logic = 'AND') static 表达式查询
+ * @method $this whereExp(string $field, string $condition, array $bind = [], string $logic = 'AND') static 字段表达式查询
+ * @method $this when(mixed $condition, mixed $query, mixed $otherwise = null) static 条件查询
+ * @method $this join(mixed $join, mixed $condition = null, string $type = 'INNER', array $bind = []) static JOIN查询
+ * @method $this view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询
+ * @method $this with(mixed $with, callable $callback = null) static 关联预载入
+ * @method $this count(string $field = '*') static Count统计查询
+ * @method $this min(string $field, bool $force = true) static Min统计查询
+ * @method $this max(string $field, bool $force = true) static Max统计查询
+ * @method $this sum(string $field) static SUM统计查询
+ * @method $this avg(string $field) static Avg统计查询
+ * @method $this field(mixed $field, boolean $except = false, string $tableName = '', string $prefix = '', string $alias = '') static 指定查询字段
+ * @method $this fieldRaw(string $field) static 指定查询字段
+ * @method $this union(mixed $union, boolean $all = false) static UNION查询
+ * @method $this limit(mixed $offset, integer $length = null) static 查询LIMIT
+ * @method $this order(mixed $field, string $order = null) static 查询ORDER
+ * @method $this orderRaw(string $field, array $bind = []) static 查询ORDER
+ * @method $this cache(mixed $key = null , integer|\DateTime $expire = null, string $tag = null) static 设置查询缓存
+ * @method mixed value(string $field, mixed $default = null) static 获取某个字段的值
+ * @method array column(string $field, string $key = '') static 获取某个列的值
+ * @method $this find(mixed $data = null) static 查询单个记录
+ * @method $this findOrFail(mixed $data = null) 查询单个记录
+ * @method Collection|$this[] select(mixed $data = null) static 查询多个记录
+ * @method $this get(mixed $data = null,mixed $with = [],bool $cache = false, bool $failException = false) static 查询单个记录 支持关联预载入
+ * @method $this getOrFail(mixed $data = null,mixed $with = [],bool $cache = false) static 查询单个记录 不存在则抛出异常
+ * @method $this findOrEmpty(mixed $data = null) static 查询单个记录 不存在则返回空模型
+ * @method Collection|$this[] all(mixed $data = null,mixed $with = [],bool $cache = false) static 查询多个记录 支持关联预载入
+ * @method $this withAttr(array $name,\Closure $closure = null) static 动态定义获取器
+ * @method $this withJoin(string|array $with, string $joinType = '') static
+ * @method $this withCount(string|array $relation, bool $subQuery = true) static 关联统计
+ * @method $this withSum(string|array $relation, string $field, bool $subQuery = true) static 关联SUM统计
+ * @method $this withMax(string|array $relation, string $field, bool $subQuery = true) static 关联MAX统计
+ * @method $this withMin(string|array $relation, string $field, bool $subQuery = true) static 关联Min统计
+ * @method $this withAvg(string|array $relation, string $field, bool $subQuery = true) static 关联Avg统计
+ * @method Paginator|$this paginate() static 分页
*/
abstract class Model implements \JsonSerializable, \ArrayAccess
{
@@ -123,6 +160,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
*/
protected $defaultSoftDelete;
+ /**
+ * 全局查询范围
+ * @var array
+ */
+ protected $globalScope = [];
+
/**
* 架构函数
* @access public
@@ -271,7 +314,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
/**
* 获取当前模型的数据库查询对象
* @access public
- * @param bool $useBaseQuery 是否调用全局查询范围
+ * @param bool|array $useBaseQuery 是否调用全局查询范围(或者指定查询范围名称)
* @return Query
*/
public function db($useBaseQuery = true)
@@ -288,10 +331,16 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
}
// 全局作用域
- if ($useBaseQuery && method_exists($this, 'base')) {
+ if (true === $useBaseQuery && method_exists($this, 'base')) {
call_user_func_array([$this, 'base'], [ & $query]);
}
+ $globalScope = is_array($useBaseQuery) && $useBaseQuery ? $useBaseQuery : $this->globalScope;
+
+ if ($globalScope && false !== $useBaseQuery) {
+ $query->scope($globalScope);
+ }
+
// 返回当前模型的数据库查询对象
return $query;
}
@@ -379,11 +428,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
* 设置数据是否存在
* @access public
* @param bool $exists
- * @return void
+ * @return $this
*/
public function exists($exists)
{
$this->exists = $exists;
+ return $this;
}
/**
@@ -396,6 +446,16 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
return $this->exists;
}
+ /**
+ * 判断模型是否为空
+ * @access public
+ * @return bool
+ */
+ public function isEmpty()
+ {
+ return empty($this->data);
+ }
+
/**
* 保存当前数据对象
* @access public
@@ -426,6 +486,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
// 重新记录原始数据
$this->origin = $this->data;
+ $this->set = [];
return true;
}
@@ -519,7 +580,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$this->autoRelationUpdate();
}
- return false;
+ return true;
} elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) {
// 自动写入更新时间
$data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime);
@@ -728,12 +789,19 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
// 删除条件
$pk = $this->getPk();
+ $where = [];
if (is_string($pk) && isset($this->data[$pk])) {
$where[] = [$pk, '=', $this->data[$pk]];
- } elseif (!empty($this->updateWhere)) {
- $where = $this->updateWhere;
- } else {
- $where = null;
+ } elseif (is_array($pk)) {
+ foreach ($pk as $field) {
+ if (isset($this->data[$field])) {
+ $where[] = [$field, '=', $this->data[$field]];
+ }
+ }
+ }
+
+ if (empty($where)) {
+ $where = empty($this->updateWhere) ? null : $this->updateWhere;
}
return $where;
@@ -820,7 +888,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
try {
// 删除当前模型数据
- $result = $db->where($where)->delete();
+ $db->where($where)->delete();
// 关联删除
if (!empty($this->relationWrite)) {
@@ -895,93 +963,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
return $model;
}
- /**
- * 查找单条记录
- * @access public
- * @param mixed $data 主键值或者查询条件(闭包)
- * @param mixed $with 关联预查询
- * @param bool $cache 是否缓存
- * @param bool $failException 是否抛出异常
- * @return static|null
- * @throws exception\DbException
- */
- public static function get($data, $with = [], $cache = false, $failException = false)
- {
- if (is_null($data)) {
- return;
- }
-
- if (true === $with || is_int($with)) {
- $cache = $with;
- $with = [];
- }
-
- $query = static::parseQuery($data, $with, $cache);
-
- return $query->failException($failException)->find($data);
- }
-
- /**
- * 查找单条记录 如果不存在直接抛出异常
- * @access public
- * @param mixed $data 主键值或者查询条件(闭包)
- * @param mixed $with 关联预查询
- * @param bool $cache 是否缓存
- * @return static|null
- * @throws exception\DbException
- */
- public static function getOrFail($data, $with = [], $cache = false)
- {
- return self::get($data, $with, $cache, true);
- }
-
- /**
- * 查找所有记录
- * @access public
- * @param mixed $data 主键列表或者查询条件(闭包)
- * @param array|string $with 关联预查询
- * @param bool $cache 是否缓存
- * @return static[]|false
- * @throws exception\DbException
- */
- public static function all($data = null, $with = [], $cache = false)
- {
- if (true === $with || is_int($with)) {
- $cache = $with;
- $with = [];
- }
-
- $query = static::parseQuery($data, $with, $cache);
-
- return $query->select($data);
- }
-
- /**
- * 分析查询表达式
- * @access public
- * @param mixed $data 主键列表或者查询条件(闭包)
- * @param string $with 关联预查询
- * @param bool $cache 是否缓存
- * @return Query
- */
- protected static function parseQuery(&$data, $with, $cache)
- {
- $result = self::with($with)->cache($cache);
-
- if (is_array($data) && key($data) !== 0) {
- $result = $result->where($data);
- $data = null;
- } elseif ($data instanceof \Closure) {
- $data($result);
- $data = null;
- } elseif ($data instanceof Query) {
- $result = $data->with($with)->cache($cache);
- $data = null;
- }
-
- return $result;
- }
-
/**
* 删除记录
* @access public
@@ -1116,7 +1097,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
/**
* 设置是否使用全局查询范围
* @access public
- * @param bool $use 是否启用全局查询范围
+ * @param bool|array $use 是否启用全局查询范围(或者用数组指定查询范围名称)
* @return Query
*/
public static function useGlobalScope($use)
@@ -1128,6 +1109,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
public function __call($method, $args)
{
+ if ('withattr' == strtolower($method)) {
+ return call_user_func_array([$this, 'withAttribute'], $args);
+ }
+
return call_user_func_array([$this->db(), $method], $args);
}
diff --git a/thinkphp/library/think/Request.php b/thinkphp/library/think/Request.php
index 37760280bd5806784e168550a266e29ed8dead6f..6b6dd4b41a11c72ff267c6d398cda1680709f3aa 100644
--- a/thinkphp/library/think/Request.php
+++ b/thinkphp/library/think/Request.php
@@ -521,6 +521,18 @@ class Request
return $this->panDomain;
}
+ /**
+ * 设置当前完整URL 包括QUERY_STRING
+ * @access public
+ * @param string $url URL
+ * @return $this
+ */
+ public function setUrl($url)
+ {
+ $this->url = $url;
+ return $this;
+ }
+
/**
* 获取当前完整URL 包括QUERY_STRING
* @access public
@@ -546,6 +558,18 @@ class Request
return $complete ? $this->domain() . $this->url : $this->url;
}
+ /**
+ * 设置当前完整URL 不包括QUERY_STRING
+ * @access public
+ * @param string $url URL
+ * @return $this
+ */
+ public function setBaseUrl($url)
+ {
+ $this->baseUrl = $url;
+ return $this;
+ }
+
/**
* 获取当前URL 不含QUERY_STRING
* @access public
@@ -658,6 +682,7 @@ class Request
// 判断URL里面是否有兼容模式参数
$pathinfo = $_GET[$this->config['var_pathinfo']];
unset($_GET[$this->config['var_pathinfo']]);
+ unset($this->get[$this->config['var_pathinfo']]);
} elseif ($this->isCli()) {
// CLI模式下 index.php module/controller/action/params/...
$pathinfo = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : '';
@@ -678,6 +703,10 @@ class Request
}
}
+ if (!empty($pathinfo)) {
+ unset($this->get[$pathinfo], $this->request[$pathinfo]);
+ }
+
$this->pathinfo = empty($pathinfo) || '/' == $pathinfo ? '' : ltrim($pathinfo, '/');
}
@@ -785,9 +814,14 @@ class Request
return $this->server('REQUEST_METHOD') ?: 'GET';
} elseif (!$this->method) {
if (isset($_POST[$this->config['var_method']])) {
- $this->method = strtoupper($_POST[$this->config['var_method']]);
- $method = strtolower($this->method);
- $this->{$method} = $_POST;
+ $method = strtolower($_POST[$this->config['var_method']]);
+ if (in_array($method, ['get', 'post', 'put', 'patch', 'delete'])) {
+ $this->method = strtoupper($method);
+ $this->{$method} = $_POST;
+ } else {
+ $this->method = 'POST';
+ }
+ unset($_POST[$this->config['var_method']]);
} elseif ($this->server('HTTP_X_HTTP_METHOD_OVERRIDE')) {
$this->method = strtoupper($this->server('HTTP_X_HTTP_METHOD_OVERRIDE'));
} else {
@@ -1010,7 +1044,7 @@ class Request
protected function getInputData($content)
{
- if (false !== strpos($this->contentType(), 'application/json') || 0 === strpos($content, '{"')) {
+ if (false !== strpos($this->contentType(), 'json')) {
return (array) json_decode($content, true);
} elseif (strpos($content, '=')) {
parse_str($content, $data);
@@ -1150,13 +1184,13 @@ class Request
$files = $this->file;
if (!empty($files)) {
- // 处理上传文件
- $array = $this->dealUploadFile($files);
-
if (strpos($name, '.')) {
list($name, $sub) = explode('.', $name);
}
+ // 处理上传文件
+ $array = $this->dealUploadFile($files, $name);
+
if ('' === $name) {
// 获取全部文件
return $array;
@@ -1170,18 +1204,24 @@ class Request
return;
}
- protected function dealUploadFile($files)
+ protected function dealUploadFile($files, $name)
{
$array = [];
foreach ($files as $key => $file) {
- if (is_array($file['name'])) {
+ if ($file instanceof File) {
+ $array[$key] = $file;
+ } elseif (is_array($file['name'])) {
$item = [];
$keys = array_keys($file);
$count = count($file['name']);
for ($i = 0; $i < $count; $i++) {
- if (empty($file['tmp_name'][$i]) || !is_file($file['tmp_name'][$i])) {
- continue;
+ if ($file['error'][$i] > 0) {
+ if ($name == $key) {
+ $this->throwUploadFileError($file['error'][$i]);
+ } else {
+ continue;
+ }
}
$temp['key'] = $key;
@@ -1195,21 +1235,37 @@ class Request
$array[$key] = $item;
} else {
- if ($file instanceof File) {
- $array[$key] = $file;
- } else {
- if (empty($file['tmp_name']) || !is_file($file['tmp_name'])) {
+ if ($file['error'] > 0) {
+ if ($key == $name) {
+ $this->throwUploadFileError($file['error']);
+ } else {
continue;
}
-
- $array[$key] = (new File($file['tmp_name']))->setUploadInfo($file);
}
+
+ $array[$key] = (new File($file['tmp_name']))->setUploadInfo($file);
}
}
return $array;
}
+ protected function throwUploadFileError($error)
+ {
+ static $fileUploadErrors = [
+ 1 => 'upload File size exceeds the maximum value',
+ 2 => 'upload File size exceeds the maximum value',
+ 3 => 'only the portion of file is uploaded',
+ 4 => 'no file to uploaded',
+ 6 => 'upload temp dir not found',
+ 7 => 'file write error',
+ ];
+
+ $msg = $fileUploadErrors[$error];
+
+ throw new Exception($msg);
+ }
+
/**
* 获取环境变量
* @access public
@@ -1268,6 +1324,22 @@ class Request
return isset($this->header[$name]) ? $this->header[$name] : $default;
}
+ /**
+ * 递归重置数组指针
+ * @access public
+ * @param array $data 数据源
+ * @return void
+ */
+ public function arrayReset(array &$data)
+ {
+ foreach ($data as &$value) {
+ if (is_array($value)) {
+ $this->arrayReset($value);
+ }
+ }
+ reset($data);
+ }
+
/**
* 获取变量 支持过滤和默认值
* @access public
@@ -1307,7 +1379,10 @@ class Request
if (is_array($data)) {
array_walk_recursive($data, [$this, 'filterValue'], $filter);
- reset($data);
+ if (version_compare(PHP_VERSION, '7.1.0', '<')) {
+ // 恢复PHP版本低于 7.1 时 array_walk_recursive 中消耗的内部指针
+ $this->arrayReset($data);
+ }
} else {
$this->filterValue($data, $name, $filter);
}
@@ -1459,7 +1534,7 @@ class Request
*/
public function has($name, $type = 'param', $checkEmpty = false)
{
- if (!in_array($type, ['param', 'get', 'post', 'request', 'put', 'file', 'session', 'cookie', 'env', 'header', 'route'])) {
+ if (!in_array($type, ['param', 'get', 'post', 'request', 'put', 'patch', 'file', 'session', 'cookie', 'env', 'header', 'route'])) {
return false;
}
@@ -1561,6 +1636,16 @@ class Request
return false;
}
+ /**
+ * 当前是否JSON请求
+ * @access public
+ * @return bool
+ */
+ public function isJson()
+ {
+ return false !== strpos($this->type(), 'json');
+ }
+
/**
* 当前是否Ajax请求
* @access public
@@ -1576,7 +1661,9 @@ class Request
return $result;
}
- return $this->param($this->config['var_ajax']) ? true : $result;
+ $result = $this->param($this->config['var_ajax']) ? true : $result;
+ $this->mergeParam = false;
+ return $result;
}
/**
@@ -1593,7 +1680,9 @@ class Request
return $result;
}
- return $this->param($this->config['var_pjax']) ? true : $result;
+ $result = $this->param($this->config['var_pjax']) ? true : $result;
+ $this->mergeParam = false;
+ return $result;
}
/**
@@ -1711,7 +1800,7 @@ class Request
public function host($strict = false)
{
if (!$this->host) {
- $this->host = $this->server('HTTP_X_REAL_HOST') ?: $this->server('HTTP_HOST');
+ $this->host = $this->server('HTTP_X_REAL_HOST') ?: $this->server('HTTP_X_FORWARDED_HOST') ?: $this->server('HTTP_HOST');
}
return true === $strict && strpos($this->host, ':') ? strstr($this->host, ':', true) : $this->host;
@@ -2167,4 +2256,12 @@ class Request
{
return isset($this->param[$name]);
}
+
+ public function __debugInfo()
+ {
+ $data = get_object_vars($this);
+ unset($data['dispatch'], $data['config']);
+
+ return $data;
+ }
}
diff --git a/thinkphp/library/think/Response.php b/thinkphp/library/think/Response.php
index 09cb6a9245dca933616104a2d266f99a8507177c..5fa5402abe9bcee213ccb98ee5be2a47888c392a 100644
--- a/thinkphp/library/think/Response.php
+++ b/thinkphp/library/think/Response.php
@@ -323,7 +323,7 @@ class Response
/**
* 页面缓存控制
* @access public
- * @param string $cache 状态码
+ * @param string $cache 缓存设置
* @return $this
*/
public function cacheControl($cache)
@@ -333,6 +333,19 @@ class Response
return $this;
}
+ /**
+ * 设置页面不做任何缓存
+ * @access public
+ * @return $this
+ */
+ public function noCache()
+ {
+ $this->header['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0';
+ $this->header['Pragma'] = 'no-cache';
+
+ return $this;
+ }
+
/**
* 页面输出类型
* @access public
@@ -405,4 +418,12 @@ class Response
{
return $this->code;
}
+
+ public function __debugInfo()
+ {
+ $data = get_object_vars($this);
+ unset($data['app']);
+
+ return $data;
+ }
}
diff --git a/thinkphp/library/think/Route.php b/thinkphp/library/think/Route.php
index 43b7614c54c20395e30971abf93dbe236289eb66..97f6dc7d4518723110defd624377dcec13973539 100644
--- a/thinkphp/library/think/Route.php
+++ b/thinkphp/library/think/Route.php
@@ -13,9 +13,11 @@ namespace think;
use think\exception\RouteNotFoundException;
use think\route\AliasRule;
+use think\route\Dispatch;
use think\route\dispatch\Url as UrlDispatch;
use think\route\Domain;
use think\route\Resource;
+use think\route\Rule;
use think\route\RuleGroup;
use think\route\RuleItem;
@@ -113,6 +115,12 @@ class Route
*/
protected $lazy = true;
+ /**
+ * 路由是否测试模式
+ * @var bool
+ */
+ protected $isTest;
+
/**
* (分组)路由规则是否合并解析
* @var bool
@@ -191,6 +199,27 @@ class Route
return $this;
}
+ /**
+ * 设置路由为测试模式
+ * @access public
+ * @param bool $test 路由是否测试模式
+ * @return void
+ */
+ public function setTestMode($test)
+ {
+ $this->isTest = $test;
+ }
+
+ /**
+ * 检查路由是否为测试模式
+ * @access public
+ * @return bool
+ */
+ public function isTest()
+ {
+ return $this->isTest;
+ }
+
/**
* 设置路由域名及分组(包括资源路由)是否合并解析
* @access public
@@ -299,7 +328,7 @@ class Route
// 支持多个域名使用相同路由规则
$domainName = is_array($name) ? array_shift($name) : $name;
- if ('*' != $domainName && !strpos($domainName, '.')) {
+ if ('*' != $domainName && false === strpos($domainName, '.')) {
$domainName .= '.' . $this->request->rootDomain();
}
@@ -317,7 +346,7 @@ class Route
if (is_array($name) && !empty($name)) {
$root = $this->request->rootDomain();
foreach ($name as $item) {
- if (!strpos($item, '.')) {
+ if (false === strpos($item, '.')) {
$item .= '.' . $root;
}
@@ -367,7 +396,7 @@ class Route
$domain = $this->domain;
} elseif (true === $domain) {
return $this->bind;
- } elseif (!strpos($domain, '.')) {
+ } elseif (false === strpos($domain, '.')) {
$domain .= '.' . $this->request->rootDomain();
}
@@ -381,7 +410,7 @@ class Route
$result = $this->bind[$domain];
} elseif (isset($name) && isset($this->bind[$name])) {
$result = $this->bind[$name];
- } elseif (isset($this->bind['*'])) {
+ } elseif (!empty($subDomain) && isset($this->bind['*'])) {
$result = $this->bind['*'];
} else {
$result = null;
@@ -397,9 +426,9 @@ class Route
* @param string $domain 域名
* @return mixed
*/
- public function getName($name = null, $domain = null)
+ public function getName($name = null, $domain = null, $method = '*')
{
- return $this->app['rule_name']->get($name, $domain);
+ return $this->app['rule_name']->get($name, $domain, $method);
}
/**
@@ -466,7 +495,9 @@ class Route
// 检查路由别名
if (isset($rules['__alias__'])) {
- $this->alias($rules['__alias__']);
+ foreach ($rules['__alias__'] as $key => $val) {
+ $this->alias($key, $val);
+ }
unset($rules['__alias__']);
}
@@ -928,6 +959,17 @@ class Route
return $item;
}
+ /**
+ * 清空路由规则
+ * @access public
+ * @return void
+ */
+ public function clear()
+ {
+ $this->app['rule_name']->clear();
+ $this->group->clear();
+ }
+
/**
* 设置全局的路由分组参数
* @access public
@@ -939,4 +981,12 @@ class Route
{
return call_user_func_array([$this->group, $method], $args);
}
+
+ public function __debugInfo()
+ {
+ $data = get_object_vars($this);
+ unset($data['app'], $data['request']);
+
+ return $data;
+ }
}
diff --git a/thinkphp/library/think/Session.php b/thinkphp/library/think/Session.php
index f21c93b1e646a9e58b35d2e07e0f09da8907f836..63ee7a03e9edcd23082062a2a2cd7c9539995f16 100644
--- a/thinkphp/library/think/Session.php
+++ b/thinkphp/library/think/Session.php
@@ -197,8 +197,7 @@ class Session
}
if ($isDoStart) {
- session_start();
- $this->init = true;
+ $this->start();
} else {
$this->init = false;
}
@@ -219,7 +218,7 @@ class Session
if (false === $this->init) {
if (PHP_SESSION_ACTIVE != session_status()) {
- session_start();
+ $this->start();
}
$this->init = true;
}
diff --git a/thinkphp/library/think/Template.php b/thinkphp/library/think/Template.php
index 4892fcbd89099b447d1f74c185a15af65b6b5a8d..2855cbcb26b07803fc4e7103f249c8aed378bc1a 100644
--- a/thinkphp/library/think/Template.php
+++ b/thinkphp/library/think/Template.php
@@ -387,7 +387,7 @@ class Template
}
// 优化生成的php代码
- $content = preg_replace('/\?>\s*<\?php\s(?!echo\b)/s', '', $content);
+ $content = preg_replace('/\?>\s*<\?php\s(?!echo\b|\bend)/s', '', $content);
// 模板过滤输出
$replace = $this->config['tpl_replace_string'];
@@ -1269,9 +1269,9 @@ class Template
switch ($tagName) {
case 'block':
if ($single) {
- $regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?P[\$\w\-\/\.]+)\\1(?>[^' . $end . ']*)|\/' . $tagName . ')' . $end;
+ $regex = $begin . '(?:' . $tagName . '\b\s+(?>(?:(?!name=).)*)\bname=([\'\"])(?P[\$\w\-\/\.]+)\\1(?>[^' . $end . ']*)|\/' . $tagName . ')' . $end;
} else {
- $regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?P[\$\w\-\/\.]+)\\1(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end;
+ $regex = $begin . '(?:' . $tagName . '\b\s+(?>(?:(?!name=).)*)\bname=([\'\"])(?P[\$\w\-\/\.]+)\\1(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end;
}
break;
case 'literal':
@@ -1297,9 +1297,9 @@ class Template
$name = 'name';
}
if ($single) {
- $regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P[\$\w\-\/\.\:@,\\\\]+)\\1(?>[^' . $end . ']*)' . $end;
+ $regex = $begin . $tagName . '\b\s+(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P[\$\w\-\/\.\:@,\\\\]+)\\1(?>[^' . $end . ']*)' . $end;
} else {
- $regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P[\$\w\-\/\.\:@,\\\\]+)\\1(?>(?:(?!' . $end . ').)*)' . $end;
+ $regex = $begin . $tagName . '\b\s+(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P[\$\w\-\/\.\:@,\\\\]+)\\1(?>(?:(?!' . $end . ').)*)' . $end;
}
break;
}
@@ -1307,4 +1307,12 @@ class Template
return '/' . $regex . '/is';
}
+
+ public function __debugInfo()
+ {
+ $data = get_object_vars($this);
+ unset($data['app'], $data['storage']);
+
+ return $data;
+ }
}
diff --git a/thinkphp/library/think/Url.php b/thinkphp/library/think/Url.php
index e8159993b01070b5af4b3bc159b0e75acac0bd97..acd510aa60521d4f363615d9b280aae0a2474a0b 100644
--- a/thinkphp/library/think/Url.php
+++ b/thinkphp/library/think/Url.php
@@ -130,7 +130,9 @@ class Url
// 匹配路由命名标识
$url = $match[0];
- $domain = $match[1];
+ if ($domain) {
+ $domain = $match[1];
+ }
if (!is_null($match[2])) {
$suffix = $match[2];
@@ -286,7 +288,7 @@ class Url
$rootDomain = $this->app['request']->rootDomain();
if (true === $domain) {
// 自动判断域名
- $domain = $this->config['app_host'] ?: $this->app['request']->host(true);
+ $domain = $this->config['app_host'] ?: $this->app['request']->host();
$domains = $this->app['route']->getDomains();
@@ -316,7 +318,7 @@ class Url
}
}
}
- } elseif (!strpos($domain, '.')) {
+ } elseif (0 !== strpos($domain, $rootDomain) && false === strpos($domain, '.')) {
$domain .= '.' . $rootDomain;
}
@@ -347,23 +349,29 @@ class Url
// 匹配路由地址
public function getRuleUrl($rule, &$vars = [], $allowDomain = '')
{
+ $port = $this->app['request']->port();
foreach ($rule as $item) {
- list($url, $pattern, $domain, $suffix) = $item;
+ list($url, $pattern, $domain, $suffix, $method) = $item;
if (is_string($allowDomain) && $domain != $allowDomain) {
continue;
}
+ if ($port && !in_array($port, [80, 443])) {
+ $domain .= ':' . $port;
+ }
+
if (empty($pattern)) {
return [rtrim($url, '?/-'), $domain, $suffix];
}
$type = $this->config['url_common_param'];
+ $keys = [];
foreach ($pattern as $key => $val) {
if (isset($vars[$key])) {
- $url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key, '<' . $key . '>'], $type ? $vars[$key] : urlencode($vars[$key]), $url);
- unset($vars[$key]);
+ $url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key, '<' . $key . '>'], $type ? $vars[$key] : urlencode($vars[$key]), $url);
+ $keys[] = $key;
$url = str_replace(['/?', '-?'], ['/', '-'], $url);
$result = [rtrim($url, '?/-'), $domain, $suffix];
} elseif (2 == $val) {
@@ -371,10 +379,14 @@ class Url
$url = str_replace(['/?', '-?'], ['/', '-'], $url);
$result = [rtrim($url, '?/-'), $domain, $suffix];
} else {
+ $result = null;
+ $keys = [];
break;
}
}
+ $vars = array_diff_key($vars, array_flip($keys));
+
if (isset($result)) {
return $result;
}
@@ -389,4 +401,12 @@ class Url
$this->root = $root;
$this->app['request']->setRoot($root);
}
+
+ public function __debugInfo()
+ {
+ $data = get_object_vars($this);
+ unset($data['app']);
+
+ return $data;
+ }
}
diff --git a/thinkphp/library/think/Validate.php b/thinkphp/library/think/Validate.php
index de46956cf20b756345d856a0c7091ca1429bfea6..5fde7f3109d380c24578fc2fb818aa3496b72a8c 100644
--- a/thinkphp/library/think/Validate.php
+++ b/thinkphp/library/think/Validate.php
@@ -87,6 +87,8 @@ class Validate
'min' => 'min size of :attribute must be :rule',
'after' => ':attribute cannot be less than :rule',
'before' => ':attribute cannot exceed :rule',
+ 'afterWith' => ':attribute cannot be less than :rule',
+ 'beforeWith' => ':attribute cannot exceed :rule',
'expire' => ':attribute not within :rule',
'allowIp' => 'access IP is not allowed',
'denyIp' => 'access IP denied',
@@ -129,7 +131,7 @@ class Validate
* 内置正则验证规则
* @var array
*/
- protected $regex = [
+ protected $defaultRegex = [
'alphaDash' => '/^[A-Za-z0-9\-\_]+$/',
'chs' => '/^[\x{4e00}-\x{9fa5}]+$/u',
'chsAlpha' => '/^[\x{4e00}-\x{9fa5}a-zA-Z]+$/u',
@@ -176,6 +178,12 @@ class Validate
*/
protected $append = [];
+ /**
+ * 验证正则定义
+ * @var array
+ */
+ protected $regex = [];
+
/**
* 架构函数
* @access public
@@ -329,10 +337,10 @@ class Validate
* 移除某个字段的验证规则
* @access public
* @param string|array $field 字段名
- * @param mixed $rule 验证规则 true 移除所有规则
+ * @param mixed $rule 验证规则 null 移除所有规则
* @return $this
*/
- public function remove($field, $rule = true)
+ public function remove($field, $rule = null)
{
if (is_array($field)) {
foreach ($field as $key => $rule) {
@@ -400,6 +408,7 @@ class Validate
foreach ($this->append as $key => $rule) {
if (!isset($rules[$key])) {
$rules[$key] = $rule;
+ unset($this->append[$key]);
}
}
@@ -417,12 +426,12 @@ class Validate
continue;
}
- // 获取数据 支持二维数组
+ // 获取数据 支持多维数组
$value = $this->getDataValue($data, $key);
// 字段验证
if ($rule instanceof \Closure) {
- $result = call_user_func_array($rule, [$value, $data]);
+ $result = call_user_func_array($rule, [$value, $data, $title, $this]);
} elseif ($rule instanceof ValidateRule) {
// 验证因子
$result = $this->checkItem($key, $value, $rule->getRule(), $data, $rule->getTitle() ?: $title, $rule->getMsg());
@@ -511,10 +520,13 @@ class Validate
if (isset($this->append[$field])) {
// 追加额外的验证规则
- $rules = array_merge($rules, $this->append[$field]);
+ $rules = array_unique(array_merge($rules, $this->append[$field]), SORT_REGULAR);
+ unset($this->append[$field]);
}
- $i = 0;
+ $i = 0;
+ $result = true;
+
foreach ($rules as $key => $rule) {
if ($rule instanceof \Closure) {
$result = call_user_func_array($rule, [$value, $data]);
@@ -525,17 +537,18 @@ class Validate
if (isset($this->append[$field]) && in_array($info, $this->append[$field])) {
- } elseif (isset($this->remove[$field]) && in_array($info, $this->remove[$field])) {
+ } elseif (array_key_exists($field, $this->remove) && (null === $this->remove[$field] || in_array($info, $this->remove[$field]))) {
// 规则已经移除
$i++;
continue;
}
- if ('must' == $info || 0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) {
- // 验证类型
- $callback = isset(self::$type[$type]) ? self::$type[$type] : [$this, $type];
+ // 验证类型
+ if (isset(self::$type[$type])) {
+ $result = call_user_func_array(self::$type[$type], [$value, $rule, $data, $field, $title]);
+ } elseif ('must' == $info || 0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) {
// 验证数据
- $result = call_user_func_array($callback, [$value, $rule, $data, $field, $title]);
+ $result = call_user_func_array([$this, $type], [$value, $rule, $data, $field, $title]);
} else {
$result = true;
}
@@ -546,7 +559,7 @@ class Validate
if (!empty($msg[$i])) {
$message = $msg[$i];
if (is_string($message) && strpos($message, '{%') === 0) {
- $message = Lang::get(substr($message, 2, -1));
+ $message = facade\Lang::get(substr($message, 2, -1));
}
} else {
$message = $this->getRuleMsg($field, $title, $info, $rule);
@@ -556,10 +569,11 @@ class Validate
} elseif (true !== $result) {
// 返回自定义错误信息
if (is_string($result) && false !== strpos($result, ':')) {
- $result = str_replace(
- [':attribute', ':rule'],
- [$title, (string) $rule],
- $result);
+ $result = str_replace(':attribute', $title, $result);
+
+ if (strpos($result, ':rule') && is_scalar($rule)) {
+ $result = str_replace(':rule', (string) $rule, $result);
+ }
}
return $result;
@@ -922,8 +936,8 @@ class Validate
if (isset($rule[2])) {
$imageType = strtolower($rule[2]);
- if ('jpeg' == $imageType) {
- $imageType = 'jpg';
+ if ('jpg' == $imageType) {
+ $imageType = 'jpeg';
}
if (image_type_to_extension($type, false) != $imageType) {
@@ -997,10 +1011,16 @@ class Validate
// 支持多个字段验证
$fields = explode('^', $key);
foreach ($fields as $key) {
- $map[] = [$key, '=', $data[$key]];
+ if (isset($data[$key])) {
+ $map[] = [$key, '=', $data[$key]];
+ }
}
- } else {
+ } elseif (strpos($key, '=')) {
+ parse_str($key, $map);
+ } elseif (isset($data[$field])) {
$map[] = [$key, '=', $data[$field]];
+ } else {
+ $map = [];
}
$pk = !empty($rule[3]) ? $rule[3] : $db->getPk();
@@ -1241,9 +1261,10 @@ class Validate
* @access public
* @param mixed $value 字段值
* @param mixed $rule 验证规则
+ * @param array $data 数据
* @return bool
*/
- public function after($value, $rule)
+ public function after($value, $rule, $data)
{
return strtotime($value) >= strtotime($rule);
}
@@ -1253,13 +1274,42 @@ class Validate
* @access public
* @param mixed $value 字段值
* @param mixed $rule 验证规则
+ * @param array $data 数据
* @return bool
*/
- public function before($value, $rule)
+ public function before($value, $rule, $data)
{
return strtotime($value) <= strtotime($rule);
}
+ /**
+ * 验证日期字段
+ * @access protected
+ * @param mixed $value 字段值
+ * @param mixed $rule 验证规则
+ * @param array $data 数据
+ * @return bool
+ */
+ protected function afterWith($value, $rule, $data)
+ {
+ $rule = $this->getDataValue($data, $rule);
+ return !is_null($rule) && strtotime($value) >= strtotime($rule);
+ }
+
+ /**
+ * 验证日期字段
+ * @access protected
+ * @param mixed $value 字段值
+ * @param mixed $rule 验证规则
+ * @param array $data 数据
+ * @return bool
+ */
+ protected function beforeWith($value, $rule, $data)
+ {
+ $rule = $this->getDataValue($data, $rule);
+ return !is_null($rule) && strtotime($value) <= strtotime($rule);
+ }
+
/**
* 验证有效期
* @access public
@@ -1321,6 +1371,8 @@ class Validate
{
if (isset($this->regex[$rule])) {
$rule = $this->regex[$rule];
+ } elseif (isset($this->defaultRegex[$rule])) {
+ $rule = $this->defaultRegex[$rule];
}
if (0 !== strpos($rule, '/') && !preg_match('/\/[imsU]{0,4}$/', $rule)) {
@@ -1372,7 +1424,7 @@ class Validate
* 获取数据值
* @access protected
* @param array $data 数据
- * @param string $key 数据标识 支持二维
+ * @param string $key 数据标识 支持多维
* @return mixed
*/
protected function getDataValue($data, $key)
@@ -1380,9 +1432,14 @@ class Validate
if (is_numeric($key)) {
$value = $key;
} elseif (strpos($key, '.')) {
- // 支持二维数组验证
- list($name1, $name2) = explode('.', $key);
- $value = isset($data[$name1][$name2]) ? $data[$name1][$name2] : null;
+ // 支持多维数组验证
+ foreach (explode('.', $key) as $key) {
+ if (!isset($data[$key])) {
+ $value = null;
+ break;
+ }
+ $value = $data = $data[$key];
+ }
} else {
$value = isset($data[$key]) ? $data[$key] : null;
}
@@ -1417,13 +1474,17 @@ class Validate
$msg = $title . $lang->get('not conform to the rules');
}
- if (is_string($msg) && 0 === strpos($msg, '{%')) {
+ if (!is_string($msg)) {
+ return $msg;
+ }
+
+ if (0 === strpos($msg, '{%')) {
$msg = $lang->get(substr($msg, 2, -1));
} elseif ($lang->has($msg)) {
$msg = $lang->get($msg);
}
- if (is_string($msg) && is_scalar($rule) && false !== strpos($msg, ':')) {
+ if (is_scalar($rule) && false !== strpos($msg, ':')) {
// 变量替换
if (is_string($rule) && strpos($rule, ',')) {
$array = array_pad(explode(',', $rule), 3, '');
@@ -1431,9 +1492,12 @@ class Validate
$array = array_pad([], 3, '');
}
$msg = str_replace(
- [':attribute', ':rule', ':1', ':2', ':3'],
- [$title, (string) $rule, $array[0], $array[1], $array[2]],
+ [':attribute', ':1', ':2', ':3'],
+ [$title, $array[0], $array[1], $array[2]],
$msg);
+ if (strpos($msg, ':rule')) {
+ $msg = str_replace(':rule', (string) $rule, $msg);
+ }
}
return $msg;
@@ -1452,12 +1516,12 @@ class Validate
$scene = $this->currentScene;
}
+ $this->only = $this->append = $this->remove = [];
+
if (empty($scene)) {
return;
}
- $this->only = $this->append = $this->remove = [];
-
if (method_exists($this, 'scene' . $scene)) {
call_user_func([$this, 'scene' . $scene]);
} elseif (isset($this->scene[$scene])) {
diff --git a/thinkphp/library/think/View.php b/thinkphp/library/think/View.php
index 17860a6ba15f8651d2f2d1da5643907a004088e9..284dd41a1372f6284a7f69b880ce5d01b460f368 100644
--- a/thinkphp/library/think/View.php
+++ b/thinkphp/library/think/View.php
@@ -160,7 +160,10 @@ class View
*/
public function filter($filter)
{
- $this->filter = $filter;
+ if ($filter) {
+ $this->filter = $filter;
+ }
+
return $this;
}
diff --git a/thinkphp/library/think/cache/Driver.php b/thinkphp/library/think/cache/Driver.php
index f0ec7baf407407b4976fd7dfc1249f29ac7233b6..6421681072e2369599383d9fe2c4f65b97df595b 100644
--- a/thinkphp/library/think/cache/Driver.php
+++ b/thinkphp/library/think/cache/Driver.php
@@ -219,7 +219,7 @@ abstract class Driver
} elseif (is_null($keys)) {
$this->tag = $name;
} else {
- $key = 'tag_' . md5($name);
+ $key = $this->getTagkey($name);
if (is_string($keys)) {
$keys = explode(',', $keys);
@@ -248,20 +248,23 @@ abstract class Driver
protected function setTagItem($name)
{
if ($this->tag) {
- $key = 'tag_' . md5($this->tag);
- $prev = $this->tag;
+ $key = $this->getTagkey($this->tag);
$this->tag = null;
if ($this->has($key)) {
$value = explode(',', $this->get($key));
$value[] = $name;
- $value = implode(',', array_unique($value));
+
+ if (count($value) > 1000) {
+ array_shift($value);
+ }
+
+ $value = implode(',', array_unique($value));
} else {
$value = $name;
}
$this->set($key, $value, 0);
- $this->tag = $prev;
}
}
@@ -273,7 +276,7 @@ abstract class Driver
*/
protected function getTagItem($tag)
{
- $key = 'tag_' . md5($tag);
+ $key = $this->getTagkey($tag);
$value = $this->get($key);
if ($value) {
@@ -283,6 +286,11 @@ abstract class Driver
}
}
+ protected function getTagKey($tag)
+ {
+ return 'tag_' . md5($tag);
+ }
+
/**
* 序列化数据
* @access protected
@@ -350,4 +358,9 @@ abstract class Driver
{
return $this->writeTimes;
}
+
+ public function __call($method, $args)
+ {
+ return call_user_func_array([$this->handler, $method], $args);
+ }
}
diff --git a/thinkphp/library/think/cache/driver/File.php b/thinkphp/library/think/cache/driver/File.php
index 7c5661e3d7c1e7972bbd7dce12e5cc3c1b2dfe33..60be08db4b1a3ff43b39387ea623ea3b623faa1e 100644
--- a/thinkphp/library/think/cache/driver/File.php
+++ b/thinkphp/library/think/cache/driver/File.php
@@ -266,7 +266,7 @@ class File extends Driver
foreach ($keys as $key) {
$this->unlink($key);
}
- $this->rm('tag_' . md5($tag));
+ $this->rm($this->getTagKey($tag));
return true;
}
@@ -278,11 +278,13 @@ class File extends Driver
if (is_dir($path)) {
$matches = glob($path . DIRECTORY_SEPARATOR . '*.php');
if (is_array($matches)) {
- array_map('unlink', $matches);
+ array_map(function ($v) {
+ $this->unlink($v);
+ }, $matches);
}
rmdir($path);
} else {
- unlink($path);
+ $this->unlink($path);
}
}
diff --git a/thinkphp/library/think/cache/driver/Lite.php b/thinkphp/library/think/cache/driver/Lite.php
index 544663c057ed6ac4ec512260125c6590ca954162..0cfe3907933ebb1b6e68b412e62b70f5ace87a0c 100644
--- a/thinkphp/library/think/cache/driver/Lite.php
+++ b/thinkphp/library/think/cache/driver/Lite.php
@@ -198,7 +198,7 @@ class Lite extends Driver
unlink($key);
}
- $this->rm('tag_' . md5($tag));
+ $this->rm($this->getTagKey($tag));
return true;
}
diff --git a/thinkphp/library/think/cache/driver/Memcache.php b/thinkphp/library/think/cache/driver/Memcache.php
index ea2c0e234e4d6350a415ce5cae138955a54b4484..1c535597e863969953c8bc60ae1a8051f76314f3 100644
--- a/thinkphp/library/think/cache/driver/Memcache.php
+++ b/thinkphp/library/think/cache/driver/Memcache.php
@@ -70,7 +70,7 @@ class Memcache extends Driver
{
$key = $this->getCacheKey($name);
- return $this->handler->get($key) ? true : false;
+ return false !== $this->handler->get($key);
}
/**
@@ -188,11 +188,13 @@ class Memcache extends Driver
if ($tag) {
// 指定标签清除
$keys = $this->getTagItem($tag);
+
foreach ($keys as $key) {
$this->handler->delete($key);
}
- $this->rm('tag_' . md5($tag));
+ $tagName = $this->getTagKey($tag);
+ $this->rm($tagName);
return true;
}
@@ -200,4 +202,5 @@ class Memcache extends Driver
return $this->handler->flush();
}
+
}
diff --git a/thinkphp/library/think/cache/driver/Memcached.php b/thinkphp/library/think/cache/driver/Memcached.php
index d04fac0877796387b5e4eb2eede0320edbf1488f..4533e78ac6e409f74eb33b7e08d7be25a0a5351a 100644
--- a/thinkphp/library/think/cache/driver/Memcached.php
+++ b/thinkphp/library/think/cache/driver/Memcached.php
@@ -67,7 +67,7 @@ class Memcached extends Driver
}
$this->handler->addServers($servers);
-
+ $this->handler->setOption(\Memcached::OPT_COMPRESSION, false);
if ('' != $this->options['username']) {
$this->handler->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
$this->handler->setSaslAuthData($this->options['username'], $this->options['password']);
@@ -204,7 +204,7 @@ class Memcached extends Driver
$keys = $this->getTagItem($tag);
$this->handler->deleteMulti($keys);
- $this->rm('tag_' . md5($tag));
+ $this->rm($this->getTagKey($tag));
return true;
}
@@ -213,4 +213,67 @@ class Memcached extends Driver
return $this->handler->flush();
}
+
+ /**
+ * 缓存标签
+ * @access public
+ * @param string $name 标签名
+ * @param string|array $keys 缓存标识
+ * @param bool $overlay 是否覆盖
+ * @return $this
+ */
+ public function tag($name, $keys = null, $overlay = false)
+ {
+ if (is_null($keys)) {
+ $this->tag = $name;
+ } else {
+ $tagName = $this->getTagKey($name);
+ if ($overlay) {
+ $this->handler->delete($tagName);
+ }
+
+ if (!$this->has($tagName)) {
+ $this->handler->set($tagName, '');
+ }
+
+ foreach ($keys as $key) {
+ $this->handler->append($tagName, ',' . $key);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * 更新标签
+ * @access protected
+ * @param string $name 缓存标识
+ * @return void
+ */
+ protected function setTagItem($name)
+ {
+ if ($this->tag) {
+ $tagName = $this->getTagKey($this->tag);
+
+ if ($this->has($tagName)) {
+ $this->handler->append($tagName, ',' . $name);
+ } else {
+ $this->handler->set($tagName, $name);
+ }
+
+ $this->tag = null;
+ }
+ }
+
+ /**
+ * 获取标签包含的缓存标识
+ * @access public
+ * @param string $tag 缓存标签
+ * @return array
+ */
+ public function getTagItem($tag)
+ {
+ $tagName = $this->getTagKey($tag);
+ return explode(',', trim($this->handler->get($tagName), ','));
+ }
}
diff --git a/thinkphp/library/think/cache/driver/Redis.php b/thinkphp/library/think/cache/driver/Redis.php
index d20d3b1d18a5598e24d660313a2d78a053a52c94..4eff2cf5e49da59bf850d5b41931e8398b489297 100644
--- a/thinkphp/library/think/cache/driver/Redis.php
+++ b/thinkphp/library/think/cache/driver/Redis.php
@@ -64,12 +64,19 @@ class Redis extends Driver
} elseif (class_exists('\Predis\Client')) {
$params = [];
foreach ($this->options as $key => $val) {
- if (in_array($key, ['aggregate', 'cluster', 'connections', 'exceptions', 'prefix', 'profile', 'replication'])) {
+ if (in_array($key, ['aggregate', 'cluster', 'connections', 'exceptions', 'prefix', 'profile', 'replication', 'parameters'])) {
$params[$key] = $val;
unset($this->options[$key]);
}
}
+
+ if ('' == $this->options['password']) {
+ unset($this->options['password']);
+ }
+
$this->handler = new \Predis\Client($this->options, $params);
+
+ $this->options['prefix'] = '';
} else {
throw new \BadFunctionCallException('not support: redis');
}
@@ -83,7 +90,7 @@ class Redis extends Driver
*/
public function has($name)
{
- return $this->handler->exists($this->getCacheKey($name));
+ return $this->handler->exists($this->getCacheKey($name)) ? true : false;
}
/**
@@ -184,7 +191,7 @@ class Redis extends Driver
{
$this->writeTimes++;
- return $this->handler->delete($this->getCacheKey($name));
+ return $this->handler->del($this->getCacheKey($name));
}
/**
@@ -199,11 +206,10 @@ class Redis extends Driver
// 指定标签清除
$keys = $this->getTagItem($tag);
- foreach ($keys as $key) {
- $this->handler->delete($key);
- }
+ $this->handler->del($keys);
- $this->rm('tag_' . md5($tag));
+ $tagName = $this->getTagKey($tag);
+ $this->handler->del($tagName);
return true;
}
@@ -212,4 +218,55 @@ class Redis extends Driver
return $this->handler->flushDB();
}
+ /**
+ * 缓存标签
+ * @access public
+ * @param string $name 标签名
+ * @param string|array $keys 缓存标识
+ * @param bool $overlay 是否覆盖
+ * @return $this
+ */
+ public function tag($name, $keys = null, $overlay = false)
+ {
+ if (is_null($keys)) {
+ $this->tag = $name;
+ } else {
+ $tagName = $this->getTagKey($name);
+ if ($overlay) {
+ $this->handler->del($tagName);
+ }
+
+ foreach ($keys as $key) {
+ $this->handler->sAdd($tagName, $key);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * 更新标签
+ * @access protected
+ * @param string $name 缓存标识
+ * @return void
+ */
+ protected function setTagItem($name)
+ {
+ if ($this->tag) {
+ $tagName = $this->getTagKey($this->tag);
+ $this->handler->sAdd($tagName, $name);
+ }
+ }
+
+ /**
+ * 获取标签包含的缓存标识
+ * @access protected
+ * @param string $tag 缓存标签
+ * @return array
+ */
+ protected function getTagItem($tag)
+ {
+ $tagName = $this->getTagKey($tag);
+ return $this->handler->sMembers($tagName);
+ }
}
diff --git a/thinkphp/library/think/cache/driver/Sqlite.php b/thinkphp/library/think/cache/driver/Sqlite.php
index 7e78ec12d2e72d149b35373550864eafffcb39fc..f57361e3c4d476377cee8e06321c1ef30e60c37f 100644
--- a/thinkphp/library/think/cache/driver/Sqlite.php
+++ b/thinkphp/library/think/cache/driver/Sqlite.php
@@ -216,7 +216,7 @@ class Sqlite extends Driver
public function clear($tag = null)
{
if ($tag) {
- $name = sqlite_escape_string($tag);
+ $name = sqlite_escape_string($this->getTagKey($tag));
$sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE tag=\'' . $name . '\'';
sqlite_query($this->handler, $sql);
return true;
diff --git a/thinkphp/library/think/cache/driver/Wincache.php b/thinkphp/library/think/cache/driver/Wincache.php
index 10966e786cf8af77ea66c82f35fca7768c05f287..ef15784173ed4186f1ab99fffec885df425ad9bf 100644
--- a/thinkphp/library/think/cache/driver/Wincache.php
+++ b/thinkphp/library/think/cache/driver/Wincache.php
@@ -160,15 +160,16 @@ class Wincache extends Driver
{
if ($tag) {
$keys = $this->getTagItem($tag);
- foreach ($keys as $key) {
- wincache_ucache_delete($key);
- }
- $this->rm('tag_' . md5($tag));
+
+ wincache_ucache_delete($keys);
+
+ $tagName = $this->getTagkey($tag);
+ $this->rm($tagName);
return true;
- } else {
- $this->writeTimes++;
- return wincache_ucache_clear();
}
+
+ $this->writeTimes++;
+ return wincache_ucache_clear();
}
}
diff --git a/thinkphp/library/think/cache/driver/Xcache.php b/thinkphp/library/think/cache/driver/Xcache.php
index 6d1bf3f6cf0c047613af49521c271e8950e49877..4e698597a50f44cf4446ccc4f46e3c83e1d442ee 100644
--- a/thinkphp/library/think/cache/driver/Xcache.php
+++ b/thinkphp/library/think/cache/driver/Xcache.php
@@ -159,10 +159,12 @@ class Xcache extends Driver
if ($tag) {
// 指定标签清除
$keys = $this->getTagItem($tag);
+
foreach ($keys as $key) {
xcache_unset($key);
}
- $this->rm('tag_' . md5($tag));
+
+ $this->rm($this->getTagKey($tag));
return true;
}
diff --git a/thinkphp/library/think/console/Command.php b/thinkphp/library/think/console/Command.php
index d0caad2f6b7eb81ff0567811e87c9f395db0b805..a208e7b54b055bd99c53ff788f77f0f935601d5a 100644
--- a/thinkphp/library/think/console/Command.php
+++ b/thinkphp/library/think/console/Command.php
@@ -467,4 +467,16 @@ class Command
throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
}
}
+
+ /**
+ * 输出表格
+ * @param Table $table
+ * @return string
+ */
+ protected function table(Table $table)
+ {
+ $content = $table->render();
+ $this->output->writeln($content);
+ return $content;
+ }
}
diff --git a/thinkphp/library/think/console/Table.php b/thinkphp/library/think/console/Table.php
new file mode 100644
index 0000000000000000000000000000000000000000..9e28e266b3c01113afc771b084438e63609f01e4
--- /dev/null
+++ b/thinkphp/library/think/console/Table.php
@@ -0,0 +1,281 @@
+
+// +----------------------------------------------------------------------
+
+namespace think\console;
+
+class Table
+{
+ const ALIGN_LEFT = 1;
+ const ALIGN_RIGHT = 0;
+ const ALIGN_CENTER = 2;
+
+ /**
+ * 头信息数据
+ * @var array
+ */
+ protected $header = [];
+
+ /**
+ * 头部对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
+ * @var int
+ */
+ protected $headerAlign = 1;
+
+ /**
+ * 表格数据(二维数组)
+ * @var array
+ */
+ protected $rows = [];
+
+ /**
+ * 单元格对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
+ * @var int
+ */
+ protected $cellAlign = 1;
+
+ /**
+ * 单元格宽度信息
+ * @var array
+ */
+ protected $colWidth = [];
+
+ /**
+ * 表格输出样式
+ * @var string
+ */
+ protected $style = 'default';
+
+ /**
+ * 表格样式定义
+ * @var array
+ */
+ protected $format = [
+ 'compact' => [],
+ 'default' => [
+ 'top' => ['+', '-', '+', '+'],
+ 'cell' => ['|', ' ', '|', '|'],
+ 'middle' => ['+', '-', '+', '+'],
+ 'bottom' => ['+', '-', '+', '+'],
+ 'cross-top' => ['+', '-', '-', '+'],
+ 'cross-bottom' => ['+', '-', '-', '+'],
+ ],
+ 'markdown' => [
+ 'top' => [' ', ' ', ' ', ' '],
+ 'cell' => ['|', ' ', '|', '|'],
+ 'middle' => ['|', '-', '|', '|'],
+ 'bottom' => [' ', ' ', ' ', ' '],
+ 'cross-top' => ['|', ' ', ' ', '|'],
+ 'cross-bottom' => ['|', ' ', ' ', '|'],
+ ],
+ 'borderless' => [
+ 'top' => ['=', '=', ' ', '='],
+ 'cell' => [' ', ' ', ' ', ' '],
+ 'middle' => ['=', '=', ' ', '='],
+ 'bottom' => ['=', '=', ' ', '='],
+ 'cross-top' => ['=', '=', ' ', '='],
+ 'cross-bottom' => ['=', '=', ' ', '='],
+ ],
+ 'box' => [
+ 'top' => ['┌', '─', '┬', '┐'],
+ 'cell' => ['│', ' ', '│', '│'],
+ 'middle' => ['├', '─', '┼', '┤'],
+ 'bottom' => ['└', '─', '┴', '┘'],
+ 'cross-top' => ['├', '─', '┴', '┤'],
+ 'cross-bottom' => ['├', '─', '┬', '┤'],
+ ],
+ 'box-double' => [
+ 'top' => ['╔', '═', '╤', '╗'],
+ 'cell' => ['║', ' ', '│', '║'],
+ 'middle' => ['╠', '─', '╪', '╣'],
+ 'bottom' => ['╚', '═', '╧', '╝'],
+ 'cross-top' => ['╠', '═', '╧', '╣'],
+ 'cross-bottom' => ['╠', '═', '╤', '╣'],
+ ],
+ ];
+
+ /**
+ * 设置表格头信息 以及对齐方式
+ * @access public
+ * @param array $header 要输出的Header信息
+ * @param int $align 对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
+ * @return void
+ */
+ public function setHeader(array $header, $align = self::ALIGN_LEFT)
+ {
+ $this->header = $header;
+ $this->headerAlign = $align;
+
+ $this->checkColWidth($header);
+ }
+
+ /**
+ * 设置输出表格数据 及对齐方式
+ * @access public
+ * @param array $rows 要输出的表格数据(二维数组)
+ * @param int $align 对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
+ * @return void
+ */
+ public function setRows(array $rows, $align = self::ALIGN_LEFT)
+ {
+ $this->rows = $rows;
+ $this->cellAlign = $align;
+
+ foreach ($rows as $row) {
+ $this->checkColWidth($row);
+ }
+ }
+
+ /**
+ * 检查列数据的显示宽度
+ * @access public
+ * @param mixed $row 行数据
+ * @return void
+ */
+ protected function checkColWidth($row)
+ {
+ if (is_array($row)) {
+ foreach ($row as $key => $cell) {
+ if (!isset($this->colWidth[$key]) || strlen($cell) > $this->colWidth[$key]) {
+ $this->colWidth[$key] = strlen($cell);
+ }
+ }
+ }
+ }
+
+ /**
+ * 增加一行表格数据
+ * @access public
+ * @param mixed $row 行数据
+ * @param bool $first 是否在开头插入
+ * @return void
+ */
+ public function addRow($row, $first = false)
+ {
+ if ($first) {
+ array_unshift($this->rows, $row);
+ } else {
+ $this->rows[] = $row;
+ }
+
+ $this->checkColWidth($row);
+ }
+
+ /**
+ * 设置输出表格的样式
+ * @access public
+ * @param string $style 样式名
+ * @return void
+ */
+ public function setStyle($style)
+ {
+ $this->style = isset($this->format[$style]) ? $style : 'default';
+ }
+
+ /**
+ * 输出分隔行
+ * @access public
+ * @param string $pos 位置
+ * @return string
+ */
+ protected function renderSeparator($pos)
+ {
+ $style = $this->getStyle($pos);
+ $array = [];
+
+ foreach ($this->colWidth as $width) {
+ $array[] = str_repeat($style[1], $width + 2);
+ }
+
+ return $style[0] . implode($style[2], $array) . $style[3] . PHP_EOL;
+ }
+
+ /**
+ * 输出表格头部
+ * @access public
+ * @return string
+ */
+ protected function renderHeader()
+ {
+ $style = $this->getStyle('cell');
+ $content = $this->renderSeparator('top');
+
+ foreach ($this->header as $key => $header) {
+ $array[] = ' ' . str_pad($header, $this->colWidth[$key], $style[1], $this->headerAlign);
+ }
+
+ if (!empty($array)) {
+ $content .= $style[0] . implode(' ' . $style[2], $array) . ' ' . $style[3] . PHP_EOL;
+
+ if ($this->rows) {
+ $content .= $this->renderSeparator('middle');
+ }
+ }
+
+ return $content;
+ }
+
+ protected function getStyle($style)
+ {
+ if ($this->format[$this->style]) {
+ $style = $this->format[$this->style][$style];
+ } else {
+ $style = [' ', ' ', ' ', ' '];
+ }
+
+ return $style;
+ }
+
+ /**
+ * 输出表格
+ * @access public
+ * @param array $dataList 表格数据
+ * @return string
+ */
+ public function render($dataList = [])
+ {
+ if ($dataList) {
+ $this->setRows($dataList);
+ }
+
+ // 输出头部
+ $content = $this->renderHeader();
+ $style = $this->getStyle('cell');
+
+ if ($this->rows) {
+ foreach ($this->rows as $row) {
+ if (is_string($row) && '-' === $row) {
+ $content .= $this->renderSeparator('middle');
+ } elseif (is_scalar($row)) {
+ $content .= $this->renderSeparator('cross-top');
+ $array = str_pad($row, 3 * (count($this->colWidth) - 1) + array_reduce($this->colWidth, function ($a, $b) {
+ return $a + $b;
+ }));
+
+ $content .= $style[0] . ' ' . $array . ' ' . $style[3] . PHP_EOL;
+ $content .= $this->renderSeparator('cross-bottom');
+ } else {
+ $array = [];
+
+ foreach ($row as $key => $val) {
+ $array[] = ' ' . str_pad($val, $this->colWidth[$key], ' ', $this->cellAlign);
+ }
+
+ $content .= $style[0] . implode(' ' . $style[2], $array) . ' ' . $style[3] . PHP_EOL;
+
+ }
+ }
+ }
+
+ $content .= $this->renderSeparator('bottom');
+
+ return $content;
+ }
+}
diff --git a/thinkphp/library/think/console/command/Build.php b/thinkphp/library/think/console/command/Build.php
index 2b83199b1638ccde292d170620fbb798b7881c79..88a5bf820156366f8129c5b5be1bfdc00fa8d942 100644
--- a/thinkphp/library/think/console/command/Build.php
+++ b/thinkphp/library/think/console/command/Build.php
@@ -20,7 +20,6 @@ use think\facade\Build as AppBuild;
class Build extends Command
{
-
/**
* {@inheritdoc}
*/
diff --git a/thinkphp/library/think/console/command/Help.php b/thinkphp/library/think/console/command/Help.php
index bae2c653312f35f38f72a976d96a7346c621261f..f1b63b42e8f06364a7729a983017ece6f417afce 100644
--- a/thinkphp/library/think/console/command/Help.php
+++ b/thinkphp/library/think/console/command/Help.php
@@ -19,7 +19,6 @@ use think\console\Output;
class Help extends Command
{
-
private $command;
/**
diff --git a/thinkphp/library/think/console/command/Lists.php b/thinkphp/library/think/console/command/Lists.php
index 084ddaa231ce3b1dd8c0b602387b49da80344c74..6eb856c262e733bda6925d8abf56558e3c06e509 100644
--- a/thinkphp/library/think/console/command/Lists.php
+++ b/thinkphp/library/think/console/command/Lists.php
@@ -13,14 +13,13 @@ namespace think\console\command;
use think\console\Command;
use think\console\Input;
-use think\console\Output;
use think\console\input\Argument as InputArgument;
-use think\console\input\Option as InputOption;
use think\console\input\Definition as InputDefinition;
+use think\console\input\Option as InputOption;
+use think\console\Output;
class Lists extends Command
{
-
/**
* {@inheritdoc}
*/
@@ -68,7 +67,7 @@ EOF
{
return new InputDefinition([
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
- new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list')
+ new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
]);
}
}
diff --git a/thinkphp/library/think/console/command/Make.php b/thinkphp/library/think/console/command/Make.php
index 7474f26684bcf280cfbbc42eccaa71827c5c49c5..2f20954ac4a7baf6c640644800f050546976e108 100644
--- a/thinkphp/library/think/console/command/Make.php
+++ b/thinkphp/library/think/console/command/Make.php
@@ -21,7 +21,6 @@ use think\facade\Env;
abstract class Make extends Command
{
-
protected $type;
abstract protected function getStub();
@@ -63,12 +62,12 @@ abstract class Make extends Command
$class = str_replace($namespace . '\\', '', $name);
- return str_replace(['{%className%}', '{%namespace%}', '{%app_namespace%}'], [
+ return str_replace(['{%className%}', '{%actionSuffix%}', '{%namespace%}', '{%app_namespace%}'], [
$class,
+ Config::get('action_suffix'),
$namespace,
App::getNamespace(),
], $stub);
-
}
protected function getPathName($name)
diff --git a/thinkphp/library/think/console/command/RouteList.php b/thinkphp/library/think/console/command/RouteList.php
new file mode 100644
index 0000000000000000000000000000000000000000..0405c31b6c8131935b924008e49120bf57b6be47
--- /dev/null
+++ b/thinkphp/library/think/console/command/RouteList.php
@@ -0,0 +1,130 @@
+
+// +----------------------------------------------------------------------
+namespace think\console\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument;
+use think\console\input\Option;
+use think\console\Output;
+use think\console\Table;
+use think\Container;
+
+class RouteList extends Command
+{
+ protected $sortBy = [
+ 'rule' => 0,
+ 'route' => 1,
+ 'method' => 2,
+ 'name' => 3,
+ 'domain' => 4,
+ ];
+
+ protected function configure()
+ {
+ $this->setName('route:list')
+ ->addArgument('style', Argument::OPTIONAL, "the style of the table.", 'default')
+ ->addOption('sort', 's', Option::VALUE_OPTIONAL, 'order by rule name.', 0)
+ ->addOption('more', 'm', Option::VALUE_NONE, 'show route options.')
+ ->setDescription('show route list.');
+ }
+
+ protected function execute(Input $input, Output $output)
+ {
+ $filename = Container::get('app')->getRuntimePath() . 'route_list.php';
+
+ if (is_file($filename)) {
+ unlink($filename);
+ }
+
+ $content = $this->getRouteList();
+ file_put_contents($filename, 'Route List' . PHP_EOL . $content);
+ }
+
+ protected function getRouteList()
+ {
+ Container::get('route')->setTestMode(true);
+ // 路由检测
+ $path = Container::get('app')->getRoutePath();
+
+ $files = is_dir($path) ? scandir($path) : [];
+
+ foreach ($files as $file) {
+ if (strpos($file, '.php')) {
+ $filename = $path . DIRECTORY_SEPARATOR . $file;
+ // 导入路由配置
+ $rules = include $filename;
+
+ if (is_array($rules)) {
+ Container::get('route')->import($rules);
+ }
+ }
+ }
+
+ if (Container::get('config')->get('route_annotation')) {
+ $suffix = Container::get('config')->get('controller_suffix') || Container::get('config')->get('class_suffix');
+
+ include Container::get('build')->buildRoute($suffix);
+ }
+
+ $table = new Table();
+
+ if ($this->input->hasOption('more')) {
+ $header = ['Rule', 'Route', 'Method', 'Name', 'Domain', 'Option', 'Pattern'];
+ } else {
+ $header = ['Rule', 'Route', 'Method', 'Name', 'Domain'];
+ }
+
+ $table->setHeader($header);
+
+ $routeList = Container::get('route')->getRuleList();
+ $rows = [];
+
+ foreach ($routeList as $domain => $items) {
+ foreach ($items as $item) {
+ $item['route'] = $item['route'] instanceof \Closure ? '' : $item['route'];
+
+ if ($this->input->hasOption('more')) {
+ $item = [$item['rule'], $item['route'], $item['method'], $item['name'], $domain, json_encode($item['option']), json_encode($item['pattern'])];
+ } else {
+ $item = [$item['rule'], $item['route'], $item['method'], $item['name'], $domain];
+ }
+
+ $rows[] = $item;
+ }
+ }
+
+ if ($this->input->getOption('sort')) {
+ $sort = $this->input->getOption('sort');
+
+ if (isset($this->sortBy[$sort])) {
+ $sort = $this->sortBy[$sort];
+ }
+
+ uasort($rows, function ($a, $b) use ($sort) {
+ $itemA = isset($a[$sort]) ? $a[$sort] : null;
+ $itemB = isset($b[$sort]) ? $b[$sort] : null;
+
+ return strcasecmp($itemA, $itemB);
+ });
+ }
+
+ $table->setRows($rows);
+
+ if ($this->input->getArgument('style')) {
+ $style = $this->input->getArgument('style');
+ $table->setStyle($style);
+ }
+
+ return $this->table($table);
+ }
+
+}
diff --git a/thinkphp/library/think/console/command/RunServer.php b/thinkphp/library/think/console/command/RunServer.php
index 4dd610b6e76d99396c5c194b8c36a846e1e84fbd..2e028dc6d0fc8bf0160240f5d0896a2e0bf69867 100644
--- a/thinkphp/library/think/console/command/RunServer.php
+++ b/thinkphp/library/think/console/command/RunServer.php
@@ -18,7 +18,6 @@ use think\facade\App;
class RunServer extends Command
{
-
public function configure()
{
$this->setName('run')
diff --git a/thinkphp/library/think/console/command/Version.php b/thinkphp/library/think/console/command/Version.php
new file mode 100644
index 0000000000000000000000000000000000000000..ee7eca9c936f63e8287205e9fe39a2a29ae6f623
--- /dev/null
+++ b/thinkphp/library/think/console/command/Version.php
@@ -0,0 +1,31 @@
+
+// +----------------------------------------------------------------------
+namespace think\console\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\Output;
+use think\facade\App;
+
+class Version extends Command
+{
+ protected function configure()
+ {
+ // 指令配置
+ $this->setName('version')
+ ->setDescription('show thinkphp framework version');
+ }
+
+ protected function execute(Input $input, Output $output)
+ {
+ $output->writeln('v' . App::version());
+ }
+}
diff --git a/thinkphp/library/think/console/command/make/Command.php b/thinkphp/library/think/console/command/make/Command.php
new file mode 100644
index 0000000000000000000000000000000000000000..b539eb236e24161adabb30acfa53c2b84a92ef49
--- /dev/null
+++ b/thinkphp/library/think/console/command/make/Command.php
@@ -0,0 +1,56 @@
+
+// +----------------------------------------------------------------------
+
+namespace think\console\command\make;
+
+use think\console\command\Make;
+use think\console\input\Argument;
+use think\facade\App;
+
+class Command extends Make
+{
+ protected $type = "Command";
+
+ protected function configure()
+ {
+ parent::configure();
+ $this->setName('make:command')
+ ->addArgument('commandName', Argument::OPTIONAL, "The name of the command")
+ ->setDescription('Create a new command class');
+ }
+
+ protected function buildClass($name)
+ {
+ $commandName = $this->input->getArgument('commandName') ?: strtolower(basename($name));
+ $namespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
+
+ $class = str_replace($namespace . '\\', '', $name);
+ $stub = file_get_contents($this->getStub());
+
+ return str_replace(['{%commandName%}', '{%className%}', '{%namespace%}', '{%app_namespace%}'], [
+ $commandName,
+ $class,
+ $namespace,
+ App::getNamespace(),
+ ], $stub);
+ }
+
+ protected function getStub()
+ {
+ return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'command.stub';
+ }
+
+ protected function getNamespace($appNamespace, $module)
+ {
+ return $appNamespace . '\\command';
+ }
+
+}
diff --git a/thinkphp/library/think/console/command/make/Controller.php b/thinkphp/library/think/console/command/make/Controller.php
index e4cc5bb572af841f98771c37ad2ad2684af7c688..2a6ab770d2a2d95e87ddf95638d5b694843f1d74 100644
--- a/thinkphp/library/think/console/command/make/Controller.php
+++ b/thinkphp/library/think/console/command/make/Controller.php
@@ -17,7 +17,6 @@ use think\facade\Config;
class Controller extends Make
{
-
protected $type = "Controller";
protected function configure()
diff --git a/thinkphp/library/think/console/command/make/Validate.php b/thinkphp/library/think/console/command/make/Validate.php
index e7e39e635b0d91918c8adb25b1789cda78006afc..89830ad1d01294057524dd91485a10b59f08e262 100644
--- a/thinkphp/library/think/console/command/make/Validate.php
+++ b/thinkphp/library/think/console/command/make/Validate.php
@@ -15,7 +15,6 @@ use think\console\command\Make;
class Validate extends Make
{
-
protected $type = "Validate";
protected function configure()
diff --git a/thinkphp/library/think/console/command/make/stubs/command.stub b/thinkphp/library/think/console/command/make/stubs/command.stub
new file mode 100644
index 0000000000000000000000000000000000000000..d2c7c1e7e519f9262d9f4abb167c89a9df24afce
--- /dev/null
+++ b/thinkphp/library/think/console/command/make/stubs/command.stub
@@ -0,0 +1,24 @@
+setName('{%commandName%}');
+ // 设置参数
+
+ }
+
+ protected function execute(Input $input, Output $output)
+ {
+ // 指令输出
+ $output->writeln('{%commandName%}');
+ }
+}
diff --git a/thinkphp/library/think/console/command/make/stubs/controller.api.stub b/thinkphp/library/think/console/command/make/stubs/controller.api.stub
index aed9edfbd66a830b4d00e5b00dcbe6fce7d7d1f1..54ec0594e4df7a7bb634eb83c83102e5fd26ceb7 100644
--- a/thinkphp/library/think/console/command/make/stubs/controller.api.stub
+++ b/thinkphp/library/think/console/command/make/stubs/controller.api.stub
@@ -12,7 +12,7 @@ class {%className%} extends Controller
*
* @return \think\Response
*/
- public function index()
+ public function index{%actionSuffix%}()
{
//
}
@@ -23,7 +23,7 @@ class {%className%} extends Controller
* @param \think\Request $request
* @return \think\Response
*/
- public function save(Request $request)
+ public function save{%actionSuffix%}(Request $request)
{
//
}
@@ -34,7 +34,7 @@ class {%className%} extends Controller
* @param int $id
* @return \think\Response
*/
- public function read($id)
+ public function read{%actionSuffix%}($id)
{
//
}
@@ -46,7 +46,7 @@ class {%className%} extends Controller
* @param int $id
* @return \think\Response
*/
- public function update(Request $request, $id)
+ public function update{%actionSuffix%}(Request $request, $id)
{
//
}
@@ -57,7 +57,7 @@ class {%className%} extends Controller
* @param int $id
* @return \think\Response
*/
- public function delete($id)
+ public function delete{%actionSuffix%}($id)
{
//
}
diff --git a/thinkphp/library/think/console/command/make/stubs/controller.stub b/thinkphp/library/think/console/command/make/stubs/controller.stub
index 870ce5cfbdbcdb4407f8af8cbf862b53fa68c233..4533c07d230feb9e9d1ae8dd18c0964ff0915b5b 100644
--- a/thinkphp/library/think/console/command/make/stubs/controller.stub
+++ b/thinkphp/library/think/console/command/make/stubs/controller.stub
@@ -12,7 +12,7 @@ class {%className%} extends Controller
*
* @return \think\Response
*/
- public function index()
+ public function index{%actionSuffix%}()
{
//
}
@@ -22,7 +22,7 @@ class {%className%} extends Controller
*
* @return \think\Response
*/
- public function create()
+ public function create{%actionSuffix%}()
{
//
}
@@ -33,7 +33,7 @@ class {%className%} extends Controller
* @param \think\Request $request
* @return \think\Response
*/
- public function save(Request $request)
+ public function save{%actionSuffix%}(Request $request)
{
//
}
@@ -44,7 +44,7 @@ class {%className%} extends Controller
* @param int $id
* @return \think\Response
*/
- public function read($id)
+ public function read{%actionSuffix%}($id)
{
//
}
@@ -55,7 +55,7 @@ class {%className%} extends Controller
* @param int $id
* @return \think\Response
*/
- public function edit($id)
+ public function edit{%actionSuffix%}($id)
{
//
}
@@ -67,7 +67,7 @@ class {%className%} extends Controller
* @param int $id
* @return \think\Response
*/
- public function update(Request $request, $id)
+ public function update{%actionSuffix%}(Request $request, $id)
{
//
}
@@ -78,7 +78,7 @@ class {%className%} extends Controller
* @param int $id
* @return \think\Response
*/
- public function delete($id)
+ public function delete{%actionSuffix%}($id)
{
//
}
diff --git a/thinkphp/library/think/console/command/optimize/Autoload.php b/thinkphp/library/think/console/command/optimize/Autoload.php
index c5eab03ce2114367fbde8ebfdd8cf255b47c3bf9..b51fd2593b8b710e744702c272dcd33d5ccf2e6e 100644
--- a/thinkphp/library/think/console/command/optimize/Autoload.php
+++ b/thinkphp/library/think/console/command/optimize/Autoload.php
@@ -17,7 +17,6 @@ use think\Container;
class Autoload extends Command
{
-
protected function configure()
{
$this->setName('optimize:autoload')
@@ -39,8 +38,8 @@ EOF;
$app = Container::get('app');
$namespacesToScan = [
$app->getNamespace() . '\\' => realpath(rtrim($app->getAppPath())),
- 'think\\' => $app->getAppPath() . 'library/think',
- 'traits\\' => $app->getAppPath() . 'library/traits',
+ 'think\\' => $app->getThinkPath() . 'library/think',
+ 'traits\\' => $app->getThinkPath() . 'library/traits',
'' => realpath(rtrim($app->getRootPath() . 'extend')),
];
diff --git a/thinkphp/library/think/console/command/optimize/Config.php b/thinkphp/library/think/console/command/optimize/Config.php
index 1be4e3a2d72e5f302f3d18151aa9775cce1f0d18..da955568333aa2d4e087456398dc480d6511f2a1 100644
--- a/thinkphp/library/think/console/command/optimize/Config.php
+++ b/thinkphp/library/think/console/command/optimize/Config.php
@@ -19,9 +19,6 @@ use think\facade\App;
class Config extends Command
{
- /** @var Output */
- protected $output;
-
protected function configure()
{
$this->setName('optimize:config')
diff --git a/thinkphp/library/think/console/command/optimize/Route.php b/thinkphp/library/think/console/command/optimize/Route.php
index b6ad5b52de9c32928f865e8be286a800bd3ddf27..f6dc63288221f174e52f07b96078d133bd51b7e4 100644
--- a/thinkphp/library/think/console/command/optimize/Route.php
+++ b/thinkphp/library/think/console/command/optimize/Route.php
@@ -17,9 +17,6 @@ use think\Container;
class Route extends Command
{
- /** @var Output */
- protected $output;
-
protected function configure()
{
$this->setName('optimize:route')
@@ -39,7 +36,7 @@ class Route extends Command
protected function buildRouteCache()
{
Container::get('route')->setName([]);
- Container::get('route')->lazy(false);
+ Container::get('route')->setTestMode(true);
// 路由检测
$path = Container::get('app')->getRoutePath();
@@ -57,7 +54,8 @@ class Route extends Command
}
if (Container::get('config')->get('route_annotation')) {
- include Container::get('build')->buildRoute();
+ $suffix = Container::get('config')->get('controller_suffix') || Container::get('config')->get('class_suffix');
+ include Container::get('build')->buildRoute($suffix);
}
$content = 'setName('optimize:schema')
@@ -56,7 +53,7 @@ class Schema extends Command
return;
} elseif ($input->hasOption('table')) {
$table = $input->getOption('table');
- if (!strpos($table, '.')) {
+ if (false === strpos($table, '.')) {
$dbName = Db::getConfig('database');
}
diff --git a/thinkphp/library/think/console/output/descriptor/Console.php b/thinkphp/library/think/console/output/descriptor/Console.php
index 4648b68e665ec9aa87512c8da4ff8461035ea8b8..8739c536e747f4ecbc6f60050be2b83d9578aa0b 100644
--- a/thinkphp/library/think/console/output/descriptor/Console.php
+++ b/thinkphp/library/think/console/output/descriptor/Console.php
@@ -104,6 +104,10 @@ class Console
/** @var Command $command */
foreach ($commands as $name => $command) {
+ if (is_string($command)) {
+ $command = new $command();
+ }
+
if (!$command->getName()) {
continue;
}
diff --git a/thinkphp/library/think/console/output/question/Choice.php b/thinkphp/library/think/console/output/question/Choice.php
index f6760e5ef3fc46ec705041a044c3cb8c18f4a120..cdc3b4e4a72cd5bfb5fae5bea1101eee2e0f3ef8 100644
--- a/thinkphp/library/think/console/output/question/Choice.php
+++ b/thinkphp/library/think/console/output/question/Choice.php
@@ -147,7 +147,7 @@ class Choice extends Question
$result = $value;
}
- if (empty($result)) {
+ if (false === $result) {
throw new \InvalidArgumentException(sprintf($errorMessage, $value));
}
array_push($multiselectChoices, $result);
diff --git a/thinkphp/library/think/db/Builder.php b/thinkphp/library/think/db/Builder.php
index 0d30267b10bcc1743b2d9c3f9138d24722f0615a..60b470e86f059f877a4432eb15ed12d386247c92 100644
--- a/thinkphp/library/think/db/Builder.php
+++ b/thinkphp/library/think/db/Builder.php
@@ -87,10 +87,9 @@ abstract class Builder
* @param array $data 数据
* @param array $fields 字段信息
* @param array $bind 参数绑定
- * @param string $suffix 参数绑定后缀
* @return array
*/
- protected function parseData(Query $query, $data = [], $fields = [], $bind = [], $suffix = '')
+ protected function parseData(Query $query, $data = [], $fields = [], $bind = [])
{
if (empty($data)) {
return [];
@@ -114,13 +113,17 @@ abstract class Builder
$result = [];
foreach ($data as $key => $val) {
+ if ('*' != $options['field'] && !in_array($key, $fields, true)) {
+ continue;
+ }
+
$item = $this->parseKey($query, $key, true);
if ($val instanceof Expression) {
$result[$item] = $val->getValue();
continue;
} elseif (!is_scalar($val) && (in_array($key, (array) $query->getOptions('json')) || 'json' == $this->connection->getFieldsType($options['table'], $key))) {
- $val = json_encode($val);
+ $val = json_encode($val, JSON_UNESCAPED_UNICODE);
} elseif (is_object($val) && method_exists($val, '__toString')) {
// 对象数据写入
$val = $val->__toString();
@@ -129,8 +132,8 @@ abstract class Builder
if (false !== strpos($key, '->')) {
list($key, $name) = explode('->', $key);
$item = $this->parseKey($query, $key);
- $result[$item] = 'json_set(' . $item . ', \'$.' . $name . '\', ' . $this->parseDataBind($query, $key, $val, $bind, $suffix) . ')';
- } elseif (false === strpos($key, '.') && !in_array($key, $fields, true)) {
+ $result[$item] = 'json_set(' . $item . ', \'$.' . $name . '\', ' . $this->parseDataBind($query, $key, $val, $bind) . ')';
+ } elseif ('*' == $options['field'] && false === strpos($key, '.') && !in_array($key, $fields, true)) {
if ($options['strict']) {
throw new Exception('fields not exists:[' . $key . ']');
}
@@ -149,7 +152,7 @@ abstract class Builder
}
} elseif (is_scalar($val)) {
// 过滤非标量数据
- $result[$item] = $this->parseDataBind($query, $key, $val, $bind, $suffix);
+ $result[$item] = $this->parseDataBind($query, $key, $val, $bind);
}
}
@@ -163,20 +166,15 @@ abstract class Builder
* @param string $key 字段名
* @param mixed $data 数据
* @param array $bind 绑定数据
- * @param string $suffix 绑定后缀
* @return string
*/
- protected function parseDataBind(Query $query, $key, $data, $bind = [], $suffix = '')
+ protected function parseDataBind(Query $query, $key, $data, $bind = [])
{
- // 过滤非标量数据
- if (0 === strpos($data, ':') && $query->isBind(substr($data, 1))) {
- return $data;
+ if ($data instanceof Expression) {
+ return $data->getValue();
}
- $key = str_replace(['.', '->'], '_', $key);
- $name = 'data__' . $key . $suffix;
-
- $query->bind($name, $data, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
+ $name = $query->bind($data, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
return ':' . $name;
}
@@ -226,8 +224,8 @@ abstract class Builder
/**
* table分析
* @access protected
- * @param Query $query 查询对象
- * @param mixed $tables 表名
+ * @param Query $query 查询对象
+ * @param mixed $tables 表名
* @return string
*/
protected function parseTable(Query $query, $tables)
@@ -315,9 +313,10 @@ abstract class Builder
// 使用闭包查询
$newQuery = $query->newQuery()->setConnection($this->connection);
$value($newQuery);
- $whereClause = $this->buildWhere($query, $newQuery->getOptions('where'));
+ $whereClause = $this->buildWhere($newQuery, $newQuery->getOptions('where'));
if (!empty($whereClause)) {
+ $query->bind($newQuery->getBind(false));
$str[] = ' ' . $logic . ' ( ' . $whereClause . ' )';
}
} elseif (is_array($field)) {
@@ -362,7 +361,7 @@ abstract class Builder
}
// where子单元分析
- protected function parseWhereItem(Query $query, $field, $val, $rule = '', $binds = [], $bindName = null)
+ protected function parseWhereItem(Query $query, $field, $val, $rule = '', $binds = [])
{
// 字段分析
$key = $field ? $this->parseKey($query, $field, true) : '';
@@ -386,8 +385,7 @@ abstract class Builder
}
foreach ($val as $k => $item) {
- $bindName = 'where_' . str_replace('.', '_', $field) . '_' . $k;
- $str[] = $this->parseWhereItem($query, $field, $item, $rule, $binds, $bindName);
+ $str[] = $this->parseWhereItem($query, $field, $item, $rule, $binds);
}
return '( ' . implode(' ' . $rule . ' ', $str) . ' )';
@@ -399,13 +397,6 @@ abstract class Builder
$exp = $this->exp[$exp];
}
- $bindName = $bindName ?: 'where_' . $rule . '_' . str_replace(['.', '-'], '_', $field);
-
- if (preg_match('/\W/', $bindName)) {
- // 处理带非单词字符的字段名
- $bindName = md5($bindName);
- }
-
if ($value instanceof Expression) {
} elseif (is_object($value) && method_exists($value, '__toString')) {
@@ -417,24 +408,21 @@ abstract class Builder
$jsonType = $query->getJsonFieldType($field);
$bindType = $this->connection->getFieldBindType($jsonType);
} else {
- $bindType = isset($binds[$field]) ? $binds[$field] : PDO::PARAM_STR;
+ $bindType = isset($binds[$field]) && 'LIKE' != $exp ? $binds[$field] : PDO::PARAM_STR;
}
if (is_scalar($value) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) {
- if (strpos($value, ':') !== 0 || !$query->isBind(substr($value, 1))) {
- if ($query->isBind($bindName)) {
- $bindName .= '_' . str_replace('.', '_', uniqid('', true));
- }
-
- $query->bind($bindName, $value, $bindType);
- $value = ':' . $bindName;
+ if (0 === strpos($value, ':') && $query->isBind(substr($value, 1))) {
+ } else {
+ $name = $query->bind($value, $bindType);
+ $value = ':' . $name;
}
}
// 解析查询表达式
foreach ($this->parser as $fun => $parse) {
if (in_array($exp, $parse)) {
- $whereStr = $this->$fun($query, $key, $exp, $value, $field, $bindName, $bindType, isset($val[2]) ? $val[2] : 'AND');
+ $whereStr = $this->$fun($query, $key, $exp, $value, $field, $bindType, isset($val[2]) ? $val[2] : 'AND');
break;
}
}
@@ -454,24 +442,20 @@ abstract class Builder
* @param string $exp
* @param mixed $value
* @param string $field
- * @param string $bindName
* @param integer $bindType
* @param string $logic
* @return string
*/
- protected function parseLike(Query $query, $key, $exp, $value, $field, $bindName, $bindType, $logic)
+ protected function parseLike(Query $query, $key, $exp, $value, $field, $bindType, $logic)
{
// 模糊匹配
if (is_array($value)) {
- foreach ($value as $k => $item) {
- $bindKey = $bindName . '_' . intval($k);
- $bind[$bindKey] = [$item, $bindType];
- $array[] = $key . ' ' . $exp . ' :' . $bindKey;
+ foreach ($value as $item) {
+ $name = $query->bind($item, PDO::PARAM_STR);
+ $array[] = $key . ' ' . $exp . ' :' . $name;
}
- $query->bind($bind);
-
- $whereStr = '(' . implode($array, ' ' . strtoupper($logic) . ' ') . ')';
+ $whereStr = '(' . implode(' ' . strtoupper($logic) . ' ', $array) . ')';
} else {
$whereStr = $key . ' ' . $exp . ' ' . $value;
}
@@ -487,11 +471,10 @@ abstract class Builder
* @param string $exp
* @param array $value
* @param string $field
- * @param string $bindName
* @param integer $bindType
* @return string
*/
- protected function parseColumn(Query $query, $key, $exp, array $value, $field, $bindName, $bindType)
+ protected function parseColumn(Query $query, $key, $exp, array $value, $field, $bindType)
{
// 字段比较查询
list($op, $field2) = $value;
@@ -511,11 +494,10 @@ abstract class Builder
* @param string $exp
* @param Expression $value
* @param string $field
- * @param string $bindName
* @param integer $bindType
* @return string
*/
- protected function parseExp(Query $query, $key, $exp, Expression $value, $field, $bindName, $bindType)
+ protected function parseExp(Query $query, $key, $exp, Expression $value, $field, $bindType)
{
// 表达式查询
return '( ' . $key . ' ' . $value->getValue() . ' )';
@@ -529,11 +511,10 @@ abstract class Builder
* @param string $exp
* @param mixed $value
* @param string $field
- * @param string $bindName
* @param integer $bindType
* @return string
*/
- protected function parseNull(Query $query, $key, $exp, $value, $field, $bindName, $bindType)
+ protected function parseNull(Query $query, $key, $exp, $value, $field, $bindType)
{
// NULL 查询
return $key . ' IS ' . $exp;
@@ -547,33 +528,18 @@ abstract class Builder
* @param string $exp
* @param mixed $value
* @param string $field
- * @param string $bindName
* @param integer $bindType
* @return string
*/
- protected function parseBetween(Query $query, $key, $exp, $value, $field, $bindName, $bindType)
+ protected function parseBetween(Query $query, $key, $exp, $value, $field, $bindType)
{
// BETWEEN 查询
$data = is_array($value) ? $value : explode(',', $value);
- if ($query->isBind($bindName . '_between_1')) {
- $bindKey1 = $bindName . '_between_1' . uniqid();
- $bindKey2 = $bindName . '_between_2' . uniqid();
- } else {
- $bindKey1 = $bindName . '_between_1';
- $bindKey2 = $bindName . '_between_2';
- }
-
- $bind = [
- $bindKey1 => [$data[0], $bindType],
- $bindKey2 => [$data[1], $bindType],
- ];
+ $min = $query->bind($data[0], $bindType);
+ $max = $query->bind($data[1], $bindType);
- $query->bind($bind);
-
- $between = ':' . $bindKey1 . ' AND :' . $bindKey2;
-
- return $key . ' ' . $exp . ' ' . $between;
+ return $key . ' ' . $exp . ' :' . $min . ' AND :' . $max . ' ';
}
/**
@@ -584,11 +550,10 @@ abstract class Builder
* @param string $exp
* @param mixed $value
* @param string $field
- * @param string $bindName
* @param integer $bindType
* @return string
*/
- protected function parseExists(Query $query, $key, $exp, $value, $field, $bindName, $bindType)
+ protected function parseExists(Query $query, $key, $exp, $value, $field, $bindType)
{
// EXISTS 查询
if ($value instanceof \Closure) {
@@ -610,13 +575,12 @@ abstract class Builder
* @param string $exp
* @param mixed $value
* @param string $field
- * @param string $bindName
* @param integer $bindType
* @return string
*/
- protected function parseTime(Query $query, $key, $exp, $value, $field, $bindName, $bindType)
+ protected function parseTime(Query $query, $key, $exp, $value, $field, $bindType)
{
- return $key . ' ' . substr($exp, 0, 2) . ' ' . $this->parseDateTime($query, $value, $field, $bindName, $bindType);
+ return $key . ' ' . substr($exp, 0, 2) . ' ' . $this->parseDateTime($query, $value, $field, $bindType);
}
/**
@@ -627,11 +591,10 @@ abstract class Builder
* @param string $exp
* @param mixed $value
* @param string $field
- * @param string $bindName
* @param integer $bindType
* @return string
*/
- protected function parseCompare(Query $query, $key, $exp, $value, $field, $bindName, $bindType)
+ protected function parseCompare(Query $query, $key, $exp, $value, $field, $bindType)
{
if (is_array($value)) {
throw new Exception('where express error:' . $exp . var_export($value, true));
@@ -642,6 +605,10 @@ abstract class Builder
$value = $this->parseClosure($query, $value);
}
+ if ('=' == $exp && is_null($value)) {
+ return $key . ' IS NULL';
+ }
+
return $key . ' ' . $exp . ' ' . $value;
}
@@ -653,20 +620,19 @@ abstract class Builder
* @param string $exp
* @param mixed $value
* @param string $field
- * @param string $bindName
* @param integer $bindType
* @return string
*/
- protected function parseBetweenTime(Query $query, $key, $exp, $value, $field, $bindName, $bindType)
+ protected function parseBetweenTime(Query $query, $key, $exp, $value, $field, $bindType)
{
if (is_string($value)) {
$value = explode(',', $value);
}
return $key . ' ' . substr($exp, 0, -4)
- . $this->parseDateTime($query, $value[0], $field, $bindName . '_between_1', $bindType)
+ . $this->parseDateTime($query, $value[0], $field, $bindType)
. ' AND '
- . $this->parseDateTime($query, $value[1], $field, $bindName . '_between_2', $bindType);
+ . $this->parseDateTime($query, $value[1], $field, $bindType);
}
@@ -678,37 +644,31 @@ abstract class Builder
* @param string $exp
* @param mixed $value
* @param string $field
- * @param string $bindName
* @param integer $bindType
* @return string
*/
- protected function parseIn(Query $query, $key, $exp, $value, $field, $bindName, $bindType)
+ protected function parseIn(Query $query, $key, $exp, $value, $field, $bindType)
{
// IN 查询
if ($value instanceof \Closure) {
$value = $this->parseClosure($query, $value, false);
+ } elseif ($value instanceof Expression) {
+ $value = $value->getValue();
} else {
$value = array_unique(is_array($value) ? $value : explode(',', $value));
-
- $bind = [];
$array = [];
- $i = 0;
foreach ($value as $k => $v) {
- $i++;
- if ($query->isBind($bindName . '_in_' . $i)) {
- $bindKey = $bindName . '_in_' . uniqid() . '_' . $i;
- } else {
- $bindKey = $bindName . '_in_' . $i;
- }
- $bind[$bindKey] = [$v, $bindType];
- $array[] = ':' . $bindKey;
+ $name = $query->bind($v, $bindType);
+ $array[] = ':' . $name;
}
- $zone = implode(',', $array);
- $query->bind($bind);
-
- $value = empty($zone) ? "''" : $zone;
+ if (count($array) == 1) {
+ return $key . ('IN' == $exp ? ' = ' : ' <> ') . $array[0];
+ } else {
+ $zone = implode(',', $array);
+ $value = empty($zone) ? "''" : $zone;
+ }
}
return $key . ' ' . $exp . ' (' . $value . ')';
@@ -724,7 +684,7 @@ abstract class Builder
*/
protected function parseClosure(Query $query, $call, $show = true)
{
- $newQuery = $query->newQuery()->setConnection($this->connection);
+ $newQuery = $query->newQuery()->removeOption();
$call($newQuery);
return $newQuery->buildSql($show);
@@ -736,12 +696,10 @@ abstract class Builder
* @param Query $query 查询对象
* @param string $value
* @param string $key
- * @param array $options
- * @param string $bindName
* @param integer $bindType
* @return string
*/
- protected function parseDateTime(Query $query, $value, $key, $bindName = null, $bindType = null)
+ protected function parseDateTime(Query $query, $value, $key, $bindType = null)
{
$options = $query->getOptions();
@@ -776,15 +734,9 @@ abstract class Builder
}
}
- $bindName = $bindName ?: $key;
-
- if ($query->isBind($bindName)) {
- $bindName .= '_' . str_replace('.', '_', uniqid('', true));
- }
-
- $query->bind($bindName, $value, $bindType);
+ $name = $query->bind($value, $bindType);
- return ':' . $bindName;
+ return ':' . $name;
}
/**
@@ -846,33 +798,31 @@ abstract class Builder
*/
protected function parseOrder(Query $query, $order)
{
- if (empty($order)) {
- return '';
- }
-
- $array = [];
-
foreach ($order as $key => $val) {
if ($val instanceof Expression) {
$array[] = $val->getValue();
- } elseif (is_array($val)) {
+ } elseif (is_array($val) && preg_match('/^[\w\.]+$/', $key)) {
$array[] = $this->parseOrderField($query, $key, $val);
} elseif ('[rand]' == $val) {
$array[] = $this->parseRand($query);
- } else {
+ } elseif (is_string($val)) {
if (is_numeric($key)) {
list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' ');
} else {
$sort = $val;
}
- $sort = strtoupper($sort);
- $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';
- $array[] = $this->parseKey($query, $key, true) . $sort;
+ if (preg_match('/^[\w\.]+$/', $key)) {
+ $sort = strtoupper($sort);
+ $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';
+ $array[] = $this->parseKey($query, $key, true) . $sort;
+ } else {
+ throw new Exception('order express error:' . $key);
+ }
}
}
- return ' ORDER BY ' . implode(',', $array);
+ return empty($array) ? '' : ' ORDER BY ' . implode(',', $array);
}
/**
@@ -899,7 +849,7 @@ abstract class Builder
$bind = $this->connection->getFieldsBind($options['table']);
foreach ($val as $k => $item) {
- $val[$k] = $this->parseDataBind($query, $key, $item, $bind, $k);
+ $val[$k] = $this->parseDataBind($query, $key, $item, $bind);
}
return 'field(' . $this->parseKey($query, $key, true) . ',' . implode(',', $val) . ')' . $sort;
@@ -1112,8 +1062,8 @@ abstract class Builder
// 获取绑定信息
$bind = $this->connection->getFieldsBind($options['table']);
- foreach ($dataSet as $k => $data) {
- $data = $this->parseData($query, $data, $allowFields, $bind, '_' . $k);
+ foreach ($dataSet as $data) {
+ $data = $this->parseData($query, $data, $allowFields, $bind);
$values[] = 'SELECT ' . implode(',', array_values($data));
@@ -1171,8 +1121,7 @@ abstract class Builder
{
$options = $query->getOptions();
- $table = $this->parseTable($query, $options['table']);
- $data = $this->parseData($query, $options['data']);
+ $data = $this->parseData($query, $options['data']);
if (empty($data)) {
return '';
diff --git a/thinkphp/library/think/db/Connection.php b/thinkphp/library/think/db/Connection.php
index 72722362bbcd4171c3d38c4a6dd706b67e61de35..18b4885a625a29669c242c2c36c166294ebb7365 100644
--- a/thinkphp/library/think/db/Connection.php
+++ b/thinkphp/library/think/db/Connection.php
@@ -24,6 +24,7 @@ use think\Loader;
abstract class Connection
{
+ const PARAM_FLOAT = 21;
protected static $instance = [];
/** @var PDOStatement PDO操作实例 */
protected $PDOStatement;
@@ -303,7 +304,9 @@ abstract class Connection
{
if (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) {
$bind = PDO::PARAM_STR;
- } elseif (preg_match('/(int|double|float|decimal|real|numeric|serial|bit)/is', $type)) {
+ } elseif (preg_match('/(double|float|decimal|real|numeric)/is', $type)) {
+ $bind = self::PARAM_FLOAT;
+ } elseif (preg_match('/(int|serial|bit)/is', $type)) {
$bind = PDO::PARAM_INT;
} elseif (preg_match('/bool/is', $type)) {
$bind = PDO::PARAM_BOOL;
@@ -323,9 +326,8 @@ abstract class Connection
public function parseSqlTable($sql)
{
if (false !== strpos($sql, '__')) {
- $prefix = $this->getConfig('prefix');
- $sql = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function ($match) use ($prefix) {
- return $prefix . strtolower($match[1]);
+ $sql = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function ($match) {
+ return $this->getConfig('prefix') . strtolower($match[1]);
}, $sql);
}
@@ -359,7 +361,7 @@ abstract class Connection
list($tableName) = explode(' ', $tableName);
- if (!strpos($tableName, '.')) {
+ if (false === strpos($tableName, '.')) {
$schema = $this->getConfig('database') . '.' . $tableName;
} else {
$schema = $tableName;
@@ -584,20 +586,13 @@ abstract class Connection
$this->bind = $bind;
- // 释放前次的查询结果
- if (!empty($this->PDOStatement)) {
- $this->free();
- }
-
Db::$queryTimes++;
// 调试开始
$this->debug(true);
// 预处理
- if (empty($this->PDOStatement)) {
- $this->PDOStatement = $this->linkID->prepare($sql);
- }
+ $this->PDOStatement = $this->linkID->prepare($sql);
// 是否为存储过程调用
$procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
@@ -663,15 +658,8 @@ abstract class Connection
// 调试开始
$this->debug(true);
- // 释放前次的查询结果
- if (!empty($this->PDOStatement)) {
- $this->free();
- }
-
// 预处理
- if (empty($this->PDOStatement)) {
- $this->PDOStatement = $this->linkID->prepare($sql);
- }
+ $this->PDOStatement = $this->linkID->prepare($sql);
// 是否为存储过程调用
$procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
@@ -742,15 +730,8 @@ abstract class Connection
// 调试开始
$this->debug(true);
- //释放前次的查询结果
- if (!empty($this->PDOStatement) && $this->PDOStatement->queryString != $sql) {
- $this->free();
- }
-
// 预处理
- if (empty($this->PDOStatement)) {
- $this->PDOStatement = $this->linkID->prepare($sql);
- }
+ $this->PDOStatement = $this->linkID->prepare($sql);
// 是否为存储过程调用
$procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
@@ -846,6 +827,8 @@ abstract class Connection
// 生成查询SQL
$sql = $this->builder->select($query);
+ $query->removeOption('limit');
+
$bind = $query->getBind();
if (!empty($options['fetch_sql'])) {
@@ -924,6 +907,8 @@ abstract class Connection
// 生成查询SQL
$sql = $this->builder->select($query);
+ $query->removeOption('limit');
+
$bind = $query->getBind();
if (!empty($options['fetch_sql'])) {
@@ -1261,13 +1246,24 @@ abstract class Connection
* @access public
* @param Query $query 查询对象
* @param string $field 字段名
- * @param bool $default 默认值
+ * @param mixed $default 默认值
+ * @param bool $one 是否返回一个值
* @return mixed
*/
- public function value(Query $query, $field, $default = null)
+ public function value(Query $query, $field, $default = null, $one = true)
{
$options = $query->getOptions();
+ if (isset($options['field'])) {
+ $query->removeOption('field');
+ }
+
+ if (is_string($field)) {
+ $field = array_map('trim', explode(',', $field));
+ }
+
+ $query->setOption('field', $field);
+
if (empty($options['fetch_sql']) && !empty($options['cache'])) {
$cache = $options['cache'];
$result = $this->getCacheData($query, $cache, null, $key);
@@ -1277,20 +1273,21 @@ abstract class Connection
}
}
- if (isset($options['field'])) {
- $query->removeOption('field');
- }
-
- if (is_string($field)) {
- $field = array_map('trim', explode(',', $field));
+ if ($one) {
+ $query->setOption('limit', 1);
}
- $query->setOption('field', $field);
- $query->setOption('limit', 1);
-
// 生成查询SQL
$sql = $this->builder->select($query);
+ if (isset($options['field'])) {
+ $query->setOption('field', $options['field']);
+ } else {
+ $query->removeOption('field');
+ }
+
+ $query->removeOption('limit');
+
$bind = $query->getBind();
if (!empty($options['fetch_sql'])) {
@@ -1316,14 +1313,18 @@ abstract class Connection
* @access public
* @param Query $query 查询对象
* @param string $aggregate 聚合方法
- * @param string $field 字段名
+ * @param mixed $field 字段名
* @return mixed
*/
public function aggregate(Query $query, $aggregate, $field)
{
- $field = $aggregate . '(' . $this->builder->parseKey($query, $field, true) . ') AS tp_' . strtolower($aggregate);
+ if (is_string($field) && 0 === stripos($field, 'DISTINCT ')) {
+ list($distinct, $field) = explode(' ', $field);
+ }
+
+ $field = $aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $this->builder->parseKey($query, $field, true) . ') AS tp_' . strtolower($aggregate);
- return $this->value($query, $field, 0);
+ return $this->value($query, $field, 0, false);
}
/**
@@ -1338,35 +1339,43 @@ abstract class Connection
{
$options = $query->getOptions();
- if (empty($options['fetch_sql']) && !empty($options['cache'])) {
- // 判断查询缓存
- $cache = $options['cache'];
- $result = $this->getCacheData($query, $cache, null, $guid);
-
- if (false !== $result) {
- return $result;
- }
- }
-
if (isset($options['field'])) {
$query->removeOption('field');
}
if (is_null($field)) {
- $field = '*';
- } elseif ($key && '*' != $field) {
- $field = $key . ',' . $field;
+ $field = ['*'];
+ } elseif (is_string($field)) {
+ $field = array_map('trim', explode(',', $field));
}
- if (is_string($field)) {
- $field = array_map('trim', explode(',', $field));
+ if ($key && ['*'] != $field) {
+ array_unshift($field, $key);
+ $field = array_unique($field);
}
$query->setOption('field', $field);
+ if (empty($options['fetch_sql']) && !empty($options['cache'])) {
+ // 判断查询缓存
+ $cache = $options['cache'];
+ $result = $this->getCacheData($query, $cache, null, $guid);
+
+ if (false !== $result) {
+ return $result;
+ }
+ }
+
// 生成查询SQL
$sql = $this->builder->select($query);
+ // 还原field参数
+ if (isset($options['field'])) {
+ $query->setOption('field', $options['field']);
+ } else {
+ $query->removeOption('field');
+ }
+
$bind = $query->getBind();
if (!empty($options['fetch_sql'])) {
@@ -1382,7 +1391,7 @@ abstract class Connection
} else {
$resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC);
- if ('*' == $field && $key) {
+ if (['*'] == $field && $key) {
$result = array_column($resultSet, null, $key);
} elseif ($resultSet) {
$fields = array_keys($resultSet[0]);
@@ -1458,19 +1467,16 @@ abstract class Connection
$value = is_array($val) ? $val[0] : $val;
$type = is_array($val) ? $val[1] : PDO::PARAM_STR;
- if (PDO::PARAM_STR == $type) {
+ if ((self::PARAM_FLOAT == $type || PDO::PARAM_STR == $type) && is_string($value)) {
$value = '\'' . addslashes($value) . '\'';
- } elseif (PDO::PARAM_INT == $type) {
- $value = (float) $value;
+ } elseif (PDO::PARAM_INT == $type && '' === $value) {
+ $value = 0;
}
// 判断占位符
$sql = is_numeric($key) ?
substr_replace($sql, $value, strpos($sql, '?'), 1) :
- str_replace(
- [':' . $key . ')', ':' . $key . ',', ':' . $key . ' ', ':' . $key . PHP_EOL],
- [$value . ')', $value . ',', $value . ' ', $value . PHP_EOL],
- $sql . ' ');
+ substr_replace($sql, $value, strpos($sql, ':' . $key), strlen(':' . $key));
}
return rtrim($sql);
@@ -1489,12 +1495,16 @@ abstract class Connection
{
foreach ($bind as $key => $val) {
// 占位符
- $param = is_numeric($key) ? $key + 1 : ':' . $key;
+ $param = is_int($key) ? $key + 1 : ':' . $key;
if (is_array($val)) {
if (PDO::PARAM_INT == $val[1] && '' === $val[0]) {
$val[0] = 0;
+ } elseif (self::PARAM_FLOAT == $val[1]) {
+ $val[0] = is_string($val[0]) ? (float) $val[0] : $val[0];
+ $val[1] = PDO::PARAM_STR;
}
+
$result = $this->PDOStatement->bindValue($param, $val[0], $val[1]);
} else {
$result = $this->PDOStatement->bindValue($param, $val);
@@ -1521,7 +1531,7 @@ abstract class Connection
protected function bindParam($bind)
{
foreach ($bind as $key => $val) {
- $param = is_numeric($key) ? $key + 1 : ':' . $key;
+ $param = is_int($key) ? $key + 1 : ':' . $key;
if (is_array($val)) {
array_unshift($val, $param);
@@ -1680,13 +1690,9 @@ abstract class Connection
$this->parseSavepoint('trans' . $this->transTimes)
);
}
- } catch (\PDOException $e) {
- if ($this->isBreak($e)) {
- return $this->close()->startTrans();
- }
- throw $e;
} catch (\Exception $e) {
if ($this->isBreak($e)) {
+ --$this->transTimes;
return $this->close()->startTrans();
}
throw $e;
@@ -1826,6 +1832,9 @@ abstract class Connection
$this->linkRead = null;
$this->links = [];
+ // 释放查询
+ $this->free();
+
return $this;
}
@@ -2028,7 +2037,7 @@ abstract class Connection
// 分布式数据库配置解析
foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
- $_config[$name] = explode(',', $this->config[$name]);
+ $_config[$name] = is_string($this->config[$name]) ? explode(',', $this->config[$name]) : $this->config[$name];
}
// 主服务器序号
@@ -2074,9 +2083,6 @@ abstract class Connection
*/
public function __destruct()
{
- // 释放查询
- $this->free();
-
// 关闭连接
$this->close();
}
diff --git a/thinkphp/library/think/db/Query.php b/thinkphp/library/think/db/Query.php
index fcc11900dc3367671be25ab8344c8a2037df2874..ba082794a785ae657f79c866f2ffd616c0391b86 100644
--- a/thinkphp/library/think/db/Query.php
+++ b/thinkphp/library/think/db/Query.php
@@ -23,6 +23,7 @@ use think\exception\DbException;
use think\exception\PDOException;
use think\Loader;
use think\Model;
+use think\model\Collection as ModelCollection;
use think\model\Relation;
use think\model\relation\OneToOne;
use think\Paginator;
@@ -94,14 +95,14 @@ class Query
* @var array
*/
protected $timeRule = [
- 'today' => ['today', 'tomorrow'],
- 'yesterday' => ['yesterday', 'today'],
- 'week' => ['this week 00:00:00', 'next week 00:00:00'],
- 'last week' => ['last week 00:00:00', 'this week 00:00:00'],
- 'month' => ['first Day of this month 00:00:00', 'first Day of next month 00:00:00'],
- 'last month' => ['first Day of last month 00:00:00', 'first Day of this month 00:00:00'],
- 'year' => ['this year 1/1', 'next year 1/1'],
- 'last year' => ['last year 1/1', 'this year 1/1'],
+ 'today' => ['today', 'tomorrow -1second'],
+ 'yesterday' => ['yesterday', 'today -1second'],
+ 'week' => ['this week 00:00:00', 'next week 00:00:00 -1second'],
+ 'last week' => ['last week 00:00:00', 'this week 00:00:00 -1second'],
+ 'month' => ['first Day of this month 00:00:00', 'first Day of next month 00:00:00 -1second'],
+ 'last month' => ['first Day of last month 00:00:00', 'first Day of this month 00:00:00 -1second'],
+ 'year' => ['this year 1/1', 'next year 1/1 -1second'],
+ 'last year' => ['last year 1/1', 'this year 1/1 -1second'],
];
/**
@@ -132,7 +133,27 @@ class Query
*/
public function newQuery()
{
- return new static($this->connection);
+ $query = new static($this->connection);
+
+ if ($this->model) {
+ $query->model($this->model);
+ }
+
+ if (isset($this->options['table'])) {
+ $query->table($this->options['table']);
+ } else {
+ $query->name($this->name);
+ }
+
+ if (isset($this->options['json'])) {
+ $query->json($this->options['json'], $this->options['json_assoc']);
+ }
+
+ if (isset($this->options['field_type'])) {
+ $query->setJsonFieldType($this->options['field_type']);
+ }
+
+ return $query;
}
/**
@@ -150,7 +171,8 @@ class Query
// 调用扩展查询方法
array_unshift($args, $this);
- return Container::getInstance()->invoke(self::$extend[strtolower($method)], $args);
+ return Container::getInstance()
+ ->invoke(self::$extend[strtolower($method)], $args);
} elseif (strtolower(substr($method, 0, 5)) == 'getby') {
// 根据某个字段获取记录
$field = Loader::parseName(substr($method, 5));
@@ -175,7 +197,7 @@ class Query
call_user_func_array([$this->model, $method], $args);
return $this;
} else {
- throw new Exception('method not exist:' . static::class . '->' . $method);
+ throw new Exception('method not exist:' . ($this->model ? get_class($this->model) : static::class) . '->' . $method);
}
}
@@ -303,14 +325,14 @@ class Query
* @param string $sql sql指令
* @param array $bind 参数绑定
* @param boolean $master 是否在主服务器读操作
- * @param bool|string $class 指定返回的数据集对象
+ * @param bool $pdo 是否返回PDO对象
* @return mixed
* @throws BindParamException
* @throws PDOException
*/
- public function query($sql, $bind = [], $master = false, $class = false)
+ public function query($sql, $bind = [], $master = false, $pdo = false)
{
- return $this->connection->query($sql, $bind, $master, $class);
+ return $this->connection->query($sql, $bind, $master, $pdo);
}
/**
@@ -324,7 +346,7 @@ class Query
*/
public function execute($sql, $bind = [])
{
- return $this->connection->execute($sql, $bind);
+ return $this->connection->execute($sql, $bind, $this);
}
/**
@@ -560,12 +582,12 @@ class Query
default:
if (function_exists($type)) {
// 支持指定函数哈希
- $seq = (ord(substr($type($value), 0, 1)) % $rule['num']) + 1;
- } else {
- // 按照字段的首字母的值分表
- $seq = (ord($value[0]) % $rule['num']) + 1;
+ $value = $type($value);
}
+
+ $seq = (ord(substr($value, 0, 1)) % $rule['num']) + 1;
}
+
return $this->getTable() . '_' . $seq;
}
// 当设置的分表字段不在查询条件或者数据中
@@ -609,9 +631,9 @@ class Query
/**
* 聚合查询
* @access public
- * @param string $aggregate 聚合方法
- * @param string $field 字段名
- * @param bool $force 强制转为数字类型
+ * @param string $aggregate 聚合方法
+ * @param string|Expression $field 字段名
+ * @param bool $force 强制转为数字类型
* @return mixed
*/
public function aggregate($aggregate, $field, $force = false)
@@ -632,7 +654,7 @@ class Query
/**
* COUNT查询
* @access public
- * @param string $field 字段名
+ * @param string|Expression $field 字段名
* @return float|string
*/
public function count($field = '*')
@@ -640,7 +662,10 @@ class Query
if (!empty($this->options['group'])) {
// 支持GROUP
$options = $this->getOptions();
- $subSql = $this->options($options)->field('count(' . $field . ') AS think_count')->bind($this->bind)->buildSql();
+ $subSql = $this->options($options)
+ ->field('count(' . $field . ') AS think_count')
+ ->bind($this->bind)
+ ->buildSql();
$query = $this->newQuery()->table([$subSql => '_group_count_']);
@@ -648,16 +673,18 @@ class Query
$query->fetchSql(true);
}
- return $query->aggregate('COUNT', '*', true);
+ $count = $query->aggregate('COUNT', '*', true);
+ } else {
+ $count = $this->aggregate('COUNT', $field, true);
}
- return $this->aggregate('COUNT', $field, true);
+ return is_string($count) ? $count : (int) $count;
}
/**
* SUM查询
* @access public
- * @param string $field 字段名
+ * @param string|Expression $field 字段名
* @return float
*/
public function sum($field)
@@ -668,8 +695,8 @@ class Query
/**
* MIN查询
* @access public
- * @param string $field 字段名
- * @param bool $force 强制转为数字类型
+ * @param string|Expression $field 字段名
+ * @param bool $force 强制转为数字类型
* @return mixed
*/
public function min($field, $force = true)
@@ -680,8 +707,8 @@ class Query
/**
* MAX查询
* @access public
- * @param string $field 字段名
- * @param bool $force 强制转为数字类型
+ * @param string|Expression $field 字段名
+ * @param bool $force 强制转为数字类型
* @return mixed
*/
public function max($field, $force = true)
@@ -692,7 +719,7 @@ class Query
/**
* AVG查询
* @access public
- * @param string $field 字段名
+ * @param string|Expression $field 字段名
* @return float
*/
public function avg($field)
@@ -827,9 +854,10 @@ class Query
* @param mixed $join 关联的表名
* @param mixed $condition 条件
* @param string $type JOIN类型
+ * @param array $bind 参数绑定
* @return $this
*/
- public function join($join, $condition = null, $type = 'INNER')
+ public function join($join, $condition = null, $type = 'INNER', $bind = [])
{
if (empty($condition)) {
// 如果为组数,则循环调用join
@@ -840,7 +868,9 @@ class Query
}
} else {
$table = $this->getJoinTable($join);
-
+ if ($bind) {
+ $this->bindParams($condition, $bind);
+ }
$this->options['join'][] = [$table, strtoupper($type), $condition];
}
@@ -852,9 +882,10 @@ class Query
* @access public
* @param mixed $join 关联的表名
* @param mixed $condition 条件
+ * @param array $bind 参数绑定
* @return $this
*/
- public function leftJoin($join, $condition = null)
+ public function leftJoin($join, $condition = null, $bind = [])
{
return $this->join($join, $condition, 'LEFT');
}
@@ -864,9 +895,10 @@ class Query
* @access public
* @param mixed $join 关联的表名
* @param mixed $condition 条件
+ * @param array $bind 参数绑定
* @return $this
*/
- public function rightJoin($join, $condition = null)
+ public function rightJoin($join, $condition = null, $bind = [])
{
return $this->join($join, $condition, 'RIGHT');
}
@@ -876,9 +908,10 @@ class Query
* @access public
* @param mixed $join 关联的表名
* @param mixed $condition 条件
+ * @param array $bind 参数绑定
* @return $this
*/
- public function fullJoin($join, $condition = null)
+ public function fullJoin($join, $condition = null, $bind = [])
{
return $this->join($join, $condition, 'FULL');
}
@@ -936,6 +969,10 @@ class Query
*/
public function union($union, $all = false)
{
+ if (empty($union)) {
+ return $this;
+ }
+
$this->options['union']['type'] = $all ? 'UNION ALL' : 'UNION';
if (is_array($union)) {
@@ -998,11 +1035,13 @@ class Query
if ($tableName) {
// 添加统一的前缀
$prefix = $prefix ?: $tableName;
- foreach ($field as $key => $val) {
- if (is_numeric($key)) {
- $val = $prefix . '.' . $val . ($alias ? ' AS ' . $alias . $val : '');
+ foreach ($field as $key => &$val) {
+ if (is_numeric($key) && $alias) {
+ $field[$prefix . '.' . $val] = $alias . $val;
+ unset($field[$key]);
+ } elseif (is_numeric($key)) {
+ $val = $prefix . '.' . $val;
}
- $field[$key] = $val;
}
}
@@ -1019,31 +1058,15 @@ class Query
* 表达式方式指定查询字段
* @access public
* @param string $field 字段名
- * @param array $bind 参数绑定
* @return $this
*/
- public function fieldRaw($field, array $bind = [])
+ public function fieldRaw($field)
{
$this->options['field'][] = $this->raw($field);
- if ($bind) {
- $this->bind($bind);
- }
-
return $this;
}
- /**
- * 设置数据排除字段
- * @access public
- * @param mixed $field 字段名或者数据
- * @return $this
- */
- public function hidden($field)
- {
- return $this->field($field, true);
- }
-
/**
* 设置数据
* @access public
@@ -1423,18 +1446,19 @@ class Query
* 指定Exp查询条件
* @access public
* @param mixed $field 查询字段
- * @param string $condition 查询条件
+ * @param string $where 查询条件
* @param array $bind 参数绑定
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
- public function whereExp($field, $condition, $bind = [], $logic = 'AND')
+ public function whereExp($field, $where, $bind = [], $logic = 'AND')
{
- $this->options['where'][$logic][] = [$field, 'EXP', $this->raw($condition)];
-
if ($bind) {
- $this->bind($bind);
+ $this->bindParams($where, $bind);
}
+
+ $this->options['where'][$logic][] = [$field, 'EXP', $this->raw($where)];
+
return $this;
}
@@ -1448,15 +1472,39 @@ class Query
*/
public function whereRaw($where, $bind = [], $logic = 'AND')
{
- $this->options['where'][$logic][] = $this->raw($where);
-
if ($bind) {
- $this->bind($bind);
+ $this->bindParams($where, $bind);
}
+ $this->options['where'][$logic][] = $this->raw($where);
+
return $this;
}
+ /**
+ * 参数绑定
+ * @access public
+ * @param string $sql 绑定的sql表达式
+ * @param array $bind 参数绑定
+ * @return void
+ */
+ protected function bindParams(&$sql, array $bind = [])
+ {
+ foreach ($bind as $key => $value) {
+ if (is_array($value)) {
+ $name = $this->bind($value[0], $value[1], isset($value[2]) ? $value[2] : null);
+ } else {
+ $name = $this->bind($value);
+ }
+
+ if (is_numeric($key)) {
+ $sql = substr_replace($sql, ':' . $name, strpos($sql, '?'), 1);
+ } else {
+ $sql = str_replace(':' . $key, ':' . $name, $sql);
+ }
+ }
+ }
+
/**
* 指定表达式查询条件 OR
* @access public
@@ -1484,20 +1532,26 @@ class Query
{
if ($field instanceof $this) {
$this->options['where'] = $field->getOptions('where');
+ $this->bind($field->getBind(false));
return $this;
}
$logic = strtoupper($logic);
- if (is_string($field) && !empty($this->options['via']) && !strpos($field, '.')) {
+ if ($field instanceof Where) {
+ $this->options['where'][$logic] = $field->parse();
+ return $this;
+ }
+
+ if (is_string($field) && !empty($this->options['via']) && false === strpos($field, '.')) {
$field = $this->options['via'] . '.' . $field;
}
if ($field instanceof Expression) {
- return $this->whereRaw($field, is_array($op) ? $op : []);
+ return $this->whereRaw($field, is_array($op) ? $op : [], $logic);
} elseif ($strict) {
// 使用严格模式查询
- $where = [$field, $op, $condition];
+ $where = [$field, $op, $condition, $logic];
} elseif (is_array($field)) {
// 解析数组批量查询
return $this->parseArrayWhereItems($field, $logic);
@@ -1505,7 +1559,7 @@ class Query
$where = $field;
} elseif (is_string($field)) {
if (preg_match('/[,=\<\'\"\(\s]/', $field)) {
- return $this->whereRaw($field, $op);
+ return $this->whereRaw($field, $op, $logic);
} elseif (is_string($op) && strtolower($op) == 'exp') {
$bind = isset($param[2]) && is_array($param[2]) ? $param[2] : null;
return $this->whereExp($field, $condition, $bind, $logic);
@@ -1549,7 +1603,7 @@ class Query
// 字段相等查询
$where = [$field, '=', $op];
}
- } elseif (in_array(strtoupper($op), ['REGEXP', 'NOT REGEXP', 'EXISTS', 'NOT EXISTS', 'NOTEXISTS'], true)) {
+ } elseif (in_array(strtoupper($op), ['EXISTS', 'NOT EXISTS', 'NOTEXISTS'], true)) {
$where = [$field, $op, is_string($condition) ? $this->raw($condition) : $condition];
} else {
$where = $field ? [$field, $op, $condition, isset($param[2]) ? $param[2] : null] : null;
@@ -1622,6 +1676,7 @@ class Query
{
if (true === $option) {
$this->options = [];
+ $this->bind = [];
} elseif (is_string($option) && isset($this->options[$option])) {
unset($this->options[$option]);
}
@@ -1709,7 +1764,7 @@ class Query
* var_page:分页变量,
* list_rows:每页数量
* type:分页类名
- * @return \think\Paginator
+ * @return $this[]|\think\Paginator
* @throws DbException
*/
public function paginate($listRows = null, $simple = false, $config = [])
@@ -1755,6 +1810,9 @@ class Query
$results = $this->page($page, $listRows)->select();
}
+ $this->removeOption('limit');
+ $this->removeOption('page');
+
return $class::make($results, $listRows, $page, $total, $simple, $config);
}
@@ -1876,14 +1934,14 @@ class Query
* @param array $bind 参数绑定
* @return $this
*/
- public function orderRaw($field, array $bind = [])
+ public function orderRaw($field, $bind = [])
{
- $this->options['order'][] = $this->raw($field);
-
if ($bind) {
- $this->bind($bind);
+ $this->bindParams($field, $bind);
}
+ $this->options['order'][] = $this->raw($field);
+
return $this;
}
@@ -2071,6 +2129,19 @@ class Query
return $this;
}
+ /**
+ * 设置是否返回数据集对象(支持设置数据集对象类名)
+ * @access public
+ * @param bool|string $collection 是否返回数据集对象
+ * @return $this
+ */
+ public function fetchCollection($collection = true)
+ {
+ $this->options['collection'] = $collection;
+
+ return $this;
+ }
+
/**
* 设置从主服务器读取数据
* @access public
@@ -2118,6 +2189,64 @@ class Query
return $this;
}
+ /**
+ * 设置需要隐藏的输出属性
+ * @access public
+ * @param mixed $hidden 需要隐藏的字段名
+ * @return $this
+ */
+ public function hidden($hidden)
+ {
+ if ($this->model) {
+ $this->options['hidden'] = $hidden;
+ return $this;
+ }
+
+ return $this->field($hidden, true);
+ }
+
+ /**
+ * 设置需要输出的属性
+ * @access public
+ * @param array $visible 需要输出的属性
+ * @return $this
+ */
+ public function visible(array $visible)
+ {
+ $this->options['visible'] = $visible;
+ return $this;
+ }
+
+ /**
+ * 设置需要附加的输出属性
+ * @access public
+ * @param array $append 属性列表
+ * @return $this
+ */
+ public function append(array $append = [])
+ {
+ $this->options['append'] = $append;
+ return $this;
+ }
+
+ /**
+ * 设置数据字段获取器
+ * @access public
+ * @param string|array $name 字段名
+ * @param callable $callback 闭包获取器
+ * @return $this
+ */
+ public function withAttr($name, $callback = null)
+ {
+ if (is_array($name)) {
+ $this->options['with_attr'] = $name;
+ } else {
+ $this->options['with_attr'][$name] = $callback;
+ }
+
+ return $this;
+ }
+
/**
* 设置JSON字段信息
* @access public
@@ -2155,6 +2284,18 @@ class Query
return isset($this->options['field_type'][$field]) ? $this->options['field_type'][$field] : null;
}
+ /**
+ * 是否允许返回空数据(或空模型)
+ * @access public
+ * @param bool $allowEmpty 是否允许为空
+ * @return $this
+ */
+ public function allowEmpty($allowEmpty = true)
+ {
+ $this->options['allow_empty'] = $allowEmpty;
+ return $this;
+ }
+
/**
* 添加查询范围
* @access public
@@ -2190,6 +2331,33 @@ class Query
return $this;
}
+ /**
+ * 使用搜索器条件搜索字段
+ * @access public
+ * @param array $fields 搜索字段
+ * @param array $data 搜索数据
+ * @param string $prefix 字段前缀标识
+ * @return $this
+ */
+ public function withSearch(array $fields, array $data = [], $prefix = '')
+ {
+ foreach ($fields as $key => $field) {
+ if ($field instanceof \Closure) {
+ $field($this, isset($data[$key]) ? $data[$key] : null, $data, $prefix);
+ } elseif ($this->model) {
+ // 检测搜索器
+ $fieldName = is_numeric($key) ? $field : $key;
+ $method = 'search' . Loader::parseName($fieldName, 1) . 'Attr';
+
+ if (method_exists($this->model, $method)) {
+ $this->model->$method($this, isset($data[$field]) ? $data[$field] : null, $data, $prefix);
+ }
+ }
+ }
+
+ return $this;
+ }
+
/**
* 指定数据表主键
* @access public
@@ -2312,17 +2480,20 @@ class Query
/**
* 参数绑定
* @access public
- * @param mixed $key 参数名
* @param mixed $value 绑定变量值
* @param integer $type 绑定类型
- * @return $this
+ * @param string $name 绑定名称
+ * @return $this|string
*/
- public function bind($key, $value = false, $type = PDO::PARAM_STR)
+ public function bind($value, $type = PDO::PARAM_STR, $name = null)
{
- if (is_array($key)) {
- $this->bind = array_merge($this->bind, $key);
+ if (is_array($value)) {
+ $this->bind = array_merge($this->bind, $value);
} else {
- $this->bind[$key] = [$value, $type];
+ $name = $name ?: 'ThinkBind_' . (count($this->bind) + 1) . '_' . mt_rand() . '_';
+
+ $this->bind[$name] = [$value, $type];
+ return $name;
}
return $this;
@@ -2412,20 +2583,15 @@ class Query
/** @var Model $class */
$class = $this->model;
foreach ($with as $key => $relation) {
- $subRelation = '';
- $closure = false;
+ $closure = null;
if ($relation instanceof \Closure) {
// 支持闭包查询过滤关联条件
- $closure = $relation;
- $relation = $key;
- $with[$key] = $key;
+ $closure = $relation;
+ $relation = $key;
} elseif (is_array($relation)) {
- $subRelation = $relation;
- $relation = $key;
+ $relation = $key;
} elseif (is_string($relation) && strpos($relation, '.')) {
- $with[$key] = $relation;
-
list($relation, $subRelation) = explode('.', $relation, 2);
}
@@ -2434,20 +2600,74 @@ class Query
$model = $class->$relation();
if ($model instanceof OneToOne && 0 == $model->getEagerlyType()) {
- $model->removeOption()->eagerly($this, $relation, $subRelation, $closure, $first);
+ $table = $model->getTable();
+ $model->removeOption()
+ ->table($table)
+ ->eagerly($this, $relation, true, '', $closure, $first);
$first = false;
- } elseif ($closure) {
- $with[$key] = $closure;
}
}
+
$this->via();
- if (isset($this->options['with'])) {
- $this->options['with'] = array_merge($this->options['with'], $with);
- } else {
- $this->options['with'] = $with;
+ $this->options['with'] = $with;
+
+ return $this;
+ }
+
+ /**
+ * 关联预载入 JOIN方式(不支持嵌套)
+ * @access protected
+ * @param string|array $with 关联方法名
+ * @param string $joinType JOIN方式
+ * @return $this
+ */
+ public function withJoin($with, $joinType = '')
+ {
+ if (empty($with)) {
+ return $this;
+ }
+
+ if (is_string($with)) {
+ $with = explode(',', $with);
}
+ $first = true;
+
+ /** @var Model $class */
+ $class = $this->model;
+ foreach ($with as $key => $relation) {
+ $closure = null;
+ $field = true;
+
+ if ($relation instanceof \Closure) {
+ // 支持闭包查询过滤关联条件
+ $closure = $relation;
+ $relation = $key;
+ } elseif (is_array($relation)) {
+ $field = $relation;
+ $relation = $key;
+ } elseif (is_string($relation) && strpos($relation, '.')) {
+ list($relation, $subRelation) = explode('.', $relation, 2);
+ }
+
+ /** @var Relation $model */
+ $relation = Loader::parseName($relation, 1, false);
+ $model = $class->$relation();
+
+ if ($model instanceof OneToOne) {
+ $model->eagerly($this, $relation, $field, $joinType, $closure, $first);
+ $first = false;
+ } else {
+ // 不支持其它关联
+ unset($with[$key]);
+ }
+ }
+
+ $this->via();
+
+ $this->options['with_join'] = $with;
+
return $this;
}
@@ -2472,7 +2692,8 @@ class Query
}
foreach ($relations as $key => $relation) {
- $closure = false;
+ $closure = $aggregateField = null;
+
if ($relation instanceof \Closure) {
$closure = $relation;
$relation = $key;
@@ -2481,14 +2702,15 @@ class Query
$relation = $key;
}
- if (!isset($aggregateField)) {
+ $relation = Loader::parseName($relation, 1, false);
+
+ $count = $this->model->$relation()->getRelationCountQuery($closure, $aggregate, $field, $aggregateField);
+
+ if (empty($aggregateField)) {
$aggregateField = Loader::parseName($relation) . '_' . $aggregate;
}
- $relation = Loader::parseName($relation, 1, false);
- $count = '(' . $this->model->$relation()->getRelationCountQuery($closure, $aggregate, $field) . ')';
-
- $this->field([$count => $aggregateField]);
+ $this->field(['(' . $count . ')' => $aggregateField]);
}
}
@@ -2809,44 +3031,95 @@ class Query
return $resultSet;
}
+ // 返回结果处理
+ if (!empty($this->options['fail']) && count($resultSet) == 0) {
+ $this->throwNotFound($this->options);
+ }
+
// 数据列表读取后的处理
if (!empty($this->model)) {
// 生成模型对象
- if (count($resultSet) > 0) {
- foreach ($resultSet as $key => &$result) {
- // 数据转换为模型对象
- $this->resultToModel($result, $this->options, true);
- }
+ $resultSet = $this->resultSetToModelCollection($resultSet);
+ } else {
+ $this->resultSet($resultSet);
+ }
- if (!empty($this->options['with'])) {
- // 预载入
- $result->eagerlyResultSet($resultSet, $this->options['with']);
- }
+ return $resultSet;
+ }
- // 模型数据集转换
- $resultSet = $result->toCollection($resultSet);
- } else {
- $resultSet = $this->model->toCollection($resultSet);
- }
- } else {
- if (!empty($this->options['json'])) {
- foreach ($resultSet as &$result) {
- $this->jsonResult($result, $this->options['json'], true);
+ /**
+ * 查询数据转换为模型数据集对象
+ * @access protected
+ * @param array $resultSet 数据集
+ * @return ModelCollection
+ */
+ protected function resultSetToModelCollection(array $resultSet)
+ {
+ if (!empty($this->options['collection']) && is_string($this->options['collection'])) {
+ $collection = $this->options['collection'];
+ }
+
+ if (empty($resultSet)) {
+ return $this->model->toCollection([], isset($collection) ? $collection : null);
+ }
+
+ // 检查动态获取器
+ if (!empty($this->options['with_attr'])) {
+ foreach ($this->options['with_attr'] as $name => $val) {
+ if (strpos($name, '.')) {
+ list($relation, $field) = explode('.', $name);
+
+ $withRelationAttr[$relation][$field] = $val;
+ unset($this->options['with_attr'][$name]);
}
}
+ }
+
+ $withRelationAttr = isset($withRelationAttr) ? $withRelationAttr : [];
+
+ foreach ($resultSet as $key => &$result) {
+ // 数据转换为模型对象
+ $this->resultToModel($result, $this->options, true, $withRelationAttr);
+ }
+
+ if (!empty($this->options['with'])) {
+ // 预载入
+ $result->eagerlyResultSet($resultSet, $this->options['with'], $withRelationAttr);
+ }
+
+ if (!empty($this->options['with_join'])) {
+ // JOIN预载入
+ $result->eagerlyResultSet($resultSet, $this->options['with_join'], $withRelationAttr, true);
+ }
- if ('collection' == $this->connection->getConfig('resultset_type')) {
- // 返回Collection对象
- $resultSet = new Collection($resultSet);
+ // 模型数据集转换
+ return $result->toCollection($resultSet, isset($collection) ? $collection : null);
+ }
+
+ /**
+ * 处理数据集
+ * @access public
+ * @param array $resultSet
+ * @return void
+ */
+ protected function resultSet(&$resultSet)
+ {
+ if (!empty($this->options['json'])) {
+ foreach ($resultSet as &$result) {
+ $this->jsonResult($result, $this->options['json'], true);
}
}
- // 返回结果处理
- if (!empty($this->options['fail']) && count($resultSet) == 0) {
- $this->throwNotFound($this->options);
+ if (!empty($this->options['with_attr'])) {
+ foreach ($resultSet as &$result) {
+ $this->getResultAttr($result, $this->options['with_attr']);
+ }
}
- return $resultSet;
+ if (!empty($this->options['collection']) || 'collection' == $this->connection->getConfig('resultset_type')) {
+ // 返回Collection对象
+ $resultSet = new Collection($resultSet);
+ }
}
/**
@@ -2883,33 +3156,185 @@ class Query
}
// 数据处理
- if (!empty($result)) {
- if (!empty($this->model)) {
- // 返回模型对象
- $this->resultToModel($result, $this->options);
- } elseif (!empty($this->options['json'])) {
- $this->jsonResult($result, $this->options['json'], true);
- }
+ if (empty($result)) {
+ return $this->resultToEmpty();
+ }
+
+ if (!empty($this->model)) {
+ // 返回模型对象
+ $this->resultToModel($result, $this->options);
+ } else {
+ $this->result($result);
+ }
+
+ return $result;
+ }
+
+ /**
+ * 处理空数据
+ * @access protected
+ * @return array|Model|null
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws DataNotFoundException
+ */
+ protected function resultToEmpty()
+ {
+ if (!empty($this->options['allow_empty'])) {
+ return !empty($this->model) ? $this->model->newInstance([], $this->getModelUpdateCondition($this->options)) : [];
} elseif (!empty($this->options['fail'])) {
$this->throwNotFound($this->options);
}
+ }
+
+ /**
+ * 查找单条记录
+ * @access public
+ * @param mixed $data 主键值或者查询条件(闭包)
+ * @param mixed $with 关联预查询
+ * @param bool $cache 是否缓存
+ * @param bool $failException 是否抛出异常
+ * @return static|null
+ * @throws exception\DbException
+ */
+ public function get($data, $with = [], $cache = false, $failException = false)
+ {
+ if (is_null($data)) {
+ return;
+ }
+
+ if (true === $with || is_int($with)) {
+ $cache = $with;
+ $with = [];
+ }
+
+ return $this->parseQuery($data, $with, $cache)
+ ->failException($failException)
+ ->find($data);
+ }
+
+ /**
+ * 查找单条记录 如果不存在直接抛出异常
+ * @access public
+ * @param mixed $data 主键值或者查询条件(闭包)
+ * @param mixed $with 关联预查询
+ * @param bool $cache 是否缓存
+ * @return static|null
+ * @throws exception\DbException
+ */
+ public function getOrFail($data, $with = [], $cache = false)
+ {
+ return $this->get($data, $with, $cache, true);
+ }
+
+ /**
+ * 查找所有记录
+ * @access public
+ * @param mixed $data 主键列表或者查询条件(闭包)
+ * @param array|string $with 关联预查询
+ * @param bool $cache 是否缓存
+ * @return static[]|false
+ * @throws exception\DbException
+ */
+ public function all($data = null, $with = [], $cache = false)
+ {
+ if (true === $with || is_int($with)) {
+ $cache = $with;
+ $with = [];
+ }
+
+ return $this->parseQuery($data, $with, $cache)->select($data);
+ }
+
+ /**
+ * 分析查询表达式
+ * @access public
+ * @param mixed $data 主键列表或者查询条件(闭包)
+ * @param string $with 关联预查询
+ * @param bool $cache 是否缓存
+ * @return Query
+ */
+ protected function parseQuery(&$data, $with, $cache)
+ {
+ $result = $this->with($with)->cache($cache);
+
+ if ((is_array($data) && key($data) !== 0) || $data instanceof Where) {
+ $result = $result->where($data);
+ $data = null;
+ } elseif ($data instanceof \Closure) {
+ $data($result);
+ $data = null;
+ } elseif ($data instanceof Query) {
+ $result = $data->with($with)->cache($cache);
+ $data = null;
+ }
return $result;
}
/**
- * JSON字段数据转换
+ * 处理数据
+ * @access protected
+ * @param array $result 查询数据
+ * @return void
+ */
+ protected function result(&$result)
+ {
+ if (!empty($this->options['json'])) {
+ $this->jsonResult($result, $this->options['json'], true);
+ }
+
+ if (!empty($this->options['with_attr'])) {
+ $this->getResultAttr($result, $this->options['with_attr']);
+ }
+ }
+
+ /**
+ * 使用获取器处理数据
* @access protected
* @param array $result 查询数据
- * @param array $json JSON字段
- * @param bool $assoc 是否转换为数组
+ * @param array $withAttr 字段获取器
* @return void
*/
- protected function jsonResult(&$result, $json = [], $assoc = false)
+ protected function getResultAttr(&$result, $withAttr = [])
+ {
+ foreach ($withAttr as $name => $closure) {
+ $name = Loader::parseName($name);
+
+ if (strpos($name, '.')) {
+ // 支持JSON字段 获取器定义
+ list($key, $field) = explode('.', $name);
+
+ if (isset($result[$key])) {
+ $result[$key][$field] = $closure(isset($result[$key][$field]) ? $result[$key][$field] : null, $result[$key]);
+ }
+ } else {
+ $result[$name] = $closure(isset($result[$name]) ? $result[$name] : null, $result);
+ }
+ }
+ }
+
+ /**
+ * JSON字段数据转换
+ * @access protected
+ * @param array $result 查询数据
+ * @param array $json JSON字段
+ * @param bool $assoc 是否转换为数组
+ * @param array $withRelationAttr 关联获取器
+ * @return void
+ */
+ protected function jsonResult(&$result, $json = [], $assoc = false, $withRelationAttr = [])
{
foreach ($json as $name) {
if (isset($result[$name])) {
$result[$name] = json_decode($result[$name], $assoc);
+
+ if (isset($withRelationAttr[$name])) {
+ foreach ($withRelationAttr[$name] as $key => $closure) {
+ $data = get_object_vars($result[$name]);
+ $result[$name]->$key = $closure(isset($result[$name]->$key) ? $result[$name]->$key : null, $data);
+ }
+ }
}
}
}
@@ -2917,28 +3342,62 @@ class Query
/**
* 查询数据转换为模型对象
* @access protected
- * @param array $result 查询数据
- * @param array $options 查询参数
- * @param bool $resultSet 是否为数据集查询
+ * @param array $result 查询数据
+ * @param array $options 查询参数
+ * @param bool $resultSet 是否为数据集查询
+ * @param array $withRelationAttr 关联字段获取器
* @return void
*/
- protected function resultToModel(&$result, $options = [], $resultSet = false)
+ protected function resultToModel(&$result, $options = [], $resultSet = false, $withRelationAttr = [])
{
+ // 动态获取器
+ if (!empty($options['with_attr']) && empty($withRelationAttr)) {
+ foreach ($options['with_attr'] as $name => $val) {
+ if (strpos($name, '.')) {
+ list($relation, $field) = explode('.', $name);
+
+ $withRelationAttr[$relation][$field] = $val;
+ unset($options['with_attr'][$name]);
+ }
+ }
+ }
+
+ // JSON 数据处理
if (!empty($options['json'])) {
- $this->jsonResult($result, $options['json'], $options['json_assoc']);
+ $this->jsonResult($result, $options['json'], $options['json_assoc'], $withRelationAttr);
}
- $condition = (!$resultSet && isset($options['where']['AND'])) ? $options['where']['AND'] : null;
- $result = $this->model->newInstance($result, $condition);
+ $result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options));
+
+ // 动态获取器
+ if (!empty($options['with_attr'])) {
+ $result->withAttribute($options['with_attr']);
+ }
+
+ // 输出属性控制
+ if (!empty($options['visible'])) {
+ $result->visible($options['visible'], true);
+ } elseif (!empty($options['hidden'])) {
+ $result->hidden($options['hidden'], true);
+ }
+
+ if (!empty($options['append'])) {
+ $result->append($options['append'], true);
+ }
// 关联查询
if (!empty($options['relation'])) {
- $result->relationQuery($options['relation']);
+ $result->relationQuery($options['relation'], $withRelationAttr);
}
// 预载入查询
if (!$resultSet && !empty($options['with'])) {
- $result->eagerlyResult($result, $options['with']);
+ $result->eagerlyResult($result, $options['with'], $withRelationAttr);
+ }
+
+ // JOIN预载入查询
+ if (!$resultSet && !empty($options['with_join'])) {
+ $result->eagerlyResult($result, $options['with_join'], $withRelationAttr, true);
}
// 关联统计
@@ -2949,6 +3408,16 @@ class Query
}
}
+ /**
+ * 获取模型的更新条件
+ * @access protected
+ * @param array $options 查询参数
+ */
+ protected function getModelUpdateCondition(array $options)
+ {
+ return isset($options['where']['AND']) ? $options['where']['AND'] : null;
+ }
+
/**
* 查询失败 抛出异常
* @access protected
@@ -2994,6 +3463,20 @@ class Query
return $this->failException(true)->find($data);
}
+ /**
+ * 查找单条记录 不存在则返回空模型
+ * @access public
+ * @param array|string|Query|\Closure $data
+ * @return array|\PDOStatement|string|Model
+ * @throws DbException
+ * @throws ModelNotFoundException
+ * @throws DataNotFoundException
+ */
+ public function findOrEmpty($data = null)
+ {
+ return $this->allowEmpty(true)->find($data);
+ }
+
/**
* 分批数据返回处理
* @access public
@@ -3096,6 +3579,10 @@ class Query
*/
protected function parseView(&$options)
{
+ if (!isset($options['map'])) {
+ return;
+ }
+
foreach (['AND', 'OR'] as $logic) {
if (isset($options['where'][$logic])) {
foreach ($options['where'][$logic] as $key => $val) {
@@ -3115,7 +3602,7 @@ class Query
$options['order'] = explode(',', $options['order']);
}
foreach ($options['order'] as $key => $val) {
- if (is_numeric($key)) {
+ if (is_numeric($key) && is_string($val)) {
if (strpos($val, ' ')) {
list($field, $sort) = explode(' ', $val);
if (array_key_exists($field, $options['map'])) {
@@ -3207,7 +3694,7 @@ class Query
$options['field'] = '*';
}
- foreach (['data', 'order'] as $name) {
+ foreach (['data', 'order', 'join', 'union'] as $name) {
if (!isset($options[$name])) {
$options[$name] = [];
}
@@ -3227,7 +3714,7 @@ class Query
$options['master'] = true;
}
- foreach (['join', 'union', 'group', 'having', 'limit', 'force', 'comment'] as $name) {
+ foreach (['group', 'having', 'limit', 'force', 'comment'] as $name) {
if (!isset($options[$name])) {
$options[$name] = '';
}
diff --git a/thinkphp/library/think/db/Where.php b/thinkphp/library/think/db/Where.php
new file mode 100644
index 0000000000000000000000000000000000000000..9132e5461e7e17274a503e4c43c33517f4c180ea
--- /dev/null
+++ b/thinkphp/library/think/db/Where.php
@@ -0,0 +1,178 @@
+
+// +----------------------------------------------------------------------
+
+namespace think\db;
+
+use ArrayAccess;
+
+class Where implements ArrayAccess
+{
+ /**
+ * 查询表达式
+ * @var array
+ */
+ protected $where = [];
+
+ /**
+ * 是否需要增加括号
+ * @var bool
+ */
+ protected $enclose = false;
+
+ /**
+ * 创建一个查询表达式
+ *
+ * @param array $where 查询条件数组
+ * @param bool $enclose 是否增加括号
+ */
+ public function __construct(array $where = [], $enclose = false)
+ {
+ $this->where = $where;
+ $this->enclose = $enclose;
+ }
+
+ /**
+ * 设置是否添加括号
+ * @access public
+ * @param bool $enclose
+ * @return $this
+ */
+ public function enclose($enclose = true)
+ {
+ $this->enclose = $enclose;
+ return $this;
+ }
+
+ /**
+ * 解析为Query对象可识别的查询条件数组
+ * @access public
+ * @return array
+ */
+ public function parse()
+ {
+ $where = [];
+
+ foreach ($this->where as $key => $val) {
+ if ($val instanceof Expression) {
+ $where[] = [$key, 'exp', $val];
+ } elseif (is_null($val)) {
+ $where[] = [$key, 'NULL', ''];
+ } elseif (is_array($val)) {
+ $where[] = $this->parseItem($key, $val);
+ } else {
+ $where[] = [$key, '=', $val];
+ }
+ }
+
+ return $this->enclose ? [$where] : $where;
+ }
+
+ /**
+ * 分析查询表达式
+ * @access protected
+ * @param string $field 查询字段
+ * @param array $where 查询条件
+ * @return array
+ */
+ protected function parseItem($field, $where = [])
+ {
+ $op = $where[0];
+ $condition = isset($where[1]) ? $where[1] : null;
+
+ if (is_array($op)) {
+ // 同一字段多条件查询
+ array_unshift($where, $field);
+ } elseif (is_null($condition)) {
+ if (in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) {
+ // null查询
+ $where = [$field, $op, ''];
+ } elseif (in_array($op, ['=', 'eq', 'EQ', null], true)) {
+ $where = [$field, 'NULL', ''];
+ } elseif (in_array($op, ['<>', 'neq', 'NEQ'], true)) {
+ $where = [$field, 'NOTNULL', ''];
+ } else {
+ // 字段相等查询
+ $where = [$field, '=', $op];
+ }
+ } else {
+ $where = [$field, $op, $condition];
+ }
+
+ return $where;
+ }
+
+ /**
+ * 修改器 设置数据对象的值
+ * @access public
+ * @param string $name 名称
+ * @param mixed $value 值
+ * @return void
+ */
+ public function __set($name, $value)
+ {
+ $this->where[$name] = $value;
+ }
+
+ /**
+ * 获取器 获取数据对象的值
+ * @access public
+ * @param string $name 名称
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ return isset($this->where[$name]) ? $this->where[$name] : null;
+ }
+
+ /**
+ * 检测数据对象的值
+ * @access public
+ * @param string $name 名称
+ * @return boolean
+ */
+ public function __isset($name)
+ {
+ return isset($this->where[$name]);
+ }
+
+ /**
+ * 销毁数据对象的值
+ * @access public
+ * @param string $name 名称
+ * @return void
+ */
+ public function __unset($name)
+ {
+ unset($this->where[$name]);
+ }
+
+ // ArrayAccess
+ public function offsetSet($name, $value)
+ {
+ $this->__set($name, $value);
+ }
+
+ public function offsetExists($name)
+ {
+ return $this->__isset($name);
+ }
+
+ public function offsetUnset($name)
+ {
+ $this->__unset($name);
+ }
+
+ public function offsetGet($name)
+ {
+ return $this->__get($name);
+ }
+
+}
diff --git a/thinkphp/library/think/db/builder/Mysql.php b/thinkphp/library/think/db/builder/Mysql.php
index ab21648e157a48e1c011fe5f079befd98f6d411c..f7384b31e74daf47fb89b2db3d6bdd1b985479ec 100644
--- a/thinkphp/library/think/db/builder/Mysql.php
+++ b/thinkphp/library/think/db/builder/Mysql.php
@@ -14,6 +14,7 @@ namespace think\db\builder;
use think\db\Builder;
use think\db\Expression;
use think\db\Query;
+use think\Exception;
/**
* mysql数据库驱动
@@ -61,7 +62,7 @@ class Mysql extends Builder
$bind = $this->connection->getFieldsBind($options['table']);
foreach ($dataSet as $k => $data) {
- $data = $this->parseData($query, $data, $allowFields, $bind, '_' . $k);
+ $data = $this->parseData($query, $data, $allowFields, $bind);
$values[] = '( ' . implode(',', array_values($data)) . ' )';
@@ -93,13 +94,17 @@ class Mysql extends Builder
* @param Query $query 查询对象
* @param string $key
* @param string $exp
- * @param Expression $value
+ * @param mixed $value
* @param string $field
* @return string
*/
- protected function parseRegexp(Query $query, $key, $exp, Expression $value, $field)
+ protected function parseRegexp(Query $query, $key, $exp, $value, $field)
{
- return $key . ' ' . $exp . ' ' . $value->getValue();
+ if ($value instanceof Expression) {
+ $value = $value->getValue();
+ }
+
+ return $key . ' ' . $exp . ' ' . $value;
}
/**
@@ -120,11 +125,17 @@ class Mysql extends Builder
$key = trim($key);
- if (strpos($key, '->') && false === strpos($key, '(')) {
+ if(strpos($key, '->>') && false === strpos($key, '(')){
+ // JSON字段支持
+ list($field, $name) = explode('->>', $key, 2);
+
+ return $this->parseKey($query, $field, true) . '->>\'$' . (strpos($name, '[') === 0 ? '' : '.') . str_replace('->>', '.', $name) . '\'';
+ }
+ elseif (strpos($key, '->') && false === strpos($key, '(')) {
// JSON字段支持
list($field, $name) = explode('->', $key, 2);
- return 'json_extract(' . $this->parseKey($query, $field) . ', \'$.' . str_replace('->', '.', $name) . '\')';
+ return 'json_extract(' . $this->parseKey($query, $field, true) . ', \'$' . (strpos($name, '[') === 0 ? '' : '.') . str_replace('->', '.', $name) . '\')';
} elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) {
list($table, $key) = explode('.', $key, 2);
@@ -140,7 +151,11 @@ class Mysql extends Builder
}
}
- if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) {
+ if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
+ throw new Exception('not support data:' . $key);
+ }
+
+ if ('*' != $key && !preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
$key = '`' . $key . '`';
}
diff --git a/thinkphp/library/think/db/builder/Sqlsrv.php b/thinkphp/library/think/db/builder/Sqlsrv.php
index 25da4d9fda92aaa716515cfbc4e05a3016ab1506..ef27aafa32035e6ec6f14c2a41e39c496b190e38 100644
--- a/thinkphp/library/think/db/builder/Sqlsrv.php
+++ b/thinkphp/library/think/db/builder/Sqlsrv.php
@@ -14,6 +14,7 @@ namespace think\db\builder;
use think\db\Builder;
use think\db\Expression;
use think\db\Query;
+use think\Exception;
/**
* Sqlsrv数据库驱动
@@ -40,8 +41,6 @@ class Sqlsrv extends Builder
return ' ORDER BY rand()';
}
- $array = [];
-
foreach ($order as $key => $val) {
if ($val instanceof Expression) {
$array[] = $val->getValue();
@@ -54,12 +53,17 @@ class Sqlsrv extends Builder
$sort = $val;
}
- $sort = in_array(strtolower($sort), ['asc', 'desc'], true) ? ' ' . $sort : '';
- $array[] = $this->parseKey($query, $key, true) . $sort;
+ if (preg_match('/^[\w\.]+$/', $key)) {
+ $sort = strtoupper($sort);
+ $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';
+ $array[] = $this->parseKey($query, $key, true) . $sort;
+ } else {
+ throw new Exception('order express error:' . $key);
+ }
}
}
- return ' ORDER BY ' . implode(',', $array);
+ return empty($array) ? '' : ' ORDER BY ' . implode(',', $array);
}
/**
@@ -106,7 +110,11 @@ class Sqlsrv extends Builder
}
}
- if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)\[.\s]/', $key))) {
+ if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
+ throw new Exception('not support data:' . $key);
+ }
+
+ if ('*' != $key && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) {
$key = '[' . $key . ']';
}
diff --git a/thinkphp/library/think/db/connector/Mysql.php b/thinkphp/library/think/db/connector/Mysql.php
index 93b8a1825bde159ecb0cf11cf6b7e1eca3d70c90..cfd2ac72b954ff2b2d793f42f60914530a665293 100644
--- a/thinkphp/library/think/db/connector/Mysql.php
+++ b/thinkphp/library/think/db/connector/Mysql.php
@@ -97,10 +97,10 @@ class Mysql extends Connection
$info[$val['field']] = [
'name' => $val['field'],
'type' => $val['type'],
- 'notnull' => (bool) ('' === $val['null']), // not null is empty, null is yes
+ 'notnull' => 'NO' == $val['null'],
'default' => $val['default'],
- 'primary' => (strtolower($val['key']) == 'pri'),
- 'autoinc' => (strtolower($val['extra']) == 'auto_increment'),
+ 'primary' => strtolower($val['key']) == 'pri',
+ 'autoinc' => strtolower($val['extra']) == 'auto_increment',
];
}
}
@@ -136,7 +136,27 @@ class Mysql extends Connection
*/
protected function getExplain($sql)
{
- $pdo = $this->linkID->query("EXPLAIN " . $sql);
+ $pdo = $this->linkID->prepare("EXPLAIN " . $this->queryStr);
+
+ foreach ($this->bind as $key => $val) {
+ // 占位符
+ $param = is_int($key) ? $key + 1 : ':' . $key;
+
+ if (is_array($val)) {
+ if (PDO::PARAM_INT == $val[1] && '' === $val[0]) {
+ $val[0] = 0;
+ } elseif (self::PARAM_FLOAT == $val[1]) {
+ $val[0] = is_string($val[0]) ? (float) $val[0] : $val[0];
+ $val[1] = PDO::PARAM_STR;
+ }
+
+ $result = $pdo->bindValue($param, $val[0], $val[1]);
+ } else {
+ $result = $pdo->bindValue($param, $val);
+ }
+ }
+
+ $pdo->execute();
$result = $pdo->fetch(PDO::FETCH_ASSOC);
$result = array_change_key_case($result);
@@ -167,7 +187,7 @@ class Mysql extends Connection
return false;
}
- $this->execute("XA START '$xid'");
+ $this->linkID->exec("XA START '$xid'");
}
/**
@@ -179,8 +199,8 @@ class Mysql extends Connection
public function prepareXa($xid)
{
$this->initConnect(true);
- $this->execute("XA END '$xid'");
- $this->execute("XA PREPARE '$xid'");
+ $this->linkID->exec("XA END '$xid'");
+ $this->linkID->exec("XA PREPARE '$xid'");
}
/**
@@ -192,7 +212,7 @@ class Mysql extends Connection
public function commitXa($xid)
{
$this->initConnect(true);
- $this->execute("XA COMMIT '$xid'");
+ $this->linkID->exec("XA COMMIT '$xid'");
}
/**
@@ -204,6 +224,6 @@ class Mysql extends Connection
public function rollbackXa($xid)
{
$this->initConnect(true);
- $this->execute("XA ROLLBACK '$xid'");
+ $this->linkID->exec("XA ROLLBACK '$xid'");
}
}
diff --git a/thinkphp/library/think/db/connector/Sqlsrv.php b/thinkphp/library/think/db/connector/Sqlsrv.php
index aba405fd95874041ac78906f4c8c0a7ad1fe47e2..123affb87f4360f0e3be8a10cc7c0f7f495bef51 100644
--- a/thinkphp/library/think/db/connector/Sqlsrv.php
+++ b/thinkphp/library/think/db/connector/Sqlsrv.php
@@ -56,6 +56,8 @@ class Sqlsrv extends Connection
public function getFields($tableName)
{
list($tableName) = explode(' ', $tableName);
+ $tableNames = explode('.', $tableName);
+ $tableName = isset($tableNames[1]) ? $tableNames[1] : $tableNames[0];
$sql = "SELECT column_name, data_type, column_default, is_nullable
FROM information_schema.tables AS t
diff --git a/thinkphp/library/think/db/connector/pgsql.sql b/thinkphp/library/think/db/connector/pgsql.sql
index e1a09a30c54d24947948be8c0f69a57891094179..5a4442d02d042e1b96787775a51ae04b12faf6b2 100644
--- a/thinkphp/library/think/db/connector/pgsql.sql
+++ b/thinkphp/library/think/db/connector/pgsql.sql
@@ -37,6 +37,10 @@ DECLARE
v_sql varchar;
v_rec RECORD;
v_key varchar;
+ v_conkey smallint[];
+ v_pk varchar[];
+ v_len smallint;
+ v_pos smallint := 1;
BEGIN
SELECT
pg_class.oid INTO v_oid
@@ -49,6 +53,31 @@ BEGIN
RETURN;
END IF;
+ SELECT
+ pg_constraint.conkey INTO v_conkey
+ FROM
+ pg_constraint
+ INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid
+ INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid
+ INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid
+ WHERE
+ pg_class.relname = a_table_name
+ AND pg_constraint.contype = 'p';
+
+ v_len := array_length(v_conkey,1) + 1;
+ WHILE v_pos < v_len LOOP
+ SELECT
+ pg_attribute.attname INTO v_key
+ FROM pg_constraint
+ INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid
+ INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid AND pg_attribute.attnum = pg_constraint.conkey [ v_conkey[v_pos] ]
+ INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid
+ WHERE pg_class.relname = a_table_name AND pg_constraint.contype = 'p';
+ v_pk := array_append(v_pk,v_key);
+
+ v_pos := v_pos + 1;
+ END LOOP;
+
v_sql='
SELECT
pg_attribute.attname AS fields_name,
@@ -83,12 +112,19 @@ BEGIN
v_ret.fields_not_null=v_rec.fields_not_null;
v_ret.fields_default=v_rec.fields_default;
v_ret.fields_comment=v_rec.fields_comment;
- SELECT constraint_name INTO v_key FROM information_schema.key_column_usage WHERE table_schema=a_schema_name AND table_name=a_table_name AND column_name=v_rec.fields_name;
- IF FOUND THEN
- v_ret.fields_key_name=v_key;
- ELSE
- v_ret.fields_key_name='';
- END IF;
+
+ v_ret.fields_key_name='';
+
+ v_len := array_length(v_pk,1) + 1;
+ v_pos := 1;
+ WHILE v_pos < v_len LOOP
+ IF v_rec.fields_name = v_pk[v_pos] THEN
+ v_ret.fields_key_name=v_pk[v_pos];
+ EXIT;
+ END IF;
+ v_pos := v_pos + 1;
+ END LOOP;
+
RETURN NEXT v_ret;
END LOOP;
RETURN ;
diff --git a/thinkphp/library/think/debug/Console.php b/thinkphp/library/think/debug/Console.php
index dd063c1c32026e500ff7ad6a80bc42e77482eabd..5cbaa0f2fa1d7baed72a94959ff7d36314e07388 100644
--- a/thinkphp/library/think/debug/Console.php
+++ b/thinkphp/library/think/debug/Console.php
@@ -54,8 +54,8 @@ class Console
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
$mem = number_format((memory_get_usage() - Container::get('app')->getBeginMem()) / 1024, 2);
- if (isset($_SERVER['HTTP_HOST'])) {
- $uri = $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
+ if ($request->host()) {
+ $uri = $request->protocol() . ' ' . $request->method() . ' : ' . $request->url(true);
} else {
$uri = 'cmd:' . implode(' ', $_SERVER['argv']);
}
diff --git a/thinkphp/library/think/debug/Html.php b/thinkphp/library/think/debug/Html.php
index 85f354af00140c0c10d4498c56c93a16ea0e4dc9..a123762eea3b07ba5520e5cdc9dc011403b9cea2 100644
--- a/thinkphp/library/think/debug/Html.php
+++ b/thinkphp/library/think/debug/Html.php
@@ -54,8 +54,8 @@ class Html
$mem = number_format((memory_get_usage() - Container::get('app')->getBeginMem()) / 1024, 2);
// 页面Trace信息
- if (isset($_SERVER['HTTP_HOST'])) {
- $uri = $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
+ if ($request->host()) {
+ $uri = $request->protocol() . ' ' . $request->method() . ' : ' . $request->url(true);
} else {
$uri = 'cmd:' . implode(' ', $_SERVER['argv']);
}
diff --git a/thinkphp/library/think/exception/DbException.php b/thinkphp/library/think/exception/DbException.php
index 0f504257c821b5233abd06cdc560dfa89600b9f9..6baafb5138bafd2e8038cf2740cc03a373f7d765 100644
--- a/thinkphp/library/think/exception/DbException.php
+++ b/thinkphp/library/think/exception/DbException.php
@@ -26,7 +26,7 @@ class DbException extends Exception
* @param string $sql
* @param int $code
*/
- public function __construct($message, array $config, $sql, $code = 10500)
+ public function __construct($message, array $config = [], $sql = '', $code = 10500)
{
$this->message = $message;
$this->code = $code;
diff --git a/thinkphp/library/think/exception/ValidateException.php b/thinkphp/library/think/exception/ValidateException.php
index b3684169c53b1ea564f2c7e689be16e0353c167a..81ddfe211b91727a022cefbf9b5e039d0c1fc5d6 100644
--- a/thinkphp/library/think/exception/ValidateException.php
+++ b/thinkphp/library/think/exception/ValidateException.php
@@ -15,10 +15,11 @@ class ValidateException extends \RuntimeException
{
protected $error;
- public function __construct($error)
+ public function __construct($error, $code = 0)
{
$this->error = $error;
- $this->message = is_array($error) ? implode("\n\r", $error) : $error;
+ $this->message = is_array($error) ? implode(PHP_EOL, $error) : $error;
+ $this->code = $code;
}
/**
diff --git a/thinkphp/library/think/facade/App.php b/thinkphp/library/think/facade/App.php
index 37d995106860a562031d0e1e7e298309f742afdc..b375aa09ff99d007f5e0dca7686a5e869c55acf2 100644
--- a/thinkphp/library/think/facade/App.php
+++ b/thinkphp/library/think/facade/App.php
@@ -51,4 +51,13 @@ use think\Facade;
*/
class App extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'app';
+ }
}
diff --git a/thinkphp/library/think/facade/Build.php b/thinkphp/library/think/facade/Build.php
index 465962367394438521f4960ccc125c433f8230c2..c051bea11aba710078208602a2e57e160ffb95b6 100644
--- a/thinkphp/library/think/facade/Build.php
+++ b/thinkphp/library/think/facade/Build.php
@@ -21,4 +21,13 @@ use think\Facade;
*/
class Build extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'build';
+ }
}
diff --git a/thinkphp/library/think/facade/Cache.php b/thinkphp/library/think/facade/Cache.php
index 3011d6b3a5ae7073a6a792b05a461d1173c70277..9743486ebe4d108f409dbb7446c4c3de1c5ee792 100644
--- a/thinkphp/library/think/facade/Cache.php
+++ b/thinkphp/library/think/facade/Cache.php
@@ -33,4 +33,13 @@ use think\Facade;
*/
class Cache extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'cache';
+ }
}
diff --git a/thinkphp/library/think/facade/Config.php b/thinkphp/library/think/facade/Config.php
index 3332413b8a2e35e0db3ee725b08dc6ee832078dc..824d2b6af915dacdde5efcf5b4727878bd34000d 100644
--- a/thinkphp/library/think/facade/Config.php
+++ b/thinkphp/library/think/facade/Config.php
@@ -16,12 +16,24 @@ use think\Facade;
/**
* @see \think\Config
* @mixin \think\Config
+ * @method array load(string $file, string $name = '') static 加载配置文件
* @method bool has(string $name) static 检测配置是否存在
- * @method array pull(string $name) static 获取一级配置
- * @method mixed get(string $name) static 获取配置参数
- * @method mixed set(string $name, mixed $value = null) static 设置配置参数
- * @method array reset(string $prefix ='') static 重置配置参数
+ * @method array pull(string $name) static 获取一级配置参数
+ * @method mixed get(string $name,mixed $default = null) static 获取配置参数
+ * @method array set(mixed $name, mixed $value = null) static 设置配置参数
+ * @method array reset(string $name ='') static 重置配置参数
+ * @method void remove(string $name = '') static 移除配置
+ * @method void setYaconf(mixed $yaconf) static 设置开启Yaconf 或者指定配置文件名
*/
class Config extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'config';
+ }
}
diff --git a/thinkphp/library/think/facade/Cookie.php b/thinkphp/library/think/facade/Cookie.php
index 18efbde0cb82e48c5afe8a48610e1e3607422490..4d7cea2501a49ab2113151f616bb2ab25d05af04 100644
--- a/thinkphp/library/think/facade/Cookie.php
+++ b/thinkphp/library/think/facade/Cookie.php
@@ -27,4 +27,13 @@ use think\Facade;
*/
class Cookie extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'cookie';
+ }
}
diff --git a/thinkphp/library/think/facade/Debug.php b/thinkphp/library/think/facade/Debug.php
index ac482a41eb29ff89c45f468c8343cbbf0d8cc167..df20086d163c8fd52eb95676a0d9b815e4c97e8d 100644
--- a/thinkphp/library/think/facade/Debug.php
+++ b/thinkphp/library/think/facade/Debug.php
@@ -28,4 +28,13 @@ use think\Facade;
*/
class Debug extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'debug';
+ }
}
diff --git a/thinkphp/library/think/facade/Env.php b/thinkphp/library/think/facade/Env.php
index d430d683fda675f8c3575718e346f58d7fdf71f8..5d0472443b8340f587f5f72b54e80a1b7b334e63 100644
--- a/thinkphp/library/think/facade/Env.php
+++ b/thinkphp/library/think/facade/Env.php
@@ -22,4 +22,13 @@ use think\Facade;
*/
class Env extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'env';
+ }
}
diff --git a/thinkphp/library/think/facade/Hook.php b/thinkphp/library/think/facade/Hook.php
index e18f83b5d5ecbe34a6186203cf4f3f4b9695abf6..e9e120838f6bba98ed80b8e2a24bb26ac050010c 100644
--- a/thinkphp/library/think/facade/Hook.php
+++ b/thinkphp/library/think/facade/Hook.php
@@ -25,4 +25,13 @@ use think\Facade;
*/
class Hook extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'hook';
+ }
}
diff --git a/thinkphp/library/think/facade/Lang.php b/thinkphp/library/think/facade/Lang.php
index 3eb5d4acd2a752faf12adecbc9860522e406ff77..56c4777db50211409df35f752ffeb8795c5429e2 100644
--- a/thinkphp/library/think/facade/Lang.php
+++ b/thinkphp/library/think/facade/Lang.php
@@ -29,4 +29,13 @@ use think\Facade;
*/
class Lang extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'lang';
+ }
}
diff --git a/thinkphp/library/think/facade/Log.php b/thinkphp/library/think/facade/Log.php
index 3a1fbf88ff9d907b3d4f5d38398868f51695bb39..ae627a5cfd96e5dd55b988a6c566979ee4acd8d8 100644
--- a/thinkphp/library/think/facade/Log.php
+++ b/thinkphp/library/think/facade/Log.php
@@ -21,6 +21,7 @@ use think\Facade;
* @method \think\Log record(mixed $msg, string $type = 'info', array $context = []) static 记录日志信息
* @method \think\Log clear() static 清空日志信息
* @method \think\Log key(string $key) static 当前日志记录的授权key
+ * @method \think\Log close() static 关闭本次请求日志写入
* @method bool check(array $config) static 检查日志写入权限
* @method bool save() static 保存调试信息
* @method void write(mixed $msg, string $type = 'info', bool $force = false) static 实时写入日志信息
@@ -37,4 +38,13 @@ use think\Facade;
*/
class Log extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'log';
+ }
}
diff --git a/thinkphp/library/think/facade/Middleware.php b/thinkphp/library/think/facade/Middleware.php
index 46827964246dadbbdbde099b5702168ea411dde3..5e4cac74782cd9f3766c6af0e906063fbeb91835 100644
--- a/thinkphp/library/think/facade/Middleware.php
+++ b/thinkphp/library/think/facade/Middleware.php
@@ -24,4 +24,13 @@ use think\Facade;
*/
class Middleware extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'middleware';
+ }
}
diff --git a/thinkphp/library/think/facade/Request.php b/thinkphp/library/think/facade/Request.php
index d0eedb2463d0b1ea4e33b74c119f7a7d97339024..0989253f9ae2f52208839feee077a54d8e4d1311 100644
--- a/thinkphp/library/think/facade/Request.php
+++ b/thinkphp/library/think/facade/Request.php
@@ -85,4 +85,13 @@ use think\Facade;
*/
class Request extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'request';
+ }
}
diff --git a/thinkphp/library/think/facade/Response.php b/thinkphp/library/think/facade/Response.php
index 4a4de71298008056dc58d0dc1ec5ccb941706937..d7de142f011d358b64f9b034836a6069266f32e2 100644
--- a/thinkphp/library/think/facade/Response.php
+++ b/thinkphp/library/think/facade/Response.php
@@ -35,4 +35,13 @@ use think\Facade;
*/
class Response extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'response';
+ }
}
diff --git a/thinkphp/library/think/facade/Route.php b/thinkphp/library/think/facade/Route.php
index c9f843d9067993e91772dd7a2e661412932c846c..6457ba4b86238709242e0243e8c720b6a80bcb70 100644
--- a/thinkphp/library/think/facade/Route.php
+++ b/thinkphp/library/think/facade/Route.php
@@ -27,7 +27,7 @@ use think\Facade;
* @method void import(array $rules, string $type = '*') static 导入配置文件的路由规则
* @method \think\route\RuleItem rule(string $rule, mixed $route, string $method = '*', array $option = [], array $pattern = []) static 注册路由规则
* @method void rules(array $rules, string $method = '*', array $option = [], array $pattern = []) static 批量注册路由规则
- * @method \think\route\RuleGroup group(string|array $name, mixed $route, string $method = '*', array $option = [], array $pattern = []) static 注册路由分组
+ * @method \think\route\RuleGroup group(string|array $name, array|\Closure $route, array $method = '*', array $option = [], array $pattern = []) static 注册路由分组
* @method \think\route\RuleItem any(string $rule, mixed $route, array $option = [], array $pattern = []) static 注册路由
* @method \think\route\RuleItem get(string $rule, mixed $route, array $option = [], array $pattern = []) static 注册路由
* @method \think\route\RuleItem post(string $rule, mixed $route, array $option = [], array $pattern = []) static 注册路由
@@ -45,4 +45,13 @@ use think\Facade;
*/
class Route extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'route';
+ }
}
diff --git a/thinkphp/library/think/facade/Session.php b/thinkphp/library/think/facade/Session.php
index a68db382a3b028ff1cbc3bc097c6dd4dad8dc47f..fb9206afa1b552153c7dc0024aa37bc7079c0de4 100644
--- a/thinkphp/library/think/facade/Session.php
+++ b/thinkphp/library/think/facade/Session.php
@@ -34,4 +34,13 @@ use think\Facade;
*/
class Session extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'session';
+ }
}
diff --git a/thinkphp/library/think/facade/Template.php b/thinkphp/library/think/facade/Template.php
new file mode 100644
index 0000000000000000000000000000000000000000..f91b11821a03eefa20033f4472f12559967c5606
--- /dev/null
+++ b/thinkphp/library/think/facade/Template.php
@@ -0,0 +1,36 @@
+
+// +----------------------------------------------------------------------
+
+namespace think\facade;
+
+use think\Facade;
+
+/**
+ * @see \think\Template
+ * @mixin \think\Template
+ * @method void assign(mixed $name, mixed $value = '') static 模板变量赋值
+ * @method mixed get(string $name = '') static 获取模板变量
+ * @method void fetch(string $template, array $vars = [], array $config = []) static 渲染模板文件
+ * @method void display(string $content, array $vars = [], array $config = []) static 渲染模板内容
+ * @method mixed layout(string $name, string $replace = '') static 设置模板布局
+ */
+class Template extends Facade
+{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'template';
+ }
+}
diff --git a/thinkphp/library/think/facade/Url.php b/thinkphp/library/think/facade/Url.php
index db5a16f2be19a73289b4a3b0cf9cb45e1d924e35..639591ac82f365bcc05cc4bc3c2af4cb4cc83a21 100644
--- a/thinkphp/library/think/facade/Url.php
+++ b/thinkphp/library/think/facade/Url.php
@@ -21,4 +21,13 @@ use think\Facade;
*/
class Url extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'url';
+ }
}
diff --git a/thinkphp/library/think/facade/Validate.php b/thinkphp/library/think/facade/Validate.php
index 423446d4de625a0d84b17f0d811096d1c3385b62..a6eec23e4f3e448194439309edb852351fbedcbc 100644
--- a/thinkphp/library/think/facade/Validate.php
+++ b/thinkphp/library/think/facade/Validate.php
@@ -62,4 +62,14 @@ use think\Facade;
*/
class Validate extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'validate';
+ }
+
}
diff --git a/thinkphp/library/think/facade/View.php b/thinkphp/library/think/facade/View.php
index 0309a760fb9e5d21d6c3dce32b619a50e69b7662..084339178240cd837aebba72361d847996347672 100644
--- a/thinkphp/library/think/facade/View.php
+++ b/thinkphp/library/think/facade/View.php
@@ -23,9 +23,18 @@ use think\Facade;
* @method \think\View exists(mixed $name) static 检查模板是否存在
* @method \think\View filter(Callable $filter) static 视图内容过滤
* @method \think\View engine(mixed $engine = []) static 设置当前模板解析的引擎
- * @method string fetch(string $template = '', array $vars = [], array $replace = [], array $config = [], bool $renderContent = false) static 解析和获取模板内容
- * @method string display(string $content = '', array $vars = [], array $replace = [], array $config = []) static 渲染内容输出
+ * @method string fetch(string $template = '', array $vars = [], array $config = [], bool $renderContent = false) static 解析和获取模板内容
+ * @method string display(string $content = '', array $vars = [], array $config = []) static 渲染内容输出
*/
class View extends Facade
{
+ /**
+ * 获取当前Facade对应类名(或者已经绑定的容器对象标识)
+ * @access protected
+ * @return string
+ */
+ protected static function getFacadeClass()
+ {
+ return 'view';
+ }
}
diff --git a/thinkphp/library/think/log/driver/File.php b/thinkphp/library/think/log/driver/File.php
index dfd963c28c768f6e4d2ee8ea12dd27d858ecd337..3f6522d1a39ba18168213dbf49eac70b650f58da 100644
--- a/thinkphp/library/think/log/driver/File.php
+++ b/thinkphp/library/think/log/driver/File.php
@@ -19,7 +19,7 @@ use think\App;
class File
{
protected $config = [
- 'time_format' => ' c ',
+ 'time_format' => 'c',
'single' => false,
'file_size' => 2097152,
'path' => '',
@@ -107,7 +107,13 @@ class File
$info['timestamp'] = date($this->config['time_format']);
foreach ($message as $type => $msg) {
- $info[$type] = is_array($msg) ? implode("\r\n", $msg) : $msg;
+ $msg = is_array($msg) ? implode(PHP_EOL, $msg) : $msg;
+ if (PHP_SAPI == 'cli') {
+ $info['msg'] = $msg;
+ $info['type'] = $type;
+ } else {
+ $info[$type] = $msg;
+ }
}
if (PHP_SAPI == 'cli') {
@@ -129,23 +135,26 @@ class File
*/
protected function getMasterLogFile()
{
+ if ($this->config['max_files']) {
+ $files = glob($this->config['path'] . '*.log');
+
+ try {
+ if (count($files) > $this->config['max_files']) {
+ unlink($files[0]);
+ }
+ } catch (\Exception $e) {
+ }
+ }
+
+ $cli = PHP_SAPI == 'cli' ? '_cli' : '';
+
if ($this->config['single']) {
$name = is_string($this->config['single']) ? $this->config['single'] : 'single';
- $destination = $this->config['path'] . $name . '.log';
+ $destination = $this->config['path'] . $name . $cli . '.log';
} else {
- $cli = PHP_SAPI == 'cli' ? '_cli' : '';
-
if ($this->config['max_files']) {
$filename = date('Ymd') . $cli . '.log';
- $files = glob($this->config['path'] . '*.log');
-
- try {
- if (count($files) > $this->config['max_files']) {
- unlink($files[0]);
- }
- } catch (\Exception $e) {
- }
} else {
$filename = date('Ym') . DIRECTORY_SEPARATOR . date('d') . $cli . '.log';
}
@@ -169,15 +178,13 @@ class File
if ($this->config['single']) {
$name = is_string($this->config['single']) ? $this->config['single'] : 'single';
-
- $name .= '_' . $type;
} elseif ($this->config['max_files']) {
- $name = date('Ymd') . '_' . $type . $cli;
+ $name = date('Ymd');
} else {
- $name = date('d') . '_' . $type . $cli;
+ $name = date('d');
}
- return $path . DIRECTORY_SEPARATOR . $name . '.log';
+ return $path . DIRECTORY_SEPARATOR . $name . '_' . $type . $cli . '.log';
}
/**
@@ -205,14 +212,14 @@ class File
protected function parseCliLog($info)
{
if ($this->config['json']) {
- $message = json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n";
+ $message = json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
} else {
$now = $info['timestamp'];
unset($info['timestamp']);
- $message = implode("\r\n", $info);
+ $message = implode(PHP_EOL, $info);
- $message = "[{$now}]" . $message . "\r\n";
+ $message = "[{$now}]" . $message . PHP_EOL;
}
return $message;
@@ -235,13 +242,13 @@ class File
if ($this->config['json']) {
$info = $requestInfo + $info;
- return json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n";
+ return json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
}
- array_unshift($info, "---------------------------------------------------------------\r\n[{$info['timestamp']}] {$requestInfo['ip']} {$requestInfo['method']} {$requestInfo['host']}{$requestInfo['uri']}");
+ array_unshift($info, "---------------------------------------------------------------" . PHP_EOL . "\r\n[{$info['timestamp']}] {$requestInfo['ip']} {$requestInfo['method']} {$requestInfo['host']}{$requestInfo['uri']}");
unset($info['timestamp']);
- return implode("\r\n", $info) . "\r\n";
+ return implode(PHP_EOL, $info) . PHP_EOL;
}
protected function getDebugLog(&$info, $append, $apart)
diff --git a/thinkphp/library/think/log/driver/Socket.php b/thinkphp/library/think/log/driver/Socket.php
index b4a2cadc7050c8b473d87609f1c399f96ab4cc51..5e4f8bfd825bc5fad642f63f46926262582a20ab 100644
--- a/thinkphp/library/think/log/driver/Socket.php
+++ b/thinkphp/library/think/log/driver/Socket.php
@@ -30,6 +30,8 @@ class Socket
'force_client_ids' => [],
// 限制允许读取日志的client_id
'allow_client_ids' => [],
+ //输出到浏览器默认展开的日志级别
+ 'expand_level' => ['debug'],
];
protected $css = [
@@ -95,7 +97,7 @@ class Socket
foreach ($log as $type => $val) {
$trace[] = [
- 'type' => 'groupCollapsed',
+ 'type' => in_array($type, $this->config['expand_level']) ? 'group' : 'groupCollapsed',
'msg' => '[ ' . $type . ' ]',
'css' => isset($this->css[$type]) ? $this->css[$type] : '',
];
diff --git a/thinkphp/library/think/model/Collection.php b/thinkphp/library/think/model/Collection.php
index 3a9b60f590586baee595960c665035f6b8477e4b..fc0967cfe949cea9a2f6d941e1afd84ee4d445f0 100644
--- a/thinkphp/library/think/model/Collection.php
+++ b/thinkphp/library/think/model/Collection.php
@@ -17,27 +17,33 @@ use think\Model;
class Collection extends BaseCollection
{
/**
- * 返回数组中指定的一列
+ * 延迟预载入关联查询
* @access public
- * @param string $column_key
- * @param string|null $index_key
- * @return array
+ * @param mixed $relation 关联
+ * @return $this
*/
- public function column($column_key, $index_key = null)
+ public function load($relation)
{
- return array_column($this->toArray(), $column_key, $index_key);
+ if (!$this->isEmpty()) {
+ $item = current($this->items);
+ $item->eagerlyResultSet($this->items, $relation);
+ }
+
+ return $this;
}
/**
- * 延迟预载入关联查询
- * @access public
- * @param mixed $relation 关联
+ * 绑定(一对一)关联属性到当前模型
+ * @access protected
+ * @param string $relation 关联名称
+ * @param array $attrs 绑定属性
* @return $this
*/
- public function load($relation)
+ public function bindAttr($relation, array $attrs = [])
{
- $item = current($this->items);
- $item->eagerlyResultSet($this->items, $relation);
+ $this->each(function (Model $model) use ($relation, $attrs) {
+ $model->bindAttr($relation, $attrs);
+ });
return $this;
}
@@ -93,4 +99,20 @@ class Collection extends BaseCollection
return $this;
}
+ /**
+ * 设置数据字段获取器
+ * @access public
+ * @param string|array $name 字段名
+ * @param callable $callback 闭包获取器
+ * @return $this
+ */
+ public function withAttr($name, $callback = null)
+ {
+ $this->each(function ($model) use ($name, $callback) {
+ /** @var Model $model */
+ $model && $model->withAttribute($name, $callback);
+ });
+
+ return $this;
+ }
}
diff --git a/thinkphp/library/think/model/Relation.php b/thinkphp/library/think/model/Relation.php
index b969bca2aa1d8c2f01c76652ad53b725d5a50df0..ac6dd4cf95d9094bf117437ecaf21d0fc3c539e0 100644
--- a/thinkphp/library/think/model/Relation.php
+++ b/thinkphp/library/think/model/Relation.php
@@ -58,6 +58,16 @@ abstract class Relation
return $this->query->getModel();
}
+ /**
+ * 获取当前的关联模型类的实例
+ * @access public
+ * @return Query
+ */
+ public function getQuery()
+ {
+ return $this->query;
+ }
+
/**
* 设置当前关联为自关联
* @access public
@@ -119,14 +129,27 @@ abstract class Relation
protected function getQueryWhere(&$where, $relation)
{
- foreach ($where as $key => $val) {
+ foreach ($where as $key => &$val) {
if (is_string($key)) {
$where[] = [false === strpos($key, '.') ? $relation . '.' . $key : $key, '=', $val];
unset($where[$key]);
+ } elseif (isset($val[0]) && false === strpos($val[0], '.')) {
+ $val[0] = $relation . '.' . $val[0];
}
}
}
+ /**
+ * 更新数据
+ * @access public
+ * @param array $data 更新数据
+ * @return integer|string
+ */
+ public function update(array $data = [])
+ {
+ return $this->query->update($data);
+ }
+
/**
* 删除记录
* @access public
@@ -156,7 +179,7 @@ abstract class Relation
$result = call_user_func_array([$this->query->getModel(), $method], $args);
- return $result === $this->query ? $this : $result;
+ return $result === $this->query && !in_array(strtolower($method), ['fetchsql', 'fetchpdo']) ? $this : $result;
} else {
throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
}
diff --git a/thinkphp/library/think/model/concern/Attribute.php b/thinkphp/library/think/model/concern/Attribute.php
index c65c557bb6b21c17bb0e1412744c46c208f7437b..66627b383c066c1513d6d9828058b809fdbd4688 100644
--- a/thinkphp/library/think/model/concern/Attribute.php
+++ b/thinkphp/library/think/model/concern/Attribute.php
@@ -12,6 +12,7 @@
namespace think\model\concern;
use InvalidArgumentException;
+use think\db\Expression;
use think\Exception;
use think\Loader;
use think\model\Relation;
@@ -72,12 +73,24 @@ trait Attribute
*/
private $data = [];
+ /**
+ * 修改器执行记录
+ * @var array
+ */
+ private $set = [];
+
/**
* 原始数据
* @var array
*/
private $origin = [];
+ /**
+ * 动态获取器
+ * @var array
+ */
+ private $withAttr = [];
+
/**
* 获取模型对象的主键
* @access public
@@ -106,6 +119,21 @@ trait Attribute
return false;
}
+ /**
+ * 获取模型对象的主键值
+ * @access public
+ * @return integer
+ */
+ public function getKey()
+ {
+ $pk = $this->getPk();
+ if (is_string($pk) && array_key_exists($pk, $this->data)) {
+ return $this->data[$pk];
+ }
+
+ return;
+ }
+
/**
* 设置允许写入的字段
* @access public
@@ -283,10 +311,14 @@ trait Attribute
* @param string $name 属性名
* @param mixed $value 属性值
* @param array $data 数据
- * @return $this
+ * @return void
*/
public function setAttr($name, $value, $data = [])
{
+ if (isset($this->set[$name])) {
+ return;
+ }
+
if (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) {
// 自动写入的时间戳字段
$value = $this->autoWriteTimestamp($name);
@@ -295,7 +327,13 @@ trait Attribute
$method = 'set' . Loader::parseName($name, 1) . 'Attr';
if (method_exists($this, $method)) {
- $value = $this->$method($value, array_merge($this->data, $data));
+ $origin = $this->data;
+ $value = $this->$method($value, array_merge($this->data, $data));
+
+ $this->set[$name] = true;
+ if (is_null($value) && $origin !== $this->data) {
+ return;
+ }
} elseif (isset($this->type[$name])) {
// 类型转换
$value = $this->writeTransform($value, $this->type[$name]);
@@ -304,8 +342,6 @@ trait Attribute
// 设置数据对象属性
$this->data[$name] = $value;
-
- return $this;
}
/**
@@ -339,8 +375,7 @@ trait Attribute
switch ($type) {
case 'datetime':
case 'date':
- $format = !empty($param) ? $param : $this->dateFormat;
- $value = $this->formatDateTime(time(), $format);
+ $value = $this->formatDateTime('Y-m-d H:i:s.u');
break;
case 'timestamp':
case 'integer':
@@ -353,9 +388,9 @@ trait Attribute
'date',
'timestamp',
])) {
- $value = $this->formatDateTime(time(), $this->dateFormat);
+ $value = $this->formatDateTime('Y-m-d H:i:s.u');
} else {
- $value = $this->formatDateTime(time(), $this->dateFormat, true);
+ $value = time();
}
return $value;
@@ -374,6 +409,10 @@ trait Attribute
return;
}
+ if ($value instanceof Expression) {
+ return $value;
+ }
+
if (is_array($type)) {
list($type, $param) = $type;
} elseif (strpos($type, ':')) {
@@ -400,9 +439,8 @@ trait Attribute
}
break;
case 'datetime':
- $format = !empty($param) ? $param : $this->dateFormat;
- $value = is_numeric($value) ? $value : strtotime($value);
- $value = $this->formatDateTime($value, $format);
+ $value = is_numeric($value) ? $value : strtotime($value);
+ $value = $this->formatDateTime('Y-m-d H:i:s.u', $value);
break;
case 'object':
if (is_object($value)) {
@@ -442,9 +480,18 @@ trait Attribute
}
// 检测属性获取器
- $method = 'get' . Loader::parseName($name, 1) . 'Attr';
+ $fieldName = Loader::parseName($name);
+ $method = 'get' . Loader::parseName($name, 1) . 'Attr';
+
+ if (isset($this->withAttr[$fieldName])) {
+ if ($notFound && $relation = $this->isRelationAttr($name)) {
+ $modelRelation = $this->$relation();
+ $value = $this->getRelationData($modelRelation);
+ }
- if (method_exists($this, $method)) {
+ $closure = $this->withAttr[$fieldName];
+ $value = $closure($value, $this->data);
+ } elseif (method_exists($this, $method)) {
if ($notFound && $relation = $this->isRelationAttr($name)) {
$modelRelation = $this->$relation();
$value = $this->getRelationData($modelRelation);
@@ -460,9 +507,9 @@ trait Attribute
'date',
'timestamp',
])) {
- $value = $this->formatDateTime(strtotime($value), $this->dateFormat);
+ $value = $this->formatDateTime($this->dateFormat, $value);
} else {
- $value = $this->formatDateTime($value, $this->dateFormat);
+ $value = $this->formatDateTime($this->dateFormat, $value, true);
}
} elseif ($notFound) {
$value = $this->getRelationAttribute($name, $item);
@@ -548,13 +595,13 @@ trait Attribute
case 'timestamp':
if (!is_null($value)) {
$format = !empty($param) ? $param : $this->dateFormat;
- $value = $this->formatDateTime($value, $format);
+ $value = $this->formatDateTime($format, $value, true);
}
break;
case 'datetime':
if (!is_null($value)) {
$format = !empty($param) ? $param : $this->dateFormat;
- $value = $this->formatDateTime(strtotime($value), $format);
+ $value = $this->formatDateTime($format, $value);
}
break;
case 'json':
@@ -583,4 +630,27 @@ trait Attribute
return $value;
}
+ /**
+ * 设置数据字段获取器
+ * @access public
+ * @param string|array $name 字段名
+ * @param callable $callback 闭包获取器
+ * @return $this
+ */
+ public function withAttribute($name, $callback = null)
+ {
+ if (is_array($name)) {
+ foreach ($name as $key => $val) {
+ $key = Loader::parseName($key);
+
+ $this->withAttr[$key] = $val;
+ }
+ } else {
+ $name = Loader::parseName($name);
+
+ $this->withAttr[$name] = $callback;
+ }
+
+ return $this;
+ }
}
diff --git a/thinkphp/library/think/model/concern/Conversion.php b/thinkphp/library/think/model/concern/Conversion.php
index b88528adbc584a47bbd5bcb12f9e95bbff6a1bd2..de4db9311fcc53969bf540c89b97f9d8f3434ff2 100644
--- a/thinkphp/library/think/model/concern/Conversion.php
+++ b/thinkphp/library/think/model/concern/Conversion.php
@@ -87,7 +87,7 @@ trait Conversion
if (isset($this->data[$key])) {
throw new Exception('bind attr has exists:' . $key);
} else {
- $this->data[$key] = $model->$attr;
+ $this->data[$key] = $model->getAttr($attr);
}
}
}
@@ -130,34 +130,52 @@ trait Conversion
*/
public function toArray()
{
- $item = [];
- $visible = [];
- $hidden = [];
+ $item = [];
+ $hasVisible = false;
+
+ foreach ($this->visible as $key => $val) {
+ if (is_string($val)) {
+ if (strpos($val, '.')) {
+ list($relation, $name) = explode('.', $val);
+ $this->visible[$relation][] = $name;
+ } else {
+ $this->visible[$val] = true;
+ $hasVisible = true;
+ }
+ unset($this->visible[$key]);
+ }
+ }
+
+ foreach ($this->hidden as $key => $val) {
+ if (is_string($val)) {
+ if (strpos($val, '.')) {
+ list($relation, $name) = explode('.', $val);
+ $this->hidden[$relation][] = $name;
+ } else {
+ $this->hidden[$val] = true;
+ }
+ unset($this->hidden[$key]);
+ }
+ }
// 合并关联数据
$data = array_merge($this->data, $this->relation);
- // 过滤属性
- if (!empty($this->visible)) {
- $array = $this->parseAttr($this->visible, $visible);
- $data = array_intersect_key($data, array_flip($array));
- } elseif (!empty($this->hidden)) {
- $array = $this->parseAttr($this->hidden, $hidden, false);
- $data = array_diff_key($data, array_flip($array));
- }
-
foreach ($data as $key => $val) {
if ($val instanceof Model || $val instanceof ModelCollection) {
// 关联模型对象
- if (isset($visible[$key])) {
- $val->visible($visible[$key]);
- } elseif (isset($hidden[$key])) {
- $val->hidden($hidden[$key]);
+ if (isset($this->visible[$key]) && is_array($this->visible[$key])) {
+ $val->visible($this->visible[$key]);
+ } elseif (isset($this->hidden[$key]) && is_array($this->hidden[$key])) {
+ $val->hidden($this->hidden[$key]);
}
// 关联模型对象
- $item[$key] = $val->toArray();
- } else {
- // 模型属性
+ if (!isset($this->hidden[$key]) || true !== $this->hidden[$key]) {
+ $item[$key] = $val->toArray();
+ }
+ } elseif (isset($this->visible[$key])) {
+ $item[$key] = $this->getAttr($key);
+ } elseif (!isset($this->hidden[$key]) && !$hasVisible) {
$item[$key] = $this->getAttr($key);
}
}
@@ -171,10 +189,12 @@ trait Conversion
if (!$relation) {
$relation = $this->getAttr($key);
- $relation->visible($name);
+ if ($relation) {
+ $relation->visible($name);
+ }
}
- $item[$key] = $relation->append($name)->toArray();
+ $item[$key] = $relation ? $relation->append($name)->toArray() : [];
} elseif (strpos($name, '.')) {
list($key, $attr) = explode('.', $name);
// 追加关联对象属性
@@ -182,15 +202,14 @@ trait Conversion
if (!$relation) {
$relation = $this->getAttr($key);
- $relation->visible([$attr]);
+ if ($relation) {
+ $relation->visible([$attr]);
+ }
}
- $item[$key] = $relation->append([$attr])->toArray();
+ $item[$key] = $relation ? $relation->append([$attr])->toArray() : [];
} else {
- $value = $this->getAttr($name, $item);
- if (false !== $value) {
- $item[$name] = $value;
- }
+ $item[$name] = $this->getAttr($name, $item);
}
}
}
@@ -251,38 +270,4 @@ trait Conversion
return $collection;
}
- /**
- * 解析隐藏及显示属性
- * @access protected
- * @param array $attrs 属性
- * @param array $result 结果集
- * @param bool $visible
- * @return array
- */
- protected function parseAttr($attrs, &$result, $visible = true)
- {
- $array = [];
-
- foreach ($attrs as $key => $val) {
- if (is_array($val)) {
- if ($visible) {
- $array[] = $key;
- }
-
- $result[$key] = $val;
- } elseif (strpos($val, '.')) {
- list($key, $name) = explode('.', $val);
-
- if ($visible) {
- $array[] = $key;
- }
-
- $result[$key][] = $name;
- } else {
- $array[] = $val;
- }
- }
-
- return $array;
- }
}
diff --git a/thinkphp/library/think/model/concern/ModelEvent.php b/thinkphp/library/think/model/concern/ModelEvent.php
index 2bb6d5fe0619ac18136fb7c7c8d5d8f60fb21386..3a874846f76e58873b204892035a88d47cdfb11e 100644
--- a/thinkphp/library/think/model/concern/ModelEvent.php
+++ b/thinkphp/library/think/model/concern/ModelEvent.php
@@ -80,6 +80,8 @@ trait ModelEvent
*/
public static function observe($class)
{
+ self::flushEvent();
+
foreach (static::$observe as $event) {
$eventFuncName = Loader::parseName($event, 1, false);
diff --git a/thinkphp/library/think/model/concern/RelationShip.php b/thinkphp/library/think/model/concern/RelationShip.php
index 2c993bd2013411b5089f1700c779bf1a7e275641..48579b700e18d1ab6b076711e50ab503c0594082 100644
--- a/thinkphp/library/think/model/concern/RelationShip.php
+++ b/thinkphp/library/think/model/concern/RelationShip.php
@@ -13,6 +13,7 @@ namespace think\model\concern;
use think\Collection;
use think\db\Query;
+use think\Exception;
use think\Loader;
use think\Model;
use think\model\Relation;
@@ -115,6 +116,32 @@ trait RelationShip
return $this;
}
+ /**
+ * 绑定(一对一)关联属性到当前模型
+ * @access protected
+ * @param string $relation 关联名称
+ * @param array $attrs 绑定属性
+ * @return $this
+ * @throws Exception
+ */
+ public function bindAttr($relation, array $attrs = [])
+ {
+ $relation = $this->getRelation($relation);
+
+ foreach ($attrs as $key => $attr) {
+ $key = is_numeric($key) ? $attr : $key;
+ $value = $this->getOrigin($key);
+
+ if (!is_null($value)) {
+ throw new Exception('bind attr has exists:' . $key);
+ }
+
+ $this->setAttr($key, $relation ? $relation->getAttr($attr) : null);
+ }
+
+ return $this;
+ }
+
/**
* 关联数据写入
* @access public
@@ -171,10 +198,11 @@ trait RelationShip
/**
* 查询当前模型的关联数据
* @access public
- * @param string|array $relations 关联名
+ * @param string|array $relations 关联名
+ * @param array $withRelationAttr 关联获取器
* @return $this
*/
- public function relationQuery($relations)
+ public function relationQuery($relations, $withRelationAttr = [])
{
if (is_string($relations)) {
$relations = explode(',', $relations);
@@ -197,9 +225,16 @@ trait RelationShip
list($relation, $subRelation) = explode('.', $relation, 2);
}
- $method = Loader::parseName($relation, 1, false);
+ $method = Loader::parseName($relation, 1, false);
+ $relationName = Loader::parseName($relation);
- $this->relation[$relation] = $this->$method()->getRelation($subRelation, $closure);
+ $relationResult = $this->$method();
+
+ if (isset($withRelationAttr[$relationName])) {
+ $relationResult->getQuery()->withAttr($withRelationAttr[$relationName]);
+ }
+
+ $this->relation[$relation] = $relationResult->getRelation($subRelation, $closure);
}
return $this;
@@ -208,17 +243,19 @@ trait RelationShip
/**
* 预载入关联查询 返回数据集
* @access public
- * @param array $resultSet 数据集
- * @param string $relation 关联名
+ * @param array $resultSet 数据集
+ * @param string $relation 关联名
+ * @param array $withRelationAttr 关联获取器
+ * @param bool $join 是否为JOIN方式
* @return array
*/
- public function eagerlyResultSet(&$resultSet, $relation)
+ public function eagerlyResultSet(&$resultSet, $relation, $withRelationAttr = [], $join = false)
{
$relations = is_string($relation) ? explode(',', $relation) : $relation;
foreach ($relations as $key => $relation) {
$subRelation = '';
- $closure = false;
+ $closure = null;
if ($relation instanceof \Closure) {
$closure = $relation;
@@ -232,26 +269,35 @@ trait RelationShip
list($relation, $subRelation) = explode('.', $relation, 2);
}
- $relation = Loader::parseName($relation, 1, false);
+ $relation = Loader::parseName($relation, 1, false);
+ $relationName = Loader::parseName($relation);
+
+ $relationResult = $this->$relation();
+
+ if (isset($withRelationAttr[$relationName])) {
+ $relationResult->getQuery()->withAttr($withRelationAttr[$relationName]);
+ }
- $this->$relation()->eagerlyResultSet($resultSet, $relation, $subRelation, $closure);
+ $relationResult->eagerlyResultSet($resultSet, $relation, $subRelation, $closure, $join);
}
}
/**
* 预载入关联查询 返回模型对象
* @access public
- * @param Model $result 数据对象
- * @param string $relation 关联名
+ * @param Model $result 数据对象
+ * @param string $relation 关联名
+ * @param array $withRelationAttr 关联获取器
+ * @param bool $join 是否为JOIN方式
* @return Model
*/
- public function eagerlyResult(&$result, $relation)
+ public function eagerlyResult(&$result, $relation, $withRelationAttr = [], $join = false)
{
$relations = is_string($relation) ? explode(',', $relation) : $relation;
foreach ($relations as $key => $relation) {
$subRelation = '';
- $closure = false;
+ $closure = null;
if ($relation instanceof \Closure) {
$closure = $relation;
@@ -265,9 +311,16 @@ trait RelationShip
list($relation, $subRelation) = explode('.', $relation, 2);
}
- $relation = Loader::parseName($relation, 1, false);
+ $relation = Loader::parseName($relation, 1, false);
+ $relationName = Loader::parseName($relation);
+
+ $relationResult = $this->$relation();
- $this->$relation()->eagerlyResult($result, $relation, $subRelation, $closure);
+ if (isset($withRelationAttr[$relationName])) {
+ $relationResult->getQuery()->withAttr($withRelationAttr[$relationName]);
+ }
+
+ $relationResult->eagerlyResult($result, $relation, $subRelation, $closure, $join);
}
}
@@ -283,7 +336,7 @@ trait RelationShip
public function relationCount(&$result, $relations, $aggregate = 'sum', $field = '*')
{
foreach ($relations as $key => $relation) {
- $closure = false;
+ $closure = $name = null;
if ($relation instanceof \Closure) {
$closure = $relation;
@@ -294,9 +347,10 @@ trait RelationShip
}
$relation = Loader::parseName($relation, 1, false);
- $count = $this->$relation()->relationCount($result, $closure, $aggregate, $field);
- if (!isset($name)) {
+ $count = $this->$relation()->relationCount($result, $closure, $aggregate, $field, $name);
+
+ if (empty($name)) {
$name = Loader::parseName($relation) . '_' . $aggregate;
}
@@ -533,7 +587,7 @@ trait RelationShip
{
$relation = Loader::parseName($attr, 1, false);
- if (method_exists($this, $relation)) {
+ if (method_exists($this, $relation) && !method_exists('think\Model', $relation)) {
return $relation;
}
diff --git a/thinkphp/library/think/model/concern/SoftDelete.php b/thinkphp/library/think/model/concern/SoftDelete.php
index 7ba0324321922d8ab0b63f94c4e9bafd525ddeb3..ec866ac06fb209826a9a181a26f749a2a14578f5 100644
--- a/thinkphp/library/think/model/concern/SoftDelete.php
+++ b/thinkphp/library/think/model/concern/SoftDelete.php
@@ -97,7 +97,8 @@ trait SoftDelete
return false;
}
- $name = $this->getDeleteTimeField();
+ $force = $force ?: $this->isForce();
+ $name = $this->getDeleteTimeField();
if ($name && !$force) {
// 软删除
@@ -138,8 +139,12 @@ trait SoftDelete
*/
public static function destroy($data, $force = false)
{
+ // 传入空不执行删除,但是0可以删除
+ if (empty($data) && 0 !== $data) {
+ return false;
+ }
// 包含软删除数据
- $query = self::withTrashed();
+ $query = (new static())->db(false);
if (is_array($data) && key($data) !== 0) {
$query->where($data);
@@ -211,7 +216,7 @@ trait SoftDelete
return false;
}
- if (!strpos($field, '.')) {
+ if (false === strpos($field, '.')) {
$field = '__TABLE__.' . $field;
}
@@ -234,7 +239,8 @@ trait SoftDelete
$field = $this->getDeleteTimeField(true);
if ($field) {
- $query->useSoftDelete($field, $this->defaultSoftDelete);
+ $condition = is_null($this->defaultSoftDelete) ? ['null', ''] : ['=', $this->defaultSoftDelete];
+ $query->useSoftDelete($field, $condition);
}
}
}
diff --git a/thinkphp/library/think/model/concern/TimeStamp.php b/thinkphp/library/think/model/concern/TimeStamp.php
index 923b7d45331427515eb478afb2cfaad6e67a8d47..99a31fa7d3b1fae460a3fe9adabfc2c40c7d5cb5 100644
--- a/thinkphp/library/think/model/concern/TimeStamp.php
+++ b/thinkphp/library/think/model/concern/TimeStamp.php
@@ -11,6 +11,8 @@
namespace think\model\concern;
+use DateTime;
+
/**
* 自动时间戳
*/
@@ -43,20 +45,31 @@ trait TimeStamp
/**
* 时间日期字段格式化处理
* @access protected
- * @param mixed $time 时间日期表达式
* @param mixed $format 日期格式
+ * @param mixed $time 时间日期表达式
* @param bool $timestamp 是否进行时间戳转换
* @return mixed
*/
- protected function formatDateTime($time, $format, $timestamp = false)
+ protected function formatDateTime($format, $time = 'now', $timestamp = false)
{
- if (false !== strpos($format, '\\')) {
- $time = new $format($time);
- } elseif (!$timestamp && false !== $format) {
- $time = date($format, $time);
+ if (empty($time)) {
+ return;
+ }
+
+ if (false === $format) {
+ return $time;
+ } elseif (false !== strpos($format, '\\')) {
+ return new $format($time);
+ }
+
+ if ($timestamp) {
+ $dateTime = new DateTime();
+ $dateTime->setTimestamp($time);
+ } else {
+ $dateTime = new DateTime($time);
}
- return $time;
+ return $dateTime->format($format);
}
/**
diff --git a/thinkphp/library/think/model/relation/BelongsTo.php b/thinkphp/library/think/model/relation/BelongsTo.php
index 8ffa9ea161b00893f862741030b62d2d5b18afb9..056c7d766cf98fe7e7d9541b4c3dccc161f03600 100644
--- a/thinkphp/library/think/model/relation/BelongsTo.php
+++ b/thinkphp/library/think/model/relation/BelongsTo.php
@@ -11,6 +11,7 @@
namespace think\model\relation;
+use Closure;
use think\Loader;
use think\Model;
@@ -49,7 +50,7 @@ class BelongsTo extends OneToOne
*/
public function getRelation($subRelation = '', $closure = null)
{
- if ($closure) {
+ if ($closure instanceof Closure) {
$closure($this->query);
}
@@ -68,6 +69,62 @@ class BelongsTo extends OneToOne
return $relationModel;
}
+ /**
+ * 创建关联统计子查询
+ * @access public
+ * @param \Closure $closure 闭包
+ * @param string $aggregate 聚合查询方法
+ * @param string $field 字段
+ * @param string $aggregateAlias 聚合字段别名
+ * @return string
+ */
+ public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '')
+ {
+ if ($closure instanceof Closure) {
+ $return = $closure($this->query);
+
+ if ($return && is_string($return)) {
+ $aggregateAlias = $return;
+ }
+ }
+
+ return $this->query
+ ->whereExp($this->localKey, '=' . $this->parent->getTable() . '.' . $this->foreignKey)
+ ->fetchSql()
+ ->$aggregate($field);
+ }
+
+ /**
+ * 关联统计
+ * @access public
+ * @param Model $result 数据对象
+ * @param \Closure $closure 闭包
+ * @param string $aggregate 聚合查询方法
+ * @param string $field 字段
+ * @param string $name 统计字段别名
+ * @return integer
+ */
+ public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '')
+ {
+ $foreignKey = $this->foreignKey;
+
+ if (!isset($result->$foreignKey)) {
+ return 0;
+ }
+
+ if ($closure instanceof Closure) {
+ $return = $closure($this->query);
+
+ if ($return && is_string($return)) {
+ $name = $return;
+ }
+ }
+
+ return $this->query
+ ->where($this->localKey, '=', $result->$foreignKey)
+ ->$aggregate($field);
+ }
+
/**
* 根据关联条件查询当前模型
* @access public
@@ -79,7 +136,23 @@ class BelongsTo extends OneToOne
*/
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
{
- return $this->parent;
+ $table = $this->query->getTable();
+ $model = basename(str_replace('\\', '/', get_class($this->parent)));
+ $relation = basename(str_replace('\\', '/', $this->model));
+ $localKey = $this->localKey;
+ $foreignKey = $this->foreignKey;
+ $softDelete = $this->query->getOptions('soft_delete');
+
+ return $this->parent->db()
+ ->alias($model)
+ ->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey) {
+ $query->table([$table => $relation])
+ ->field($relation . '.' . $localKey)
+ ->whereExp($model . '.' . $foreignKey, '=' . $relation . '.' . $localKey)
+ ->when($softDelete, function ($query) use ($softDelete, $relation) {
+ $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+ });
+ });
}
/**
@@ -99,12 +172,16 @@ class BelongsTo extends OneToOne
$this->getQueryWhere($where, $relation);
}
- $fields = $this->getRelationQueryFields($fields, $model);
+ $fields = $this->getRelationQueryFields($fields, $model);
+ $softDelete = $this->query->getOptions('soft_delete');
return $this->parent->db()
->alias($model)
->field($fields)
->join([$table => $relation], $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $this->joinType)
+ ->when($softDelete, function ($query) use ($softDelete, $relation) {
+ $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+ })
->where($where);
}
@@ -208,10 +285,7 @@ class BelongsTo extends OneToOne
*/
public function associate($model)
{
- $foreignKey = $this->foreignKey;
- $pk = $model->getPk();
-
- $this->parent->setAttr($foreignKey, $model->$pk);
+ $this->parent->setAttr($this->foreignKey, $model->getKey());
$this->parent->save();
return $this->parent->setRelation($this->relation, $model);
@@ -224,9 +298,7 @@ class BelongsTo extends OneToOne
*/
public function dissociate()
{
- $foreignKey = $this->foreignKey;
-
- $this->parent->setAttr($foreignKey, null);
+ $this->parent->setAttr($this->foreignKey, null);
$this->parent->save();
return $this->parent->setRelation($this->relation, null);
diff --git a/thinkphp/library/think/model/relation/BelongsToMany.php b/thinkphp/library/think/model/relation/BelongsToMany.php
index 02e666695c19a856414ccc54dfdddf81196249b2..6105e233c3a891bed1e8bb971ed06220a7d430fe 100644
--- a/thinkphp/library/think/model/relation/BelongsToMany.php
+++ b/thinkphp/library/think/model/relation/BelongsToMany.php
@@ -11,6 +11,7 @@
namespace think\model\relation;
+use Closure;
use think\Collection;
use think\db\Query;
use think\Exception;
@@ -18,6 +19,7 @@ use think\Loader;
use think\Model;
use think\model\Pivot;
use think\model\Relation;
+use think\Paginator;
class BelongsToMany extends Relation
{
@@ -25,6 +27,8 @@ class BelongsToMany extends Relation
protected $middle;
// 中间表模型名称
protected $pivotName;
+ // 中间表数据名称
+ protected $pivotDataName = 'pivot';
// 中间表模型对象
protected $pivot;
@@ -67,6 +71,18 @@ class BelongsToMany extends Relation
return $this;
}
+ /**
+ * 设置中间表数据名称
+ * @access public
+ * @param string $name
+ * @return $this
+ */
+ public function pivotDataName($name)
+ {
+ $this->pivotDataName = $name;
+ return $this;
+ }
+
/**
* 获取中间表更新条件
* @param $data
@@ -121,7 +137,7 @@ class BelongsToMany extends Relation
}
}
- $model->setRelation('pivot', $this->newPivot($pivot, true));
+ $model->setRelation($this->pivotDataName, $this->newPivot($pivot, true));
}
}
@@ -152,7 +168,7 @@ class BelongsToMany extends Relation
*/
public function getRelation($subRelation = '', $closure = null)
{
- if ($closure) {
+ if ($closure instanceof Closure) {
$closure($this->query);
}
@@ -298,7 +314,7 @@ class BelongsToMany extends Relation
// 查询关联数据
$data = $this->eagerlyManyToMany([
['pivot.' . $localKey, 'in', $range],
- ], $relation, $subRelation);
+ ], $relation, $subRelation, $closure);
// 关联属性名
$attr = Loader::parseName($relation);
@@ -332,7 +348,7 @@ class BelongsToMany extends Relation
// 查询管理数据
$data = $this->eagerlyManyToMany([
['pivot.' . $this->localKey, '=', $pk],
- ], $relation, $subRelation);
+ ], $relation, $subRelation, $closure);
// 关联数据封装
if (!isset($data[$pk])) {
@@ -350,21 +366,30 @@ class BelongsToMany extends Relation
* @param \Closure $closure 闭包
* @param string $aggregate 聚合查询方法
* @param string $field 字段
+ * @param string $name 统计字段别名
* @return integer
*/
- public function relationCount($result, $closure, $aggregate = 'count', $field = '*')
+ public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '')
{
- $pk = $result->getPk();
- $count = 0;
+ $pk = $result->getPk();
- if (isset($result->$pk)) {
- $pk = $result->$pk;
- $count = $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
- ['pivot.' . $this->localKey, '=', $pk],
- ])->$aggregate($field);
+ if (!isset($result->$pk)) {
+ return 0;
+ }
+
+ $pk = $result->$pk;
+
+ if ($closure instanceof Closure) {
+ $return = $closure($this->query);
+
+ if ($return && is_string($return)) {
+ $name = $return;
+ }
}
- return $count;
+ return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
+ ['pivot.' . $this->localKey, '=', $pk],
+ ])->$aggregate($field);
}
/**
@@ -373,10 +398,19 @@ class BelongsToMany extends Relation
* @param \Closure $closure 闭包
* @param string $aggregate 聚合查询方法
* @param string $field 字段
- * @return string
+ * @param string $aggregateAlias 聚合字段别名
+ * @return array
*/
- public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*')
+ public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '')
{
+ if ($closure instanceof Closure) {
+ $return = $closure($this->query);
+
+ if ($return && is_string($return)) {
+ $aggregateAlias = $return;
+ }
+ }
+
return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
[
'pivot.' . $this->localKey, 'exp', $this->query->raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()),
@@ -387,14 +421,19 @@ class BelongsToMany extends Relation
/**
* 多对多 关联模型预查询
* @access protected
- * @param array $where 关联预查询条件
- * @param string $relation 关联名
- * @param string $subRelation 子关联
+ * @param array $where 关联预查询条件
+ * @param string $relation 关联名
+ * @param string $subRelation 子关联
+ * @param \Closure $closure 闭包
* @return array
*/
- protected function eagerlyManyToMany($where, $relation, $subRelation = '')
+ protected function eagerlyManyToMany($where, $relation, $subRelation = '', $closure = null)
{
// 预载入关联查询 支持嵌套预载入
+ if ($closure instanceof Closure) {
+ $closure($this->query);
+ }
+
$list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where)
->with($subRelation)
->select();
@@ -413,7 +452,7 @@ class BelongsToMany extends Relation
}
}
- $set->setRelation('pivot', $this->newPivot($pivot, true));
+ $set->setRelation($this->pivotDataName, $this->newPivot($pivot, true));
$data[$pivot[$this->localKey]][] = $set;
}
@@ -424,9 +463,9 @@ class BelongsToMany extends Relation
/**
* BELONGS TO MANY 关联查询
* @access protected
- * @param string $foreignKey 关联模型关联键
- * @param string $localKey 当前模型关联键
- * @param array $condition 关联查询条件
+ * @param string $foreignKey 关联模型关联键
+ * @param string $localKey 当前模型关联键
+ * @param array $condition 关联查询条件
* @return Query
*/
protected function belongsToManyQuery($foreignKey, $localKey, $condition = [])
@@ -503,8 +542,7 @@ class BelongsToMany extends Relation
} else {
// 保存关联表数据
$model = new $this->model;
- $model->save($data);
- $id = $model->getLastInsID();
+ $id = $model->insertGetId($data);
}
} elseif (is_numeric($data) || is_string($data)) {
// 根据关联表主键直接写入中间表
@@ -523,7 +561,10 @@ class BelongsToMany extends Relation
foreach ($ids as $id) {
$pivot[$this->foreignKey] = $id;
- $this->pivot->insert($pivot, true);
+ $this->pivot->replace()
+ ->exists(false)
+ ->data([])
+ ->save($pivot);
$result[] = $this->newPivot($pivot, true);
}
@@ -538,6 +579,29 @@ class BelongsToMany extends Relation
}
}
+ /**
+ * 判断是否存在关联数据
+ * @access public
+ * @param mixed $data 数据 可以使用关联模型对象 或者 关联对象的主键
+ * @return Pivot|false
+ * @throws Exception
+ */
+ public function attached($data)
+ {
+ if ($data instanceof Model) {
+ $id = $data->getKey();
+ } else {
+ $id = $data;
+ }
+
+ $pivot = $this->pivot
+ ->where($this->localKey, $this->parent->getKey())
+ ->where($this->foreignKey, $id)
+ ->find();
+
+ return $pivot ?: false;
+ }
+
/**
* 解除关联的一个中间表数据
* @access public
diff --git a/thinkphp/library/think/model/relation/HasMany.php b/thinkphp/library/think/model/relation/HasMany.php
index 2038a960638b3fc4234db05605d2009c2b7bf341..e4df5c4b4e5cfd1dd2e27ab494fd2a34c5834ef5 100644
--- a/thinkphp/library/think/model/relation/HasMany.php
+++ b/thinkphp/library/think/model/relation/HasMany.php
@@ -11,6 +11,7 @@
namespace think\model\relation;
+use Closure;
use think\db\Query;
use think\Loader;
use think\Model;
@@ -48,7 +49,7 @@ class HasMany extends Relation
*/
public function getRelation($subRelation = '', $closure = null)
{
- if ($closure) {
+ if ($closure instanceof Closure) {
$closure($this->query);
}
@@ -152,22 +153,27 @@ class HasMany extends Relation
* @param \Closure $closure 闭包
* @param string $aggregate 聚合查询方法
* @param string $field 字段
+ * @param string $name 统计字段别名
* @return integer
*/
- public function relationCount($result, $closure, $aggregate = 'count', $field = '*')
+ public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '')
{
$localKey = $this->localKey;
- $count = 0;
- if (isset($result->$localKey)) {
- if ($closure) {
- $closure($this->query);
- }
+ if (!isset($result->$localKey)) {
+ return 0;
+ }
- $count = $this->query->where($this->foreignKey, '=', $result->$localKey)->$aggregate($field);
+ if ($closure instanceof Closure) {
+ $return = $closure($this->query);
+ if ($return && is_string($return)) {
+ $name = $return;
+ }
}
- return $count;
+ return $this->query
+ ->where($this->foreignKey, '=', $result->$localKey)
+ ->$aggregate($field);
}
/**
@@ -176,16 +182,21 @@ class HasMany extends Relation
* @param \Closure $closure 闭包
* @param string $aggregate 聚合查询方法
* @param string $field 字段
+ * @param string $aggregateAlias 聚合字段别名
* @return string
*/
- public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*')
+ public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '')
{
- if ($closure) {
- $closure($this->query);
+ if ($closure instanceof Closure) {
+ $return = $closure($this->query);
+
+ if ($return && is_string($return)) {
+ $aggregateAlias = $return;
+ }
}
- return $this->query
- ->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->parent->getPk())
+ return $this->query->alias($aggregate . '_table')
+ ->whereExp($aggregate . '_table.' . $this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey)
->fetchSql()
->$aggregate($field);
}
@@ -206,7 +217,7 @@ class HasMany extends Relation
$this->query->removeWhereField($this->foreignKey);
// 预载入关联查询 支持嵌套预载入
- if ($closure) {
+ if ($closure instanceof Closure) {
$closure($this->query);
}
@@ -230,6 +241,18 @@ class HasMany extends Relation
* @return Model|false
*/
public function save($data, $replace = true)
+ {
+ $model = $this->make();
+
+ return $model->replace($replace)->save($data) ? $model : false;
+ }
+
+ /**
+ * 创建关联对象实例
+ * @param array $data
+ * @return Model
+ */
+ public function make($data = [])
{
if ($data instanceof Model) {
$data = $data->getData();
@@ -238,19 +261,17 @@ class HasMany extends Relation
// 保存关联表数据
$data[$this->foreignKey] = $this->parent->{$this->localKey};
- $model = new $this->model;
-
- return $model->replace($replace)->save($data) ? $model : false;
+ return new $this->model($data);
}
/**
* 批量保存当前关联数据对象
* @access public
- * @param array $dataSet 数据集
+ * @param array|\think\Collection $dataSet 数据集
* @param boolean $replace 是否自动识别更新和写入
* @return array|false
*/
- public function saveAll(array $dataSet, $replace = true)
+ public function saveAll($dataSet, $replace = true)
{
$result = [];
@@ -272,14 +293,18 @@ class HasMany extends Relation
*/
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
{
- $table = $this->query->getTable();
- $model = basename(str_replace('\\', '/', get_class($this->parent)));
- $relation = basename(str_replace('\\', '/', $this->model));
+ $table = $this->query->getTable();
+ $model = basename(str_replace('\\', '/', get_class($this->parent)));
+ $relation = basename(str_replace('\\', '/', $this->model));
+ $softDelete = $this->query->getOptions('soft_delete');
return $this->parent->db()
->alias($model)
->field($model . '.*')
->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType)
+ ->when($softDelete, function ($query) use ($softDelete, $relation) {
+ $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+ })
->group($relation . '.' . $this->foreignKey)
->having('count(' . $id . ')' . $operator . $count);
}
@@ -301,13 +326,17 @@ class HasMany extends Relation
$this->getQueryWhere($where, $relation);
}
- $fields = $this->getRelationQueryFields($fields, $model);
+ $fields = $this->getRelationQueryFields($fields, $model);
+ $softDelete = $this->query->getOptions('soft_delete');
return $this->parent->db()
->alias($model)
->group($model . '.' . $this->localKey)
->field($fields)
->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey)
+ ->when($softDelete, function ($query) use ($softDelete, $relation) {
+ $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+ })
->where($where);
}
diff --git a/thinkphp/library/think/model/relation/HasManyThrough.php b/thinkphp/library/think/model/relation/HasManyThrough.php
index 411f3fccf36598b20f13c901ba60eef1e603ac2f..be0b0cd9ad1dac7e0b1f6bae0ebdc72b884b580d 100644
--- a/thinkphp/library/think/model/relation/HasManyThrough.php
+++ b/thinkphp/library/think/model/relation/HasManyThrough.php
@@ -11,8 +11,8 @@
namespace think\model\relation;
+use Closure;
use think\db\Query;
-use think\Exception;
use think\Loader;
use think\Model;
use think\model\Relation;
@@ -24,6 +24,12 @@ class HasManyThrough extends Relation
// 中间表模型
protected $through;
+ /**
+ * 中间主键
+ * @var string
+ */
+ protected $throughPk;
+
/**
* 架构函数
* @access public
@@ -38,9 +44,10 @@ class HasManyThrough extends Relation
{
$this->parent = $parent;
$this->model = $model;
- $this->through = $through;
+ $this->through = (new $through)->db();
$this->foreignKey = $foreignKey;
$this->throughKey = $throughKey;
+ $this->throughPk = $this->through->getPk();
$this->localKey = $localKey;
$this->query = (new $model)->db();
}
@@ -54,7 +61,7 @@ class HasManyThrough extends Relation
*/
public function getRelation($subRelation = '', $closure = null)
{
- if ($closure) {
+ if ($closure instanceof Closure) {
$closure($this->query);
}
@@ -74,7 +81,28 @@ class HasManyThrough extends Relation
*/
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
{
- return $this->parent;
+ $model = Loader::parseName(basename(str_replace('\\', '/', get_class($this->parent))));
+ $throughTable = $this->through->getTable();
+ $pk = $this->throughPk;
+ $throughKey = $this->throughKey;
+ $relation = (new $this->model)->db();
+ $relationTable = $relation->getTable();
+ $softDelete = $this->query->getOptions('soft_delete');
+
+ if ('*' != $id) {
+ $id = $relationTable . '.' . $relation->getPk();
+ }
+
+ return $this->parent->db()
+ ->alias($model)
+ ->field($model . '.*')
+ ->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey)
+ ->join($relationTable, $relationTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk)
+ ->when($softDelete, function ($query) use ($softDelete, $relationTable) {
+ $query->where($relationTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+ })
+ ->group($relationTable . '.' . $this->throughKey)
+ ->having('count(' . $id . ')' . $operator . $count);
}
/**
@@ -86,44 +114,225 @@ class HasManyThrough extends Relation
*/
public function hasWhere($where = [], $fields = null)
{
- throw new Exception('relation not support: hasWhere');
+ $model = Loader::parseName(basename(str_replace('\\', '/', get_class($this->parent))));
+ $throughTable = $this->through->getTable();
+ $pk = $this->throughPk;
+ $throughKey = $this->throughKey;
+ $modelTable = (new $this->model)->db()->getTable();
+
+ if (is_array($where)) {
+ $this->getQueryWhere($where, $modelTable);
+ }
+
+ $fields = $this->getRelationQueryFields($fields, $model);
+ $softDelete = $this->query->getOptions('soft_delete');
+
+ return $this->parent->db()
+ ->alias($model)
+ ->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey)
+ ->join($modelTable, $modelTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk)
+ ->when($softDelete, function ($query) use ($softDelete, $modelTable) {
+ $query->where($modelTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+ })
+ ->group($modelTable . '.' . $this->throughKey)
+ ->where($where)
+ ->field($fields);
}
/**
- * 预载入关联查询
- * @access public
- * @param array $resultSet 数据集
- * @param string $relation 当前关联名
- * @param string $subRelation 子关联名
- * @param \Closure $closure 闭包
+ * 预载入关联查询(数据集)
+ * @access protected
+ * @param array $resultSet 数据集
+ * @param string $relation 当前关联名
+ * @param mixed $subRelation 子关联名
+ * @param Closure $closure 闭包
* @return void
*/
- public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
- {}
+ public function eagerlyResultSet(array &$resultSet, $relation, $subRelation = '', $closure = null)
+ {
+ $localKey = $this->localKey;
+ $foreignKey = $this->foreignKey;
+
+ $range = [];
+ foreach ($resultSet as $result) {
+ // 获取关联外键列表
+ if (isset($result->$localKey)) {
+ $range[] = $result->$localKey;
+ }
+ }
+
+ if (!empty($range)) {
+ $this->query->removeWhereField($foreignKey);
+
+ $data = $this->eagerlyWhere([
+ [$this->foreignKey, 'in', $range],
+ ], $foreignKey, $relation, $subRelation, $closure);
+
+ // 关联属性名
+ $attr = Loader::parseName($relation);
+
+ // 关联数据封装
+ foreach ($resultSet as $result) {
+ $pk = $result->$localKey;
+ if (!isset($data[$pk])) {
+ $data[$pk] = [];
+ }
+
+ foreach ($data[$pk] as &$relationModel) {
+ $relationModel->setParent(clone $result);
+ }
+
+ // 设置关联属性
+ $result->setRelation($attr, $this->resultSetBuild($data[$pk]));
+ }
+ }
+ }
/**
- * 预载入关联查询 返回模型对象
- * @access public
- * @param Model $result 数据对象
- * @param string $relation 当前关联名
- * @param string $subRelation 子关联名
- * @param \Closure $closure 闭包
+ * 预载入关联查询(数据)
+ * @access protected
+ * @param Model $result 数据对象
+ * @param string $relation 当前关联名
+ * @param mixed $subRelation 子关联名
+ * @param Closure $closure 闭包
* @return void
*/
- public function eagerlyResult(&$result, $relation, $subRelation, $closure)
- {}
+ public function eagerlyResult($result, $relation, $subRelation = '', $closure = null)
+ {
+ $localKey = $this->localKey;
+ $foreignKey = $this->foreignKey;
+ $pk = $result->$localKey;
+
+ $this->query->removeWhereField($foreignKey);
+
+ $data = $this->eagerlyWhere([
+ [$foreignKey, '=', $pk],
+ ], $foreignKey, $relation, $subRelation, $closure);
+
+ // 关联数据封装
+ if (!isset($data[$pk])) {
+ $data[$pk] = [];
+ }
+
+ foreach ($data[$pk] as &$relationModel) {
+ $relationModel->setParent(clone $result);
+ }
+
+ $result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$pk]));
+ }
+
+ /**
+ * 关联模型预查询
+ * @access public
+ * @param array $where 关联预查询条件
+ * @param string $key 关联键名
+ * @param string $relation 关联名
+ * @param mixed $subRelation 子关联
+ * @param Closure $closure
+ * @return array
+ */
+ protected function eagerlyWhere(array $where, $key, $relation, $subRelation = '', $closure = null)
+ {
+ // 预载入关联查询 支持嵌套预载入
+ $throughList = $this->through->where($where)->select();
+ $keys = $throughList->column($this->throughPk, $this->throughPk);
+
+ if ($closure instanceof Closure) {
+ $closure($this->query);
+ }
+
+ $list = $this->query->where($this->throughKey, 'in', $keys)->select();
+
+ // 组装模型数据
+ $data = [];
+ $keys = $throughList->column($this->foreignKey, $this->throughPk);
+
+ foreach ($list as $set) {
+ $data[$keys[$set->{$this->throughKey}]][] = $set;
+ }
+
+ return $data;
+ }
/**
* 关联统计
* @access public
- * @param Model $result 数据对象
- * @param \Closure $closure 闭包
- * @param string $aggregate 聚合查询方法
- * @param string $field 字段
+ * @param Model $result 数据对象
+ * @param Closure $closure 闭包
+ * @param string $aggregate 聚合查询方法
+ * @param string $field 字段
+ * @param string $name 统计字段别名
* @return integer
*/
- public function relationCount($result, $closure, $aggregate = 'count', $field = '*')
- {}
+ public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = null)
+ {
+ $localKey = $this->localKey;
+
+ if (!isset($result->$localKey)) {
+ return 0;
+ }
+
+ if ($closure instanceof Closure) {
+ $return = $closure($this->query);
+ if ($return && is_string($return)) {
+ $name = $return;
+ }
+ }
+
+ $alias = Loader::parseName(basename(str_replace('\\', '/', $this->model)));
+ $throughTable = $this->through->getTable();
+ $pk = $this->throughPk;
+ $throughKey = $this->throughKey;
+ $modelTable = $this->parent->getTable();
+
+ if (false === strpos($field, '.')) {
+ $field = $alias . '.' . $field;
+ }
+
+ return $this->query
+ ->alias($alias)
+ ->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
+ ->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
+ ->where($throughTable . '.' . $this->foreignKey, $result->$localKey)
+ ->$aggregate($field);
+ }
+
+ /**
+ * 创建关联统计子查询
+ * @access public
+ * @param Closure $closure 闭包
+ * @param string $aggregate 聚合查询方法
+ * @param string $field 字段
+ * @param string $name 统计字段别名
+ * @return string
+ */
+ public function getRelationCountQuery($closure = null, $aggregate = 'count', $field = '*', &$name = null)
+ {
+ if ($closure instanceof Closure) {
+ $return = $closure($this->query);
+ if ($return && is_string($return)) {
+ $name = $return;
+ }
+ }
+
+ $alias = Loader::parseName(basename(str_replace('\\', '/', $this->model)));
+ $throughTable = $this->through->getTable();
+ $pk = $this->throughPk;
+ $throughKey = $this->throughKey;
+ $modelTable = $this->parent->getTable();
+
+ if (false === strpos($field, '.')) {
+ $field = $alias . '.' . $field;
+ }
+
+ return $this->query
+ ->alias($alias)
+ ->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
+ ->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
+ ->whereExp($throughTable . '.' . $this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey)
+ ->fetchSql()
+ ->$aggregate($field);
+ }
/**
* 执行基础查询(仅执行一次)
@@ -133,10 +342,9 @@ class HasManyThrough extends Relation
protected function baseQuery()
{
if (empty($this->baseQuery) && $this->parent->getData()) {
- $through = $this->through;
$alias = Loader::parseName(basename(str_replace('\\', '/', $this->model)));
- $throughTable = $through::getTable();
- $pk = (new $through)->getPk();
+ $throughTable = $this->through->getTable();
+ $pk = $this->throughPk;
$throughKey = $this->throughKey;
$modelTable = $this->parent->getTable();
$fields = $this->getQueryFields($alias);
diff --git a/thinkphp/library/think/model/relation/HasOne.php b/thinkphp/library/think/model/relation/HasOne.php
index 3ce5fea03b816c6ab296d682c25715ef1a90fedd..fe09443c999176f8fc629a7a1d25bb8a9f698679 100644
--- a/thinkphp/library/think/model/relation/HasOne.php
+++ b/thinkphp/library/think/model/relation/HasOne.php
@@ -11,6 +11,7 @@
namespace think\model\relation;
+use Closure;
use think\db\Query;
use think\Loader;
use think\Model;
@@ -50,7 +51,7 @@ class HasOne extends OneToOne
{
$localKey = $this->localKey;
- if ($closure) {
+ if ($closure instanceof Closure) {
$closure($this->query);
}
@@ -68,6 +69,61 @@ class HasOne extends OneToOne
return $relationModel;
}
+ /**
+ * 创建关联统计子查询
+ * @access public
+ * @param \Closure $closure 闭包
+ * @param string $aggregate 聚合查询方法
+ * @param string $field 字段
+ * @param string $aggregateAlias 聚合字段别名
+ * @return string
+ */
+ public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '')
+ {
+ if ($closure instanceof Closure) {
+ $return = $closure($this->query);
+
+ if ($return && is_string($return)) {
+ $aggregateAlias = $return;
+ }
+ }
+
+ return $this->query
+ ->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey)
+ ->fetchSql()
+ ->$aggregate($field);
+ }
+
+ /**
+ * 关联统计
+ * @access public
+ * @param Model $result 数据对象
+ * @param \Closure $closure 闭包
+ * @param string $aggregate 聚合查询方法
+ * @param string $field 字段
+ * @param string $name 统计字段别名
+ * @return integer
+ */
+ public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '')
+ {
+ $localKey = $this->localKey;
+
+ if (!isset($result->$localKey)) {
+ return 0;
+ }
+
+ if ($closure instanceof Closure) {
+ $return = $closure($this->query);
+ if ($return && is_string($return)) {
+ $name = $return;
+ }
+ }
+
+ return $this->query
+ ->where($this->foreignKey, '=', $result->$localKey)
+ ->$aggregate($field);
+ }
+
/**
* 根据关联条件查询当前模型
* @access public
@@ -84,13 +140,17 @@ class HasOne extends OneToOne
$relation = basename(str_replace('\\', '/', $this->model));
$localKey = $this->localKey;
$foreignKey = $this->foreignKey;
+ $softDelete = $this->query->getOptions('soft_delete');
return $this->parent->db()
->alias($model)
- ->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey) {
+ ->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey, $softDelete) {
$query->table([$table => $relation])
->field($relation . '.' . $foreignKey)
- ->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey);
+ ->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey)
+ ->when($softDelete, function ($query) use ($softDelete, $relation) {
+ $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+ });
});
}
@@ -111,12 +171,16 @@ class HasOne extends OneToOne
$this->getQueryWhere($where, $relation);
}
- $fields = $this->getRelationQueryFields($fields, $model);
+ $fields = $this->getRelationQueryFields($fields, $model);
+ $softDelete = $this->query->getOptions('soft_delete');
return $this->parent->db()
->alias($model)
->field($fields)
->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $this->joinType)
+ ->when($softDelete, function ($query) use ($softDelete, $relation) {
+ $query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
+ })
->where($where);
}
diff --git a/thinkphp/library/think/model/relation/MorphMany.php b/thinkphp/library/think/model/relation/MorphMany.php
index 3e913ccd06f6fb69d01f46a6c7be30e73ac9d528..d2af66e9b4d578bc7e34eb0f8d6ce9862e27de8e 100644
--- a/thinkphp/library/think/model/relation/MorphMany.php
+++ b/thinkphp/library/think/model/relation/MorphMany.php
@@ -11,6 +11,7 @@
namespace think\model\relation;
+use Closure;
use think\db\Query;
use think\Exception;
use think\Loader;
@@ -53,7 +54,7 @@ class MorphMany extends Relation
*/
public function getRelation($subRelation = '', $closure = null)
{
- if ($closure) {
+ if ($closure instanceof Closure) {
$closure($this->query);
}
@@ -186,27 +187,31 @@ class MorphMany extends Relation
* @param \Closure $closure 闭包
* @param string $aggregate 聚合查询方法
* @param string $field 字段
+ * @param string $name 统计字段别名
* @return integer
*/
- public function relationCount($result, $closure, $aggregate = 'count', $field = '*')
+ public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '')
{
- $pk = $result->getPk();
- $count = 0;
+ $pk = $result->getPk();
- if (isset($result->$pk)) {
- if ($closure) {
- $closur($this->query);
- }
+ if (!isset($result->$pk)) {
+ return 0;
+ }
+
+ if ($closure instanceof Closure) {
+ $return = $closure($this->query);
- $count = $this->query
- ->where([
- [$this->morphKey, '=', $result->$pk],
- [$this->morphType, '=', $this->type],
- ])
- ->$aggregate($field);
+ if ($return && is_string($return)) {
+ $name = $return;
+ }
}
- return $count;
+ return $this->query
+ ->where([
+ [$this->morphKey, '=', $result->$pk],
+ [$this->morphType, '=', $this->type],
+ ])
+ ->$aggregate($field);
}
/**
@@ -215,12 +220,17 @@ class MorphMany extends Relation
* @param \Closure $closure 闭包
* @param string $aggregate 聚合查询方法
* @param string $field 字段
+ * @param string $aggregateAlias 聚合字段别名
* @return string
*/
- public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*')
+ public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '')
{
- if ($closure) {
- $closure($this->query);
+ if ($closure instanceof Closure) {
+ $return = $closure($this->query);
+
+ if ($return && is_string($return)) {
+ $aggregateAlias = $return;
+ }
}
return $this->query
@@ -244,7 +254,7 @@ class MorphMany extends Relation
// 预载入关联查询 支持嵌套预载入
$this->query->removeOption('where');
- if ($closure) {
+ if ($closure instanceof Closure) {
$closure($this->query);
}
@@ -263,10 +273,22 @@ class MorphMany extends Relation
/**
* 保存(新增)当前关联数据对象
* @access public
- * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
+ * @param mixed $data 数据
* @return Model|false
*/
public function save($data)
+ {
+ $model = $this->make();
+
+ return $model->save($data) ? $model : false;
+ }
+
+ /**
+ * 创建关联对象实例
+ * @param array $data
+ * @return Model
+ */
+ public function make($data = [])
{
if ($data instanceof Model) {
$data = $data->getData();
@@ -275,12 +297,10 @@ class MorphMany extends Relation
// 保存关联表数据
$pk = $this->parent->getPk();
- $model = new $this->model;
-
$data[$this->morphKey] = $this->parent->$pk;
$data[$this->morphType] = $this->type;
- return $model->save($data) ? $model : false;
+ return new $this->model($data);
}
/**
diff --git a/thinkphp/library/think/model/relation/MorphOne.php b/thinkphp/library/think/model/relation/MorphOne.php
index ede680c63a3fb5d139cc28b89a3e1af6cf7dc117..6bc205c5bfcb428a23c925679749abd01d5fed53 100644
--- a/thinkphp/library/think/model/relation/MorphOne.php
+++ b/thinkphp/library/think/model/relation/MorphOne.php
@@ -11,6 +11,7 @@
namespace think\model\relation;
+use Closure;
use think\db\Query;
use think\Exception;
use think\Loader;
@@ -53,7 +54,7 @@ class MorphOne extends Relation
*/
public function getRelation($subRelation = '', $closure = null)
{
- if ($closure) {
+ if ($closure instanceof Closure) {
$closure($this->query);
}
@@ -186,11 +187,11 @@ class MorphOne extends Relation
protected function eagerlyMorphToOne($where, $relation, $subRelation = '', $closure = null)
{
// 预载入关联查询 支持嵌套预载入
- if ($closure) {
+ if ($closure instanceof Closure) {
$closure($this->query);
}
- $list = $this->query->where($where)->with($subRelation)->find();
+ $list = $this->query->where($where)->with($subRelation)->select();
$morphKey = $this->morphKey;
// 组装模型数据
@@ -206,22 +207,33 @@ class MorphOne extends Relation
/**
* 保存(新增)当前关联数据对象
* @access public
- * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
+ * @param mixed $data 数据
* @return Model|false
*/
public function save($data)
+ {
+ $model = $this->make();
+ return $model->save($data) ? $model : false;
+ }
+
+ /**
+ * 创建关联对象实例
+ * @param array $data
+ * @return Model
+ */
+ public function make($data = [])
{
if ($data instanceof Model) {
$data = $data->getData();
}
+
// 保存关联表数据
$pk = $this->parent->getPk();
- $model = new $this->model;
-
$data[$this->morphKey] = $this->parent->$pk;
$data[$this->morphType] = $this->type;
- return $model->save($data) ? $model : false;
+
+ return new $this->model($data);
}
/**
diff --git a/thinkphp/library/think/model/relation/MorphTo.php b/thinkphp/library/think/model/relation/MorphTo.php
index bb7c4d0b3a83ec9489c28871cf5f14971f8992df..0786c2fe09345ebdceb390f1005ce2cc313391bb 100644
--- a/thinkphp/library/think/model/relation/MorphTo.php
+++ b/thinkphp/library/think/model/relation/MorphTo.php
@@ -11,6 +11,7 @@
namespace think\model\relation;
+use Closure;
use think\Exception;
use think\Loader;
use think\Model;
@@ -185,8 +186,16 @@ class MorphTo extends Relation
foreach ($range as $key => $val) {
// 多态类型映射
$model = $this->parseModel($key);
- $obj = new $model;
+ $obj = (new $model)->db();
$pk = $obj->getPk();
+ // 预载入关联查询 支持嵌套预载入
+ if ($closure instanceof \Closure) {
+ $closure($obj);
+
+ if ($field = $obj->getOptions('with_field')) {
+ $obj->field($field)->removeOption('with_field');
+ }
+ }
$list = $obj->all($val, $subRelation);
$data = [];
@@ -238,9 +247,10 @@ class MorphTo extends Relation
* @param \Closure $closure 闭包
* @param string $aggregate 聚合查询方法
* @param string $field 字段
+ * @param string $name 统计字段别名
* @return integer
*/
- public function relationCount($result, $closure, $aggregate = 'count', $field = '*')
+ public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '')
{}
/**
diff --git a/thinkphp/library/think/model/relation/OneToOne.php b/thinkphp/library/think/model/relation/OneToOne.php
index ac5d4e4c43a5668c41d2fcd0907241c654be1a88..5e22b80023f3326948ce979af35b2f683c0c2093 100644
--- a/thinkphp/library/think/model/relation/OneToOne.php
+++ b/thinkphp/library/think/model/relation/OneToOne.php
@@ -11,6 +11,7 @@
namespace think\model\relation;
+use Closure;
use think\db\Query;
use think\Exception;
use think\Loader;
@@ -50,12 +51,13 @@ abstract class OneToOne extends Relation
* @access public
* @param Query $query 查询对象
* @param string $relation 关联名
- * @param string $subRelation 子关联
+ * @param mixed $field 关联字段
+ * @param string $joinType JOIN方式
* @param \Closure $closure 闭包条件
* @param bool $first
* @return void
*/
- public function eagerly(Query $query, $relation, $subRelation, $closure, $first)
+ public function eagerly(Query $query, $relation, $field, $joinType, $closure, $first)
{
$name = Loader::parseName(basename(str_replace('\\', '/', get_class($this->parent))));
@@ -64,27 +66,29 @@ abstract class OneToOne extends Relation
$query->table([$table => $name]);
if ($query->getOptions('field')) {
- $field = $query->getOptions('field');
+ $masterField = $query->getOptions('field');
$query->removeOption('field');
} else {
- $field = true;
+ $masterField = true;
}
- $query->field($field, false, $table, $name);
+ $query->field($masterField, false, $table, $name);
}
// 预载入封装
$joinTable = $this->query->getTable();
$joinAlias = $relation;
+ $joinType = $joinType ?: $this->joinType;
+
$query->via($joinAlias);
if ($this instanceof BelongsTo) {
- $query->join([$joinTable => $joinAlias], $name . '.' . $this->foreignKey . '=' . $joinAlias . '.' . $this->localKey, $this->joinType);
+ $joinOn = $name . '.' . $this->foreignKey . '=' . $joinAlias . '.' . $this->localKey;
} else {
- $query->join([$joinTable => $joinAlias], $name . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey, $this->joinType);
+ $joinOn = $name . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey;
}
- if ($closure) {
+ if ($closure instanceof Closure) {
// 执行闭包查询
$closure($query);
// 使用withField指定获取关联的字段,如
@@ -92,16 +96,11 @@ abstract class OneToOne extends Relation
if ($query->getOptions('with_field')) {
$field = $query->getOptions('with_field');
$query->removeOption('with_field');
- } else {
- $field = true;
}
- } elseif (isset($this->option['field'])) {
- $field = $this->option['field'];
- } else {
- $field = true;
}
- $query->field($field, false, $joinTable, $joinAlias, $relation . '__');
+ $query->join([$joinTable => $joinAlias], $joinOn, $joinType)
+ ->field($field, false, $joinTable, $joinAlias, $relation . '__');
}
/**
@@ -133,18 +132,19 @@ abstract class OneToOne extends Relation
* @param string $relation 当前关联名
* @param string $subRelation 子关联名
* @param \Closure $closure 闭包
+ * @param bool $join 是否为JOIN方式
* @return void
*/
- public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
+ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $join = false)
{
- if (1 == $this->eagerlyType) {
- // IN查询
- $this->eagerlySet($resultSet, $relation, $subRelation, $closure);
- } else {
- // 模型关联组装
+ if ($join || 0 == $this->eagerlyType) {
+ // 模型JOIN关联组装
foreach ($resultSet as $result) {
$this->match($this->model, $relation, $result);
}
+ } else {
+ // IN查询
+ $this->eagerlySet($resultSet, $relation, $subRelation, $closure);
}
}
@@ -155,16 +155,17 @@ abstract class OneToOne extends Relation
* @param string $relation 当前关联名
* @param string $subRelation 子关联名
* @param \Closure $closure 闭包
+ * @param bool $join 是否为JOIN方式
* @return void
*/
- public function eagerlyResult(&$result, $relation, $subRelation, $closure)
+ public function eagerlyResult(&$result, $relation, $subRelation, $closure, $join = false)
{
- if (1 == $this->eagerlyType) {
+ if (0 == $this->eagerlyType || $join) {
+ // 模型JOIN关联组装
+ $this->match($this->model, $relation, $result);
+ } else {
// IN查询
$this->eagerlyOne($result, $relation, $subRelation, $closure);
- } else {
- // 模型关联组装
- $this->match($this->model, $relation, $result);
}
}
@@ -236,20 +237,6 @@ abstract class OneToOne extends Relation
return $this->bindAttr;
}
- /**
- * 关联统计
- * @access public
- * @param Model $result 数据对象
- * @param \Closure $closure 闭包
- * @param string $aggregate 聚合查询方法
- * @param string $field 字段
- * @return integer
- */
- public function relationCount($result, $closure, $aggregate = 'count', $field = '*')
- {
- throw new Exception('relation not support: ' . $aggregate);
- }
-
/**
* 一对一 关联模型预查询拼装
* @access public
@@ -272,9 +259,15 @@ abstract class OneToOne extends Relation
}
if (isset($list[$relation])) {
- $relationModel = new $model($list[$relation]);
- $relationModel->setParent(clone $result);
- $relationModel->isUpdate(true);
+ $array = array_unique($list[$relation]);
+
+ if (count($array) == 1 && null === current($array)) {
+ $relationModel = null;
+ } else {
+ $relationModel = new $model($list[$relation]);
+ $relationModel->setParent(clone $result);
+ $relationModel->isUpdate(true);
+ }
if (!empty($this->bindAttr)) {
$this->bindAttr($relationModel, $result, $this->bindAttr);
@@ -289,20 +282,22 @@ abstract class OneToOne extends Relation
/**
* 绑定关联属性到父模型
* @access protected
- * @param Model $model 关联模型对象
- * @param Model $result 父模型对象
+ * @param Model $result 关联模型对象
+ * @param Model $model 父模型对象
* @return void
* @throws Exception
*/
protected function bindAttr($model, &$result)
{
foreach ($this->bindAttr as $key => $attr) {
- $key = is_numeric($key) ? $attr : $key;
- if (isset($result->$key)) {
+ $key = is_numeric($key) ? $attr : $key;
+ $value = $result->getOrigin($key);
+
+ if (!is_null($value)) {
throw new Exception('bind attr has exists:' . $key);
- } else {
- $result->setAttr($key, $model ? $model->$attr : null);
}
+
+ $result->setAttr($key, $model ? $model->getAttr($attr) : null);
}
}
@@ -319,7 +314,7 @@ abstract class OneToOne extends Relation
protected function eagerlyWhere($where, $key, $relation, $subRelation = '', $closure = null)
{
// 预载入关联查询 支持嵌套预载入
- if ($closure) {
+ if ($closure instanceof Closure) {
$closure($this->query);
if ($field = $this->query->getOptions('with_field')) {
diff --git a/thinkphp/library/think/response/Download.php b/thinkphp/library/think/response/Download.php
new file mode 100644
index 0000000000000000000000000000000000000000..5595f9ab4a2e0806a6f3032718ba60e59f01eaea
--- /dev/null
+++ b/thinkphp/library/think/response/Download.php
@@ -0,0 +1,148 @@
+
+// +----------------------------------------------------------------------
+
+namespace think\response;
+
+use think\Exception;
+use think\Response;
+
+class Download extends Response
+{
+ protected $expire = 360;
+ protected $name;
+ protected $mimeType;
+ protected $isContent = false;
+ protected $openinBrowser = false;
+ /**
+ * 处理数据
+ * @access protected
+ * @param mixed $data 要处理的数据
+ * @return mixed
+ * @throws \Exception
+ */
+ protected function output($data)
+ {
+ if (!$this->isContent && !is_file($data)) {
+ throw new Exception('file not exists:' . $data);
+ }
+
+ ob_end_clean();
+
+ if (!empty($this->name)) {
+ $name = $this->name;
+ } else {
+ $name = !$this->isContent ? pathinfo($data, PATHINFO_BASENAME) : '';
+ }
+
+ if ($this->isContent) {
+ $mimeType = $this->mimeType;
+ $size = strlen($data);
+ } else {
+ $mimeType = $this->getMimeType($data);
+ $size = filesize($data);
+ }
+
+ $this->header['Pragma'] = 'public';
+ $this->header['Content-Type'] = $mimeType ?: 'application/octet-stream';
+ $this->header['Cache-control'] = 'max-age=' . $this->expire;
+ $this->header['Content-Disposition'] = $this->openinBrowser ? 'inline' : 'attachment; filename="' . $name . '"';
+ $this->header['Content-Length'] = $size;
+ $this->header['Content-Transfer-Encoding'] = 'binary';
+ $this->header['Expires'] = gmdate("D, d M Y H:i:s", time() + $this->expire) . ' GMT';
+
+ $this->lastModified(gmdate('D, d M Y H:i:s', time()) . ' GMT');
+
+ $data = $this->isContent ? $data : file_get_contents($data);
+ return $data;
+ }
+
+ /**
+ * 设置是否为内容 必须配合mimeType方法使用
+ * @access public
+ * @param bool $content
+ * @return $this
+ */
+ public function isContent($content = true)
+ {
+ $this->isContent = $content;
+ return $this;
+ }
+
+ /**
+ * 设置有效期
+ * @access public
+ * @param integer $expire 有效期
+ * @return $this
+ */
+ public function expire($expire)
+ {
+ $this->expire = $expire;
+ return $this;
+ }
+
+ /**
+ * 设置文件类型
+ * @access public
+ * @param string $filename 文件名
+ * @return $this
+ */
+ public function mimeType($mimeType)
+ {
+ $this->mimeType = $mimeType;
+ return $this;
+ }
+
+ /**
+ * 获取文件类型信息
+ * @access public
+ * @param string $filename 文件名
+ * @return string
+ */
+ protected function getMimeType($filename)
+ {
+ if (!empty($this->mimeType)) {
+ return $this->mimeType;
+ }
+
+ $finfo = finfo_open(FILEINFO_MIME_TYPE);
+
+ return finfo_file($finfo, $filename);
+ }
+
+ /**
+ * 设置下载文件的显示名称
+ * @access public
+ * @param string $filename 文件名
+ * @param bool $extension 后缀自动识别
+ * @return $this
+ */
+ public function name($filename, $extension = true)
+ {
+ $this->name = $filename;
+
+ if ($extension && false === strpos($filename, '.')) {
+ $this->name .= '.' . pathinfo($this->data, PATHINFO_EXTENSION);
+ }
+
+ return $this;
+ }
+
+ /**
+ * 设置是否在浏览器中显示文件
+ * @access public
+ * @param bool $openinBrowser 是否在浏览器中显示文件
+ * @return $this
+ */
+ public function openinBrowser($openinBrowser) {
+ $this->openinBrowser = $openinBrowser;
+ return $this;
+ }
+}
diff --git a/thinkphp/library/think/response/Redirect.php b/thinkphp/library/think/response/Redirect.php
index 62fa83a7aafd42d880ee6302f60417342c13ce03..6b4f118ac0a27300fb12d7ecdba3db095614cbff 100644
--- a/thinkphp/library/think/response/Redirect.php
+++ b/thinkphp/library/think/response/Redirect.php
@@ -87,11 +87,12 @@ class Redirect extends Response
/**
* 记住当前url后跳转
* @access public
+ * @param string $url 指定记住的url
* @return $this
*/
- public function remember()
+ public function remember($url = null)
{
- $this->app['session']->set('redirect_url', $this->app['request']->url());
+ $this->app['session']->set('redirect_url', $url ?: $this->app['request']->url());
return $this;
}
@@ -99,15 +100,18 @@ class Redirect extends Response
/**
* 跳转到上次记住的url
* @access public
+ * @param string $url 闪存数据不存在时的跳转地址
* @return $this
*/
- public function restore()
+ public function restore($url = null)
{
$session = $this->app['session'];
if ($session->has('redirect_url')) {
$this->data = $session->get('redirect_url');
$session->delete('redirect_url');
+ } elseif ($url) {
+ $this->data = $url;
}
return $this;
diff --git a/thinkphp/library/think/response/View.php b/thinkphp/library/think/response/View.php
index c836ccb5d4cce0cee1f076a159db9bad707a8935..3d54c7352f91ffa8e76e9cc5d47a1f0cdb15957a 100644
--- a/thinkphp/library/think/response/View.php
+++ b/thinkphp/library/think/response/View.php
@@ -18,9 +18,16 @@ class View extends Response
// 输出参数
protected $options = [];
protected $vars = [];
+ protected $config = [];
protected $filter;
protected $contentType = 'text/html';
+ /**
+ * 是否内容渲染
+ * @var bool
+ */
+ protected $isContent = false;
+
/**
* 处理数据
* @access protected
@@ -32,7 +39,19 @@ class View extends Response
// 渲染模板输出
return $this->app['view']
->filter($this->filter)
- ->fetch($data, $this->vars);
+ ->fetch($data, $this->vars, $this->config, $this->isContent);
+ }
+
+ /**
+ * 设置是否为内容渲染
+ * @access public
+ * @param bool $content
+ * @return $this
+ */
+ public function isContent($content = true)
+ {
+ $this->isContent = $content;
+ return $this;
}
/**
@@ -68,6 +87,12 @@ class View extends Response
return $this;
}
+ public function config($config)
+ {
+ $this->config = $config;
+ return $this;
+ }
+
/**
* 视图内容过滤
* @access public
diff --git a/thinkphp/library/think/route/AliasRule.php b/thinkphp/library/think/route/AliasRule.php
index daf59fe13ebb9345759d4bf2a67624afeb72b6b5..393cb3107fea1d62dbfc93fb029423e50badd93b 100644
--- a/thinkphp/library/think/route/AliasRule.php
+++ b/thinkphp/library/think/route/AliasRule.php
@@ -75,6 +75,11 @@ class AliasRule extends Domain
$this->mergeGroupOptions();
}
+ if (isset($this->option['ext'])) {
+ // 路由ext参数 优先于系统配置的URL伪静态后缀参数
+ $bind = preg_replace('/\.(' . $request->ext() . ')$/i', '', $bind);
+ }
+
$this->parseBindAppendParam($this->route);
if (0 === strpos($this->route, '\\')) {
diff --git a/thinkphp/library/think/route/Dispatch.php b/thinkphp/library/think/route/Dispatch.php
index 4b187d1e5658ca44e3d2725bb19b27850a3477fd..7323c98db74c6cad9da4ddbf5326aeefc04841ad 100644
--- a/thinkphp/library/think/route/Dispatch.php
+++ b/thinkphp/library/think/route/Dispatch.php
@@ -11,6 +11,7 @@
namespace think\route;
+use think\App;
use think\Container;
use think\exception\ValidateException;
use think\Request;
@@ -180,9 +181,10 @@ abstract class Dispatch
$response = Response::create($data, $type);
} else {
- $data = ob_get_clean();
- $content = false === $data ? '' : $data;
- $status = false === $data ? 204 : 200;
+ $data = ob_get_clean();
+ $content = false === $data ? '' : $data;
+ $status = '' === $content && $this->request->isJson() ? 204 : 200;
+
$response = Response::create($content, '', $status);
}
@@ -273,7 +275,8 @@ abstract class Dispatch
$tag = null;
}
- $this->request->cache($key, $expire, $tag);
+ $cache = $this->request->cache($key, $expire, $tag);
+ $this->app->setResponseCache($cache);
}
/**
@@ -352,4 +355,12 @@ abstract class Dispatch
$this->app = Container::get('app');
$this->request = $this->app['request'];
}
+
+ public function __debugInfo()
+ {
+ $data = get_object_vars($this);
+ unset($data['app'], $data['request'], $data['rule']);
+
+ return $data;
+ }
}
diff --git a/thinkphp/library/think/route/Domain.php b/thinkphp/library/think/route/Domain.php
index 260ca500ad67c10b0bf1e3ab02c4e78fdc0a3ef0..923d9b42738e0eca00dbd0e1be520bc699bcd14a 100644
--- a/thinkphp/library/think/route/Domain.php
+++ b/thinkphp/library/think/route/Domain.php
@@ -13,6 +13,7 @@ namespace think\route;
use think\Container;
use think\Loader;
+use think\Request;
use think\Route;
use think\route\dispatch\Callback as CallbackDispatch;
use think\route\dispatch\Controller as ControllerDispatch;
@@ -20,8 +21,6 @@ use think\route\dispatch\Module as ModuleDispatch;
class Domain extends RuleGroup
{
- protected $bind;
-
/**
* 架构函数
* @access public
@@ -86,9 +85,7 @@ class Domain extends RuleGroup
*/
public function bind($bind)
{
- $this->bind = $bind;
$this->router->bind($bind, $this->domain);
-
return $this;
}
@@ -117,8 +114,9 @@ class Domain extends RuleGroup
*/
private function checkUrlBind($request, $url)
{
- if (!empty($this->bind)) {
- $bind = $this->bind;
+ $bind = $this->router->getBind($this->domain);
+
+ if (!empty($bind)) {
$this->parseBindAppendParam($bind);
// 记录绑定信息
@@ -163,12 +161,13 @@ class Domain extends RuleGroup
{
$array = explode('|', $url, 2);
$action = !empty($array[0]) ? $array[0] : $this->router->config('default_action');
+ $param = [];
if (!empty($array[1])) {
- $this->parseUrlParams($request, $array[1]);
+ $this->parseUrlParams($request, $array[1], $param);
}
- return new CallbackDispatch($request, $this, [$class, $action]);
+ return new CallbackDispatch($request, $this, [$class, $action], $param);
}
/**
@@ -184,12 +183,13 @@ class Domain extends RuleGroup
$array = explode('|', $url, 3);
$class = !empty($array[0]) ? $array[0] : $this->router->config('default_controller');
$method = !empty($array[1]) ? $array[1] : $this->router->config('default_action');
+ $param = [];
if (!empty($array[2])) {
- $this->parseUrlParams($request, $array[2]);
+ $this->parseUrlParams($request, $array[2], $param);
}
- return new CallbackDispatch($request, $this, [$namespace . '\\' . Loader::parseName($class, 1), $method]);
+ return new CallbackDispatch($request, $this, [$namespace . '\\' . Loader::parseName($class, 1), $method], $param);
}
/**
@@ -204,12 +204,13 @@ class Domain extends RuleGroup
{
$array = explode('|', $url, 2);
$action = !empty($array[0]) ? $array[0] : $this->router->config('default_action');
+ $param = [];
if (!empty($array[1])) {
- $this->parseUrlParams($request, $array[1]);
+ $this->parseUrlParams($request, $array[1], $param);
}
- return new ControllerDispatch($request, $this, $controller . '/' . $action);
+ return new ControllerDispatch($request, $this, $controller . '/' . $action, $param);
}
/**
@@ -224,12 +225,13 @@ class Domain extends RuleGroup
{
$array = explode('|', $url, 2);
$action = !empty($array[0]) ? $array[0] : $this->router->config('default_action');
+ $param = [];
if (!empty($array[1])) {
- $this->parseUrlParams($request, $array[1]);
+ $this->parseUrlParams($request, $array[1], $param);
}
- return new ModuleDispatch($request, $this, $controller . '/' . $action);
+ return new ModuleDispatch($request, $this, $controller . '/' . $action, $param);
}
}
diff --git a/thinkphp/library/think/route/Resource.php b/thinkphp/library/think/route/Resource.php
index 4e66d9fd866096792cf196795dc00dfa8b8aa054..ff13928244a279a495eaac22716ae46d41912558 100644
--- a/thinkphp/library/think/route/Resource.php
+++ b/thinkphp/library/think/route/Resource.php
@@ -53,20 +53,25 @@ class Resource extends RuleGroup
$this->domain = $this->parent->getDomain();
$this->parent->addRuleItem($this);
}
+
+ if ($router->isTest()) {
+ $this->buildResourceRule();
+ }
}
/**
* 生成资源路由规则
* @access protected
- * @param string $rule 路由规则
- * @param array $option 路由参数
* @return void
*/
- protected function buildResourceRule($rule, $option = [])
+ protected function buildResourceRule()
{
$origin = $this->router->getGroup();
$this->router->setGroup($this);
+ $rule = $this->resource;
+ $option = $this->option;
+
if (strpos($rule, '.')) {
// 注册嵌套资源路由
$array = explode('.', $rule);
diff --git a/thinkphp/library/think/route/Rule.php b/thinkphp/library/think/route/Rule.php
index 5cdf42c814c059956c2570869de4a84f0290d567..996305f74af1bda8cbd1be739f3b601e3815bb27 100644
--- a/thinkphp/library/think/route/Rule.php
+++ b/thinkphp/library/think/route/Rule.php
@@ -81,7 +81,7 @@ abstract class Rule
* 需要和分组合并的路由参数
* @var array
*/
- protected $mergeOptions = ['after', 'before', 'model', 'header', 'response', 'append', 'middleware'];
+ protected $mergeOptions = ['after', 'model', 'header', 'response', 'append', 'middleware'];
/**
* 是否需要后置操作
@@ -89,6 +89,12 @@ abstract class Rule
*/
protected $doAfter;
+ /**
+ * 是否锁定参数
+ * @var bool
+ */
+ protected $lockOption = false;
+
abstract public function check($request, $url, $completeMatch = false);
/**
@@ -636,17 +642,26 @@ abstract class Rule
protected function checkCrossDomain($request)
{
if (!empty($this->option['cross_domain'])) {
-
$header = [
- 'Access-Control-Allow-Origin' => '*',
- 'Access-Control-Allow-Methods' => 'GET, POST, PATCH, PUT, DELETE',
- 'Access-Control-Allow-Headers' => 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-Requested-With',
+ 'Access-Control-Allow-Credentials' => 'true',
+ 'Access-Control-Allow-Methods' => 'GET, POST, PATCH, PUT, DELETE',
+ 'Access-Control-Allow-Headers' => 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-Requested-With',
];
if (!empty($this->option['header'])) {
$header = array_merge($header, $this->option['header']);
}
+ if (!isset($header['Access-Control-Allow-Origin'])) {
+ $httpOrigin = $request->header('origin');
+
+ if ($httpOrigin && strpos(config('cookie.domain'), $httpOrigin)) {
+ $header['Access-Control-Allow-Origin'] = $httpOrigin;
+ } else {
+ $header['Access-Control-Allow-Origin'] = '*';
+ }
+ }
+
$this->option['header'] = $header;
if ($request->method(true) == 'OPTIONS') {
@@ -675,20 +690,23 @@ abstract class Rule
/**
* 合并分组参数
- * @access protected
- * @return void
+ * @access public
+ * @return array
*/
- protected function mergeGroupOptions()
+ public function mergeGroupOptions()
{
- $parentOption = $this->parent->getOption();
- // 合并分组参数
- foreach ($this->mergeOptions as $item) {
- if (isset($parentOption[$item]) && isset($this->option[$item])) {
- $this->option[$item] = array_merge($parentOption[$item], $this->option[$item]);
+ if (!$this->lockOption) {
+ $parentOption = $this->parent->getOption();
+ // 合并分组参数
+ foreach ($this->mergeOptions as $item) {
+ if (isset($parentOption[$item]) && isset($this->option[$item])) {
+ $this->option[$item] = array_merge($parentOption[$item], $this->option[$item]);
+ }
}
- }
- $this->option = array_merge($parentOption, $this->option);
+ $this->option = array_merge($parentOption, $this->option);
+ $this->lockOption = true;
+ }
return $this->option;
}
@@ -713,13 +731,17 @@ abstract class Rule
// 替换路由地址中的变量
if (is_string($route) && !empty($matches)) {
- foreach ($matches as $key => $val) {
- if (false !== strpos($route, '<' . $key . '>')) {
- $route = str_replace('<' . $key . '>', $val, $route);
- } elseif (false !== strpos($route, ':' . $key)) {
- $route = str_replace(':' . $key, $val, $route);
- }
+ $search = $replace = [];
+
+ foreach ($matches as $key => $value) {
+ $search[] = '<' . $key . '>';
+ $replace[] = $value;
+
+ $search[] = ':' . $key;
+ $replace[] = $value;
}
+
+ $route = str_replace($search, $replace, $route);
}
// 解析额外参数
@@ -727,7 +749,6 @@ abstract class Rule
$url = array_slice(explode('|', $url), $count + 1);
$this->parseUrlParams($request, implode('|', $url), $matches);
- $this->route = $route;
$this->vars = $matches;
$this->option = $option;
$this->doAfter = true;
@@ -989,7 +1010,7 @@ abstract class Rule
}
}
- $regex = str_replace($match, $replace, $rule);
+ $regex = str_replace(array_unique($match), array_unique($replace), $rule);
$regex = str_replace([')?/', ')/', ')?-', ')-', '\\\\/'], [')\/', ')\/', ')\-', ')\-', '\/'], $regex);
if (isset($hasSlash)) {
@@ -1098,4 +1119,12 @@ abstract class Rule
{
$this->router = Container::get('route');
}
+
+ public function __debugInfo()
+ {
+ $data = get_object_vars($this);
+ unset($data['parent'], $data['router'], $data['route']);
+
+ return $data;
+ }
}
diff --git a/thinkphp/library/think/route/RuleGroup.php b/thinkphp/library/think/route/RuleGroup.php
index 576fe91b67a2f0b98c708976efdd7073c621210f..5781d8cf2b70b45d9679a1ea1c037b611206ed7f 100644
--- a/thinkphp/library/think/route/RuleGroup.php
+++ b/thinkphp/library/think/route/RuleGroup.php
@@ -74,6 +74,10 @@ class RuleGroup extends Rule
if (!empty($option['cross_domain'])) {
$this->router->setCrossDomainRule($this);
}
+
+ if ($router->isTest()) {
+ $this->lazy(false);
+ }
}
/**
@@ -114,8 +118,8 @@ class RuleGroup extends Rule
*/
public function check($request, $url, $completeMatch = false)
{
+ // 跨域OPTIONS请求
if ($dispatch = $this->checkCrossDomain($request)) {
- // 跨域OPTIONS请求
return $dispatch;
}
@@ -124,9 +128,17 @@ class RuleGroup extends Rule
return false;
}
+ // 检查前置行为
+ if (isset($this->option['before'])) {
+ if (false === $this->checkBefore($this->option['before'])) {
+ return false;
+ }
+ unset($this->option['before']);
+ }
+
// 解析分组路由
if ($this instanceof Resource) {
- $this->buildResourceRule($this->resource, $this->option);
+ $this->buildResourceRule();
} elseif ($this->rule) {
if ($this->rule instanceof Response) {
return new ResponseDispatch($request, $this, $this->rule);
@@ -139,10 +151,6 @@ class RuleGroup extends Rule
$method = strtolower($request->method());
$rules = $this->getMethodRules($method);
- if (count($rules) == 0) {
- return false;
- }
-
if ($this->parent) {
// 合并分组参数
$this->mergeGroupOptions();
@@ -177,7 +185,7 @@ class RuleGroup extends Rule
$result = new UrlDispatch($request, $this, $this->auto . '/' . $url, ['auto_search' => false]);
} elseif ($this->miss && in_array($this->miss->getMethod(), ['*', $method])) {
// 未匹配所有路由的路由规则处理
- $result = $this->miss->parseRule($request, '', $this->miss->getRoute(), $url, $this->miss->getOption());
+ $result = $this->miss->parseRule($request, '', $this->miss->getRoute(), $url, $this->miss->mergeGroupOptions());
} else {
$result = false;
}
@@ -572,4 +580,22 @@ class RuleGroup extends Rule
return isset($this->rules[strtolower($method)]) ? $this->rules[strtolower($method)] : [];
}
+ /**
+ * 清空分组下的路由规则
+ * @access public
+ * @return void
+ */
+ public function clear()
+ {
+ $this->rules = [
+ '*' => [],
+ 'get' => [],
+ 'post' => [],
+ 'put' => [],
+ 'patch' => [],
+ 'delete' => [],
+ 'head' => [],
+ 'options' => [],
+ ];
+ }
}
diff --git a/thinkphp/library/think/route/RuleItem.php b/thinkphp/library/think/route/RuleItem.php
index 91c97ad4338aabb31f43f6d83ef1cc1366f42e7c..a05d2deb0bcdef06da8e0462befa705d1766dec0 100644
--- a/thinkphp/library/think/route/RuleItem.php
+++ b/thinkphp/library/think/route/RuleItem.php
@@ -17,6 +17,8 @@ use think\Route;
class RuleItem extends Rule
{
+ protected $hasSetRule;
+
/**
* 架构函数
* @access public
@@ -128,7 +130,11 @@ class RuleItem extends Rule
$value = [$this->rule, $vars, $this->parent->getDomain(), $suffix, $this->method];
Container::get('rule_name')->set($name, $value, $first);
+ }
+
+ if (!$this->hasSetRule) {
Container::get('rule_name')->setRule($this->rule, $this);
+ $this->hasSetRule = true;
}
}
@@ -143,11 +149,6 @@ class RuleItem extends Rule
*/
public function checkRule($request, $url, $match = null, $completeMatch = false)
{
- if ($dispatch = $this->checkCrossDomain($request)) {
- // 允许跨域
- return $dispatch;
- }
-
// 检查参数有效性
if (!$this->checkOption($this->option, $request)) {
return false;
@@ -156,11 +157,6 @@ class RuleItem extends Rule
// 合并分组参数
$option = $this->mergeGroupOptions();
- // 检查前置行为
- if (isset($option['before']) && false === $this->checkBefore($option['before'])) {
- return false;
- }
-
$url = $this->urlSuffixCheck($request, $url, $option);
if (is_null($match)) {
@@ -168,6 +164,20 @@ class RuleItem extends Rule
}
if (false !== $match) {
+ if (!empty($option['cross_domain'])) {
+ if ($dispatch = $this->checkCrossDomain($request)) {
+ // 允许跨域
+ return $dispatch;
+ }
+
+ $option['header'] = $this->option['header'];
+ }
+
+ // 检查前置行为
+ if (isset($option['before']) && false === $this->checkBefore($option['before'])) {
+ return false;
+ }
+
return $this->parseRule($request, $this->rule, $this->route, $url, $option, $match);
}
diff --git a/thinkphp/library/think/route/RuleName.php b/thinkphp/library/think/route/RuleName.php
index c73c36394fe12d2537ff152c4d34116666708295..202fb0e234e95214f573b09509a7fc498d5955d4 100644
--- a/thinkphp/library/think/route/RuleName.php
+++ b/thinkphp/library/think/route/RuleName.php
@@ -42,7 +42,7 @@ class RuleName
*/
public function setRule($rule, $route)
{
- $this->rule[$route->getDomain()][$rule][$route->getRoute()] = $route;
+ $this->rule[$route->getDomain()][$rule][$route->getMethod()] = $route;
}
/**
@@ -70,7 +70,7 @@ class RuleName
foreach ($this->rule as $ruleDomain => $rules) {
foreach ($rules as $rule => $items) {
foreach ($items as $item) {
- $val = [];
+ $val['domain'] = $ruleDomain;
foreach (['method', 'rule', 'name', 'route', 'pattern', 'option'] as $param) {
$call = 'get' . $param;
@@ -107,13 +107,14 @@ class RuleName
* @param string $domain 域名
* @return array|null
*/
- public function get($name = null, $domain = null)
+ public function get($name = null, $domain = null, $method = '*')
{
if (is_null($name)) {
return $this->item;
}
$name = strtolower($name);
+ $method = strtolower($method);
if (isset($this->item[$name])) {
if (is_null($domain)) {
@@ -121,7 +122,7 @@ class RuleName
} else {
$result = [];
foreach ($this->item[$name] as $item) {
- if ($item[2] == $domain) {
+ if ($item[2] == $domain && ('*' == $item[4] || $method == $item[4])) {
$result[] = $item;
}
}
@@ -133,4 +134,14 @@ class RuleName
return $result;
}
+ /**
+ * 清空路由规则
+ * @access public
+ * @return void
+ */
+ public function clear()
+ {
+ $this->item = [];
+ $this->rule = [];
+ }
}
diff --git a/thinkphp/library/think/route/dispatch/Module.php b/thinkphp/library/think/route/dispatch/Module.php
index 4224b362f58f84616de7438cbea9f5d884341716..40bd77596300043f8f19b780c1117873f6b90728 100644
--- a/thinkphp/library/think/route/dispatch/Module.php
+++ b/thinkphp/library/think/route/dispatch/Module.php
@@ -66,7 +66,8 @@ class Module extends Dispatch
// 是否自动转换控制器和操作名
$convert = is_bool($this->convert) ? $this->convert : $this->rule->getConfig('url_convert');
// 获取控制器名
- $controller = strip_tags($result[1] ?: $this->rule->getConfig('default_controller'));
+ $controller = strip_tags($result[1] ?: $this->rule->getConfig('default_controller'));
+
$this->controller = $convert ? strtolower($controller) : $controller;
// 获取操作名
@@ -114,6 +115,7 @@ class Module extends Dispatch
$vars = $this->rule->getConfig('url_param_type')
? $this->request->route()
: $this->request->param();
+ $vars = array_merge($vars, $this->param);
} elseif (is_callable([$instance, '_empty'])) {
// 空操作
$call = [$instance, '_empty'];
diff --git a/thinkphp/library/think/route/dispatch/Url.php b/thinkphp/library/think/route/dispatch/Url.php
index 0fb6e0c25ef1c9a91c596cce5e964cca5bf62f85..acc524e3e0b89f445ca2eda184fe9957a2a9c032 100644
--- a/thinkphp/library/think/route/dispatch/Url.php
+++ b/thinkphp/library/think/route/dispatch/Url.php
@@ -60,6 +60,10 @@ class Url extends Dispatch
$controller = !empty($path) ? array_shift($path) : null;
}
+ if ($controller && !preg_match('/^[A-Za-z0-9][\w|\.]*$/', $controller)) {
+ throw new HttpException(404, 'controller not exists:' . $controller);
+ }
+
// 解析操作
$action = !empty($path) ? array_shift($path) : null;
@@ -116,7 +120,9 @@ class Url extends Dispatch
$host = $this->request->host(true);
- if ($this->rule->getRouter()->getName($name, $host) || $this->rule->getRouter()->getName($name2, $host)) {
+ $method = $this->request->method();
+
+ if ($this->rule->getRouter()->getName($name, $host, $method) || $this->rule->getRouter()->getName($name2, $host, $method)) {
return true;
}
diff --git a/thinkphp/library/think/session/driver/Redis.php b/thinkphp/library/think/session/driver/Redis.php
index a521ca0f716cffe82196b5215425ab34b5a3b722..5a0e7bc7345431939ea13fcc3972d4de637ad887 100644
--- a/thinkphp/library/think/session/driver/Redis.php
+++ b/thinkphp/library/think/session/driver/Redis.php
@@ -108,10 +108,12 @@ class Redis implements SessionHandlerInterface
public function write($sessID, $sessData)
{
if ($this->config['expire'] > 0) {
- return $this->handler->setex($this->config['session_name'] . $sessID, $this->config['expire'], $sessData);
+ $result = $this->handler->setex($this->config['session_name'] . $sessID, $this->config['expire'], $sessData);
} else {
- return $this->handler->set($this->config['session_name'] . $sessID, $sessData);
+ $result = $this->handler->set($this->config['session_name'] . $sessID, $sessData);
}
+
+ return $result ? true : false;
}
/**
@@ -122,7 +124,7 @@ class Redis implements SessionHandlerInterface
*/
public function destroy($sessID)
{
- return $this->handler->delete($this->config['session_name'] . $sessID) > 0;
+ return $this->handler->del($this->config['session_name'] . $sessID) > 0;
}
/**
diff --git a/thinkphp/library/think/template/TagLib.php b/thinkphp/library/think/template/TagLib.php
index 3653b7d2f322882d5743bdc8be92088ab50bf884..bbbb2c0358548d39d24c069bf3a9ce7bfc14a11a 100644
--- a/thinkphp/library/think/template/TagLib.php
+++ b/thinkphp/library/think/template/TagLib.php
@@ -298,7 +298,7 @@ class TagLib
*/
public function parseCondition($condition)
{
- if (strpos($condition, ':')) {
+ if (!strpos($condition, '::') && strpos($condition, ':')) {
$condition = ' ' . substr(strstr($condition, ':'), 1);
}
diff --git a/thinkphp/library/think/validate/ValidateRule.php b/thinkphp/library/think/validate/ValidateRule.php
index 5253465fad5aad309bbffcbd5fe9ad374ce46431..7cd7017470af29dc2ef1198baa859d51025b46f9 100644
--- a/thinkphp/library/think/validate/ValidateRule.php
+++ b/thinkphp/library/think/validate/ValidateRule.php
@@ -36,25 +36,25 @@ namespace think\validate;
* @method ValidateRule regex(mixed $rule, string $msg = '') static 使用正则验证数据
* @method ValidateRule token(mixed $rule='__token__', string $msg = '') static 验证表单令牌
* @method ValidateRule is(mixed $rule, string $msg = '') static 验证字段值是否为有效格式
- * @method ValidateRule isRequire(mixed $rule, string $msg = '') static 验证字段必须
- * @method ValidateRule isNumber(mixed $rule, string $msg = '') static 验证字段值是否为数字
- * @method ValidateRule isArray(mixed $rule, string $msg = '') static 验证字段值是否为数组
- * @method ValidateRule isInteger(mixed $rule, string $msg = '') static 验证字段值是否为整形
- * @method ValidateRule isFloat(mixed $rule, string $msg = '') static 验证字段值是否为浮点数
- * @method ValidateRule isMobile(mixed $rule, string $msg = '') static 验证字段值是否为手机
- * @method ValidateRule isIdCard(mixed $rule, string $msg = '') static 验证字段值是否为身份证号码
- * @method ValidateRule isChs(mixed $rule, string $msg = '') static 验证字段值是否为中文
- * @method ValidateRule isChsDash(mixed $rule, string $msg = '') static 验证字段值是否为中文字母及下划线
- * @method ValidateRule isChsAlpha(mixed $rule, string $msg = '') static 验证字段值是否为中文和字母
- * @method ValidateRule isChsAlphaNum(mixed $rule, string $msg = '') static 验证字段值是否为中文字母和数字
- * @method ValidateRule isDate(mixed $rule, string $msg = '') static 验证字段值是否为有效格式
- * @method ValidateRule isBool(mixed $rule, string $msg = '') static 验证字段值是否为布尔值
- * @method ValidateRule isAlpha(mixed $rule, string $msg = '') static 验证字段值是否为字母
- * @method ValidateRule isAlphaDash(mixed $rule, string $msg = '') static 验证字段值是否为字母和下划线
- * @method ValidateRule isAlphaNum(mixed $rule, string $msg = '') static 验证字段值是否为字母和数字
- * @method ValidateRule isAccepted(mixed $rule, string $msg = '') static 验证字段值是否为yes, on, 或是 1
- * @method ValidateRule isEmail(mixed $rule, string $msg = '') static 验证字段值是否为有效邮箱格式
- * @method ValidateRule isUrl(mixed $rule, string $msg = '') static 验证字段值是否为有效URL地址
+ * @method ValidateRule isRequire(mixed $rule = null, string $msg = '') static 验证字段必须
+ * @method ValidateRule isNumber(mixed $rule = null, string $msg = '') static 验证字段值是否为数字
+ * @method ValidateRule isArray(mixed $rule = null, string $msg = '') static 验证字段值是否为数组
+ * @method ValidateRule isInteger(mixed $rule = null, string $msg = '') static 验证字段值是否为整形
+ * @method ValidateRule isFloat(mixed $rule = null, string $msg = '') static 验证字段值是否为浮点数
+ * @method ValidateRule isMobile(mixed $rule = null, string $msg = '') static 验证字段值是否为手机
+ * @method ValidateRule isIdCard(mixed $rule = null, string $msg = '') static 验证字段值是否为身份证号码
+ * @method ValidateRule isChs(mixed $rule = null, string $msg = '') static 验证字段值是否为中文
+ * @method ValidateRule isChsDash(mixed $rule = null, string $msg = '') static 验证字段值是否为中文字母及下划线
+ * @method ValidateRule isChsAlpha(mixed $rule = null, string $msg = '') static 验证字段值是否为中文和字母
+ * @method ValidateRule isChsAlphaNum(mixed $rule = null, string $msg = '') static 验证字段值是否为中文字母和数字
+ * @method ValidateRule isDate(mixed $rule = null, string $msg = '') static 验证字段值是否为有效格式
+ * @method ValidateRule isBool(mixed $rule = null, string $msg = '') static 验证字段值是否为布尔值
+ * @method ValidateRule isAlpha(mixed $rule = null, string $msg = '') static 验证字段值是否为字母
+ * @method ValidateRule isAlphaDash(mixed $rule = null, string $msg = '') static 验证字段值是否为字母和下划线
+ * @method ValidateRule isAlphaNum(mixed $rule = null, string $msg = '') static 验证字段值是否为字母和数字
+ * @method ValidateRule isAccepted(mixed $rule = null, string $msg = '') static 验证字段值是否为yes, on, 或是 1
+ * @method ValidateRule isEmail(mixed $rule = null, string $msg = '') static 验证字段值是否为有效邮箱格式
+ * @method ValidateRule isUrl(mixed $rule = null, string $msg = '') static 验证字段值是否为有效URL地址
* @method ValidateRule activeUrl(mixed $rule, string $msg = '') static 验证是否为合格的域名或者IP
* @method ValidateRule ip(mixed $rule, string $msg = '') static 验证是否有效IP
* @method ValidateRule fileExt(mixed $rule, string $msg = '') static 验证文件后缀
@@ -69,7 +69,7 @@ namespace think\validate;
* @method ValidateRule requireIf(mixed $rule, string $msg = '') static 验证某个字段等于某个值的时候必须
* @method ValidateRule requireCallback(mixed $rule, string $msg = '') static 通过回调方法验证某个字段是否必须
* @method ValidateRule requireWith(mixed $rule, string $msg = '') static 验证某个字段有值的情况下必须
- * @method ValidateRule must(mixed $rule=null, string $msg = '') static 必须验证
+ * @method ValidateRule must(mixed $rule = null, string $msg = '') static 必须验证
*/
class ValidateRule
{
diff --git a/thinkphp/library/think/view/driver/Php.php b/thinkphp/library/think/view/driver/Php.php
index b6f6a3d074d944584a9d9d44578d60da16c10b55..7948dc0538234d35d1008e9f17b6891f365d60d2 100644
--- a/thinkphp/library/think/view/driver/Php.php
+++ b/thinkphp/library/think/view/driver/Php.php
@@ -176,4 +176,8 @@ class Php
}
}
+ public function __debugInfo()
+ {
+ return ['config' => $this->config];
+ }
}
diff --git a/thinkphp/library/think/view/driver/Think.php b/thinkphp/library/think/view/driver/Think.php
index 9ea068266fab9c32988753700d0cbba08765a7fd..877aee85bc60fb5419ecbfaa3ba1679414c125e7 100644
--- a/thinkphp/library/think/view/driver/Think.php
+++ b/thinkphp/library/think/view/driver/Think.php
@@ -184,4 +184,9 @@ class Think
{
return call_user_func_array([$this->template, $method], $params);
}
+
+ public function __debugInfo()
+ {
+ return ['config' => $this->config];
+ }
}