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]; + } }