framework update

This commit is contained in:
toogee 2020-06-27 15:55:35 +08:00
parent e1cc0aeab9
commit 450c43f255
89 changed files with 2106 additions and 907 deletions

View File

@ -58,9 +58,11 @@ class Article extends BaseController
if($cateId){
$where = ['cate_id' => $cateId];
} else {
if($ename != 'all'){
// 抛出 HTTP 异常
throw new \think\exception\HttpException(404, '异常消息');
}
}
$artList = Cache::get('arts'.$ename.$type.$page);
if(!$artList){

58
composer.lock generated
View File

@ -555,16 +555,16 @@
},
{
"name": "topthink/framework",
"version": "v6.0.2",
"version": "v6.0.3",
"source": {
"type": "git",
"url": "https://github.com/top-think/framework.git",
"reference": "1444cce94b40a836958380b160a5fb7bfc165daf"
"reference": "b4046fb21e6163ba23a792b694162693dbe71b4b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/framework/zipball/1444cce94b40a836958380b160a5fb7bfc165daf",
"reference": "1444cce94b40a836958380b160a5fb7bfc165daf",
"url": "https://api.github.com/repos/top-think/framework/zipball/b4046fb21e6163ba23a792b694162693dbe71b4b",
"reference": "b4046fb21e6163ba23a792b694162693dbe71b4b",
"shasum": "",
"mirrors": [
{
@ -619,7 +619,7 @@
"orm",
"thinkphp"
],
"time": "2020-01-13T05:48:05+00:00"
"time": "2020-06-26T16:03:10+00:00"
},
{
"name": "topthink/think-captcha",
@ -723,16 +723,16 @@
},
{
"name": "topthink/think-multi-app",
"version": "v1.0.12",
"version": "v1.0.13",
"source": {
"type": "git",
"url": "https://github.com/top-think/think-multi-app.git",
"reference": "3c1914d2adc08c8753c7f67228885ca47fb9b3bf"
"reference": "21ac02a99bd69e29444963841e09c5d39b2bcecb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/think-multi-app/zipball/3c1914d2adc08c8753c7f67228885ca47fb9b3bf",
"reference": "3c1914d2adc08c8753c7f67228885ca47fb9b3bf",
"url": "https://api.github.com/repos/top-think/think-multi-app/zipball/21ac02a99bd69e29444963841e09c5d39b2bcecb",
"reference": "21ac02a99bd69e29444963841e09c5d39b2bcecb",
"shasum": "",
"mirrors": [
{
@ -769,20 +769,20 @@
}
],
"description": "thinkphp6 multi app support",
"time": "2020-03-01T13:29:35+00:00"
"time": "2020-04-24T08:53:21+00:00"
},
{
"name": "topthink/think-orm",
"version": "v2.0.32",
"version": "v2.0.33",
"source": {
"type": "git",
"url": "https://github.com/top-think/think-orm.git",
"reference": "03aaaa4d8c4475115b3acaa5aa2498bf5792e017"
"reference": "35ca511a1e4d671b39f7afb4c887703c16ef6957"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/think-orm/zipball/03aaaa4d8c4475115b3acaa5aa2498bf5792e017",
"reference": "03aaaa4d8c4475115b3acaa5aa2498bf5792e017",
"url": "https://api.github.com/repos/top-think/think-orm/zipball/35ca511a1e4d671b39f7afb4c887703c16ef6957",
"reference": "35ca511a1e4d671b39f7afb4c887703c16ef6957",
"shasum": "",
"mirrors": [
{
@ -820,7 +820,7 @@
"database",
"orm"
],
"time": "2020-04-26T13:54:48+00:00"
"time": "2020-06-22T14:57:28+00:00"
},
{
"name": "topthink/think-template",
@ -970,16 +970,16 @@
"packages-dev": [
{
"name": "symfony/polyfill-mbstring",
"version": "v1.17.0",
"version": "v1.17.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "fa79b11539418b02fc5e1897267673ba2c19419c"
"reference": "7110338d81ce1cbc3e273136e4574663627037a7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fa79b11539418b02fc5e1897267673ba2c19419c",
"reference": "fa79b11539418b02fc5e1897267673ba2c19419c",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7110338d81ce1cbc3e273136e4574663627037a7",
"reference": "7110338d81ce1cbc3e273136e4574663627037a7",
"shasum": "",
"mirrors": [
{
@ -998,6 +998,10 @@
"extra": {
"branch-alias": {
"dev-master": "1.17-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
@ -1031,7 +1035,7 @@
"portable",
"shim"
],
"time": "2020-05-12T16:47:27+00:00"
"time": "2020-06-06T08:46:27+00:00"
},
{
"name": "symfony/polyfill-php72",
@ -1096,16 +1100,16 @@
},
{
"name": "symfony/polyfill-php80",
"version": "v1.17.0",
"version": "v1.17.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "5e30b2799bc1ad68f7feb62b60a73743589438dd"
"reference": "4a5b6bba3259902e386eb80dd1956181ee90b5b2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/5e30b2799bc1ad68f7feb62b60a73743589438dd",
"reference": "5e30b2799bc1ad68f7feb62b60a73743589438dd",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4a5b6bba3259902e386eb80dd1956181ee90b5b2",
"reference": "4a5b6bba3259902e386eb80dd1956181ee90b5b2",
"shasum": "",
"mirrors": [
{
@ -1121,6 +1125,10 @@
"extra": {
"branch-alias": {
"dev-master": "1.17-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
@ -1160,7 +1168,7 @@
"portable",
"shim"
],
"time": "2020-05-12T16:47:27+00:00"
"time": "2020-06-06T08:46:27+00:00"
},
{
"name": "symfony/var-dumper",

View File

@ -509,17 +509,17 @@
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.17.0",
"version_normalized": "1.17.0.0",
"version": "v1.17.1",
"version_normalized": "1.17.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "fa79b11539418b02fc5e1897267673ba2c19419c"
"reference": "7110338d81ce1cbc3e273136e4574663627037a7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fa79b11539418b02fc5e1897267673ba2c19419c",
"reference": "fa79b11539418b02fc5e1897267673ba2c19419c",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7110338d81ce1cbc3e273136e4574663627037a7",
"reference": "7110338d81ce1cbc3e273136e4574663627037a7",
"shasum": "",
"mirrors": [
{
@ -534,11 +534,15 @@
"suggest": {
"ext-mbstring": "For best performance"
},
"time": "2020-05-12T16:47:27+00:00",
"time": "2020-06-06T08:46:27+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.17-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"installation-source": "dist",
@ -639,17 +643,17 @@
},
{
"name": "symfony/polyfill-php80",
"version": "v1.17.0",
"version_normalized": "1.17.0.0",
"version": "v1.17.1",
"version_normalized": "1.17.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "5e30b2799bc1ad68f7feb62b60a73743589438dd"
"reference": "4a5b6bba3259902e386eb80dd1956181ee90b5b2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/5e30b2799bc1ad68f7feb62b60a73743589438dd",
"reference": "5e30b2799bc1ad68f7feb62b60a73743589438dd",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4a5b6bba3259902e386eb80dd1956181ee90b5b2",
"reference": "4a5b6bba3259902e386eb80dd1956181ee90b5b2",
"shasum": "",
"mirrors": [
{
@ -661,11 +665,15 @@
"require": {
"php": ">=7.0.8"
},
"time": "2020-05-12T16:47:27+00:00",
"time": "2020-06-06T08:46:27+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.17-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"installation-source": "dist",
@ -851,17 +859,17 @@
},
{
"name": "topthink/framework",
"version": "v6.0.2",
"version_normalized": "6.0.2.0",
"version": "v6.0.3",
"version_normalized": "6.0.3.0",
"source": {
"type": "git",
"url": "https://github.com/top-think/framework.git",
"reference": "1444cce94b40a836958380b160a5fb7bfc165daf"
"reference": "b4046fb21e6163ba23a792b694162693dbe71b4b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/framework/zipball/1444cce94b40a836958380b160a5fb7bfc165daf",
"reference": "1444cce94b40a836958380b160a5fb7bfc165daf",
"url": "https://api.github.com/repos/top-think/framework/zipball/b4046fb21e6163ba23a792b694162693dbe71b4b",
"reference": "b4046fb21e6163ba23a792b694162693dbe71b4b",
"shasum": "",
"mirrors": [
{
@ -888,7 +896,7 @@
"mockery/mockery": "^1.2",
"phpunit/phpunit": "^7.0"
},
"time": "2020-01-13T05:48:05+00:00",
"time": "2020-06-26T16:03:10+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -1025,17 +1033,17 @@
},
{
"name": "topthink/think-multi-app",
"version": "v1.0.12",
"version_normalized": "1.0.12.0",
"version": "v1.0.13",
"version_normalized": "1.0.13.0",
"source": {
"type": "git",
"url": "https://github.com/top-think/think-multi-app.git",
"reference": "3c1914d2adc08c8753c7f67228885ca47fb9b3bf"
"reference": "21ac02a99bd69e29444963841e09c5d39b2bcecb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/think-multi-app/zipball/3c1914d2adc08c8753c7f67228885ca47fb9b3bf",
"reference": "3c1914d2adc08c8753c7f67228885ca47fb9b3bf",
"url": "https://api.github.com/repos/top-think/think-multi-app/zipball/21ac02a99bd69e29444963841e09c5d39b2bcecb",
"reference": "21ac02a99bd69e29444963841e09c5d39b2bcecb",
"shasum": "",
"mirrors": [
{
@ -1048,7 +1056,7 @@
"php": ">=7.1.0",
"topthink/framework": "^6.0.0"
},
"time": "2020-03-01T13:29:35+00:00",
"time": "2020-04-24T08:53:21+00:00",
"type": "library",
"extra": {
"think": {
@ -1077,17 +1085,17 @@
},
{
"name": "topthink/think-orm",
"version": "v2.0.32",
"version_normalized": "2.0.32.0",
"version": "v2.0.33",
"version_normalized": "2.0.33.0",
"source": {
"type": "git",
"url": "https://github.com/top-think/think-orm.git",
"reference": "03aaaa4d8c4475115b3acaa5aa2498bf5792e017"
"reference": "35ca511a1e4d671b39f7afb4c887703c16ef6957"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/think-orm/zipball/03aaaa4d8c4475115b3acaa5aa2498bf5792e017",
"reference": "03aaaa4d8c4475115b3acaa5aa2498bf5792e017",
"url": "https://api.github.com/repos/top-think/think-orm/zipball/35ca511a1e4d671b39f7afb4c887703c16ef6957",
"reference": "35ca511a1e4d671b39f7afb4c887703c16ef6957",
"shasum": "",
"mirrors": [
{
@ -1103,7 +1111,7 @@
"psr/simple-cache": "^1.0",
"topthink/think-helper": "^3.1"
},
"time": "2020-04-26T13:54:48+00:00",
"time": "2020-06-22T14:57:28+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {

2
vendor/services.php vendored
View File

@ -1,5 +1,5 @@
<?php
// This file is automatically generated at:2020-05-21 12:59:52
// This file is automatically generated at:2020-06-27 15:54:24
declare (strict_types = 1);
return array (
0 => 'think\\captcha\\CaptchaService',

View File

@ -1,6 +1,6 @@
<?php
return array(
return array (
'A' => 'a',
'B' => 'b',
'C' => 'c',
@ -510,6 +510,138 @@ return array(
'Ⴥ' => 'ⴥ',
'Ⴧ' => 'ⴧ',
'Ⴭ' => 'ⴭ',
'' => 'ꭰ',
'' => 'ꭱ',
'' => 'ꭲ',
'Ꭳ' => 'ꭳ',
'Ꭴ' => 'ꭴ',
'' => '',
'Ꭶ' => 'ꭶ',
'Ꭷ' => 'ꭷ',
'Ꭸ' => 'ꭸ',
'' => 'ꭹ',
'' => 'ꭺ',
'' => 'ꭻ',
'' => 'ꭼ',
'Ꭽ' => 'ꭽ',
'' => 'ꭾ',
'Ꭿ' => 'ꭿ',
'Ꮀ' => 'ꮀ',
'Ꮁ' => '',
'Ꮂ' => 'ꮂ',
'' => '',
'Ꮄ' => 'ꮄ',
'Ꮅ' => 'ꮅ',
'Ꮆ' => 'ꮆ',
'' => 'ꮇ',
'Ꮈ' => 'ꮈ',
'Ꮉ' => 'ꮉ',
'Ꮊ' => 'ꮊ',
'' => 'ꮋ',
'Ꮌ' => 'ꮌ',
'' => 'ꮍ',
'Ꮎ' => 'ꮎ',
'Ꮏ' => 'ꮏ',
'' => 'ꮐ',
'Ꮑ' => 'ꮑ',
'' => 'ꮒ',
'' => '',
'Ꮔ' => 'ꮔ',
'Ꮕ' => 'ꮕ',
'Ꮖ' => 'ꮖ',
'Ꮗ' => 'ꮗ',
'Ꮘ' => 'ꮘ',
'Ꮙ' => 'ꮙ',
'Ꮚ' => 'ꮚ',
'Ꮛ' => 'ꮛ',
'Ꮜ' => 'ꮜ',
'Ꮝ' => 'ꮝ',
'' => 'ꮞ',
'' => 'ꮟ',
'Ꮠ' => 'ꮠ',
'Ꮡ' => 'ꮡ',
'' => 'ꮢ',
'Ꮣ' => 'ꮣ',
'' => 'ꮤ',
'' => 'ꮥ',
'Ꮦ' => 'ꮦ',
'Ꮧ' => 'ꮧ',
'Ꮨ' => 'ꮨ',
'' => '',
'' => '',
'Ꮫ' => 'ꮫ',
'Ꮬ' => 'ꮬ',
'Ꮭ' => 'ꮭ',
'' => 'ꮮ',
'' => '',
'Ꮰ' => 'ꮰ',
'Ꮱ' => 'ꮱ',
'' => 'ꮲ',
'Ꮳ' => 'ꮳ',
'Ꮴ' => 'ꮴ',
'Ꮵ' => 'ꮵ',
'' => 'ꮶ',
'' => 'ꮷ',
'Ꮸ' => 'ꮸ',
'Ꮹ' => 'ꮹ',
'Ꮺ' => 'ꮺ',
'Ꮻ' => 'ꮻ',
'Ꮼ' => 'ꮼ',
'Ꮽ' => 'ꮽ',
'' => 'ꮾ',
'Ꮿ' => 'ꮿ',
'Ᏸ' => 'ᏸ',
'Ᏹ' => 'ᏹ',
'Ᏺ' => 'ᏺ',
'' => 'ᏻ',
'' => 'ᏼ',
'Ᏽ' => 'ᏽ',
'Ა' => 'ა',
'Ბ' => 'ბ',
'Გ' => 'გ',
'Დ' => 'დ',
'Ე' => 'ე',
'Ვ' => 'ვ',
'Ზ' => 'ზ',
'Თ' => 'თ',
'Ი' => 'ი',
'Კ' => 'კ',
'Ლ' => 'ლ',
'Მ' => 'მ',
'Ნ' => 'ნ',
'Ო' => 'ო',
'Პ' => 'პ',
'Ჟ' => 'ჟ',
'Რ' => 'რ',
'Ს' => 'ს',
'Ტ' => 'ტ',
'Უ' => 'უ',
'Ფ' => 'ფ',
'Ქ' => 'ქ',
'Ღ' => 'ღ',
'Ყ' => '',
'Შ' => 'შ',
'Ჩ' => 'ჩ',
'Ც' => 'ც',
'Ძ' => 'ძ',
'Წ' => 'წ',
'Ჭ' => 'ჭ',
'Ხ' => 'ხ',
'Ჯ' => 'ჯ',
'Ჰ' => 'ჰ',
'Ჱ' => 'ჱ',
'Ჲ' => 'ჲ',
'Ჳ' => 'ჳ',
'Ჴ' => 'ჴ',
'Ჵ' => 'ჵ',
'Ჶ' => 'ჶ',
'Ჷ' => 'ჷ',
'Ჸ' => 'ჸ',
'Ჹ' => 'ჹ',
'Ჺ' => 'ჺ',
'Ჽ' => 'ჽ',
'Ჾ' => 'ჾ',
'Ჿ' => '',
'Ḁ' => 'ḁ',
'Ḃ' => 'ḃ',
'Ḅ' => 'ḅ',
@ -993,8 +1125,24 @@ return array(
'' => 'ɜ',
'Ɡ' => 'ɡ',
'Ɬ' => 'ɬ',
'Ɪ' => 'ɪ',
'Ʞ' => 'ʞ',
'Ʇ' => 'ʇ',
'' => 'ʝ',
'' => 'ꭓ',
'' => 'ꞵ',
'Ꞷ' => 'ꞷ',
'Ꞹ' => 'ꞹ',
'Ꞻ' => 'ꞻ',
'Ꞽ' => 'ꞽ',
'Ꞿ' => 'ꞿ',
'Ꟃ' => 'ꟃ',
'Ꞔ' => 'ꞔ',
'Ʂ' => 'ʂ',
'Ᶎ' => 'ᶎ',
'Ꟈ' => 'ꟈ',
'Ꟊ' => 'ꟊ',
'Ꟶ' => 'ꟶ',
'' => '',
'' => '',
'' => '',
@ -1061,6 +1209,93 @@ return array(
'𐐥' => '𐑍',
'𐐦' => '𐑎',
'𐐧' => '𐑏',
'𐒰' => '𐓘',
'𐒱' => '𐓙',
'𐒲' => '𐓚',
'𐒳' => '𐓛',
'𐒴' => '𐓜',
'𐒵' => '𐓝',
'𐒶' => '𐓞',
'𐒷' => '𐓟',
'𐒸' => '𐓠',
'𐒹' => '𐓡',
'𐒺' => '𐓢',
'𐒻' => '𐓣',
'𐒼' => '𐓤',
'𐒽' => '𐓥',
'𐒾' => '𐓦',
'𐒿' => '𐓧',
'𐓀' => '𐓨',
'𐓁' => '𐓩',
'𐓂' => '𐓪',
'𐓃' => '𐓫',
'𐓄' => '𐓬',
'𐓅' => '𐓭',
'𐓆' => '𐓮',
'𐓇' => '𐓯',
'𐓈' => '𐓰',
'𐓉' => '𐓱',
'𐓊' => '𐓲',
'𐓋' => '𐓳',
'𐓌' => '𐓴',
'𐓍' => '𐓵',
'𐓎' => '𐓶',
'𐓏' => '𐓷',
'𐓐' => '𐓸',
'𐓑' => '𐓹',
'𐓒' => '𐓺',
'𐓓' => '𐓻',
'𐲀' => '𐳀',
'𐲁' => '𐳁',
'𐲂' => '𐳂',
'𐲃' => '𐳃',
'𐲄' => '𐳄',
'𐲅' => '𐳅',
'𐲆' => '𐳆',
'𐲇' => '𐳇',
'𐲈' => '𐳈',
'𐲉' => '𐳉',
'𐲊' => '𐳊',
'𐲋' => '𐳋',
'𐲌' => '𐳌',
'𐲍' => '𐳍',
'𐲎' => '𐳎',
'𐲏' => '𐳏',
'𐲐' => '𐳐',
'𐲑' => '𐳑',
'𐲒' => '𐳒',
'𐲓' => '𐳓',
'𐲔' => '𐳔',
'𐲕' => '𐳕',
'𐲖' => '𐳖',
'𐲗' => '𐳗',
'𐲘' => '𐳘',
'𐲙' => '𐳙',
'𐲚' => '𐳚',
'𐲛' => '𐳛',
'𐲜' => '𐳜',
'𐲝' => '𐳝',
'𐲞' => '𐳞',
'𐲟' => '𐳟',
'𐲠' => '𐳠',
'𐲡' => '𐳡',
'𐲢' => '𐳢',
'𐲣' => '𐳣',
'𐲤' => '𐳤',
'𐲥' => '𐳥',
'𐲦' => '𐳦',
'𐲧' => '𐳧',
'𐲨' => '𐳨',
'𐲩' => '𐳩',
'𐲪' => '𐳪',
'𐲫' => '𐳫',
'𐲬' => '𐳬',
'𐲭' => '𐳭',
'𐲮' => '𐳮',
'𐲯' => '𐳯',
'𐲰' => '𐳰',
'𐲱' => '𐳱',
'𐲲' => '𐳲',
'𑢠' => '𑣀',
'𑢡' => '𑣁',
'𑢢' => '𑣂',
@ -1093,4 +1328,70 @@ return array(
'𑢽' => '𑣝',
'𑢾' => '𑣞',
'𑢿' => '𑣟',
'𖹀' => '𖹠',
'𖹁' => '𖹡',
'𖹂' => '𖹢',
'𖹃' => '𖹣',
'𖹄' => '𖹤',
'𖹅' => '𖹥',
'𖹆' => '𖹦',
'𖹇' => '𖹧',
'𖹈' => '𖹨',
'𖹉' => '𖹩',
'𖹊' => '𖹪',
'𖹋' => '𖹫',
'𖹌' => '𖹬',
'𖹍' => '𖹭',
'𖹎' => '𖹮',
'𖹏' => '𖹯',
'𖹐' => '𖹰',
'𖹑' => '𖹱',
'𖹒' => '𖹲',
'𖹓' => '𖹳',
'𖹔' => '𖹴',
'𖹕' => '𖹵',
'𖹖' => '𖹶',
'𖹗' => '𖹷',
'𖹘' => '𖹸',
'𖹙' => '𖹹',
'𖹚' => '𖹺',
'𖹛' => '𖹻',
'𖹜' => '𖹼',
'𖹝' => '𖹽',
'𖹞' => '𖹾',
'𖹟' => '𖹿',
'𞤀' => '𞤢',
'𞤁' => '𞤣',
'𞤂' => '𞤤',
'𞤃' => '𞤥',
'𞤄' => '𞤦',
'𞤅' => '𞤧',
'𞤆' => '𞤨',
'𞤇' => '𞤩',
'𞤈' => '𞤪',
'𞤉' => '𞤫',
'𞤊' => '𞤬',
'𞤋' => '𞤭',
'𞤌' => '𞤮',
'𞤍' => '𞤯',
'𞤎' => '𞤰',
'𞤏' => '𞤱',
'𞤐' => '𞤲',
'𞤑' => '𞤳',
'𞤒' => '𞤴',
'𞤓' => '𞤵',
'𞤔' => '𞤶',
'𞤕' => '𞤷',
'𞤖' => '𞤸',
'𞤗' => '𞤹',
'𞤘' => '𞤺',
'𞤙' => '𞤻',
'𞤚' => '𞤼',
'𞤛' => '𞤽',
'𞤜' => '𞤾',
'𞤝' => '𞤿',
'𞤞' => '𞥀',
'𞤟' => '𞥁',
'𞤠' => '𞥂',
'𞤡' => '𞥃',
);

View File

@ -1,6 +1,6 @@
<?php
return array(
return array (
'a' => 'A',
'b' => 'B',
'c' => 'C',
@ -225,6 +225,7 @@ return array(
'ɦ' => 'Ɦ',
'ɨ' => 'Ɨ',
'ɩ' => 'Ɩ',
'ɪ' => 'Ɪ',
'ɫ' => 'Ɫ',
'ɬ' => 'Ɬ',
'ɯ' => 'Ɯ',
@ -233,6 +234,7 @@ return array(
'ɵ' => 'Ɵ',
'ɽ' => 'Ɽ',
'ʀ' => 'Ʀ',
'ʂ' => 'Ʂ',
'ʃ' => 'Ʃ',
'ʇ' => 'Ʇ',
'ʈ' => 'Ʈ',
@ -241,6 +243,7 @@ return array(
'ʋ' => 'Ʋ',
'ʌ' => 'Ʌ',
'ʒ' => 'Ʒ',
'ʝ' => '',
'ʞ' => 'Ʞ',
'ͅ' => 'Ι',
'ͱ' => 'Ͱ',
@ -493,8 +496,70 @@ return array(
'ք' => 'Ք',
'օ' => 'Օ',
'ֆ' => 'Ֆ',
'ა' => 'Ა',
'ბ' => 'Ბ',
'გ' => 'Გ',
'დ' => 'Დ',
'ე' => 'Ე',
'ვ' => 'Ვ',
'ზ' => 'Ზ',
'თ' => 'Თ',
'ი' => 'Ი',
'კ' => 'Კ',
'ლ' => 'Ლ',
'მ' => 'Მ',
'ნ' => 'Ნ',
'ო' => 'Ო',
'პ' => 'Პ',
'ჟ' => 'Ჟ',
'რ' => 'Რ',
'ს' => 'Ს',
'ტ' => 'Ტ',
'უ' => 'Უ',
'ფ' => 'Ფ',
'ქ' => 'Ქ',
'ღ' => 'Ღ',
'' => 'Ყ',
'შ' => 'Შ',
'ჩ' => 'Ჩ',
'ც' => 'Ც',
'ძ' => 'Ძ',
'წ' => 'Წ',
'ჭ' => 'Ჭ',
'ხ' => 'Ხ',
'ჯ' => 'Ჯ',
'ჰ' => 'Ჰ',
'ჱ' => 'Ჱ',
'ჲ' => 'Ჲ',
'ჳ' => 'Ჳ',
'ჴ' => 'Ჴ',
'ჵ' => 'Ჵ',
'ჶ' => 'Ჶ',
'ჷ' => 'Ჷ',
'ჸ' => 'Ჸ',
'ჹ' => 'Ჹ',
'ჺ' => 'Ჺ',
'ჽ' => 'Ჽ',
'ჾ' => 'Ჾ',
'' => 'Ჿ',
'ᏸ' => 'Ᏸ',
'ᏹ' => 'Ᏹ',
'ᏺ' => 'Ᏺ',
'ᏻ' => '',
'ᏼ' => '',
'ᏽ' => 'Ᏽ',
'ᲀ' => 'В',
'ᲁ' => 'Д',
'ᲂ' => 'О',
'ᲃ' => 'С',
'ᲄ' => 'Т',
'ᲅ' => 'Т',
'ᲆ' => 'Ъ',
'ᲇ' => 'Ѣ',
'ᲈ' => 'Ꙋ',
'ᵹ' => 'Ᵹ',
'ᵽ' => 'Ᵽ',
'ᶎ' => 'Ᶎ',
'ḁ' => 'Ḁ',
'ḃ' => 'Ḃ',
'ḅ' => 'Ḅ',
@ -993,6 +1058,7 @@ return array(
'' => 'Ꞌ',
'ꞑ' => 'Ꞑ',
'ꞓ' => 'Ꞓ',
'ꞔ' => 'Ꞔ',
'ꞗ' => 'Ꞗ',
'' => '',
'ꞛ' => 'Ꞛ',
@ -1003,6 +1069,97 @@ return array(
'ꞥ' => 'Ꞥ',
'ꞧ' => 'Ꞧ',
'ꞩ' => 'Ꞩ',
'ꞵ' => '',
'ꞷ' => 'Ꞷ',
'ꞹ' => 'Ꞹ',
'ꞻ' => 'Ꞻ',
'ꞽ' => 'Ꞽ',
'ꞿ' => 'Ꞿ',
'ꟃ' => 'Ꟃ',
'ꟈ' => 'Ꟈ',
'ꟊ' => 'Ꟊ',
'ꟶ' => 'Ꟶ',
'ꭓ' => '',
'ꭰ' => '',
'ꭱ' => '',
'ꭲ' => '',
'ꭳ' => 'Ꭳ',
'ꭴ' => 'Ꭴ',
'' => '',
'ꭶ' => 'Ꭶ',
'ꭷ' => 'Ꭷ',
'ꭸ' => 'Ꭸ',
'ꭹ' => '',
'ꭺ' => '',
'ꭻ' => '',
'ꭼ' => '',
'ꭽ' => 'Ꭽ',
'ꭾ' => '',
'ꭿ' => 'Ꭿ',
'ꮀ' => 'Ꮀ',
'' => 'Ꮁ',
'ꮂ' => 'Ꮂ',
'' => '',
'ꮄ' => 'Ꮄ',
'ꮅ' => 'Ꮅ',
'ꮆ' => 'Ꮆ',
'ꮇ' => '',
'ꮈ' => 'Ꮈ',
'ꮉ' => 'Ꮉ',
'ꮊ' => 'Ꮊ',
'ꮋ' => '',
'ꮌ' => 'Ꮌ',
'ꮍ' => '',
'ꮎ' => 'Ꮎ',
'ꮏ' => 'Ꮏ',
'ꮐ' => '',
'ꮑ' => 'Ꮑ',
'ꮒ' => '',
'' => '',
'ꮔ' => 'Ꮔ',
'ꮕ' => 'Ꮕ',
'ꮖ' => 'Ꮖ',
'ꮗ' => 'Ꮗ',
'ꮘ' => 'Ꮘ',
'ꮙ' => 'Ꮙ',
'ꮚ' => 'Ꮚ',
'ꮛ' => 'Ꮛ',
'ꮜ' => 'Ꮜ',
'ꮝ' => 'Ꮝ',
'ꮞ' => '',
'ꮟ' => '',
'ꮠ' => 'Ꮠ',
'ꮡ' => 'Ꮡ',
'ꮢ' => '',
'ꮣ' => 'Ꮣ',
'ꮤ' => '',
'ꮥ' => '',
'ꮦ' => 'Ꮦ',
'ꮧ' => 'Ꮧ',
'ꮨ' => 'Ꮨ',
'' => '',
'' => '',
'ꮫ' => 'Ꮫ',
'ꮬ' => 'Ꮬ',
'ꮭ' => 'Ꮭ',
'ꮮ' => '',
'' => '',
'ꮰ' => 'Ꮰ',
'ꮱ' => 'Ꮱ',
'ꮲ' => '',
'ꮳ' => 'Ꮳ',
'ꮴ' => 'Ꮴ',
'ꮵ' => 'Ꮵ',
'ꮶ' => '',
'ꮷ' => '',
'ꮸ' => 'Ꮸ',
'ꮹ' => 'Ꮹ',
'ꮺ' => 'Ꮺ',
'ꮻ' => 'Ꮻ',
'ꮼ' => 'Ꮼ',
'ꮽ' => 'Ꮽ',
'ꮾ' => '',
'ꮿ' => 'Ꮿ',
'' => '',
'' => '',
'' => '',
@ -1069,6 +1226,93 @@ return array(
'𐑍' => '𐐥',
'𐑎' => '𐐦',
'𐑏' => '𐐧',
'𐓘' => '𐒰',
'𐓙' => '𐒱',
'𐓚' => '𐒲',
'𐓛' => '𐒳',
'𐓜' => '𐒴',
'𐓝' => '𐒵',
'𐓞' => '𐒶',
'𐓟' => '𐒷',
'𐓠' => '𐒸',
'𐓡' => '𐒹',
'𐓢' => '𐒺',
'𐓣' => '𐒻',
'𐓤' => '𐒼',
'𐓥' => '𐒽',
'𐓦' => '𐒾',
'𐓧' => '𐒿',
'𐓨' => '𐓀',
'𐓩' => '𐓁',
'𐓪' => '𐓂',
'𐓫' => '𐓃',
'𐓬' => '𐓄',
'𐓭' => '𐓅',
'𐓮' => '𐓆',
'𐓯' => '𐓇',
'𐓰' => '𐓈',
'𐓱' => '𐓉',
'𐓲' => '𐓊',
'𐓳' => '𐓋',
'𐓴' => '𐓌',
'𐓵' => '𐓍',
'𐓶' => '𐓎',
'𐓷' => '𐓏',
'𐓸' => '𐓐',
'𐓹' => '𐓑',
'𐓺' => '𐓒',
'𐓻' => '𐓓',
'𐳀' => '𐲀',
'𐳁' => '𐲁',
'𐳂' => '𐲂',
'𐳃' => '𐲃',
'𐳄' => '𐲄',
'𐳅' => '𐲅',
'𐳆' => '𐲆',
'𐳇' => '𐲇',
'𐳈' => '𐲈',
'𐳉' => '𐲉',
'𐳊' => '𐲊',
'𐳋' => '𐲋',
'𐳌' => '𐲌',
'𐳍' => '𐲍',
'𐳎' => '𐲎',
'𐳏' => '𐲏',
'𐳐' => '𐲐',
'𐳑' => '𐲑',
'𐳒' => '𐲒',
'𐳓' => '𐲓',
'𐳔' => '𐲔',
'𐳕' => '𐲕',
'𐳖' => '𐲖',
'𐳗' => '𐲗',
'𐳘' => '𐲘',
'𐳙' => '𐲙',
'𐳚' => '𐲚',
'𐳛' => '𐲛',
'𐳜' => '𐲜',
'𐳝' => '𐲝',
'𐳞' => '𐲞',
'𐳟' => '𐲟',
'𐳠' => '𐲠',
'𐳡' => '𐲡',
'𐳢' => '𐲢',
'𐳣' => '𐲣',
'𐳤' => '𐲤',
'𐳥' => '𐲥',
'𐳦' => '𐲦',
'𐳧' => '𐲧',
'𐳨' => '𐲨',
'𐳩' => '𐲩',
'𐳪' => '𐲪',
'𐳫' => '𐲫',
'𐳬' => '𐲬',
'𐳭' => '𐲭',
'𐳮' => '𐲮',
'𐳯' => '𐲯',
'𐳰' => '𐲰',
'𐳱' => '𐲱',
'𐳲' => '𐲲',
'𑣀' => '𑢠',
'𑣁' => '𑢡',
'𑣂' => '𑢢',
@ -1101,4 +1345,70 @@ return array(
'𑣝' => '𑢽',
'𑣞' => '𑢾',
'𑣟' => '𑢿',
'𖹠' => '𖹀',
'𖹡' => '𖹁',
'𖹢' => '𖹂',
'𖹣' => '𖹃',
'𖹤' => '𖹄',
'𖹥' => '𖹅',
'𖹦' => '𖹆',
'𖹧' => '𖹇',
'𖹨' => '𖹈',
'𖹩' => '𖹉',
'𖹪' => '𖹊',
'𖹫' => '𖹋',
'𖹬' => '𖹌',
'𖹭' => '𖹍',
'𖹮' => '𖹎',
'𖹯' => '𖹏',
'𖹰' => '𖹐',
'𖹱' => '𖹑',
'𖹲' => '𖹒',
'𖹳' => '𖹓',
'𖹴' => '𖹔',
'𖹵' => '𖹕',
'𖹶' => '𖹖',
'𖹷' => '𖹗',
'𖹸' => '𖹘',
'𖹹' => '𖹙',
'𖹺' => '𖹚',
'𖹻' => '𖹛',
'𖹼' => '𖹜',
'𖹽' => '𖹝',
'𖹾' => '𖹞',
'𖹿' => '𖹟',
'𞤢' => '𞤀',
'𞤣' => '𞤁',
'𞤤' => '𞤂',
'𞤥' => '𞤃',
'𞤦' => '𞤄',
'𞤧' => '𞤅',
'𞤨' => '𞤆',
'𞤩' => '𞤇',
'𞤪' => '𞤈',
'𞤫' => '𞤉',
'𞤬' => '𞤊',
'𞤭' => '𞤋',
'𞤮' => '𞤌',
'𞤯' => '𞤍',
'𞤰' => '𞤎',
'𞤱' => '𞤏',
'𞤲' => '𞤐',
'𞤳' => '𞤑',
'𞤴' => '𞤒',
'𞤵' => '𞤓',
'𞤶' => '𞤔',
'𞤷' => '𞤕',
'𞤸' => '𞤖',
'𞤹' => '𞤗',
'𞤺' => '𞤘',
'𞤻' => '𞤙',
'𞤼' => '𞤚',
'𞤽' => '𞤛',
'𞤾' => '𞤜',
'𞤿' => '𞤝',
'𞥀' => '𞤞',
'𞥁' => '𞤟',
'𞥂' => '𞤠',
'𞥃' => '𞤡',
);

View File

@ -29,6 +29,10 @@
"extra": {
"branch-alias": {
"dev-master": "1.17-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
}
}

View File

@ -31,6 +31,10 @@
"extra": {
"branch-alias": {
"dev-master": "1.17-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
}
}

View File

@ -13,6 +13,8 @@ ThinkPHP 6.0
ThinkPHP6.0底层架构采用PHP7.1改写和进一步优化。
[官方应用服务市场](https://www.thinkphp.cn/service) | [`ThinkPHP`开发者扶持计划](https://sites.thinkphp.cn/1782366)
## 主要新特性
* 采用`PHP7`强类型(严格模式)
@ -69,7 +71,7 @@ composer update topthink/framework
## 参与开发
请参阅 [ThinkPHP核心框架包](https://github.com/top-think/framework)。
直接提交PR或者Issue即可
## 版权信息
@ -77,9 +79,7 @@ ThinkPHP遵循Apache2开源协议发布并提供免费使用。
本项目包含的第三方源码和二进制文件之版权信息另行标注。
版权所有Copyright © 2006-2019 by ThinkPHP (http://thinkphp.cn)
All rights reserved。
版权所有Copyright © 2006-2020 by ThinkPHP (http://thinkphp.cn) All rights reserved。
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。

View File

@ -44,7 +44,7 @@
},
"autoload-dev": {
"psr-4": {
"think\\tests\\": "src/tests/"
"think\\tests\\": "tests/"
}
},
"minimum-stability": "dev",

View File

@ -359,7 +359,7 @@ if (!function_exists('parse_name')) {
return $ucfirst ? ucfirst($name) : lcfirst($name);
}
return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
return strtolower(trim(preg_replace('/[A-Z]/', '_\\0', $name), '_'));
}
}
@ -632,7 +632,7 @@ if (!function_exists('public_path')) {
*/
function public_path($path = '')
{
return app()->getRootPath() . ($path ? ltrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $path);
return app()->getRootPath() . 'public' . DIRECTORY_SEPARATOR . ($path ? ltrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $path);
}
}

View File

@ -39,7 +39,7 @@ use think\initializer\RegisterService;
*/
class App extends Container
{
const VERSION = '6.0.2';
const VERSION = '6.0.3';
/**
* 应用调试模式
@ -602,9 +602,7 @@ class App extends Container
*/
protected function getDefaultRootPath(): string
{
$path = dirname(dirname(dirname(dirname($this->thinkPath))));
return $path . DIRECTORY_SEPARATOR;
return dirname($this->thinkPath, 4) . DIRECTORY_SEPARATOR;
}
}

View File

@ -15,6 +15,7 @@ namespace think;
/**
* 数据库管理类
* @package think
* @property Config $config
*/
class Db extends DbManager
{
@ -32,7 +33,9 @@ class Db extends DbManager
$db->setConfig($config);
$db->setEvent($event);
$db->setLog($log);
$db->setCache($cache);
$store = $db->getConfig('cache_store');
$db->setCache($cache->store($store));
$db->triggerSql();
return $db;

View File

@ -39,12 +39,6 @@ class Event
'LogWrite' => event\LogWrite::class,
];
/**
* 是否需要事件响应
* @var bool
*/
protected $withEvent = true;
/**
* 应用对象
* @var App
@ -56,18 +50,6 @@ class Event
$this->app = $app;
}
/**
* 设置是否开启事件响应
* @access protected
* @param bool $event 是否需要事件响应
* @return $this
*/
public function withEvent(bool $event)
{
$this->withEvent = $event;
return $this;
}
/**
* 批量注册事件监听
* @access public
@ -76,10 +58,6 @@ class Event
*/
public function listenEvents(array $events)
{
if (!$this->withEvent) {
return $this;
}
foreach ($events as $event => $listeners) {
if (isset($this->bind[$event])) {
$event = $this->bind[$event];
@ -101,10 +79,6 @@ class Event
*/
public function listen(string $event, $listener, bool $first = false)
{
if (!$this->withEvent) {
return $this;
}
if (isset($this->bind[$event])) {
$event = $this->bind[$event];
}
@ -169,10 +143,6 @@ class Event
*/
public function subscribe($subscriber)
{
if (!$this->withEvent) {
return $this;
}
$subscribers = (array) $subscriber;
foreach ($subscribers as $subscriber) {
@ -201,10 +171,6 @@ class Event
*/
public function observe($observer, string $prefix = '')
{
if (!$this->withEvent) {
return $this;
}
if (is_string($observer)) {
$observer = $this->app->make($observer);
}
@ -238,10 +204,6 @@ class Event
*/
public function trigger($event, $params = null, bool $once = false)
{
if (!$this->withEvent) {
return;
}
if (is_object($event)) {
$params = $event;
$event = get_class($event);

View File

@ -157,11 +157,11 @@ class File extends SplFileInfo
/**
* 自动生成文件名
* @access protected
* @access public
* @param string|\Closure $rule
* @return string
*/
public function hashName($rule = 'date'): string
public function hashName($rule = ''): string
{
if (!$this->hashName) {
if ($rule instanceof \Closure) {

View File

@ -191,9 +191,6 @@ class Http
// 加载全局中间件
$this->loadMiddleware();
// 设置开启事件机制
$this->app->event->withEvent($this->app->config->get('app.with_event', true));
// 监听HttpRun
$this->app->event->trigger(HttpRun::class);

View File

@ -33,6 +33,8 @@ class Lang
'extend_list' => [],
// 多语言cookie变量
'cookie_var' => 'think_lang',
// 多语言header变量
'header_var' => 'think-lang',
// 多语言自动侦测变量名
'detect_var' => 'lang',
// Accept-Language转义为对应语言包名称
@ -118,9 +120,9 @@ class Lang
$lang = [];
foreach ((array) $file as $_file) {
if (is_file($_file)) {
$result = $this->parse($_file);
foreach ((array) $file as $name) {
if (is_file($name)) {
$result = $this->parse($name);
$lang = array_change_key_case($result) + $lang;
}
}
@ -239,6 +241,9 @@ class Lang
if ($request->get($this->config['detect_var'])) {
// url中设置了语言变量
$langSet = strtolower($request->get($this->config['detect_var']));
} elseif ($request->header($this->config['header_var'])) {
// Header中设置了语言变量
$langSet = strtolower($request->header($this->config['header_var']));
} elseif ($request->cookie($this->config['cookie_var'])) {
// Cookie中设置了语言变量
$langSet = strtolower($request->cookie($this->config['cookie_var']));

View File

@ -12,6 +12,7 @@ declare (strict_types = 1);
namespace think;
use ArrayAccess;
use think\file\UploadedFile;
use think\route\Rule;
@ -19,7 +20,7 @@ use think\route\Rule;
* 请求管理类
* @package think
*/
class Request
class Request implements ArrayAccess
{
/**
* 兼容PATH_INFO获取
@ -899,6 +900,7 @@ class Request
public function setRoute(array $route)
{
$this->route = array_merge($this->route, $route);
$this->mergeParam = false;
return $this;
}
@ -985,7 +987,7 @@ class Request
protected function getInputData($content): array
{
$contentType = $this->contentType();
if ($contentType == 'application/x-www-form-urlencoded') {
if ('application/x-www-form-urlencoded' == $contentType) {
parse_str($content, $data);
return $data;
} elseif (false !== strpos($contentType, 'json')) {
@ -1754,7 +1756,7 @@ class Request
if ($this->host) {
$host = $this->host;
} else {
$host = strval($this->server('HTTP_X_REAL_HOST') ?: $this->server('HTTP_HOST'));
$host = strval($this->server('HTTP_X_FORWARDED_HOST') ?: $this->server('HTTP_HOST'));
}
return true === $strict && strpos($host, ':') ? strstr($host, ':', true) : $host;
@ -1767,7 +1769,7 @@ class Request
*/
public function port(): int
{
return (int) $this->server('SERVER_PORT', '');
return (int) ($this->server('HTTP_X_FORWARDED_PORT') ?: $this->server('SERVER_PORT', ''));
}
/**
@ -2126,4 +2128,22 @@ class Request
{
return isset($this->middleware[$name]);
}
// ArrayAccess
public function offsetExists($name): bool
{
return $this->has($name);
}
public function offsetGet($name)
{
return $this->param($name);
}
public function offsetSet($name, $value)
{}
public function offsetUnset($name)
{}
}

View File

@ -15,6 +15,7 @@ namespace think;
use Closure;
use think\exception\RouteNotFoundException;
use think\route\Dispatch;
use think\route\dispatch\Callback;
use think\route\dispatch\Url as UrlDispatch;
use think\route\Domain;
use think\route\Resource;
@ -120,7 +121,7 @@ class Route
/**
* 域名对象
* @var array
* @var Domain[]
*/
protected $domains = [];
@ -134,7 +135,7 @@ class Route
* 路由是否延迟解析
* @var bool
*/
protected $lazy = true;
protected $lazy = false;
/**
* 路由是否测试模式
@ -433,7 +434,7 @@ class Route
* 批量导入路由标识
* @access public
* @param array $name 路由标识
* @return $this
* @return void
*/
public function import(array $name): void
{
@ -510,6 +511,12 @@ class Route
*/
public function rule(string $rule, $route = null, string $method = '*'): RuleItem
{
if ($route instanceof Response) {
// 兼容之前的路由到响应对象,感觉不需要,使用场景很少,闭包就能实现
$route = function () use ($route) {
return $route;
};
}
return $this->group->addRule($rule, $route, $method);
}
@ -658,7 +665,9 @@ class Route
*/
public function view(string $rule, string $template = '', array $vars = []): RuleItem
{
return $this->rule($rule, $template, 'GET')->view($vars);
return $this->rule($rule, function () use ($vars, $template) {
return Response::create($template, 'view')->assign($vars);
}, 'GET');
}
/**
@ -671,7 +680,9 @@ class Route
*/
public function redirect(string $rule, string $route = '', int $status = 301): RuleItem
{
return $this->rule($rule, $route, '*')->redirect()->status($status);
return $this->rule($rule, function () use ($status, $route) {
return Response::create($route, 'redirect')->code($status);
}, '*');
}
/**
@ -722,10 +733,10 @@ class Route
/**
* 路由调度
* @param Request $request
* @param Closure $withRoute
* @param Closure|bool $withRoute
* @return Response
*/
public function dispatch(Request $request, $withRoute = null)
public function dispatch(Request $request, $withRoute = true)
{
$this->request = $request;
$this->host = $this->request->host(true);
@ -733,7 +744,9 @@ class Route
if ($withRoute) {
//加载路由
if ($withRoute instanceof Closure) {
$withRoute();
}
$dispatch = $this->check();
} else {
$dispatch = $this->url($this->path());
@ -751,10 +764,10 @@ class Route
/**
* 检测URL路由
* @access public
* @return Dispatch
* @return Dispatch|false
* @throws RouteNotFoundException
*/
public function check(): Dispatch
public function check()
{
// 自动检测域名路由
$url = str_replace($this->config['pathinfo_depr'], '|', $this->path());
@ -807,8 +820,15 @@ class Route
* @param string $url URL地址
* @return Dispatch
*/
public function url(string $url): UrlDispatch
public function url(string $url): Dispatch
{
if ($this->request->method() == 'OPTIONS') {
// 自动响应options请求
return new Callback($this->request, $this->group, function () {
return Response::create('', 'html', 204)->header(['Allow' => 'GET, POST, PUT, DELETE']);
});
}
return new UrlDispatch($this->request, $this->group, $url);
}

View File

@ -472,15 +472,15 @@ class Validate
{
$this->error = [];
if ($this->currentScene) {
$this->getScene($this->currentScene);
}
if (empty($rules)) {
// 读取验证规则
$rules = $this->rule;
}
if ($this->currentScene) {
$this->getScene($this->currentScene);
}
foreach ($this->append as $key => $rule) {
if (!isset($rules[$key])) {
$rules[$key] = $rule;
@ -607,6 +607,10 @@ class Validate
$rules = array_unique(array_merge($rules, $this->append[$field]), SORT_REGULAR);
}
if (empty($rules)) {
return true;
}
$i = 0;
foreach ($rules as $key => $rule) {
if ($rule instanceof Closure) {
@ -617,7 +621,6 @@ class Validate
[$type, $rule, $info] = $this->getValidateType($key, $rule);
if (isset($this->append[$field]) && in_array($info, $this->append[$field])) {
} elseif (isset($this->remove[$field]) && in_array($info, $this->remove[$field])) {
// 规则已经移除
$i++;
@ -1611,7 +1614,8 @@ class Validate
$msg = str_replace(
[':attribute', ':1', ':2', ':3'],
[$title, $array[0], $array[1], $array[2]],
$msg);
$msg
);
if (strpos($msg, ':rule')) {
$msg = str_replace(':rule', (string) $rule, $msg);

View File

@ -2,7 +2,7 @@
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// | Copyright (c) 2006~2020 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
@ -42,16 +42,22 @@ use Throwable;
*/
class Output
{
// 不显示信息(静默)
const VERBOSITY_QUIET = 0;
// 正常信息
const VERBOSITY_NORMAL = 1;
// 详细信息
const VERBOSITY_VERBOSE = 2;
// 非常详细的信息
const VERBOSITY_VERY_VERBOSE = 3;
// 调试信息
const VERBOSITY_DEBUG = 4;
const OUTPUT_NORMAL = 0;
const OUTPUT_RAW = 1;
const OUTPUT_PLAIN = 2;
// 输出信息级别
private $verbosity = self::VERBOSITY_NORMAL;
/** @var Buffer|Console|Nothing */
@ -162,7 +168,8 @@ class Output
}
/**
* {@inheritdoc}
* 设置输出信息级别
* @param int $level 输出信息级别
*/
public function setVerbosity(int $level)
{
@ -170,7 +177,8 @@ class Output
}
/**
* {@inheritdoc}
* 获取输出信息级别
* @return int
*/
public function getVerbosity(): int
{
@ -220,5 +228,4 @@ class Output
throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
}
}
}

View File

@ -25,6 +25,7 @@ class Clear extends Command
->addOption('cache', 'c', Option::VALUE_NONE, 'clear cache file')
->addOption('log', 'l', Option::VALUE_NONE, 'clear log file')
->addOption('dir', 'r', Option::VALUE_NONE, 'clear empty dir')
->addOption('expire', 'e', Option::VALUE_NONE, 'clear cache file if cache has expired')
->setDescription('Clear runtime file');
}
@ -41,24 +42,44 @@ class Clear extends Command
}
$rmdir = $input->getOption('dir') ? true : false;
$this->clear(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR, $rmdir);
// --expire 仅当 --cache 时生效
$cache_expire = $input->getOption('expire') && $input->getOption('cache') ? true : false;
$this->clear(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR, $rmdir, $cache_expire);
$output->writeln("<info>Clear Successed</info>");
}
protected function clear(string $path, bool $rmdir): void
protected function clear(string $path, bool $rmdir, bool $cache_expire): void
{
$files = is_dir($path) ? scandir($path) : [];
foreach ($files as $file) {
if ('.' != $file && '..' != $file && is_dir($path . $file)) {
array_map('unlink', glob($path . $file . DIRECTORY_SEPARATOR . '*.*'));
$this->clear($path . $file . DIRECTORY_SEPARATOR, $rmdir, $cache_expire);
if ($rmdir) {
rmdir($path . $file);
@rmdir($path . $file);
}
} elseif ('.gitignore' != $file && is_file($path . $file)) {
if ($cache_expire) {
if ($this->cacheHasExpired($path . $file)) {
unlink($path . $file);
}
} else {
unlink($path . $file);
}
}
}
}
/**
* 缓存文件是否已过期
* @param $filename string 文件路径
* @return bool
*/
protected function cacheHasExpired($filename) {
$content = file_get_contents($filename);
$expire = (int) substr($content, 8, 12);
return 0 != $expire && time() - $expire > filemtime($filename);
}
}

View File

@ -6,7 +6,7 @@ namespace {%namespace%};
use think\Model;
/**
* @mixin think\Model
* @mixin \think\Model
*/
class {%className%} extends Model
{

View File

@ -15,6 +15,7 @@ use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\db\PDOConnection;
class Schema extends Command
{
@ -22,7 +23,7 @@ class Schema extends Command
{
$this->setName('optimize:schema')
->addArgument('dir', Argument::OPTIONAL, 'dir name .')
->addOption('db', null, Option::VALUE_REQUIRED, 'db name .')
->addOption('connection', null, Option::VALUE_REQUIRED, 'connection name .')
->addOption('table', null, Option::VALUE_REQUIRED, 'table name .')
->setDescription('Build database schema cache.');
}
@ -31,22 +32,24 @@ class Schema extends Command
{
$dir = $input->getArgument('dir') ?: '';
$schemaPath = $this->app->db->getConnection()->getConfig('schema_cache_path');
if (!is_dir($schemaPath)) {
mkdir($schemaPath, 0755, true);
}
if ($input->hasOption('table')) {
$connection = $this->app->db->connect($input->getOption('connection'));
if (!$connection instanceof PDOConnection) {
$output->error("only PDO connection support schema cache!");
return;
}
$table = $input->getOption('table');
if (false === strpos($table, '.')) {
$dbName = $this->app->db->getConnection()->getConfig('database');
$dbName = $connection->getConfig('database');
} else {
[$dbName, $table] = explode('.', $table);
}
$tables[] = $table;
} elseif ($input->hasOption('db')) {
$dbName = $input->getOption('db');
$tables = $this->app->db->getConnection()->getTables($dbName);
if ($table == '*') {
$table = $connection->getTables($dbName);
}
$this->buildDataBaseSchema($connection, (array) $table, $dbName);
} else {
if ($dir) {
$appPath = $this->app->getBasePath() . $dir . DIRECTORY_SEPARATOR;
@ -66,14 +69,8 @@ class Schema extends Command
$class = '\\' . $namespace . '\\model\\' . pathinfo($file, PATHINFO_FILENAME);
$this->buildModelSchema($class);
}
$output->writeln('<info>Succeed!</info>');
return;
}
$db = isset($dbName) ? $dbName . '.' : '';
$this->buildDataBaseSchema($schemaPath, $tables, $db);
$output->writeln('<info>Succeed!</info>');
}
@ -83,34 +80,20 @@ class Schema extends Command
if (!$reflect->isAbstract() && $reflect->isSubclassOf('\think\Model')) {
/** @var \think\Model $model */
$model = new $class;
$connection = $model->db()->getConnection();
if ($connection instanceof PDOConnection) {
$table = $model->getTable();
$dbName = $model->db()->getConnection()->getConfig('database');
$path = $model->db()->getConnection()->getConfig('schema_cache_path');
if (!is_dir($path)) {
mkdir($path, 0755, true);
//预读字段信息
$connection->getSchemaInfo($table, true);
}
$content = '<?php ' . PHP_EOL . 'return ';
$info = $model->db()->getConnection()->getTableFieldsInfo($table);
$content .= var_export($info, true) . ';';
file_put_contents($path . $dbName . '.' . $table . '.php', $content);
}
}
protected function buildDataBaseSchema(string $path, array $tables, string $db): void
protected function buildDataBaseSchema(PDOConnection $connection, array $tables, string $dbName): void
{
if ('' == $db) {
$dbName = $this->app->db->getConnection()->getConfig('database') . '.';
} else {
$dbName = $db;
}
foreach ($tables as $table) {
$content = '<?php ' . PHP_EOL . 'return ';
$info = $this->app->db->getConnection()->getTableFieldsInfo($db . $table);
$content .= var_export($info, true) . ';';
file_put_contents($path . $dbName . $table . '.php', $content);
//预读字段信息
$connection->getSchemaInfo("{$dbName}.{$table}", true);
}
}
}

View File

@ -216,7 +216,7 @@ class Descriptor
$description = new ConsoleDescription($console, $describedNamespace);
if (isset($options['raw_text']) && $options['raw_text']) {
$width = $this->getColumnWidth($description->getCommands());
$width = $this->getColumnWidth($description->getNamespaces());
foreach ($description->getCommands() as $command) {
$this->writeText(sprintf("%-${width}s %s", $command->getName(), $command->getDescription()), $options);
@ -235,7 +235,7 @@ class Descriptor
$this->writeText("\n");
$this->writeText("\n");
$width = $this->getColumnWidth($description->getCommands());
$width = $this->getColumnWidth($description->getNamespaces());
if ($describedNamespace) {
$this->writeText(sprintf('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options);
@ -282,14 +282,18 @@ class Descriptor
}
/**
* @param Command[] $commands
* @param Namespaces[] $namespaces
* @return int
*/
private function getColumnWidth(array $commands)
private function getColumnWidth(array $namespaces)
{
$width = 0;
foreach ($commands as $command) {
$width = strlen($command->getName()) > $width ? strlen($command->getName()) : $width;
foreach ($namespaces as $namespace) {
foreach ($namespace['commands'] as $name) {
if (strlen($name) > $width) {
$width = strlen($name);
}
}
}
return $width + 2;

View File

@ -77,7 +77,7 @@ class Handle
try {
$this->app->log->record($log, 'error');
} catch (Exception $e){}
} catch (Exception $e) {}
}
}
@ -175,10 +175,8 @@ class Handle
'POST Data' => $this->app->request->post(),
'Files' => $this->app->request->file(),
'Cookies' => $this->app->request->cookie(),
'Session' => $this->app->session->all(),
'Session' => $this->app->exists('session') ? $this->app->session->all() : [],
'Server/Request Data' => $this->app->request->server(),
'Environment Variables' => $this->app->request->env(),
'ThinkPHP Constants' => $this->getConst(),
],
];
} else {

View File

@ -18,6 +18,32 @@ use think\Facade;
* @see \think\App
* @package think\facade
* @mixin \think\App
* @method static \think\Service|null register(\think\Service|string $service, bool $force = false) 注册服务
* @method static mixed bootService(\think\Service $service) 执行服务
* @method static \think\Service|null getService(string|\think\Service $service) 获取服务
* @method static \think\App debug(bool $debug = true) 开启应用调试模式
* @method static bool isDebug() 是否为调试模式
* @method static \think\App setNamespace(string $namespace) 设置应用命名空间
* @method static string getNamespace() 获取应用类库命名空间
* @method static string version() 获取框架版本
* @method static string getRootPath() 获取应用根目录
* @method static string getBasePath() 获取应用基础目录
* @method static string getAppPath() 获取当前应用目录
* @method static mixed setAppPath(string $path) 设置应用目录
* @method static string getRuntimePath() 获取应用运行时目录
* @method static void setRuntimePath(string $path) 设置runtime目录
* @method static string getThinkPath() 获取核心框架目录
* @method static string getConfigPath() 获取应用配置目录
* @method static string getConfigExt() 获取配置后缀
* @method static float getBeginTime() 获取应用开启时间
* @method static integer getBeginMem() 获取应用初始内存占用
* @method static \think\App initialize() 初始化应用
* @method static bool initialized() 是否初始化过
* @method static void loadLangPack(string $langset) 加载语言包
* @method static void boot() 引导应用
* @method static void loadEvent(array $event) 注册应用事件
* @method static string parseClass(string $layer, string $name) 解析应用类的类名
* @method static bool runningInConsole() 是否运行在命令行下
*/
class App extends Facade
{

View File

@ -13,11 +13,26 @@ declare (strict_types = 1);
namespace think\facade;
use think\Facade;
use think\cache\Driver;
use think\cache\TagSet;
/**
* @see \think\Cache
* @package think\facade
* @mixin \think\Cache
* @method static string|null getDefaultDriver() 默认驱动
* @method static mixed getConfig(null|string $name = null, mixed $default = null) 获取缓存配置
* @method static array getStoreConfig(string $store, string $name = null, null $default = null) 获取驱动配置
* @method static Driver store(string $name = null) 连接或者切换缓存
* @method static bool clear() 清空缓冲池
* @method static mixed get(string $key, mixed $default = null) 读取缓存
* @method static bool set(string $key, mixed $value, int|\DateTime $ttl = null) 写入缓存
* @method static bool delete(string $key) 删除缓存
* @method static iterable getMultiple(iterable $keys, mixed $default = null) 读取缓存
* @method static bool setMultiple(iterable $values, null|int|\DateInterval $ttl = null) 写入缓存
* @method static bool deleteMultiple(iterable $keys) 删除缓存
* @method static bool has(string $key) 判断缓存是否存在
* @method static TagSet tag(string|array $name) 缓存标签
*/
class Cache extends Facade
{

View File

@ -18,6 +18,10 @@ use think\Facade;
* @see \think\Config
* @package think\facade
* @mixin \think\Config
* @method static array load(string $file, string $name = '') 加载配置文件(多种格式)
* @method static bool has(string $name) 检测配置是否存在
* @method static mixed get(string $name = null, mixed $default = null) 获取配置参数 为空则获取所有配置
* @method static array set(array $config, string $name = null) 设置配置参数 name为数组则为批量设置
*/
class Config extends Facade
{

View File

@ -13,11 +13,34 @@ declare (strict_types = 1);
namespace think\facade;
use think\Facade;
use think\console\Command;
use think\console\Input;
use think\console\input\Definition as InputDefinition;
use think\console\Output;
use think\console\output\driver\Buffer;
/**
* Class Console
* @package think\facade
* @mixin \think\Console
* @method static Output|Buffer call(string $command, array $parameters = [], string $driver = 'buffer')
* @method static int run() 执行当前的指令
* @method static int doRun(Input $input, Output $output) 执行指令
* @method static void setDefinition(InputDefinition $definition) 设置输入参数定义
* @method static InputDefinition The InputDefinition instance getDefinition() 获取输入参数定义
* @method static string A help message. getHelp() Gets the help message.
* @method static void setCatchExceptions(bool $boolean) 是否捕获异常
* @method static void setAutoExit(bool $boolean) 是否自动退出
* @method static string getLongVersion() 获取完整的版本号
* @method static void addCommands(array $commands) 添加指令集
* @method static Command|void addCommand(string|Command $command, string $name = '') 添加一个指令
* @method static Command getCommand(string $name) 获取指令
* @method static bool hasCommand(string $name) 某个指令是否存在
* @method static array getNamespaces() 获取所有的命名空间
* @method static string findNamespace(string $namespace) 查找注册命名空间中的名称或缩写。
* @method static Command find(string $name) 查找指令
* @method static Command[] all(string $namespace = null) 获取所有的指令
* @method static string extractNamespace(string $name, int $limit = 0) 返回命名空间部分
*/
class Console extends Facade
{

View File

@ -18,6 +18,13 @@ use think\Facade;
* @see \think\Cookie
* @package think\facade
* @mixin \think\Cookie
* @method static mixed get(mixed $name = '', string $default = null) 获取cookie
* @method static bool has(string $name) 是否存在Cookie参数
* @method static void set(string $name, string $value, mixed $option = null) Cookie 设置
* @method static void forever(string $name, string $value = '', mixed $option = null) 永久保存Cookie数据
* @method static void delete(string $name) Cookie删除
* @method static array getCookie() 获取cookie保存数据
* @method static void save() 保存Cookie
*/
class Cookie extends Facade
{

View File

@ -18,6 +18,17 @@ use think\Facade;
* @see \think\Env
* @package think\facade
* @mixin \think\Env
* @method static void load(string $file) 读取环境变量定义文件
* @method static mixed get(string $name = null, mixed $default = null) 获取环境变量值
* @method static void set(string|array $env, mixed $value = null) 设置环境变量值
* @method static bool has(string $name) 检测是否存在环境变量
* @method static void __set(string $name, mixed $value) 设置环境变量
* @method static mixed __get(string $name) 获取环境变量
* @method static bool __isset(string $name) 检测是否存在环境变量
* @method static void offsetSet($name, $value)
* @method static bool offsetExists($name)
* @method static mixed offsetUnset($name)
* @method static mixed offsetGet($name)
*/
class Env extends Facade
{

View File

@ -18,6 +18,15 @@ use think\Facade;
* @see \think\Event
* @package think\facade
* @mixin \think\Event
* @method static \think\Event listenEvents(array $events) 批量注册事件监听
* @method static \think\Event listen(string $event, mixed $listener, bool $first = false) 注册事件监听
* @method static bool hasListener(string $event) 是否存在事件监听
* @method static void remove(string $event) 移除事件监听
* @method static \think\Event bind(array $events) 指定事件别名标识 便于调用
* @method static \think\Event subscribe(mixed $subscriber) 注册事件订阅者
* @method static \think\Event observe(string|object $observer, null|string $prefix = '') 自动注册事件观察者
* @method static mixed trigger(string|object $event, mixed $params = null, bool $once = false) 触发事件
* @method static mixed until($event, $params = null) 触发事件(只获取一个有效返回值)
*/
class Event extends Facade
{

View File

@ -13,11 +13,16 @@ declare (strict_types = 1);
namespace think\facade;
use think\Facade;
use think\filesystem\Driver;
/**
* Class Filesystem
* @package think\facade
* @mixin \think\Filesystem
* @method static Driver disk(string $name = null) ,null|string
* @method static mixed getConfig(null|string $name = null, mixed $default = null) 获取缓存配置
* @method static array getDiskConfig(string $disk, null $name = null, null $default = null) 获取磁盘配置
* @method static string|null getDefaultDriver() 默认驱动
*/
class Filesystem extends Facade
{

View File

@ -18,6 +18,14 @@ use think\Facade;
* @see \think\Lang
* @package think\facade
* @mixin \think\Lang
* @method static void setLangSet(string $lang) 设置当前语言
* @method static string getLangSet() 获取当前语言
* @method static string defaultLangSet() 获取默认语言
* @method static array load(string|array $file, string $range = '') 加载语言定义(不区分大小写)
* @method static bool has(string|null $name, string $range = '') 判断是否存在语言定义(不区分大小写)
* @method static mixed get(string|null $name = null, array $vars = [], string $range = '') 获取语言定义(不区分大小写)
* @method static string detect(\think\Request $request) 自动侦测设置获取语言选择
* @method static void saveToCookie(\think\Cookie $cookie) 保存当前语言到Cookie
*/
class Lang extends Facade
{

View File

@ -13,11 +13,36 @@ declare (strict_types = 1);
namespace think\facade;
use think\Facade;
use think\log\Channel;
use think\log\ChannelSet;
/**
* @see \think\Log
* @package think\facade
* @mixin \think\Log
* @method static string|null getDefaultDriver() 默认驱动
* @method static mixed getConfig(null|string $name = null, mixed $default = null) 获取日志配置
* @method static array getChannelConfig(string $channel, null $name = null, null $default = null) 获取渠道配置
* @method static Channel|ChannelSet channel(string|array $name = null) driver() 的别名
* @method static mixed createDriver(string $name)
* @method static \think\Log clear(string|array $channel = '*') 清空日志信息
* @method static \think\Log close(string|array $channel = '*') 关闭本次请求日志写入
* @method static array getLog(string $channel = null) 获取日志信息
* @method static bool save() 保存日志信息
* @method static \think\Log record(mixed $msg, string $type = 'info', array $context = [], bool $lazy = true) 记录日志信息
* @method static \think\Log write(mixed $msg, string $type = 'info', array $context = []) 实时写入日志信息
* @method static Event listen($listener) 注册日志写入事件监听
* @method static void log(string $level, mixed $message, array $context = []) 记录日志信息
* @method static void emergency(mixed $message, array $context = []) 记录emergency信息
* @method static void alert(mixed $message, array $context = []) 记录警报信息
* @method static void critical(mixed $message, array $context = []) 记录紧急情况
* @method static void error(mixed $message, array $context = []) 记录错误信息
* @method static void warning(mixed $message, array $context = []) 记录warning信息
* @method static void notice(mixed $message, array $context = []) 记录notice信息
* @method static void info(mixed $message, array $context = []) 记录一般信息
* @method static void debug(mixed $message, array $context = []) 记录调试信息
* @method static void sql(mixed $message, array $context = []) 记录sql信息
* @method static mixed __call($method, $parameters)
*/
class Log extends Facade
{

View File

@ -18,6 +18,15 @@ use think\Facade;
* @see \think\Middleware
* @package think\facade
* @mixin \think\Middleware
* @method static void import(array $middlewares = [], string $type = 'global') 导入中间件
* @method static void add(mixed $middleware, string $type = 'global') 注册中间件
* @method static void route(mixed $middleware) 注册路由中间件
* @method static void controller(mixed $middleware) 注册控制器中间件
* @method static mixed unshift(mixed $middleware, string $type = 'global') 注册中间件到开始位置
* @method static array all(string $type = 'global') 获取注册的中间件
* @method static Pipeline pipeline(string $type = 'global') 调度管道
* @method static mixed end(\think\Response $response) 结束调度
* @method static \think\Response handleException(\think\Request $passable, \Throwable $e) 异常处理
*/
class Middleware extends Facade
{

View File

@ -13,11 +13,112 @@ declare (strict_types = 1);
namespace think\facade;
use think\Facade;
use think\file\UploadedFile;
use think\route\Rule;
/**
* @see \think\Request
* @package think\facade
* @mixin \think\Request
* @method static \think\Request setDomain(string $domain) 设置当前包含协议的域名
* @method static string domain(bool $port = false) 获取当前包含协议的域名
* @method static string rootDomain() 获取当前根域名
* @method static \think\Request setSubDomain(string $domain) 设置当前泛域名的值
* @method static string subDomain() 获取当前子域名
* @method static \think\Request setPanDomain(string $domain) 设置当前泛域名的值
* @method static string panDomain() 获取当前泛域名的值
* @method static \think\Request setUrl(string $url) 设置当前完整URL 包括QUERY_STRING
* @method static string url(bool $complete = false) 获取当前完整URL 包括QUERY_STRING
* @method static \think\Request setBaseUrl(string $url) 设置当前URL 不含QUERY_STRING
* @method static string baseUrl(bool $complete = false) 获取当前URL 不含QUERY_STRING
* @method static string baseFile(bool $complete = false) 获取当前执行的文件 SCRIPT_NAME
* @method static \think\Request setRoot(string $url) 设置URL访问根地址
* @method static string root(bool $complete = false) 获取URL访问根地址
* @method static string rootUrl() 获取URL访问根目录
* @method static \think\Request setPathinfo(string $pathinfo) 设置当前请求的pathinfo
* @method static string pathinfo() 获取当前请求URL的pathinfo信息含URL后缀
* @method static string ext() 当前URL的访问后缀
* @method static integer|float time(bool $float = false) 获取当前请求的时间
* @method static string type() 当前请求的资源类型
* @method static void mimeType(string|array $type, string $val = '') 设置资源类型
* @method static \think\Request setMethod(string $method) 设置请求类型
* @method static string method(bool $origin = false) 当前的请求类型
* @method static bool isGet() 是否为GET请求
* @method static bool isPost() 是否为POST请求
* @method static bool isPut() 是否为PUT请求
* @method static bool isDelete() 是否为DELTE请求
* @method static bool isHead() 是否为HEAD请求
* @method static bool isPatch() 是否为PATCH请求
* @method static bool isOptions() 是否为OPTIONS请求
* @method static bool isCli() 是否为cli
* @method static bool isCgi() 是否为cgi
* @method static mixed param(string|array $name = '', mixed $default = null, string|array $filter = '') 获取当前请求的参数
* @method static \think\Request setRule(Rule $rule) 设置路由变量
* @method static Rule|null rule() 获取当前路由对象
* @method static \think\Request setRoute(array $route) 设置路由变量
* @method static mixed route(string|array $name = '', mixed $default = null, string|array $filter = '') 获取路由参数
* @method static mixed get(string|array $name = '', mixed $default = null, string|array $filter = '') 获取GET参数
* @method static mixed middleware(mixed $name, mixed $default = null) 获取中间件传递的参数
* @method static mixed post(string|array $name = '', mixed $default = null, string|array $filter = '') 获取POST参数
* @method static mixed put(string|array $name = '', mixed $default = null, string|array $filter = '') 获取PUT参数
* @method static mixed delete(mixed $name = '', mixed $default = null, string|array $filter = '') 设置获取DELETE参数
* @method static mixed patch(mixed $name = '', mixed $default = null, string|array $filter = '') 设置获取PATCH参数
* @method static mixed request(string|array $name = '', mixed $default = null, string|array $filter = '') 获取request变量
* @method static mixed env(string $name = '', string $default = null) 获取环境变量
* @method static mixed session(string $name = '', string $default = null) 获取session数据
* @method static mixed cookie(mixed $name = '', string $default = null, string|array $filter = '') 获取cookie参数
* @method static mixed server(string $name = '', string $default = '') 获取server参数
* @method static null|array|UploadedFile file(string $name = '') 获取上传的文件信息
* @method static string|array header(string $name = '', string $default = null) 设置或者获取当前的Header
* @method static mixed input(array $data = [], string|false $name = '', mixed $default = null, string|array $filter = '') 获取变量 支持过滤和默认值
* @method static mixed filter(mixed $filter = null) 设置或获取当前的过滤规则
* @method static mixed filterValue(mixed &$value, mixed $key, array $filters) 递归过滤给定的值
* @method static bool has(string $name, string $type = 'param', bool $checkEmpty = false) 是否存在某个请求参数
* @method static array only(array $name, mixed $data = 'param', string|array $filter = '') 获取指定的参数
* @method static mixed except(array $name, string $type = 'param') 排除指定参数获取
* @method static bool isSsl() 当前是否ssl
* @method static bool isJson() 当前是否JSON请求
* @method static bool isAjax(bool $ajax = false) 当前是否Ajax请求
* @method static bool isPjax(bool $pjax = false) 当前是否Pjax请求
* @method static string ip() 获取客户端IP地址
* @method static boolean isValidIP(string $ip, string $type = '') 检测是否是合法的IP地址
* @method static string ip2bin(string $ip) 将IP地址转换为二进制字符串
* @method static bool isMobile() 检测是否使用手机访问
* @method static string scheme() 当前URL地址中的scheme参数
* @method static string query() 当前请求URL地址中的query参数
* @method static \think\Request setHost(string $host) 设置当前请求的host包含端口
* @method static string host(bool $strict = false) 当前请求的host
* @method static int port() 当前请求URL地址中的port参数
* @method static string protocol() 当前请求 SERVER_PROTOCOL
* @method static int remotePort() 当前请求 REMOTE_PORT
* @method static string contentType() 当前请求 HTTP_CONTENT_TYPE
* @method static string secureKey() 获取当前请求的安全Key
* @method static \think\Request setController(string $controller) 设置当前的控制器名
* @method static \think\Request setAction(string $action) 设置当前的操作名
* @method static string controller(bool $convert = false) 获取当前的控制器名
* @method static string action(bool $convert = false) 获取当前的操作名
* @method static string getContent() 设置或者获取当前请求的content
* @method static string getInput() 获取当前请求的php://input
* @method static string buildToken(string $name = '__token__', mixed $type = 'md5') 生成请求令牌
* @method static bool checkToken(string $token = '__token__', array $data = []) 检查请求令牌
* @method static \think\Request withMiddleware(array $middleware) 设置在中间件传递的数据
* @method static \think\Request withGet(array $get) 设置GET数据
* @method static \think\Request withPost(array $post) 设置POST数据
* @method static \think\Request withCookie(array $cookie) 设置COOKIE数据
* @method static \think\Request withSession(Session $session) 设置SESSION数据
* @method static \think\Request withServer(array $server) 设置SERVER数据
* @method static \think\Request withHeader(array $header) 设置HEADER数据
* @method static \think\Request withEnv(Env $env) 设置ENV数据
* @method static \think\Request withInput(string $input) 设置php://input数据
* @method static \think\Request withFiles(array $files) 设置文件上传数据
* @method static \think\Request withRoute(array $route) 设置ROUTE变量
* @method static mixed __set(string $name, mixed $value) 设置中间传递数据
* @method static mixed __get(string $name) 获取中间传递数据的值
* @method static boolean __isset(string $name) 检测中间传递数据的值
* @method static bool offsetExists($name)
* @method static mixed offsetGet($name)
* @method static mixed offsetSet($name, $value)
* @method static mixed offsetUnset($name)
*/
class Request extends Facade
{

View File

@ -13,11 +13,61 @@ declare (strict_types = 1);
namespace think\facade;
use think\Facade;
use think\route\Dispatch;
use think\route\Domain;
use think\route\Rule;
use think\route\RuleGroup;
use think\route\RuleItem;
use think\route\RuleName;
use think\route\Url as UrlBuild;
/**
* @see \think\Route
* @package think\facade
* @mixin \think\Route
* @method static mixed config(string $name = null)
* @method static \think\Route lazy(bool $lazy = true) 设置路由域名及分组(包括资源路由)是否延迟解析
* @method static void setTestMode(bool $test) 设置路由为测试模式
* @method static bool isTest() 检查路由是否为测试模式
* @method static \think\Route mergeRuleRegex(bool $merge = true) 设置路由域名及分组(包括资源路由)是否合并解析
* @method static void setGroup(RuleGroup $group) 设置当前分组
* @method static RuleGroup getGroup(string $name = null) 获取指定标识的路由分组 不指定则获取当前分组
* @method static \think\Route pattern(array $pattern) 注册变量规则
* @method static \think\Route option(array $option) 注册路由参数
* @method static Domain domain(string|array $name, mixed $rule = null) 注册域名路由
* @method static array getDomains() 获取域名
* @method static RuleName getRuleName() 获取RuleName对象
* @method static \think\Route bind(string $bind, string $domain = null) 设置路由绑定
* @method static array getBind() 读取路由绑定信息
* @method static string|null getDomainBind(string $domain = null) 读取路由绑定
* @method static RuleItem[] getName(string $name = null, string $domain = null, string $method = '*') 读取路由标识
* @method static void import(array $name) 批量导入路由标识
* @method static void setName(string $name, RuleItem $ruleItem, bool $first = false) 注册路由标识
* @method static void setRule(string $rule, RuleItem $ruleItem = null) 保存路由规则
* @method static RuleItem[] getRule(string $rule) 读取路由
* @method static array getRuleList() 读取路由列表
* @method static void clear() 清空路由规则
* @method static RuleItem rule(string $rule, mixed $route = null, string $method = '*') 注册路由规则
* @method static \think\Route setCrossDomainRule(Rule $rule, string $method = '*') 设置跨域有效路由规则
* @method static RuleGroup group(string|\Closure $name, mixed $route = null) 注册路由分组
* @method static RuleItem any(string $rule, mixed $route) 注册路由
* @method static RuleItem get(string $rule, mixed $route) 注册GET路由
* @method static RuleItem post(string $rule, mixed $route) 注册POST路由
* @method static RuleItem put(string $rule, mixed $route) 注册PUT路由
* @method static RuleItem delete(string $rule, mixed $route) 注册DELETE路由
* @method static RuleItem patch(string $rule, mixed $route) 注册PATCH路由
* @method static RuleItem options(string $rule, mixed $route) 注册OPTIONS路由
* @method static Resource resource(string $rule, string $route) 注册资源路由
* @method static RuleItem view(string $rule, string $template = '', array $vars = []) 注册视图路由
* @method static RuleItem redirect(string $rule, string $route = '', int $status = 301) 注册重定向路由
* @method static \think\Route rest(string|array $name, array|bool $resource = []) rest方法定义和修改
* @method static array|null getRest(string $name = null) 获取rest方法定义的参数
* @method static RuleItem miss(string|Closure $route, string $method = '*') 注册未匹配路由规则后的处理
* @method static Response dispatch(\think\Request $request, Closure|bool $withRoute = true) 路由调度
* @method static Dispatch|false check() 检测URL路由
* @method static Dispatch url(string $url) 默认URL解析
* @method static UrlBuild buildUrl(string $url = '', array $vars = []) URL生成 支持路由反射
* @method static RuleGroup __call(string $method, array $args) 设置全局的路由分组参数
*/
class Route extends Facade
{

View File

@ -18,6 +18,8 @@ use think\Facade;
* @see \think\Session
* @package think\facade
* @mixin \think\Session
* @method static mixed getConfig(null|string $name = null, mixed $default = null) 获取Session配置
* @method static string|null getDefaultDriver() 默认驱动
*/
class Session extends Facade
{

View File

@ -18,6 +18,62 @@ use think\Facade;
* @see \think\Validate
* @package think\facade
* @mixin \think\Validate
* @method static void setLang(\think\Lang $lang) 设置Lang对象
* @method static void setDb(\think\Db $db) 设置Db对象
* @method static void setRequest(\think\Request $request) 设置Request对象
* @method static \think\Validate rule(string|array $name, mixed $rule = '') 添加字段验证规则
* @method static \think\Validate extend(string $type, callable $callback = null, string $message = null) 注册验证(类型)规则
* @method static void setTypeMsg(string|array $type, string $msg = null) 设置验证规则的默认提示信息
* @method static Validate message(array $message) 设置提示信息
* @method static \think\Validate scene(string $name) 设置验证场景
* @method static bool hasScene(string $name) 判断是否存在某个验证场景
* @method static \think\Validate batch(bool $batch = true) 设置批量验证
* @method static \think\Validate failException(bool $fail = true) 设置验证失败后是否抛出异常
* @method static \think\Validate only(array $fields) 指定需要验证的字段列表
* @method static \think\Validate remove(string|array $field, mixed $rule = null) 移除某个字段的验证规则
* @method static \think\Validate append(string|array $field, mixed $rule = null) 追加某个字段的验证规则
* @method static bool check(array $data, array $rules = []) 数据自动验证
* @method static bool checkRule(mixed $value, mixed $rules) 根据验证规则验证数据
* @method static bool confirm(mixed $value, mixed $rule, array $data = [], string $field = '') 验证是否和某个字段的值一致
* @method static bool different(mixed $value, mixed $rule, array $data = []) 验证是否和某个字段的值是否不同
* @method static bool egt(mixed $value, mixed $rule, array $data = []) 验证是否大于等于某个值
* @method static bool gt(mixed $value, mixed $rule, array $data = []) 验证是否大于某个值
* @method static bool elt(mixed $value, mixed $rule, array $data = []) 验证是否小于等于某个值
* @method static bool lt(mixed $value, mixed $rule, array $data = []) 验证是否小于某个值
* @method static bool eq(mixed $value, mixed $rule) 验证是否等于某个值
* @method static bool must(mixed $value, mixed $rule = null) 必须验证
* @method static bool is(mixed $value, string $rule, array $data = []) 验证字段值是否为有效格式
* @method static bool token(mixed $value, mixed $rule, array $data) 验证表单令牌
* @method static bool activeUrl(mixed $value, mixed $rule = 'MX') 验证是否为合格的域名或者IP 支持AMXNSSOAPTRCNAMEAAAAA6 SRVNAPTRTXT 或者 ANY类型
* @method static bool ip(mixed $value, mixed $rule = 'ipv4') 验证是否有效IP
* @method static bool fileExt(mixed $file, mixed $rule) 验证上传文件后缀
* @method static bool fileMime(mixed $file, mixed $rule) 验证上传文件类型
* @method static bool fileSize(mixed $file, mixed $rule) 验证上传文件大小
* @method static bool image(mixed $file, mixed $rule) 验证图片的宽高及类型
* @method static bool dateFormat(mixed $value, mixed $rule) 验证时间和日期是否符合指定格式
* @method static bool unique(mixed $value, mixed $rule, array $data = [], string $field = '') 验证是否唯一
* @method static bool filter(mixed $value, mixed $rule) 使用filter_var方式验证
* @method static bool requireIf(mixed $value, mixed $rule, array $data = []) 验证某个字段等于某个值的时候必须
* @method static bool requireCallback(mixed $value, mixed $rule, array $data = []) 通过回调方法验证某个字段是否必须
* @method static bool requireWith(mixed $value, mixed $rule, array $data = []) 验证某个字段有值的情况下必须
* @method static bool requireWithout(mixed $value, mixed $rule, array $data = []) 验证某个字段没有值的情况下必须
* @method static bool in(mixed $value, mixed $rule) 验证是否在范围内
* @method static bool notIn(mixed $value, mixed $rule) 验证是否不在某个范围
* @method static bool between(mixed $value, mixed $rule) between验证数据
* @method static bool notBetween(mixed $value, mixed $rule) 使用notbetween验证数据
* @method static bool length(mixed $value, mixed $rule) 验证数据长度
* @method static bool max(mixed $value, mixed $rule) 验证数据最大长度
* @method static bool min(mixed $value, mixed $rule) 验证数据最小长度
* @method static bool after(mixed $value, mixed $rule, array $data = []) 验证日期
* @method static bool before(mixed $value, mixed $rule, array $data = []) 验证日期
* @method static bool afterWith(mixed $value, mixed $rule, array $data = []) 验证日期
* @method static bool beforeWith(mixed $value, mixed $rule, array $data = []) 验证日期
* @method static bool expire(mixed $value, mixed $rule) 验证有效期
* @method static bool allowIp(mixed $value, mixed $rule) 验证IP许可
* @method static bool denyIp(mixed $value, mixed $rule) 验证IP禁用
* @method static bool regex(mixed $value, mixed $rule) 使用正则验证数据
* @method static array|string getError() 获取错误信息
* @method static bool __call(string $method, array $args) 动态方法 直接调用is方法进行验证
*/
class Validate extends Facade
{

View File

@ -18,6 +18,15 @@ use think\Facade;
* @see \think\View
* @package think\facade
* @mixin \think\View
* @method static \think\View engine(string $type = null) 获取模板引擎
* @method static \think\View assign(string|array $name, mixed $value = null) 模板变量赋值
* @method static \think\View filter(\think\Callable $filter = null) 视图过滤
* @method static string fetch(string $template = '', array $vars = []) 解析和获取模板内容 用于输出
* @method static string display(string $content, array $vars = []) 渲染内容输出
* @method static mixed __set(string $name, mixed $value) 模板变量赋值
* @method static mixed __get(string $name) 取得模板显示变量的值
* @method static bool __isset(string $name) 检测模板变量是否设置
* @method static string|null getDefaultDriver() 默认驱动
*/
class View extends Facade
{

View File

@ -60,13 +60,13 @@ class UploadedFile extends File
$error = $msg;
});
$moved = move_uploaded_file($this->getPathname(), $target);
$moved = move_uploaded_file($this->getPathname(), (string) $target);
restore_error_handler();
if (!$moved) {
throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error)));
}
@chmod($target, 0666 & ~umask());
@chmod((string) $target, 0666 & ~umask());
return $target;
}

View File

@ -72,7 +72,7 @@ class File implements LogHandlerInterface
$info = [];
// 日志信息封装
$time = date($this->config['time_format']);
$time = \DateTime::createFromFormat('0.u00 U', microtime())->setTimezone(new \DateTimeZone(date_default_timezone_get()))->format($this->config['time_format']);
foreach ($log as $type => $val) {
$message = [];

View File

@ -90,14 +90,14 @@ class Socket implements LogHandlerInterface
if ($this->config['debug']) {
if ($this->app->exists('request')) {
$current_uri = $this->app->request->url(true);
$currentUri = $this->app->request->url(true);
} else {
$current_uri = 'cmd:' . implode(' ', $_SERVER['argv'] ?? []);
$currentUri = 'cmd:' . implode(' ', $_SERVER['argv'] ?? []);
}
if (!empty($this->config['format_head'])) {
try {
$current_uri = $this->app->invoke($this->config['format_head'], [$current_uri]);
$currentUri = $this->app->invoke($this->config['format_head'], [$currentUri]);
} catch (NotFoundExceptionInterface $notFoundException) {
// Ignore exception
}
@ -106,16 +106,16 @@ class Socket implements LogHandlerInterface
// 基本信息
$trace[] = [
'type' => 'group',
'msg' => $current_uri,
'msg' => $currentUri,
'css' => $this->css['page'],
];
}
$expand_level = array_flip($this->config['expand_level']);
$expandLevel = array_flip($this->config['expand_level']);
foreach ($log as $type => $val) {
$trace[] = [
'type' => isset($expand_level[$type]) ? 'group' : 'groupCollapsed',
'type' => isset($expandLevel[$type]) ? 'group' : 'groupCollapsed',
'msg' => '[ ' . $type . ' ]',
'css' => $this->css[$type] ?? '',
];
@ -166,18 +166,18 @@ class Socket implements LogHandlerInterface
$tabid = $this->getClientArg('tabid');
if (!$client_id = $this->getClientArg('client_id')) {
$client_id = '';
if (!$clientId = $this->getClientArg('client_id')) {
$clientId = '';
}
if (!empty($this->allowForceClientIds)) {
//强制推送到多个client_id
foreach ($this->allowForceClientIds as $force_client_id) {
$client_id = $force_client_id;
$this->sendToClient($tabid, $client_id, $trace, $force_client_id);
foreach ($this->allowForceClientIds as $forceClientId) {
$clientId = $forceClientId;
$this->sendToClient($tabid, $clientId, $trace, $forceClientId);
}
} else {
$this->sendToClient($tabid, $client_id, $trace, '');
$this->sendToClient($tabid, $clientId, $trace, '');
}
return true;
@ -188,21 +188,21 @@ class Socket implements LogHandlerInterface
* @access protected
* @author Zjmainstay
* @param $tabid
* @param $client_id
* @param $clientId
* @param $logs
* @param $force_client_id
* @param $forceClientId
*/
protected function sendToClient($tabid, $client_id, $logs, $force_client_id)
protected function sendToClient($tabid, $clientId, $logs, $forceClientId)
{
$logs = [
'tabid' => $tabid,
'client_id' => $client_id,
'client_id' => $clientId,
'logs' => $logs,
'force_client_id' => $force_client_id,
'force_client_id' => $forceClientId,
];
$msg = json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR);
$address = '/' . $client_id; //将client_id作为地址 server端通过地址判断将日志发布给谁
$address = '/' . $clientId; //将client_id作为地址 server端通过地址判断将日志发布给谁
$this->send($this->config['host'], $this->config['port'], $msg, $address);
}
@ -222,17 +222,17 @@ class Socket implements LogHandlerInterface
}
//用户认证
$allow_client_ids = $this->config['allow_client_ids'];
$allowClientIds = $this->config['allow_client_ids'];
if (!empty($allow_client_ids)) {
if (!empty($allowClientIds)) {
//通过数组交集得出授权强制推送的client_id
$this->allowForceClientIds = array_intersect($allow_client_ids, $this->config['force_client_ids']);
$this->allowForceClientIds = array_intersect($allowClientIds, $this->config['force_client_ids']);
if (!$tabid && count($this->allowForceClientIds)) {
return true;
}
$client_id = $this->getClientArg('client_id');
if (!in_array($client_id, $allow_client_ids)) {
$clientId = $this->getClientArg('client_id');
if (!in_array($clientId, $allowClientIds)) {
return false;
}
} else {

View File

@ -26,6 +26,7 @@ class AllowCrossDomain
protected $header = [
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Max-Age' => 1800,
'Access-Control-Allow-Methods' => 'GET, POST, PATCH, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers' => 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With',
];
@ -57,10 +58,6 @@ class AllowCrossDomain
}
}
if ($request->method(true) == 'OPTIONS') {
return Response::create()->code(204)->header($header);
}
return $next($request)->header($header);
}
}

View File

@ -24,6 +24,7 @@ class File extends Response
protected $name;
protected $mimeType;
protected $isContent = false;
protected $force = true;
public function __construct($data = '', int $code = 200)
{
@ -62,7 +63,7 @@ class File extends Response
$this->header['Pragma'] = 'public';
$this->header['Content-Type'] = $mimeType ?: 'application/octet-stream';
$this->header['Cache-control'] = 'max-age=' . $this->expire;
$this->header['Content-Disposition'] = 'attachment; filename="' . $name . '"';
$this->header['Content-Disposition'] = ($this->force ? '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';
@ -108,6 +109,18 @@ class File extends Response
return $this;
}
/**
* 设置文件强制下载
* @access public
* @param bool $force 强制浏览器下载
* @return $this
*/
public function force(bool $force)
{
$this->force = $force;
return $this;
}
/**
* 获取文件类型信息
* @access public

View File

@ -51,8 +51,8 @@ class Jsonp extends Response
{
try {
// 返回JSON数据格式到客户端 包含状态信息 [当url_common_param为false时是无法获取到$_GET的数据的故使用Request来获取<xiaobo.sun@qq.com>]
$var_jsonp_handler = $this->request->param($this->options['var_jsonp_handler'], "");
$handler = !empty($var_jsonp_handler) ? $var_jsonp_handler : $this->options['default_jsonp_handler'];
$varJsonpHandler = $this->request->param($this->options['var_jsonp_handler'], "");
$handler = !empty($varJsonpHandler) ? $varJsonpHandler : $this->options['default_jsonp_handler'];
$data = json_encode($data, $this->options['json_encode_param']);

View File

@ -53,19 +53,12 @@ abstract class Dispatch
*/
protected $param;
/**
* 状态码
* @var int
*/
protected $code;
public function __construct(Request $request, Rule $rule, $dispatch, array $param = [], int $code = null)
public function __construct(Request $request, Rule $rule, $dispatch, array $param = [])
{
$this->request = $request;
$this->rule = $rule;
$this->dispatch = $dispatch;
$this->param = $param;
$this->code = $code;
}
public function init(App $app)
@ -244,7 +237,7 @@ abstract class Dispatch
public function __sleep()
{
return ['rule', 'dispatch', 'param', 'code', 'controller', 'actionName'];
return ['rule', 'dispatch', 'param', 'controller', 'actionName'];
}
public function __wakeup()
@ -258,7 +251,6 @@ abstract class Dispatch
return [
'dispatch' => $this->dispatch,
'param' => $this->param,
'code' => $this->code,
'rule' => $this->rule,
];
}

View File

@ -18,13 +18,9 @@ use think\middleware\AllowCrossDomain;
use think\middleware\CheckRequestCache;
use think\middleware\FormTokenCheck;
use think\Request;
use think\Response;
use think\Route;
use think\route\dispatch\Callback as CallbackDispatch;
use think\route\dispatch\Controller as ControllerDispatch;
use think\route\dispatch\Redirect as RedirectDispatch;
use think\route\dispatch\Response as ResponseDispatch;
use think\route\dispatch\View as ViewDispatch;
/**
* 路由规则基础类
@ -505,38 +501,16 @@ abstract class Rule
}
/**
* 当前路由到一个模板地址 当使用数组的时候可以传入模板变量
* 路由到一个模板地址 需要额外传入的模板变量
* @access public
* @param bool|array $view 视图
* @param array $view 视图
* @return $this
*/
public function view($view = true)
public function view(array $view = [])
{
return $this->setOption('view', $view);
}
/**
* 当前路由为重定向
* @access public
* @param bool $redirect 是否为重定向
* @return $this
*/
public function redirect(bool $redirect = true)
{
return $this->setOption('redirect', $redirect);
}
/**
* 设置status
* @access public
* @param int $status 状态码
* @return $this
*/
public function status(int $status)
{
return $this->setOption('status', $status);
}
/**
* 设置路由完整匹配
* @access public
@ -651,20 +625,14 @@ abstract class Rule
*/
protected function dispatch(Request $request, $route, array $option): Dispatch
{
if ($route instanceof Dispatch) {
$result = $route;
if (is_subclass_of($route, Dispatch::class)) {
$result = new $route($request, $this, $route, $this->vars);
} elseif ($route instanceof Closure) {
// 执行闭包
$result = new CallbackDispatch($request, $this, $route, $this->vars);
} elseif ($route instanceof Response) {
$result = new ResponseDispatch($request, $this, $route);
} elseif (isset($option['view']) && false !== $option['view']) {
$result = new ViewDispatch($request, $this, $route, is_array($option['view']) ? $option['view'] : $this->vars);
} elseif (!empty($option['redirect'])) {
// 路由到重定向地址
$result = new RedirectDispatch($request, $this, $route, $this->vars, $option['status'] ?? 301);
} elseif (false !== strpos($route, '\\')) {
} elseif (false !== strpos($route, '@') || false !== strpos($route, '::')) {
// 路由到类的方法
$route = str_replace('::', '@', $route);
$result = $this->dispatchMethod($request, $route);
} else {
// 路由到控制器/操作

View File

@ -16,9 +16,7 @@ use Closure;
use think\Container;
use think\Exception;
use think\Request;
use think\Response;
use think\Route;
use think\route\dispatch\Response as ResponseDispatch;
/**
* 路由分组类
@ -29,16 +27,7 @@ class RuleGroup extends Rule
* 分组路由(包括子分组)
* @var array
*/
protected $rules = [
'*' => [],
'get' => [],
'post' => [],
'put' => [],
'patch' => [],
'delete' => [],
'head' => [],
'options' => [],
];
protected $rules = [];
/**
* 分组路由规则
@ -151,15 +140,13 @@ class RuleGroup extends Rule
// 解析分组路由
if ($this instanceof Resource) {
$this->buildResourceRule();
} elseif ($this->rule instanceof Response) {
return new ResponseDispatch($request, $this, $this->rule);
} else {
$this->parseGroupRule($this->rule);
}
// 获取当前路由规则
$method = strtolower($request->method());
$rules = $this->getMethodRules($method);
$rules = $this->getRules($method);
if ($this->parent) {
// 合并分组参数
@ -183,7 +170,7 @@ class RuleGroup extends Rule
// 检查分组路由
foreach ($rules as $key => $item) {
$result = $item->check($request, $url, $completeMatch);
$result = $item[1]->check($request, $url, $completeMatch);
if (false !== $result) {
return $result;
@ -200,17 +187,6 @@ class RuleGroup extends Rule
return $result;
}
/**
* 获取当前请求的路由规则(包括子分组、资源路由)
* @access protected
* @param string $method 请求类型
* @return array
*/
protected function getMethodRules(string $method): array
{
return array_merge($this->rules[$method], $this->rules['*']);
}
/**
* 分组URL匹配检查
* @access protected
@ -302,7 +278,8 @@ class RuleGroup extends Rule
$regex = [];
$items = [];
foreach ($rules as $key => $item) {
foreach ($rules as $key => $val) {
$item = $val[1];
if ($item instanceof RuleItem) {
$rule = $depr . str_replace('/', $depr, $item->getRule());
if ($depr == $rule && $depr != $url) {
@ -460,10 +437,10 @@ class RuleGroup extends Rule
$method = '*';
}
$this->rules[$method][] = $rule;
$this->rules[] = [$method, $rule];
if ($rule instanceof RuleItem && 'options' != $method) {
$this->rules['options'][] = $rule->setAutoOptions();
$this->rules[] = ['options', $rule->setAutoOptions()];
}
return $this;
@ -517,7 +494,9 @@ class RuleGroup extends Rule
return $this->rules;
}
return $this->rules[strtolower($method)] ?? [];
return array_filter($this->rules, function ($item) use ($method) {
return $method == $item[0] || $item[0] == '*';
});
}
/**
@ -527,15 +506,6 @@ class RuleGroup extends Rule
*/
public function clear(): void
{
$this->rules = [
'*' => [],
'get' => [],
'post' => [],
'put' => [],
'patch' => [],
'delete' => [],
'head' => [],
'options' => [],
];
$this->rules = [];
}
}

View File

@ -164,9 +164,9 @@ class Url
$domains = $this->route->getDomains();
if (!empty($domains)) {
$route_domain = array_keys($domains);
foreach ($route_domain as $domain_prefix) {
if (0 === strpos($domain_prefix, '*.') && strpos($domain, ltrim($domain_prefix, '*.')) !== false) {
$routeDomain = array_keys($domains);
foreach ($routeDomain as $domainPrefix) {
if (0 === strpos($domainPrefix, '*.') && strpos($domain, ltrim($domainPrefix, '*.')) !== false) {
foreach ($domains as $key => $rule) {
$rule = is_array($rule) ? $rule[0] : $rule;
if (is_string($rule) && false === strpos($key, '*') && 0 === strpos($url, $rule)) {

View File

@ -145,7 +145,10 @@ class Controller extends Dispatch
}
if (is_string($val) && strpos($val, ':')) {
$val = explode(':', $val, 2);
$val = explode(':', $val);
if (count($val) > 1) {
$val = [$val[0], array_slice($val, 1)];
}
}
$this->app->middleware->controller($val);

View File

@ -1,27 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
declare (strict_types = 1);
namespace think\route\dispatch;
use think\Response;
use think\route\Dispatch;
/**
* Redirect Dispatcher
*/
class Redirect extends Dispatch
{
public function exec()
{
return Response::create($this->dispatch, 'redirect')->code($this->code);
}
}

View File

@ -1,27 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
declare (strict_types = 1);
namespace think\route\dispatch;
use think\route\Dispatch;
/**
* Response Dispatcher
*/
class Response extends Dispatch
{
public function exec()
{
return $this->dispatch;
}
}

View File

@ -23,14 +23,14 @@ use think\route\Rule;
class Url extends Controller
{
public function __construct(Request $request, Rule $rule, $dispatch, array $param = [], int $code = null)
public function __construct(Request $request, Rule $rule, $dispatch)
{
$this->request = $request;
$this->rule = $rule;
// 解析默认的URL规则
$dispatch = $this->parseUrl($dispatch);
parent::__construct($request, $rule, $dispatch, $this->param, $code);
parent::__construct($request, $rule, $dispatch, $this->param);
}
/**

View File

@ -1,28 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
declare (strict_types = 1);
namespace think\route\dispatch;
use think\Response;
use think\route\Dispatch;
/**
* View Dispatcher
*/
class View extends Dispatch
{
public function exec()
{
// 渲染模板输出
return Response::create($this->dispatch, 'view')->assign($this->param);
}
}

View File

@ -5,6 +5,7 @@ namespace think\tests;
use Mockery as m;
use PHPUnit\Framework\TestCase;
use think\Cache;
use think\cache\Driver;
use think\Config;
use think\Db;
use think\Event;
@ -23,6 +24,10 @@ class DbTest extends TestCase
$config = m::mock(Config::class);
$log = m::mock(Log::class);
$cache = m::mock(Cache::class);
$store = m::mock(Driver::class);
$config->shouldReceive('get')->with('database.cache_store', null)->andReturn(null);
$cache->shouldReceive('store')->with(null)->andReturn($store);
$db = Db::__make($event, $config, $log, $cache);

View File

@ -108,15 +108,6 @@ class EventTest extends TestCase
$this->event->trigger('bar');
}
public function testWithoutEvent()
{
$this->event->withEvent(false);
$this->event->listen('SomeListener', TestListener::class);
$this->assertFalse($this->event->hasListener('SomeListener'));
}
}
class TestListener

View File

@ -0,0 +1,30 @@
<?php
namespace think\tests;
use Mockery as m;
use Mockery\MockInterface;
use think\App;
use think\Config;
use think\Container;
trait InteractsWithApp
{
/** @var App|MockInterface */
protected $app;
/** @var Config|MockInterface */
protected $config;
protected function prepareApp()
{
$this->app = m::mock(App::class)->makePartial();
Container::setInstance($this->app);
$this->app->shouldReceive('make')->with(App::class)->andReturn($this->app);
$this->app->shouldReceive('isDebug')->andReturnTrue();
$this->config = m::mock(Config::class)->makePartial();
$this->config->shouldReceive('get')->with('app.show_error_msg')->andReturnTrue();
$this->app->shouldReceive('get')->with('config')->andReturn($this->config);
$this->app->shouldReceive('runningInConsole')->andReturn(false);
}
}

View File

@ -7,23 +7,16 @@ use Mockery as m;
use Mockery\MockInterface;
use org\bovigo\vfs\vfsStream;
use PHPUnit\Framework\TestCase;
use think\App;
use think\Config;
use think\Container;
use think\Log;
use think\log\ChannelSet;
class LogTest extends TestCase
{
/** @var App|MockInterface */
protected $app;
use InteractsWithApp;
/** @var Log|MockInterface */
protected $log;
/** @var Config|MockInterface */
protected $config;
protected function tearDown(): void
{
m::close();
@ -31,13 +24,7 @@ class LogTest extends TestCase
protected function setUp()
{
$this->app = m::mock(App::class)->makePartial();
Container::setInstance($this->app);
$this->app->shouldReceive('make')->with(App::class)->andReturn($this->app);
$this->config = m::mock(Config::class)->makePartial();
$this->app->shouldReceive('get')->with('config')->andReturn($this->config);
$this->app->shouldReceive('runningInConsole')->andReturn(false);
$this->prepareApp();
$this->log = new Log($this->app);
}

View File

@ -5,9 +5,6 @@ namespace think\tests;
use Mockery as m;
use Mockery\MockInterface;
use PHPUnit\Framework\TestCase;
use think\App;
use think\Config;
use think\Container;
use think\Exception;
use think\exception\Handle;
use think\Middleware;
@ -17,15 +14,11 @@ use think\Response;
class MiddlewareTest extends TestCase
{
/** @var App|MockInterface */
protected $app;
use InteractsWithApp;
/** @var Middleware|MockInterface */
protected $middleware;
/** @var Config|MockInterface */
protected $config;
protected function tearDown(): void
{
m::close();
@ -33,13 +26,7 @@ class MiddlewareTest extends TestCase
protected function setUp()
{
$this->app = m::mock(App::class)->makePartial();
Container::setInstance($this->app);
$this->app->shouldReceive('make')->with(App::class)->andReturn($this->app);
$this->config = m::mock(Config::class)->makePartial();
$this->app->shouldReceive('get')->with('config')->andReturn($this->config);
$this->app->shouldReceive('runningInConsole')->andReturn(false);
$this->prepareApp();
$this->middleware = new Middleware($this->app);
}

View File

@ -0,0 +1,285 @@
<?php
namespace think\tests;
use Closure;
use Mockery as m;
use Mockery\MockInterface;
use PHPUnit\Framework\TestCase;
use think\helper\Str;
use think\Request;
use think\response\Redirect;
use think\response\View;
use think\Route;
class RouteTest extends TestCase
{
use InteractsWithApp;
/** @var Route|MockInterface */
protected $route;
protected function tearDown(): void
{
m::close();
}
protected function setUp()
{
$this->prepareApp();
$this->route = new Route($this->app);
}
/**
* @param $path
* @param string $method
* @param string $host
* @return m\Mock|Request
*/
protected function makeRequest($path, $method = 'GET', $host = 'localhost')
{
$request = m::mock(Request::class)->makePartial();
$request->shouldReceive('host')->andReturn($host);
$request->shouldReceive('pathinfo')->andReturn($path);
$request->shouldReceive('url')->andReturn('/' . $path);
$request->shouldReceive('method')->andReturn(strtoupper($method));
return $request;
}
public function testSimpleRequest()
{
$this->route->get('foo', function () {
return 'get-foo';
});
$this->route->put('foo', function () {
return 'put-foo';
});
$this->route->group(function () {
$this->route->post('foo', function () {
return 'post-foo';
});
});
$request = $this->makeRequest('foo', 'post');
$response = $this->route->dispatch($request);
$this->assertEquals(200, $response->getCode());
$this->assertEquals('post-foo', $response->getContent());
$request = $this->makeRequest('foo', 'get');
$response = $this->route->dispatch($request);
$this->assertEquals(200, $response->getCode());
$this->assertEquals('get-foo', $response->getContent());
}
public function testOptionsRequest()
{
$this->route->get('foo', function () {
return 'get-foo';
});
$this->route->put('foo', function () {
return 'put-foo';
});
$this->route->group(function () {
$this->route->post('foo', function () {
return 'post-foo';
});
});
$this->route->group('abc', function () {
$this->route->post('foo/:id', function () {
return 'post-abc-foo';
});
});
$this->route->post('foo/:id', function () {
return 'post-abc-foo';
});
$this->route->resource('bar', 'SomeClass');
$request = $this->makeRequest('foo', 'options');
$response = $this->route->dispatch($request);
$this->assertEquals(204, $response->getCode());
$this->assertEquals('GET, PUT, POST', $response->getHeader('Allow'));
$request = $this->makeRequest('bar', 'options');
$response = $this->route->dispatch($request);
$this->assertEquals(204, $response->getCode());
$this->assertEquals('GET, POST', $response->getHeader('Allow'));
$request = $this->makeRequest('bar/1', 'options');
$response = $this->route->dispatch($request);
$this->assertEquals(204, $response->getCode());
$this->assertEquals('GET, PUT, DELETE', $response->getHeader('Allow'));
$request = $this->makeRequest('xxxx', 'options');
$response = $this->route->dispatch($request);
$this->assertEquals(204, $response->getCode());
$this->assertEquals('GET, POST, PUT, DELETE', $response->getHeader('Allow'));
}
public function testAllowCrossDomain()
{
$this->route->get('foo', function () {
return 'get-foo';
})->allowCrossDomain(['some' => 'bar']);
$request = $this->makeRequest('foo', 'get');
$response = $this->route->dispatch($request);
$this->assertEquals('bar', $response->getHeader('some'));
$this->assertArrayHasKey('Access-Control-Allow-Credentials', $response->getHeader());
$request = $this->makeRequest('foo2', 'options');
$response = $this->route->dispatch($request);
$this->assertEquals(204, $response->getCode());
$this->assertArrayHasKey('Access-Control-Allow-Credentials', $response->getHeader());
$this->assertEquals('GET, POST, PUT, DELETE', $response->getHeader('Allow'));
}
public function testControllerDispatch()
{
$this->route->get('foo', 'foo/bar');
$controller = m::Mock(\stdClass::class);
$this->app->shouldReceive('parseClass')->with('controller', 'Foo')->andReturn($controller->mockery_getName());
$this->app->shouldReceive('make')->with($controller->mockery_getName(), [], true)->andReturn($controller);
$controller->shouldReceive('bar')->andReturn('bar');
$request = $this->makeRequest('foo');
$response = $this->route->dispatch($request);
$this->assertEquals('bar', $response->getContent());
}
public function testEmptyControllerDispatch()
{
$this->route->get('foo', 'foo/bar');
$controller = m::Mock(\stdClass::class);
$this->app->shouldReceive('parseClass')->with('controller', 'Error')->andReturn($controller->mockery_getName());
$this->app->shouldReceive('make')->with($controller->mockery_getName(), [], true)->andReturn($controller);
$controller->shouldReceive('bar')->andReturn('bar');
$request = $this->makeRequest('foo');
$response = $this->route->dispatch($request);
$this->assertEquals('bar', $response->getContent());
}
protected function createMiddleware($times = 1)
{
$middleware = m::mock(Str::random(5));
$middleware->shouldReceive('handle')->times($times)->andReturnUsing(function ($request, Closure $next) {
return $next($request);
});
$this->app->shouldReceive('make')->with($middleware->mockery_getName())->andReturn($middleware);
return $middleware;
}
public function testControllerWithMiddleware()
{
$this->route->get('foo', 'foo/bar');
$controller = m::mock(FooClass::class);
$controller->middleware = [
$this->createMiddleware()->mockery_getName() . ":params1:params2",
$this->createMiddleware(0)->mockery_getName() => ['except' => 'bar'],
$this->createMiddleware()->mockery_getName() => ['only' => 'bar'],
];
$this->app->shouldReceive('parseClass')->with('controller', 'Foo')->andReturn($controller->mockery_getName());
$this->app->shouldReceive('make')->with($controller->mockery_getName(), [], true)->andReturn($controller);
$controller->shouldReceive('bar')->once()->andReturn('bar');
$request = $this->makeRequest('foo');
$response = $this->route->dispatch($request);
$this->assertEquals('bar', $response->getContent());
}
public function testUrlDispatch()
{
$controller = m::mock(FooClass::class);
$controller->shouldReceive('index')->andReturn('bar');
$this->app->shouldReceive('parseClass')->once()->with('controller', 'Foo')->andReturn($controller->mockery_getName());
$this->app->shouldReceive('make')->with($controller->mockery_getName(), [], true)->andReturn($controller);
$request = $this->makeRequest('foo');
$response = $this->route->dispatch($request);
$this->assertEquals('bar', $response->getContent());
}
public function testRedirectDispatch()
{
$this->route->redirect('foo', 'http://localhost', 302);
$request = $this->makeRequest('foo');
$response = $this->route->dispatch($request);
$this->assertInstanceOf(Redirect::class, $response);
$this->assertEquals(302, $response->getCode());
$this->assertEquals('http://localhost', $response->getData());
}
public function testViewDispatch()
{
$this->route->view('foo', 'index/hello', ['city' => 'shanghai']);
$request = $this->makeRequest('foo');
$response = $this->route->dispatch($request);
$this->assertInstanceOf(View::class, $response);
$this->assertEquals(['city' => 'shanghai'], $response->getVars());
$this->assertEquals('index/hello', $response->getData());
}
public function testResponseDispatch()
{
$this->route->get('hello/:name', response()
->data('Hello,ThinkPHP')
->code(200)
->contentType('text/plain'));
$request = $this->makeRequest('hello/some');
$response = $this->route->dispatch($request);
$this->assertEquals('Hello,ThinkPHP', $response->getContent());
$this->assertEquals(200, $response->getCode());
}
public function testDomainBindResponse()
{
$this->route->domain('test', function () {
$this->route->get('/', function () {
return 'Hello,ThinkPHP';
});
});
$request = $this->makeRequest('', 'get', 'test.domain.com');
$response = $this->route->dispatch($request);
$this->assertEquals('Hello,ThinkPHP', $response->getContent());
$this->assertEquals(200, $response->getCode());
}
}
class FooClass
{
public $middleware = [];
public function bar()
{
}
}

View File

@ -1,3 +1,3 @@
<?php
require __DIR__.'/../vendor/autoload.php';
include __DIR__.'/../src/helper.php';

View File

@ -59,6 +59,7 @@ class Url extends UrlBuild
$bind = $this->app->config->get('app.domain_bind', []);
if ($key = array_search($app, $bind)) {
isset($bind[$_SERVER['SERVER_NAME']]) && $domain = $_SERVER['SERVER_NAME'];
$domain = is_bool($domain) ? $key : $domain;
} else {
$map = $this->app->config->get('app.app_map', []);

View File

@ -229,21 +229,11 @@ class DbManager
* @access public
* @param string|null $name 连接配置标识
* @param bool $force 强制重新连接
* @return BaseQuery
* @return ConnectionInterface
*/
public function connect(string $name = null, bool $force = false): BaseQuery
public function connect(string $name = null, bool $force = false)
{
$connection = $this->instance($name, $force);
$class = $connection->getQueryClass();
$query = new $class($connection);
$timeRule = $this->getConfig('time_query_rule');
if (!empty($timeRule)) {
$query->timeRule($timeRule);
}
return $query;
return $this->instance($name, $force);
}
/**

View File

@ -632,9 +632,8 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
// 模型更新
$db = $this->db();
$db->startTrans();
try {
$db->transaction(function () use ($data, $allowFields, $db) {
$this->key = null;
$where = $this->getWhere();
@ -651,17 +650,12 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
if (!empty($this->relationWrite)) {
$this->autoRelationUpdate();
}
$db->commit();
});
// 更新回调
$this->trigger('AfterUpdate');
return true;
} catch (\Exception $e) {
$db->rollback();
throw $e;
}
}
/**
@ -693,9 +687,8 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
$allowFields = $this->checkAllowFields();
$db = $this->db();
$db->startTrans();
try {
$db->transaction(function () use ($sequence, $allowFields, $db) {
$result = $db->strict(false)
->field($allowFields)
->replace($this->replace)
@ -715,8 +708,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
if (!empty($this->relationWrite)) {
$this->autoRelationInsert();
}
$db->commit();
});
// 标记数据已经存在
$this->exists = true;
@ -725,10 +717,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
$this->trigger('AfterInsert');
return true;
} catch (\Exception $e) {
$db->rollback();
throw $e;
}
}
/**
@ -769,9 +757,9 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
public function saveAll(iterable $dataSet, bool $replace = true): Collection
{
$db = $this->db();
$db->startTrans();
try {
$result = $db->transaction(function () use ($replace, $dataSet) {
$pk = $this->getPk();
if (is_string($pk) && $replace) {
@ -790,13 +778,10 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
}
}
$db->commit();
return $result;
});
return $this->toCollection($result);
} catch (\Exception $e) {
$db->rollback();
throw $e;
}
}
/**
@ -814,9 +799,8 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
$where = $this->getWhere();
$db = $this->db();
$db->startTrans();
try {
$db->transaction(function () use ($where, $db) {
// 删除当前模型数据
$db->where($where)->delete();
@ -824,8 +808,7 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
if (!empty($this->relationWrite)) {
$this->autoRelationDelete();
}
$db->commit();
});
$this->trigger('AfterDelete');
@ -833,10 +816,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
$this->lazySave = false;
return true;
} catch (\Exception $e) {
$db->rollback();
throw $e;
}
}
/**

View File

@ -239,6 +239,10 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
static::$currentPathResolver = $resolver;
}
/**
* 获取数据总条数
* @return int
*/
public function total(): int
{
if ($this->simple) {
@ -248,16 +252,28 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
return $this->total;
}
/**
* 获取每页数量
* @return int
*/
public function listRows(): int
{
return $this->listRows;
}
/**
* 获取当前页页码
* @return int
*/
public function currentPage(): int
{
return $this->currentPage;
}
/**
* 获取最后一页页码
* @return int
*/
public function lastPage(): int
{
if ($this->simple) {
@ -445,7 +461,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
}
/**
* Count elements of an object
* 统计数据集条数
* @return int
*/
public function count(): int
{
@ -457,6 +474,10 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
return (string) $this->render();
}
/**
* 转换为数组
* @return array
*/
public function toArray(): array
{
try {

View File

@ -149,7 +149,7 @@ abstract class Builder
$item = $this->parseKey($query, $key, true);
if ($val instanceof Raw) {
$result[$item] = $val->getValue();
$result[$item] = $this->parseRaw($query, $val);
continue;
} elseif (!is_scalar($val) && (in_array($key, (array) $query->getOptions('json')) || 'json' == $query->getFieldType($key))) {
$val = json_encode($val);
@ -165,7 +165,7 @@ abstract class Builder
}
} elseif (is_null($val)) {
$result[$item] = 'NULL';
} elseif (is_array($val) && !empty($val)) {
} elseif (is_array($val) && !empty($val) && is_string($val[0])) {
switch (strtoupper($val[0])) {
case 'INC':
$result[$item] = $item . ' + ' . floatval($val[1]);
@ -195,7 +195,7 @@ abstract class Builder
protected function parseDataBind(Query $query, string $key, $data, array $bind = []): string
{
if ($data instanceof Raw) {
return $data->getValue();
return $this->parseRaw($query, $data);
}
$name = $query->bindValue($data, $bind[$key] ?? PDO::PARAM_STR);
@ -243,7 +243,7 @@ abstract class Builder
foreach ($fields as $key => $field) {
if ($field instanceof Raw) {
$array[] = $field->getValue();
$array[] = $this->parseRaw($query, $field);
} elseif (!is_numeric($key)) {
$array[] = $this->parseKey($query, $key) . ' AS ' . $this->parseKey($query, $field, true);
} else {
@ -273,7 +273,7 @@ abstract class Builder
foreach ((array) $tables as $key => $table) {
if ($table instanceof Raw) {
$item[] = $table->getValue();
$item[] = $this->parseRaw($query, $table);
} elseif (!is_numeric($key)) {
$item[] = $this->parseKey($query, $key) . ' ' . $this->parseKey($query, $table);
} elseif (isset($options['alias'][$table])) {
@ -350,7 +350,7 @@ abstract class Builder
$where = [];
foreach ($val as $value) {
if ($value instanceof Raw) {
$where[] = ' ' . $logic . ' ( ' . $value->getValue() . ' )';
$where[] = ' ' . $logic . ' ( ' . $this->parseRaw($query, $value) . ' )';
continue;
}
@ -577,7 +577,7 @@ abstract class Builder
protected function parseExp(Query $query, string $key, string $exp, Raw $value, string $field, int $bindType): string
{
// 表达式查询
return '( ' . $key . ' ' . $value->getValue() . ' )';
return '( ' . $key . ' ' . $this->parseRaw($query, $value) . ' )';
}
/**
@ -659,7 +659,7 @@ abstract class Builder
if ($value instanceof Closure) {
$value = $this->parseClosure($query, $value, false);
} elseif ($value instanceof Raw) {
$value = $value->getValue();
$value = $this->parseRaw($query, $value);
} else {
throw new Exception('where express error:' . $value);
}
@ -753,7 +753,7 @@ abstract class Builder
if ($value instanceof Closure) {
$value = $this->parseClosure($query, $value, false);
} elseif ($value instanceof Raw) {
$value = $value->getValue();
$value = $this->parseRaw($query, $value);
} else {
$value = array_unique(is_array($value) ? $value : explode(',', $value));
$array = [];
@ -891,7 +891,7 @@ abstract class Builder
$array = [];
foreach ($order as $key => $val) {
if ($val instanceof Raw) {
$array[] = $val->getValue();
$array[] = $this->parseRaw($query, $val);
} elseif (is_array($val) && preg_match('/^[\w\.]+$/', $key)) {
$array[] = $this->parseOrderField($query, $key, $val);
} elseif ('[rand]' == $val) {
@ -916,6 +916,25 @@ abstract class Builder
return empty($array) ? '' : ' ORDER BY ' . implode(',', $array);
}
/**
* 分析Raw对象
* @access protected
* @param Query $query 查询对象
* @param Raw $raw Raw对象
* @return string
*/
protected function parseRaw(Query $query, Raw $raw): string
{
$sql = $raw->getValue();
$bind = $raw->getBind();
if ($bind) {
$query->bindParams($sql, $bind);
}
return $sql;
}
/**
* 随机排序
* @access protected

View File

@ -14,12 +14,11 @@ namespace think\db;
use Psr\SimpleCache\CacheInterface;
use think\DbManager;
use think\db\CacheItem;
/**
* 数据库连接基础类
*/
abstract class Connection
abstract class Connection implements ConnectionInterface
{
/**
@ -90,7 +89,7 @@ abstract class Connection
/**
* Db对象
* @var Db
* @var DbManager
*/
protected $db;
@ -108,10 +107,27 @@ abstract class Connection
/**
* 缓存对象
* @var Cache
* @var CacheInterface
*/
protected $cache;
/**
* 架构函数 读取数据库配置信息
* @access public
* @param array $config 数据库配置数组
*/
public function __construct(array $config = [])
{
if (!empty($config)) {
$this->config = array_merge($this->config, $config);
}
// 创建Builder对象
$class = $this->getBuilderClass();
$this->builder = new $class($this);
}
/**
* 获取当前的builder实例对象
* @access public
@ -122,6 +138,45 @@ abstract class Connection
return $this->builder;
}
/**
* 创建查询对象
*/
public function newQuery()
{
$class = $this->getQueryClass();
/** @var BaseQuery $query */
$query = new $class($this);
$timeRule = $this->db->getConfig('time_query_rule');
if (!empty($timeRule)) {
$query->timeRule($timeRule);
}
return $query;
}
/**
* 指定表名开始查询
* @param $table
* @return BaseQuery
*/
public function table($table)
{
return $this->newQuery()->table($table);
}
/**
* 指定表名开始查询(不带前缀)
* @param $name
* @return BaseQuery
*/
public function name($name)
{
return $this->newQuery()->name($name);
}
/**
* 设置当前的数据库Db对象
* @access public
@ -214,7 +269,7 @@ abstract class Connection
* 分析缓存Key
* @access protected
* @param BaseQuery $query 查询对象
* @param string $method查询方法
* @param string $method 查询方法
* @return string
*/
protected function getCacheKey(BaseQuery $query, string $method = ''): string
@ -233,7 +288,7 @@ abstract class Connection
* @access protected
* @param BaseQuery $query 查询对象
* @param array $cache 缓存信息
* @param string $method查询方法
* @param string $method 查询方法
* @return CacheItem
*/
protected function parseCache(BaseQuery $query, array $cache, string $method = ''): CacheItem

View File

@ -27,6 +27,20 @@ interface ConnectionInterface
*/
public function getQueryClass(): string;
/**
* 指定表名开始查询
* @param $table
* @return BaseQuery
*/
public function table($table);
/**
* 指定表名开始查询(不带前缀)
* @param $name
* @return BaseQuery
*/
public function name($name);
/**
* 连接数据库方法
* @access public
@ -72,9 +86,6 @@ interface ConnectionInterface
* @access public
* @param BaseQuery $query 查询对象
* @return array
* @throws DbException
* @throws ModelNotFoundException
* @throws DataNotFoundException
*/
public function find(BaseQuery $query): array;
@ -83,9 +94,6 @@ interface ConnectionInterface
* @access public
* @param BaseQuery $query 查询对象
* @return array
* @throws DbException
* @throws ModelNotFoundException
* @throws DataNotFoundException
*/
public function select(BaseQuery $query): array;
@ -104,8 +112,6 @@ interface ConnectionInterface
* @param BaseQuery $query 查询对象
* @param mixed $dataSet 数据集
* @return integer
* @throws \Exception
* @throws \Throwable
*/
public function insertAll(BaseQuery $query, array $dataSet = []): int;
@ -114,8 +120,6 @@ interface ConnectionInterface
* @access public
* @param BaseQuery $query 查询对象
* @return integer
* @throws Exception
* @throws PDOException
*/
public function update(BaseQuery $query): int;
@ -124,8 +128,6 @@ interface ConnectionInterface
* @access public
* @param BaseQuery $query 查询对象
* @return int
* @throws Exception
* @throws PDOException
*/
public function delete(BaseQuery $query): int;
@ -135,7 +137,6 @@ interface ConnectionInterface
* @param BaseQuery $query 查询对象
* @param string $field 字段名
* @param mixed $default 默认值
* @param bool $one 返回一个值
* @return mixed
*/
public function value(BaseQuery $query, string $field, $default = null);
@ -155,9 +156,6 @@ interface ConnectionInterface
* @access public
* @param callable $callback 数据操作方法回调
* @return mixed
* @throws PDOException
* @throws \Exception
* @throws \Throwable
*/
public function transaction(callable $callback);
@ -165,8 +163,6 @@ interface ConnectionInterface
* 启动事务
* @access public
* @return void
* @throws \PDOException
* @throws \Exception
*/
public function startTrans();
@ -174,7 +170,6 @@ interface ConnectionInterface
* 用于非自动提交状态下面的查询提交
* @access public
* @return void
* @throws PDOException
*/
public function commit();
@ -182,7 +177,6 @@ interface ConnectionInterface
* 事务回滚
* @access public
* @return void
* @throws PDOException
*/
public function rollback();

View File

@ -71,6 +71,7 @@ class Fetch
* @access public
* @param string $field 字段名
* @param mixed $default 默认值
* @param bool $one
* @return string
*/
public function value(string $field, $default = null, bool $one = true): string

View File

@ -9,38 +9,24 @@
declare (strict_types = 1);
namespace think\db;
use MongoDB\Driver\BulkWrite;
use MongoDB\Driver\Command;
use MongoDB\Driver\Cursor;
use MongoDB\Driver\Exception\AuthenticationException;
use MongoDB\Driver\Exception\BulkWriteException;
use MongoDB\Driver\Exception\ConnectionException;
use MongoDB\Driver\Exception\InvalidArgumentException;
use MongoDB\Driver\Exception\RuntimeException;
use MongoDB\Driver\Query as MongoQuery;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\WriteConcern;
use think\Collection;
use think\db\connector\Mongo as Connection;
use think\db\exception\DbException as Exception;
use think\Paginator;
class Mongo extends BaseQuery
{
/**
* 执行查询 返回数据集
* @access public
* @param MongoQuery $query 查询对象
* @return mixed
* @throws AuthenticationException
* @throws InvalidArgumentException
* @throws ConnectionException
* @throws RuntimeException
* 当前数据库连接对象
* @var \think\db\connector\Mongo
*/
public function query(MongoQuery $query)
{
return $this->connection->query($this, $query);
}
protected $connection;
/**
* 执行指令 返回数据集
@ -60,22 +46,6 @@ class Mongo extends BaseQuery
return $this->connection->command($command, $dbName, $readPreference, $typeMap);
}
/**
* 执行语句
* @access public
* @param BulkWrite $bulk
* @return int
* @throws AuthenticationException
* @throws InvalidArgumentException
* @throws ConnectionException
* @throws RuntimeException
* @throws BulkWriteException
*/
public function execute(BulkWrite $bulk)
{
return $this->connection->execute($this, $bulk);
}
/**
* 执行command
* @access public
@ -699,7 +669,8 @@ class Mongo extends BaseQuery
if (isset($options['page'])) {
// 根据页数计算limit
list($page, $listRows) = $options['page'];
[$page, $listRows] = $options['page'];
$page = $page > 0 ? $page : 1;
$listRows = $listRows > 0 ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 20);
$offset = $listRows * ($page - 1);

View File

@ -16,14 +16,13 @@ use Closure;
use PDO;
use PDOStatement;
use think\db\exception\BindParamException;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\db\exception\DbException;
use think\db\exception\PDOException;
/**
* 数据库连接基础类
*/
abstract class PDOConnection extends Connection implements ConnectionInterface
abstract class PDOConnection extends Connection
{
const PARAM_FLOAT = 21;
@ -76,8 +75,6 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
'break_reconnect' => false,
// 断线标识字符串
'break_match_str' => [],
// 字段缓存路径
'schema_cache_path' => '',
];
/**
@ -180,23 +177,6 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
*/
protected $bind = [];
/**
* 架构函数 读取数据库配置信息
* @access public
* @param array $config 数据库配置数组
*/
public function __construct(array $config = [])
{
if (!empty($config)) {
$this->config = array_merge($this->config, $config);
}
// 创建Builder对象
$class = $this->getBuilderClass();
$this->builder = new $class($this);
}
/**
* 获取当前连接器类对应的Query类
* @access public
@ -285,6 +265,8 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
$result = 'timestamp';
} elseif (0 === strpos($type, 'datetime')) {
$result = 'datetime';
} elseif (0 === strpos($type, 'date')) {
$result = 'date';
} else {
$result = 'string';
}
@ -318,46 +300,42 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
}
/**
* 获取数据表信息
* @access public
* @param mixed $tableName 数据表名 留空自动获取
* @param string $fetch 获取信息类型 包括 fields type bind pk
* @return mixed
* 获取数据表信息缓存key
* @access protected
* @param string $schema 数据表名称
* @return string
*/
public function getTableInfo($tableName, string $fetch = '')
protected function getSchemaCacheKey(string $schema): string
{
if (is_array($tableName)) {
$tableName = key($tableName) ?: current($tableName);
return $this->getConfig('hostname') . ':' . $this->getConfig('hostport') . '@' . $schema;
}
if (strpos($tableName, ',') || strpos($tableName, ')')) {
// 多表不获取字段信息
return [];
}
[$tableName] = explode(' ', $tableName);
/**
* @param string $tableName 数据表名称
* @param bool $force 强制从数据库获取
* @return array
*/
public function getSchemaInfo(string $tableName, $force = false)
{
if (!strpos($tableName, '.')) {
$schema = $this->getConfig('database') . '.' . $tableName;
} else {
$schema = $tableName;
}
if (!isset($this->info[$schema])) {
if (!isset($this->info[$schema]) || $force) {
// 读取字段缓存
$cacheFile = $this->config['schema_cache_path'] . $schema . '.php';
$cacheKey = $this->getSchemaCacheKey($schema);
$cacheField = $this->config['fields_cache'] && !empty($this->cache);
if ($this->config['fields_cache'] && is_file($cacheFile)) {
$info = include $cacheFile;
} else {
$info = $this->getTableFieldsInfo($tableName);
if ($this->config['fields_cache']) {
if (!is_dir($this->config['schema_cache_path'])) {
mkdir($this->config['schema_cache_path'], 0755, true);
if ($cacheField && !$force) {
$info = $this->cache->get($cacheKey);
}
$content = '<?php ' . PHP_EOL . 'return ' . var_export($info, true) . ';';
file_put_contents($cacheFile, $content);
if (empty($info)) {
$info = $this->getTableFieldsInfo($tableName);
if ($cacheField) {
$this->cache->set($cacheKey, $info);
}
}
@ -379,7 +357,32 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
];
}
return $fetch ? $this->info[$schema][$fetch] : $this->info[$schema];
return $this->info[$schema];
}
/**
* 获取数据表信息
* @access public
* @param mixed $tableName 数据表名 留空自动获取
* @param string $fetch 获取信息类型 包括 fields type bind pk
* @return mixed
*/
public function getTableInfo($tableName, string $fetch = '')
{
if (is_array($tableName)) {
$tableName = key($tableName) ?: current($tableName);
}
if (strpos($tableName, ',') || strpos($tableName, ')')) {
// 多表不获取字段信息
return [];
}
[$tableName] = explode(' ', $tableName);
$info = $this->getSchemaInfo($tableName);
return $fetch ? $info[$fetch] : $info;
}
/**
@ -541,6 +544,17 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
}
}
/**
* 视图查询
* @access public
* @param array $args
* @return BaseQuery
*/
public function view(...$args)
{
return $this->newQuery()->view(...$args);
}
/**
* 创建PDO实例
* @param $dsn
@ -604,16 +618,46 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
/**
* 执行查询 返回数据集
* @access public
* @param string $sql sql指令
* @param array $bind 参数绑定
* @param bool $master 主库读取
* @return array
* @throws BindParamException
* @throws \PDOException
*/
public function query(string $sql, array $bind = [], bool $master = false): array
{
return $this->pdoQuery($this->newQuery(), $sql, $bind, $master);
}
/**
* 执行语句
* @access public
* @param string $sql sql指令
* @param array $bind 参数绑定
* @return int
* @throws BindParamException
* @throws \PDOException
*/
public function execute(string $sql, array $bind = []): int
{
return $this->pdoExecute($this->newQuery(), $sql, $bind, true);
}
/**
* 执行查询 返回数据集
* @access protected
* @param BaseQuery $query 查询对象
* @param mixed $sql sql指令
* @param array $bind 参数绑定
* @param bool $master 主库读取
* @return array
* @throws BindParamException
* @throws \PDOException
* @throws \Exception
* @throws \Throwable
*/
public function query(BaseQuery $query, $sql, array $bind = []): array
protected function pdoQuery(BaseQuery $query, $sql, array $bind = [], bool $master = null): array
{
// 分析查询表达式
$query->parseOptions();
@ -623,8 +667,10 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
$cacheItem = $this->parseCache($query, $query->getOptions('cache'));
$key = $cacheItem->getKey();
if ($this->cache->has($key)) {
return $this->cache->get($key);
$data = $this->cache->get($key);
if (null !== $data) {
return $data;
}
}
@ -633,7 +679,10 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
$bind = $query->getBind();
}
if (!isset($master)) {
$master = $query->getOptions('master') ? true : false;
}
$procedure = $query->getOptions('procedure') ? true : in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
$this->getPDOStatement($sql, $bind, $master, $procedure);
@ -728,7 +777,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
/**
* 执行语句
* @access public
* @access protected
* @param BaseQuery $query 查询对象
* @param string $sql sql指令
* @param array $bind 参数绑定
@ -739,7 +788,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
* @throws \Exception
* @throws \Throwable
*/
public function execute(BaseQuery $query, string $sql, array $bind = [], bool $origin = false): int
protected function pdoExecute(BaseQuery $query, string $sql, array $bind = [], bool $origin = false): int
{
if ($origin) {
$query->parseOptions();
@ -784,8 +833,6 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
* @param BaseQuery $query 查询对象
* @return array
* @throws DbException
* @throws ModelNotFoundException
* @throws DataNotFoundException
*/
public function find(BaseQuery $query): array
{
@ -794,7 +841,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
if (!$result) {
// 执行查询
$resultSet = $this->query($query, function ($query) {
$resultSet = $this->pdoQuery($query, function ($query) {
return $this->builder->select($query, true);
});
@ -830,8 +877,6 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
* @param BaseQuery $query 查询对象
* @return array
* @throws DbException
* @throws ModelNotFoundException
* @throws DataNotFoundException
*/
public function select(BaseQuery $query): array
{
@ -839,7 +884,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
if (!$resultSet) {
// 执行查询操作
$resultSet = $this->query($query, function ($query) {
$resultSet = $this->pdoQuery($query, function ($query) {
return $this->builder->select($query);
});
}
@ -863,7 +908,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
$sql = $this->builder->insert($query);
// 执行操作
$result = '' == $sql ? 0 : $this->execute($query, $sql, $query->getBind());
$result = '' == $sql ? 0 : $this->pdoExecute($query, $sql, $query->getBind());
if ($result) {
$sequence = $options['sequence'] ?? null;
@ -923,7 +968,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
foreach ($array as $item) {
$sql = $this->builder->insertAll($query, $item, $replace);
$count += $this->execute($query, $sql, $query->getBind());
$count += $this->pdoExecute($query, $sql, $query->getBind());
}
// 提交事务
@ -938,7 +983,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
$sql = $this->builder->insertAll($query, $dataSet, $replace);
return $this->execute($query, $sql, $query->getBind());
return $this->pdoExecute($query, $sql, $query->getBind());
}
/**
@ -957,7 +1002,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
$sql = $this->builder->selectInsert($query, $fields, $table);
return $this->execute($query, $sql, $query->getBind());
return $this->pdoExecute($query, $sql, $query->getBind());
}
/**
@ -975,7 +1020,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
$sql = $this->builder->update($query);
// 执行操作
$result = '' == $sql ? 0 : $this->execute($query, $sql, $query->getBind());
$result = '' == $sql ? 0 : $this->pdoExecute($query, $sql, $query->getBind());
if ($result) {
$this->db->trigger('after_update', $query);
@ -1000,7 +1045,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
$sql = $this->builder->delete($query);
// 执行操作
$result = $this->execute($query, $sql, $query->getBind());
$result = $this->pdoExecute($query, $sql, $query->getBind());
if ($result) {
$this->db->trigger('after_delete', $query);
@ -1457,7 +1502,7 @@ abstract class PDOConnection extends Connection implements ConnectionInterface
try {
foreach ($sqlArray as $sql) {
$this->execute($query, $sql, $bind);
$this->pdoExecute($query, $sql, $bind);
}
// 提交事务
$this->commit();

View File

@ -33,11 +33,7 @@ class Query extends BaseQuery
*/
public function orderRaw(string $field, array $bind = [])
{
if (!empty($bind)) {
$this->bindParams($field, $bind);
}
$this->options['order'][] = new Raw($field);
$this->options['order'][] = new Raw($field, $bind);
return $this;
}
@ -111,34 +107,6 @@ class Query extends BaseQuery
return $this;
}
/**
* 执行查询 返回数据集
* @access public
* @param string $sql sql指令
* @param array $bind 参数绑定
* @return array
* @throws BindParamException
* @throws PDOException
*/
public function query(string $sql, array $bind = []): array
{
return $this->connection->query($this, $sql, $bind);
}
/**
* 执行语句
* @access public
* @param string $sql sql指令
* @param array $bind 参数绑定
* @return int
* @throws BindParamException
* @throws PDOException
*/
public function execute(string $sql, array $bind = []): int
{
return $this->connection->execute($this, $sql, $bind, true);
}
/**
* 获取执行的SQL语句而不进行实际的查询
* @access public

View File

@ -24,15 +24,24 @@ class Raw
*/
protected $value;
/**
* 参数绑定
*
* @var array
*/
protected $bind = [];
/**
* 创建一个查询表达式
*
* @param string $value
* @param array $bind
* @return void
*/
public function __construct(string $value)
public function __construct(string $value, array $bind = [])
{
$this->value = $value;
$this->bind = $bind;
}
/**
@ -45,6 +54,16 @@ class Raw
return $this->value;
}
/**
* 获取参数绑定
*
* @return string
*/
public function getBind(): array
{
return $this->bind;
}
public function __toString()
{
return (string) $this->value;

View File

@ -270,7 +270,7 @@ class Mysql extends Builder
protected function parseRegexp(Query $query, string $key, string $exp, $value, string $field): string
{
if ($value instanceof Raw) {
$value = $value->getValue();
$value = $this->parseRaw($query, $value);
}
return $key . ' ' . $exp . ' ' . $value;
@ -289,7 +289,7 @@ class Mysql extends Builder
protected function parseFindInSet(Query $query, string $key, string $exp, $value, string $field): string
{
if ($value instanceof Raw) {
$value = $value->getValue();
$value = $this->parseRaw($query, $value);
}
return 'FIND_IN_SET(' . $value . ', ' . $key . ')';
@ -308,7 +308,7 @@ class Mysql extends Builder
if (is_int($key)) {
return (string) $key;
} elseif ($key instanceof Raw) {
return $key->getValue();
return $this->parseRaw($query, $key);
}
$key = trim($key);
@ -396,7 +396,7 @@ class Mysql extends Builder
}
if ($duplicate instanceof Raw) {
return ' ON DUPLICATE KEY UPDATE ' . $duplicate->getValue() . ' ';
return ' ON DUPLICATE KEY UPDATE ' . $this->parseRaw($query, $duplicate) . ' ';
}
if (is_string($duplicate)) {
@ -409,7 +409,7 @@ class Mysql extends Builder
$val = $this->parseKey($query, $val);
$updates[] = $val . ' = VALUES(' . $val . ')';
} elseif ($val instanceof Raw) {
$updates[] = $this->parseKey($query, $key) . " = " . $val->getValue();
$updates[] = $this->parseKey($query, $key) . " = " . $this->parseRaw($query, $val);
} else {
$name = $query->bindValue($val, $query->getConnection()->getFieldBindType($key));
$updates[] = $this->parseKey($query, $key) . " = :" . $name;

View File

@ -69,7 +69,7 @@ class Pgsql extends Builder
if (is_int($key)) {
return (string) $key;
} elseif ($key instanceof Raw) {
return $key->getValue();
return $this->parseRaw($query, $key);
}
$key = trim($key);

View File

@ -68,7 +68,7 @@ class Sqlite extends Builder
if (is_int($key)) {
return (string) $key;
} elseif ($key instanceof Raw) {
return $key->getValue();
return $this->parseRaw($query, $key);
}
$key = trim($key);

View File

@ -73,7 +73,7 @@ class Sqlsrv extends Builder
foreach ($order as $key => $val) {
if ($val instanceof Raw) {
$array[] = $val->getValue();
$array[] = $this->parseRaw($query, $val);
} elseif ('[rand]' == $val) {
$array[] = $this->parseRand($query);
} else {
@ -115,7 +115,7 @@ class Sqlsrv extends Builder
if (is_int($key)) {
return (string) $key;
} elseif ($key instanceof Raw) {
return $key->getValue();
return $this->parseRaw($query, $key);
}
$key = trim($key);

View File

@ -71,7 +71,7 @@ trait ParamsBind
* @param array $bind 参数绑定
* @return void
*/
protected function bindParams(string &$sql, array $bind = []): void
public function bindParams(string &$sql, array $bind = []): void
{
foreach ($bind as $key => $value) {
if (is_array($value)) {

View File

@ -290,11 +290,7 @@ trait WhereQuery
*/
public function whereExp(string $field, string $where, array $bind = [], string $logic = 'AND')
{
if (!empty($bind)) {
$this->bindParams($where, $bind);
}
$this->options['where'][$logic][] = [$field, 'EXP', new Raw($where)];
$this->options['where'][$logic][] = [$field, 'EXP', new Raw($where, $bind)];
return $this;
}
@ -329,11 +325,7 @@ trait WhereQuery
*/
public function whereRaw(string $where, array $bind = [], string $logic = 'AND')
{
if (!empty($bind)) {
$this->bindParams($where, $bind);
}
$this->options['where'][$logic][] = new Raw($where);
$this->options['where'][$logic][] = new Raw($where, $bind);
return $this;
}

View File

@ -27,14 +27,13 @@ use MongoDB\Driver\WriteConcern;
use think\db\BaseQuery;
use think\db\builder\Mongo as Builder;
use think\db\Connection;
use think\db\ConnectionInterface;
use think\db\exception\DbException as Exception;
use think\db\Mongo as Query;
/**
* Mongo数据库驱动
*/
class Mongo extends Connection implements ConnectionInterface
class Mongo extends Connection
{
// 查询数据类型
@ -45,6 +44,9 @@ class Mongo extends Connection implements ConnectionInterface
protected $session_uuid; // sessions会话列表当前会话数组key 随机生成
protected $sessions = []; // 会话列表
/** @var Builder */
protected $builder;
// 数据库连接参数配置
protected $config = [
// 数据库类型
@ -97,23 +99,6 @@ class Mongo extends Connection implements ConnectionInterface
'type_map' => ['root' => 'array', 'document' => 'array'],
];
/**
* 架构函数 读取数据库配置信息
* @access public
* @param array $config 数据库配置数组
*/
public function __construct(array $config = [])
{
if (!empty($config)) {
$this->config = array_merge($this->config, $config);
}
// 创建Builder对象
$class = $this->getBuilderClass();
$this->builder = new $class($this);
}
/**
* 获取当前连接器类对应的Query类
* @access public
@ -129,7 +114,7 @@ class Mongo extends Connection implements ConnectionInterface
* @access public
* @return Builder
*/
public function getBuilder(): Builder
public function getBuilder()
{
return $this->builder;
}
@ -201,7 +186,7 @@ class Mongo extends Connection implements ConnectionInterface
* 设置/获取当前操作的database
* @access public
* @param string $db db
* @throws Exception
* @return string
*/
public function db(string $db = null)
{
@ -215,10 +200,10 @@ class Mongo extends Connection implements ConnectionInterface
/**
* 执行查询但只返回Cursor对象
* @access public
* @param BaseQuery $query 查询对象
* @param Query $query 查询对象
* @return Cursor
*/
public function cursor(BaseQuery $query)
public function cursor($query)
{
// 分析查询表达式
$options = $query->parseOptions();
@ -271,7 +256,7 @@ class Mongo extends Connection implements ConnectionInterface
if ($session = $this->getSession()) {
$this->cursor = $this->mongo->executeQuery($namespace, $query, [
'readPreference' => is_null($readPreference) ? new ReadPreference(ReadPreference::RP_PRIMARY) : $readPreference,
'session' => $session
'session' => $session,
]);
} else {
$this->cursor = $this->mongo->executeQuery($namespace, $mongoQuery, $readPreference);
@ -286,8 +271,39 @@ class Mongo extends Connection implements ConnectionInterface
}
/**
* 执行查询
* 执行查询 返回数据集
* @access public
* @param MongoQuery $query 查询对象
* @return mixed
* @throws AuthenticationException
* @throws InvalidArgumentException
* @throws ConnectionException
* @throws RuntimeException
*/
public function query(MongoQuery $query)
{
return $this->mongoQuery($this->newQuery(), $query);
}
/**
* 执行语句
* @access public
* @param BulkWrite $bulk
* @return int
* @throws AuthenticationException
* @throws InvalidArgumentException
* @throws ConnectionException
* @throws RuntimeException
* @throws BulkWriteException
*/
public function execute(BulkWrite $bulk)
{
return $this->mongoExecute($this->newQuery(), $bulk);
}
/**
* 执行查询
* @access protected
* @param BaseQuery $query 查询对象
* @param MongoQuery|Closure $mongoQuery Mongo查询对象
* @return array
@ -296,7 +312,7 @@ class Mongo extends Connection implements ConnectionInterface
* @throws ConnectionException
* @throws RuntimeException
*/
public function query(BaseQuery $query, $mongoQuery): array
protected function mongoQuery(BaseQuery $query, $mongoQuery): array
{
$options = $query->parseOptions();
@ -330,7 +346,7 @@ class Mongo extends Connection implements ConnectionInterface
/**
* 执行写操作
* @access public
* @access protected
* @param BaseQuery $query
* @param BulkWrite $bulk
*
@ -341,7 +357,7 @@ class Mongo extends Connection implements ConnectionInterface
* @throws RuntimeException
* @throws BulkWriteException
*/
public function execute(BaseQuery $query, BulkWrite $bulk)
protected function mongoExecute(BaseQuery $query, BulkWrite $bulk)
{
$this->initConnect(true);
$this->db->updateQueryTimes();
@ -364,7 +380,7 @@ class Mongo extends Connection implements ConnectionInterface
if ($session = $this->getSession()) {
$writeResult = $this->mongo->executeBulkWrite($namespace, $bulk, [
'session' => $session,
'writeConcern' => is_null($writeConcern) ? new WriteConcern(1) : $writeConcern
'writeConcern' => is_null($writeConcern) ? new WriteConcern(1) : $writeConcern,
]);
} else {
$writeResult = $this->mongo->executeBulkWrite($namespace, $bulk, $writeConcern);
@ -423,7 +439,7 @@ class Mongo extends Connection implements ConnectionInterface
if ($session = $this->getSession()) {
$this->cursor = $this->mongo->executeCommand($dbName, $command, [
'readPreference' => is_null($readPreference) ? new ReadPreference(ReadPreference::RP_PRIMARY) : $readPreference,
'session' => $session
'session' => $session,
]);
} else {
$this->cursor = $this->mongo->executeCommand($dbName, $command, $readPreference);
@ -705,7 +721,7 @@ class Mongo extends Connection implements ConnectionInterface
// 生成bulk对象
$bulk = $this->builder->insert($query);
$writeResult = $this->execute($query, $bulk);
$writeResult = $this->mongoExecute($query, $bulk);
$result = $writeResult->getInsertedCount();
if ($result) {
@ -776,7 +792,7 @@ class Mongo extends Connection implements ConnectionInterface
// 生成bulkWrite对象
$bulk = $this->builder->insertAll($query, $dataSet);
$writeResult = $this->execute($query, $bulk);
$writeResult = $this->mongoExecute($query, $bulk);
return $writeResult->getInsertedCount();
}
@ -800,7 +816,7 @@ class Mongo extends Connection implements ConnectionInterface
// 生成bulkWrite对象
$bulk = $this->builder->update($query);
$writeResult = $this->execute($query, $bulk);
$writeResult = $this->mongoExecute($query, $bulk);
$result = $writeResult->getModifiedCount();
@ -832,7 +848,7 @@ class Mongo extends Connection implements ConnectionInterface
$bulk = $this->builder->delete($query);
// 执行操作
$writeResult = $this->execute($query, $bulk);
$writeResult = $this->mongoExecute($query, $bulk);
$result = $writeResult->getDeletedCount();
@ -860,7 +876,7 @@ class Mongo extends Connection implements ConnectionInterface
$resultSet = $this->db->trigger('before_select', $query);
if (!$resultSet) {
$resultSet = $this->query($query, function ($query) {
$resultSet = $this->mongoQuery($query, function ($query) {
return $this->builder->select($query);
});
}
@ -887,7 +903,7 @@ class Mongo extends Connection implements ConnectionInterface
if (!$result) {
// 执行查询
$resultSet = $this->query($query, function ($query) {
$resultSet = $this->mongoQuery($query, function ($query) {
return $this->builder->select($query, true);
});
@ -932,7 +948,7 @@ class Mongo extends Connection implements ConnectionInterface
}
// 执行查询操作
$resultSet = $this->query($query, $mongoQuery);
$resultSet = $this->mongoQuery($query, $mongoQuery);
if (!empty($resultSet)) {
$data = array_shift($resultSet);
@ -992,7 +1008,7 @@ class Mongo extends Connection implements ConnectionInterface
}
// 执行查询操作
$resultSet = $this->query($query, $mongoQuery);
$resultSet = $this->mongoQuery($query, $mongoQuery);
if (('*' == $field || strpos($field, ',')) && $key) {
$result = array_column($resultSet, null, $key);

View File

@ -74,10 +74,10 @@ class Mysql extends PDOConnection
$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',
'comment' => $val['comment'],
];
}

View File

@ -69,7 +69,7 @@ class BelongsTo extends OneToOne
if ($relationModel) {
if (!empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($relationModel, $this->parent);
$this->bindAttr($this->parent, $relationModel);
}
$relationModel->setParent(clone $this->parent);
@ -233,9 +233,9 @@ class BelongsTo extends OneToOne
$relationModel->exists(true);
}
if ($relationModel && !empty($this->bindAttr)) {
if (!empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($relationModel, $result);
$this->bindAttr($result, $relationModel);
} else {
// 设置关联属性
$result->setRelation($relation, $relationModel);
@ -274,9 +274,9 @@ class BelongsTo extends OneToOne
$relationModel->exists(true);
}
if ($relationModel && !empty($this->bindAttr)) {
if (!empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($relationModel, $result);
$this->bindAttr($result, $relationModel);
} else {
// 设置关联属性
$result->setRelation($relation, $relationModel);

View File

@ -16,7 +16,6 @@ use think\Collection;
use think\db\BaseQuery as Query;
use think\db\exception\DbException as Exception;
use think\db\Raw;
use think\helper\Str;
use think\Model;
use think\model\Pivot;
use think\model\Relation;
@ -146,22 +145,6 @@ class BelongsToMany extends Relation
}
}
/**
* 创建关联查询Query对象
* @access protected
* @return Query
*/
protected function buildQuery(): Query
{
$foreignKey = $this->foreignKey;
$localKey = $this->localKey;
// 关联查询
$condition = ['pivot.' . $localKey, '=', $this->parent->getKey()];
return $this->belongsToManyQuery($foreignKey, $localKey, [$condition]);
}
/**
* 延迟获取关联数据
* @access public
@ -175,8 +158,7 @@ class BelongsToMany extends Relation
$closure($this->getClosureType($closure));
}
$result = $this->buildQuery()
->relation($subRelation)
$result = $this->relation($subRelation)
->select()
->setParent(clone $this->parent);
@ -193,7 +175,8 @@ class BelongsToMany extends Relation
*/
public function select($data = null): Collection
{
$result = $this->buildQuery()->select($data);
$this->baseQuery();
$result = $this->query->select($data);
$this->hydratePivot($result);
return $result;
@ -208,7 +191,8 @@ class BelongsToMany extends Relation
*/
public function paginate($listRows = null, $simple = false): Paginator
{
$result = $this->buildQuery()->paginate($listRows, $simple);
$this->baseQuery();
$result = $this->query->paginate($listRows, $simple);
$this->hydratePivot($result);
return $result;
@ -222,37 +206,16 @@ class BelongsToMany extends Relation
*/
public function find($data = null)
{
$result = $this->buildQuery()->findOrEmpty($data);
$this->baseQuery();
$result = $this->query->find($data);
if (!$result->isEmpty()) {
if ($result && !$result->isEmpty()) {
$this->hydratePivot([$result]);
}
return $result;
}
/**
* 查找多条记录 如果不存在则抛出异常
* @access public
* @param array|string|Query|\Closure $data
* @return Collection
*/
public function selectOrFail($data = null): Collection
{
return $this->buildQuery()->failException(true)->select($data);
}
/**
* 查找单条记录 如果不存在则抛出异常
* @access public
* @param array|string|Query|\Closure $data
* @return Model
*/
public function findOrFail($data = null): Model
{
return $this->buildQuery()->failException(true)->find($data);
}
/**
* 根据关联条件查询当前模型
* @access public
@ -485,17 +448,13 @@ class BelongsToMany extends Relation
$this->query->limit($this->withLimit);
}
$query = $this->query
$this->query
->field($fields)
->tableField(true, $table, 'pivot', 'pivot__');
if (empty($this->baseQuery)) {
$relationFk = $this->query->getPk();
$query->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk)
->tableField(true, $table, 'pivot', 'pivot__')
->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $this->query->getPk())
->where($condition);
}
return $query;
return $this->query;
}
/**
@ -695,4 +654,24 @@ class BelongsToMany extends Relation
return $changes;
}
/**
* 执行基础查询(仅执行一次)
* @access protected
* @return void
*/
protected function baseQuery(): void
{
if (empty($this->baseQuery)) {
$foreignKey = $this->foreignKey;
$localKey = $this->localKey;
// 关联查询
$condition = ['pivot.' . $localKey, '=', $this->parent->getKey()];
$this->belongsToManyQuery($foreignKey, $localKey, [$condition]);
$this->baseQuery = true;
}
}
}

View File

@ -68,7 +68,7 @@ class HasOne extends OneToOne
if ($relationModel) {
if (!empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($relationModel, $this->parent);
$this->bindAttr($this->parent, $relationModel);
}
$relationModel->setParent(clone $this->parent);
@ -232,9 +232,9 @@ class HasOne extends OneToOne
$relationModel->exists(true);
}
if ($relationModel && !empty($this->bindAttr)) {
if (!empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($relationModel, $result);
$this->bindAttr($result, $relationModel);
} else {
// 设置关联属性
$result->setRelation($relation, $relationModel);
@ -273,9 +273,9 @@ class HasOne extends OneToOne
$relationModel->exists(true);
}
if ($relationModel && !empty($this->bindAttr)) {
if (!empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($relationModel, $result);
$this->bindAttr($result, $relationModel);
} else {
$result->setRelation($relation, $relationModel);
}

View File

@ -12,11 +12,11 @@
namespace think\model\relation;
use Closure;
use Exception;
use think\db\BaseQuery as Query;
use think\db\Raw;
use think\helper\Str;
use think\Model;
use think\model\Relation;
use think\model\Pivot;
/**
* 多态多对多关联
@ -24,12 +24,6 @@ use think\model\Relation;
class MorphToMany extends BelongsToMany
{
/**
* 多态关联外键
* @var string
*/
protected $morphKey;
/**
* 多态字段名
* @var string
@ -61,12 +55,14 @@ class MorphToMany extends BelongsToMany
*/
public function __construct(Model $parent, string $model, string $middle, string $morphType, string $morphKey, string $localKey, bool $inverse = false)
{
$this->morphKey = $morphKey;
$this->morphType = $morphType;
$this->inverse = $inverse;
$this->morphClass = $inverse ? $model : get_class($parent);
parent::__construct($parent, $model, $middle, $morphKey, $localKey);
$foreignKey = $inverse ? $morphKey : $localKey;
$localKey = $inverse ? $localKey : $morphKey;
parent::__construct($parent, $model, $middle, $foreignKey, $localKey);
}
/**
@ -94,7 +90,7 @@ class MorphToMany extends BelongsToMany
if (!empty($range)) {
// 查询关联数据
$data = $this->eagerlyManyToMany([
['pivot.' . $this->morphKey, 'in', $range],
['pivot.' . $this->localKey, 'in', $range],
['pivot.' . $this->morphType, '=', $this->morphClass],
], $subRelation, $closure, $cache);
@ -127,7 +123,7 @@ class MorphToMany extends BelongsToMany
$pk = $result->$pk;
// 查询管理数据
$data = $this->eagerlyManyToMany([
['pivot.' . $this->morphKey, '=', $pk],
['pivot.' . $this->localKey, '=', $pk],
['pivot.' . $this->morphType, '=', $this->morphClass],
], $subRelation, $closure, $cache);
@ -164,8 +160,8 @@ class MorphToMany extends BelongsToMany
$closure($this->getClosureType($closure), $name);
}
return $this->belongsToManyQuery($this->morphKey, $this->localKey, [
['pivot.' . ($this->inverse ? $this->localKey : $this->morphKey), '=', $pk],
return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
['pivot.' . $this->localKey, '=', $pk],
['pivot.' . $this->morphType, '=', $this->morphClass],
])->$aggregate($field);
}
@ -185,8 +181,8 @@ class MorphToMany extends BelongsToMany
$closure($this->getClosureType($closure), $name);
}
return $this->belongsToManyQuery($this->morphKey, $this->localKey, [
['pivot.' . ($this->inverse ? $this->localKey : $this->morphKey), 'exp', new Raw('=' . $this->parent->db(false)->getTable() . '.' . $this->parent->getPk())],
return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
['pivot.' . $this->localKey, 'exp', new Raw('=' . $this->parent->db(false)->getTable() . '.' . $this->parent->getPk())],
['pivot.' . $this->morphType, '=', $this->morphClass],
])->fetchSql()->$aggregate($field);
}
@ -239,7 +235,7 @@ class MorphToMany extends BelongsToMany
}
// 预载入关联查询 支持嵌套预载入
$list = $this->belongsToManyQuery($this->morphKey, $this->localKey, $where)
$list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where)
->with($subRelation)
->cache($cache[0] ?? false, $cache[1] ?? null, $cache[2] ?? null)
->select();
@ -258,7 +254,7 @@ class MorphToMany extends BelongsToMany
}
}
$key = $pivot[$this->morphKey];
$key = $pivot[$this->localKey];
if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) {
continue;
@ -278,7 +274,6 @@ class MorphToMany extends BelongsToMany
* @param mixed $data 数据 可以使用数组、关联模型对象 或者 关联对象的主键
* @param array $pivot 中间表额外数据
* @return array|Pivot
* @throws Exception
*/
public function attach($data, array $pivot = [])
{
@ -290,23 +285,24 @@ class MorphToMany extends BelongsToMany
$model = new $this->model;
$id = $model->insertGetId($data);
}
} elseif (is_numeric($data) || is_string($data)) {
} else if (is_numeric($data) || is_string($data)) {
// 根据关联表主键直接写入中间表
$id = $data;
} elseif ($data instanceof Model) {
} else if ($data instanceof Model) {
// 根据关联表主键直接写入中间表
$id = $data->getKey();
}
if (!empty($id)) {
// 保存中间表数据
$pivot[$this->inverse ? $this->localKey : $this->morphKey] = $this->parent->getKey();
$pivot[$this->localKey] = $this->parent->getKey();
$pivot[$this->morphType] = $this->morphClass;
$ids = (array) $id;
$result = [];
foreach ($ids as $id) {
$pivot[$this->inverse ? $this->morphKey : $this->localKey] = $id;
$pivot[$this->foreignKey] = $id;
$this->pivot->replace()
->exists(false)
@ -341,9 +337,9 @@ class MorphToMany extends BelongsToMany
}
$pivot = $this->pivot
->where($this->inverse ? $this->localKey : $this->morphKey, $this->parent->getKey())
->where($this->localKey, $this->parent->getKey())
->where($this->morphType, $this->morphClass)
->where($this->inverse ? $this->morphKey : $this->localKey, $id)
->where($this->foreignKey, $id)
->find();
return $pivot ?: false;
@ -360,22 +356,22 @@ class MorphToMany extends BelongsToMany
{
if (is_array($data)) {
$id = $data;
} elseif (is_numeric($data) || is_string($data)) {
} else if (is_numeric($data) || is_string($data)) {
// 根据关联表主键直接写入中间表
$id = $data;
} elseif ($data instanceof Model) {
} else if ($data instanceof Model) {
// 根据关联表主键直接写入中间表
$id = $data->getKey();
}
// 删除中间表数据
$pivot = [
[$this->inverse ? $this->localKey : $this->morphKey, '=', $this->parent->getKey()],
[$this->localKey, '=', $this->parent->getKey()],
[$this->morphType, '=', $this->morphClass],
];
if (isset($id)) {
$pivot[] = [$this->inverse ? $this->morphKey : $this->localKey, is_array($id) ? 'in' : '=', $id];
$pivot[] = [$this->foreignKey, is_array($id) ? 'in' : '=', $id];
}
$result = $this->pivot->where($pivot)->delete();
@ -405,9 +401,9 @@ class MorphToMany extends BelongsToMany
];
$current = $this->pivot
->where($this->inverse ? $this->localKey : $this->morphKey, $this->parent->getKey())
->where($this->localKey, $this->parent->getKey())
->where($this->morphType, $this->morphClass)
->column($this->inverse ? $this->morphKey : $this->localKey);
->column($this->foreignKey);
$records = [];
@ -430,7 +426,7 @@ class MorphToMany extends BelongsToMany
if (!in_array($id, $current)) {
$this->attach($id, $attributes);
$changes['attached'][] = $id;
} elseif (count($attributes) > 0 && $this->attach($id, $attributes)) {
} else if (count($attributes) > 0 && $this->attach($id, $attributes)) {
$changes['updated'][] = $id;
}
}
@ -438,22 +434,6 @@ class MorphToMany extends BelongsToMany
return $changes;
}
/**
* 创建关联查询Query对象
* @access protected
* @return Query
*/
protected function buildQuery(): Query
{
// 关联查询
$condition = [
['pivot.' . $this->morphKey, '=', $this->parent->getKey()],
['pivot.' . $this->morphType, '=', $this->morphClass],
];
return $this->belongsToManyQuery($this->morphKey, $this->localKey, $condition);
}
/**
* 执行基础查询(仅执行一次)
* @access protected
@ -461,10 +441,14 @@ class MorphToMany extends BelongsToMany
*/
protected function baseQuery(): void
{
if (empty($this->baseQuery) && $this->parent->getData()) {
$this->query->where([
[$this->morphKey, '=', $this->parent->getKey()],
[$this->morphType, '=', $this->morphClass],
if (empty($this->baseQuery)) {
$foreignKey = $this->foreignKey;
$localKey = $this->localKey;
// 关联查询
$this->belongsToManyQuery($foreignKey, $localKey, [
['pivot.' . $localKey, '=', $this->parent->getKey()],
['pivot.' . $this->morphType, '=', $this->morphClass],
]);
$this->baseQuery = true;

View File

@ -253,8 +253,8 @@ abstract class OneToOne extends Relation
$relationModel->exists(true);
}
if ($relationModel && !empty($this->bindAttr)) {
$this->bindAttr($relationModel, $result);
if (!empty($this->bindAttr)) {
$this->bindAttr($result, $relationModel);
}
} else {
$relationModel = null;
@ -266,12 +266,12 @@ abstract class OneToOne extends Relation
/**
* 绑定关联属性到父模型
* @access protected
* @param Model $model 关联模型对象
* @param Model $result 父模型对象
* @param Model $model 关联模型对象
* @return void
* @throws Exception
*/
protected function bindAttr(Model $model, Model $result): void
protected function bindAttr(Model $result, Model $model = null): void
{
foreach ($this->bindAttr as $key => $attr) {
$key = is_numeric($key) ? $attr : $key;