升级vendor

This commit is contained in:
liyukun 2021-12-31 01:32:17 +08:00
parent b719a4b772
commit 47d436c637
44 changed files with 611 additions and 404 deletions

View File

@ -12,6 +12,6 @@ return array(
'35fab96057f1bf5e7aba31a8a6d5fdde' => $vendorDir . '/topthink/think-orm/stubs/load_stubs.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'1cfd2761b63b0a29ed23657ea394cb2d' => $vendorDir . '/topthink/think-captcha/src/helper.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
);

View File

@ -9,7 +9,7 @@ return array(
'think\\view\\driver\\' => array($vendorDir . '/topthink/think-view/src'),
'think\\captcha\\' => array($vendorDir . '/topthink/think-captcha/src'),
'think\\app\\' => array($vendorDir . '/topthink/think-multi-app/src'),
'think\\' => array($vendorDir . '/topthink/framework/src/think', $vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-orm/src', $vendorDir . '/topthink/think-template/src'),
'think\\' => array($vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-template/src', $vendorDir . '/topthink/think-orm/src', $vendorDir . '/topthink/framework/src/think'),
'learn\\' => array($baseDir . '/learn'),
'app\\' => array($baseDir . '/app'),
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
@ -20,6 +20,7 @@ return array(
'Spatie\\Macroable\\' => array($vendorDir . '/spatie/macroable/src'),
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'),

View File

@ -13,8 +13,8 @@ class ComposerStaticInit179ad4390eaa61356c3a52b9b610e467
'35fab96057f1bf5e7aba31a8a6d5fdde' => __DIR__ . '/..' . '/topthink/think-orm/stubs/load_stubs.php',
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
'1cfd2761b63b0a29ed23657ea394cb2d' => __DIR__ . '/..' . '/topthink/think-captcha/src/helper.php',
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
);
public static $prefixLengthsPsr4 = array (
@ -46,6 +46,7 @@ class ComposerStaticInit179ad4390eaa61356c3a52b9b610e467
array (
'Psr\\SimpleCache\\' => 16,
'Psr\\Log\\' => 8,
'Psr\\Http\\Message\\' => 17,
'Psr\\Container\\' => 14,
'Psr\\Cache\\' => 10,
'PHPMailer\\PHPMailer\\' => 20,
@ -83,10 +84,10 @@ class ComposerStaticInit179ad4390eaa61356c3a52b9b610e467
),
'think\\' =>
array (
0 => __DIR__ . '/..' . '/topthink/framework/src/think',
1 => __DIR__ . '/..' . '/topthink/think-helper/src',
0 => __DIR__ . '/..' . '/topthink/think-helper/src',
1 => __DIR__ . '/..' . '/topthink/think-template/src',
2 => __DIR__ . '/..' . '/topthink/think-orm/src',
3 => __DIR__ . '/..' . '/topthink/think-template/src',
3 => __DIR__ . '/..' . '/topthink/framework/src/think',
),
'learn\\' =>
array (
@ -128,6 +129,10 @@ class ComposerStaticInit179ad4390eaa61356c3a52b9b610e467
array (
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
),
'Psr\\Http\\Message\\' =>
array (
0 => __DIR__ . '/..' . '/psr/http-message/src',
),
'Psr\\Container\\' =>
array (
0 => __DIR__ . '/..' . '/psr/container/src',

View File

@ -666,6 +666,68 @@
},
"install-path": "../psr/container"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"version_normalized": "1.0.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.3.0"
},
"time": "2016-08-06T14:39:51+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"support": {
"source": "https://github.com/php-fig/http-message/tree/master"
},
"install-path": "../psr/http-message"
},
{
"name": "psr/log",
"version": "1.1.4",
@ -922,17 +984,17 @@
},
{
"name": "symfony/http-foundation",
"version": "v5.4.1",
"version_normalized": "5.4.1.0",
"version": "v5.4.2",
"version_normalized": "5.4.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "5dad3780023a707f4c24beac7d57aead85c1ce3c"
"reference": "ce952af52877eaf3eab5d0c08cc0ea865ed37313"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/5dad3780023a707f4c24beac7d57aead85c1ce3c",
"reference": "5dad3780023a707f4c24beac7d57aead85c1ce3c",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/ce952af52877eaf3eab5d0c08cc0ea865ed37313",
"reference": "ce952af52877eaf3eab5d0c08cc0ea865ed37313",
"shasum": "",
"mirrors": [
{
@ -956,7 +1018,7 @@
"suggest": {
"symfony/mime": "To use the file extension guesser"
},
"time": "2021-12-09T12:46:57+00:00",
"time": "2021-12-28T17:15:56+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -984,7 +1046,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v5.4.1"
"source": "https://github.com/symfony/http-foundation/tree/v5.4.2"
},
"funding": [
{
@ -1270,17 +1332,17 @@
},
{
"name": "symfony/var-dumper",
"version": "v4.4.34",
"version_normalized": "4.4.34.0",
"version": "v4.4.36",
"version_normalized": "4.4.36.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "2d0c056b2faaa3d785bdbd5adecc593a5be9c16e"
"reference": "02685c62fcbc4262235cc72a54fbd45ab719ce3c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/2d0c056b2faaa3d785bdbd5adecc593a5be9c16e",
"reference": "2d0c056b2faaa3d785bdbd5adecc593a5be9c16e",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/02685c62fcbc4262235cc72a54fbd45ab719ce3c",
"reference": "02685c62fcbc4262235cc72a54fbd45ab719ce3c",
"shasum": "",
"mirrors": [
{
@ -1310,7 +1372,7 @@
"ext-intl": "To show region name in time zone dump",
"symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
},
"time": "2021-11-12T10:50:54+00:00",
"time": "2021-12-29T09:28:53+00:00",
"bin": [
"Resources/bin/var-dump-server"
],
@ -1348,7 +1410,7 @@
"dump"
],
"support": {
"source": "https://github.com/symfony/var-dumper/tree/v4.4.34"
"source": "https://github.com/symfony/var-dumper/tree/v4.4.36"
},
"funding": [
{
@ -1368,17 +1430,17 @@
},
{
"name": "topthink/framework",
"version": "v6.0.9",
"version_normalized": "6.0.9.0",
"version": "v6.0.10",
"version_normalized": "6.0.10.0",
"source": {
"type": "git",
"url": "https://github.com/top-think/framework.git",
"reference": "0b5fb453f0e533de3af3a1ab6a202510b61be617"
"reference": "109ade68d6ad3b10af5c8d9d8f53161a513eec18"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/framework/zipball/0b5fb453f0e533de3af3a1ab6a202510b61be617",
"reference": "0b5fb453f0e533de3af3a1ab6a202510b61be617",
"url": "https://api.github.com/repos/top-think/framework/zipball/109ade68d6ad3b10af5c8d9d8f53161a513eec18",
"reference": "109ade68d6ad3b10af5c8d9d8f53161a513eec18",
"shasum": "",
"mirrors": [
{
@ -1394,17 +1456,19 @@
"league/flysystem-cached-adapter": "^1.0",
"php": ">=7.2.5",
"psr/container": "~1.0",
"psr/http-message": "^1.0",
"psr/log": "~1.0",
"psr/simple-cache": "^1.0",
"topthink/think-helper": "^3.1.1",
"topthink/think-orm": "^2.0"
},
"require-dev": {
"guzzlehttp/psr7": "^2.1.0",
"mikey179/vfsstream": "^1.6",
"mockery/mockery": "^1.2",
"phpunit/phpunit": "^7.0"
},
"time": "2021-07-22T03:24:49+00:00",
"time": "2021-12-30T13:54:49+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -1436,7 +1500,7 @@
],
"support": {
"issues": "https://github.com/top-think/framework/issues",
"source": "https://github.com/top-think/framework/tree/v6.0.9"
"source": "https://github.com/top-think/framework/tree/v6.0.10"
},
"install-path": "../topthink/framework"
},
@ -1613,17 +1677,17 @@
},
{
"name": "topthink/think-orm",
"version": "v2.0.45",
"version_normalized": "2.0.45.0",
"version": "v2.0.46",
"version_normalized": "2.0.46.0",
"source": {
"type": "git",
"url": "https://github.com/top-think/think-orm.git",
"reference": "3dcf9af447b048103093840833e8c74ab849152f"
"reference": "51ec287abdb99521dbbc66526b114f2e32b8fff4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/think-orm/zipball/3dcf9af447b048103093840833e8c74ab849152f",
"reference": "3dcf9af447b048103093840833e8c74ab849152f",
"url": "https://api.github.com/repos/top-think/think-orm/zipball/51ec287abdb99521dbbc66526b114f2e32b8fff4",
"reference": "51ec287abdb99521dbbc66526b114f2e32b8fff4",
"shasum": "",
"mirrors": [
{
@ -1643,7 +1707,7 @@
"require-dev": {
"phpunit/phpunit": "^7|^8|^9.5"
},
"time": "2021-11-30T14:31:05+00:00",
"time": "2021-12-29T05:48:27+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -1671,7 +1735,7 @@
],
"support": {
"issues": "https://github.com/top-think/think-orm/issues",
"source": "https://github.com/top-think/think-orm/tree/v2.0.45"
"source": "https://github.com/top-think/think-orm/tree/v2.0.46"
},
"install-path": "../topthink/think-orm"
},

View File

@ -5,7 +5,7 @@
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'cb1402d64f9ca93ea47960ba194212d469fd5d98',
'reference' => 'b719a4b772e06fd2165da8eb317ef17bc73126ed',
'name' => 'topthink/think',
'dev' => true,
),
@ -91,6 +91,15 @@
'reference' => '8622567409010282b7aeebe4bb841fe98b58dcaf',
'dev_requirement' => false,
),
'psr/http-message' => array(
'pretty_version' => '1.0.1',
'version' => '1.0.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-message',
'aliases' => array(),
'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
'dev_requirement' => false,
),
'psr/log' => array(
'pretty_version' => '1.1.4',
'version' => '1.1.4.0',
@ -128,12 +137,12 @@
'dev_requirement' => false,
),
'symfony/http-foundation' => array(
'pretty_version' => 'v5.4.1',
'version' => '5.4.1.0',
'pretty_version' => 'v5.4.2',
'version' => '5.4.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/http-foundation',
'aliases' => array(),
'reference' => '5dad3780023a707f4c24beac7d57aead85c1ce3c',
'reference' => 'ce952af52877eaf3eab5d0c08cc0ea865ed37313',
'dev_requirement' => false,
),
'symfony/polyfill-mbstring' => array(
@ -164,21 +173,21 @@
'dev_requirement' => false,
),
'symfony/var-dumper' => array(
'pretty_version' => 'v4.4.34',
'version' => '4.4.34.0',
'pretty_version' => 'v4.4.36',
'version' => '4.4.36.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/var-dumper',
'aliases' => array(),
'reference' => '2d0c056b2faaa3d785bdbd5adecc593a5be9c16e',
'reference' => '02685c62fcbc4262235cc72a54fbd45ab719ce3c',
'dev_requirement' => true,
),
'topthink/framework' => array(
'pretty_version' => 'v6.0.9',
'version' => '6.0.9.0',
'pretty_version' => 'v6.0.10',
'version' => '6.0.10.0',
'type' => 'library',
'install_path' => __DIR__ . '/../topthink/framework',
'aliases' => array(),
'reference' => '0b5fb453f0e533de3af3a1ab6a202510b61be617',
'reference' => '109ade68d6ad3b10af5c8d9d8f53161a513eec18',
'dev_requirement' => false,
),
'topthink/think' => array(
@ -187,7 +196,7 @@
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'cb1402d64f9ca93ea47960ba194212d469fd5d98',
'reference' => 'b719a4b772e06fd2165da8eb317ef17bc73126ed',
'dev_requirement' => false,
),
'topthink/think-captcha' => array(
@ -218,12 +227,12 @@
'dev_requirement' => false,
),
'topthink/think-orm' => array(
'pretty_version' => 'v2.0.45',
'version' => '2.0.45.0',
'pretty_version' => 'v2.0.46',
'version' => '2.0.46.0',
'type' => 'library',
'install_path' => __DIR__ . '/../topthink/think-orm',
'aliases' => array(),
'reference' => '3dcf9af447b048103093840833e8c74ab849152f',
'reference' => '51ec287abdb99521dbbc66526b114f2e32b8fff4',
'dev_requirement' => false,
),
'topthink/think-template' => array(

2
vendor/services.php vendored
View File

@ -1,5 +1,5 @@
<?php
// This file is automatically generated at:2021-12-26 21:35:37
// This file is automatically generated at:2021-12-31 01:32:01
declare (strict_types = 1);
return array (
0 => 'think\\captcha\\CaptchaService',

View File

@ -154,8 +154,6 @@ class HeaderUtils
* is semantically equivalent to $filename. If the filename is already ASCII,
* it can be omitted, or just copied from $filename
*
* @return string
*
* @throws \InvalidArgumentException
*
* @see RFC 6266

View File

@ -89,7 +89,7 @@ class ServerBag extends ParameterBag
// PHP_AUTH_USER/PHP_AUTH_PW
if (isset($headers['PHP_AUTH_USER'])) {
$headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']);
$headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.($headers['PHP_AUTH_PW'] ?? ''));
} elseif (isset($headers['PHP_AUTH_DIGEST'])) {
$headers['AUTHORIZATION'] = $headers['PHP_AUTH_DIGEST'];
}

View File

@ -663,7 +663,7 @@ class PdoSessionHandler extends AbstractSessionHandler
$selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$insertStmt = null;
do {
while (true) {
$selectStmt->execute();
$sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM);
@ -712,7 +712,7 @@ class PdoSessionHandler extends AbstractSessionHandler
}
return '';
} while (true);
}
}
/**

View File

@ -134,7 +134,7 @@ class ReflectionCaster
array_unshift($trace, [
'function' => 'yield',
'file' => $function->getExecutingFile(),
'line' => $function->getExecutingLine() - 1,
'line' => $function->getExecutingLine() - (int) (\PHP_VERSION_ID < 80100),
]);
$trace[] = $frame;
$a[$prefix.'trace'] = new TraceStub($trace, false, 0, -1, -1);
@ -263,15 +263,17 @@ class ReflectionCaster
unset($a[$prefix.'allowsNull']);
}
try {
$a[$prefix.'default'] = $v = $c->getDefaultValue();
if ($c->isDefaultValueConstant()) {
$a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v);
if ($c->isOptional()) {
try {
$a[$prefix.'default'] = $v = $c->getDefaultValue();
if ($c->isDefaultValueConstant()) {
$a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v);
}
if (null === $v) {
unset($a[$prefix.'allowsNull']);
}
} catch (\ReflectionException $e) {
}
if (null === $v) {
unset($a[$prefix.'allowsNull']);
}
} catch (\ReflectionException $e) {
}
return $a;

View File

@ -35,7 +35,7 @@ ThinkPHP6.0底层架构采用PHP7.1改写和进一步优化。
* 统一和精简大量用法
> ThinkPHP6.0的运行环境要求PHP7.1+兼容PHP8.0。
> ThinkPHP6.0的运行环境要求PHP7.2+兼容PHP8.1
## 安装

View File

@ -27,13 +27,15 @@
"psr/log": "~1.0",
"psr/container": "~1.0",
"psr/simple-cache": "^1.0",
"psr/http-message": "^1.0",
"topthink/think-orm": "^2.0",
"topthink/think-helper": "^3.1.1"
},
"require-dev": {
"mikey179/vfsstream": "^1.6",
"mockery/mockery": "^1.2",
"phpunit/phpunit": "^7.0"
"phpunit/phpunit": "^7.0",
"guzzlehttp/psr7": "^2.1.0"
},
"autoload": {
"files": [],

View File

@ -149,7 +149,7 @@ if (!function_exists('cookie')) {
{
if (is_null($value)) {
// 删除
Cookie::delete($name);
Cookie::delete($name, $option ?: []);
} elseif ('' === $value) {
// 获取
return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1)) : Cookie::get($name);

View File

@ -39,7 +39,7 @@ use think\initializer\RegisterService;
*/
class App extends Container
{
const VERSION = '6.0.9';
const VERSION = '6.0.10LTS';
/**
* 应用调试模式
@ -168,7 +168,7 @@ class App extends Container
*/
public function __construct(string $rootPath = '')
{
$this->thinkPath = dirname(__DIR__) . DIRECTORY_SEPARATOR;
$this->thinkPath = realpath(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
$this->rootPath = $rootPath ? rtrim($rootPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath();
$this->appPath = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
$this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
@ -450,13 +450,8 @@ class App extends Container
// 加载全局初始化文件
$this->load();
// 加载框架默认语言包
$langSet = $this->lang->defaultLangSet();
$this->lang->load($this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $langSet . '.php');
// 加载应用默认语言包
$this->loadLangPack($langSet);
$this->loadLangPack();
// 监听AppInit
$this->event->trigger(AppInit::class);
@ -482,25 +477,13 @@ class App extends Container
/**
* 加载语言包
* @param string $langset 语言
* @return void
*/
public function loadLangPack($langset)
public function loadLangPack()
{
if (empty($langset)) {
return;
}
// 加载系统语言包
$files = glob($this->appPath . 'lang' . DIRECTORY_SEPARATOR . $langset . '.*');
$this->lang->load($files);
// 加载扩展(自定义)语言包
$list = $this->config->get('lang.extend_list', []);
if (isset($list[$langset])) {
$this->lang->load($list[$langset]);
}
// 加载默认语言包
$langSet = $this->lang->defaultLangSet();
$this->lang->switchLangSet($langSet);
}
/**

View File

@ -27,6 +27,7 @@ use ReflectionMethod;
use think\exception\ClassNotFoundException;
use think\exception\FuncNotFoundException;
use think\helper\Str;
use Traversable;
/**
* 容器管理类 支持PSR-11
@ -520,34 +521,38 @@ class Container implements ContainerInterface, ArrayAccess, IteratorAggregate, C
$this->delete($name);
}
public function offsetExists($key)
#[\ReturnTypeWillChange]
public function offsetExists($key): bool
{
return $this->exists($key);
}
#[\ReturnTypeWillChange]
public function offsetGet($key)
{
return $this->make($key);
}
#[\ReturnTypeWillChange]
public function offsetSet($key, $value)
{
$this->bind($key, $value);
}
#[\ReturnTypeWillChange]
public function offsetUnset($key)
{
$this->delete($key);
}
//Countable
public function count()
public function count(): int
{
return count($this->instances);
}
//IteratorAggregate
public function getIterator()
public function getIterator(): Traversable
{
return new ArrayIterator($this->instances);
}

View File

@ -158,11 +158,13 @@ class Cookie
* Cookie删除
* @access public
* @param string $name cookie名称
* @param array $options cookie参数
* @return void
*/
public function delete(string $name): void
public function delete(string $name, array $options = []): void
{
$this->setCookie($name, '', time() - 3600, $this->config);
$config = array_merge($this->config, array_change_key_case($options));
$this->setCookie($name, '', time() - 3600, $config);
}
/**

View File

@ -39,7 +39,7 @@ class Env implements ArrayAccess
*/
public function load(string $file): void
{
$env = parse_ini_file($file, true) ?: [];
$env = parse_ini_file($file, true, INI_SCANNER_RAW) ?: [];
$this->set($env);
}
@ -159,21 +159,25 @@ class Env implements ArrayAccess
}
// ArrayAccess
#[\ReturnTypeWillChange]
public function offsetSet($name, $value): void
{
$this->set($name, $value);
}
#[\ReturnTypeWillChange]
public function offsetExists($name): bool
{
return $this->__isset($name);
}
#[\ReturnTypeWillChange]
public function offsetUnset($name)
{
throw new Exception('not support: unset');
}
#[\ReturnTypeWillChange]
public function offsetGet($name)
{
return $this->get($name);

View File

@ -176,7 +176,7 @@ class File extends SplFileInfo
$this->hashName = call_user_func($rule);
break;
default:
$this->hashName = date('Ymd') . DIRECTORY_SEPARATOR . md5((string) microtime(true));
$this->hashName = date('Ymd') . DIRECTORY_SEPARATOR . md5(microtime(true) . $this->getPathname());
break;
}
}

View File

@ -18,6 +18,8 @@ namespace think;
*/
class Lang
{
protected $app;
/**
* 配置参数
* @var array
@ -62,15 +64,26 @@ class Lang
* @access public
* @param array $config
*/
public function __construct(array $config = [])
public function __construct(App $app, array $config = [])
{
$this->config = array_merge($this->config, array_change_key_case($config));
$this->range = $this->config['default_lang'];
$this->app = $app;
}
public static function __make(Config $config)
public static function __make(App $app, Config $config)
{
return new static($config->get('lang'));
return new static($app, $config->get('lang'));
}
/**
* 获取当前语言配置
* @access public
* @return array
*/
public function getConfig(): array
{
return $this->config;
}
/**
@ -104,6 +117,35 @@ class Lang
return $this->config['default_lang'];
}
/**
* 切换语言
* @access public
* @param string $langset 语言
* @return void
*/
public function switchLangSet(string $langset)
{
if (empty($langset)) {
return;
}
// 加载系统语言包
$this->load([
$this->app->getThinkPath() . 'lang' . DIRECTORY_SEPARATOR . $langset . '.php',
]);
// 加载系统语言包
$files = glob($this->app->getAppPath() . 'lang' . DIRECTORY_SEPARATOR . $langset . '.*');
$this->load($files);
// 加载扩展(自定义)语言包
$list = $this->app->config->get('lang.extend_list', []);
if (isset($list[$langset])) {
$this->load($list[$langset]);
}
}
/**
* 加载语言定义(不区分大小写)
* @access public
@ -202,6 +244,10 @@ class Lang
{
$range = $range ?: $this->range;
if (!isset($this->lang[$range])) {
$this->switchLangSet($range);
}
// 空参数返回所有定义
if (is_null($name)) {
return $this->lang[$range] ?? [];
@ -241,6 +287,7 @@ class Lang
/**
* 自动侦测设置获取语言选择
* @deprecated
* @access public
* @param Request $request
* @return string
@ -280,6 +327,7 @@ class Lang
/**
* 保存当前语言到Cookie
* @deprecated
* @access public
* @param Cookie $cookie Cookie对象
* @return void

View File

@ -13,6 +13,7 @@ declare (strict_types = 1);
namespace think;
use ArrayAccess;
use think\facade\Lang;
use think\file\UploadedFile;
use think\route\Rule;
@ -1227,7 +1228,7 @@ class Request implements ArrayAccess
7 => 'file write error',
];
$msg = $fileUploadErrors[$error];
$msg = Lang::get($fileUploadErrors[$error]);
throw new Exception($msg, $error);
}
@ -2150,19 +2151,23 @@ class Request implements ArrayAccess
}
// ArrayAccess
#[\ReturnTypeWillChange]
public function offsetExists($name): bool
{
return $this->has($name);
}
#[\ReturnTypeWillChange]
public function offsetGet($name)
{
return $this->param($name);
}
#[\ReturnTypeWillChange]
public function offsetSet($name, $value)
{}
#[\ReturnTypeWillChange]
public function offsetUnset($name)
{}

View File

@ -130,16 +130,19 @@ abstract class Response
// 处理输出数据
$data = $this->getContent();
if (!headers_sent() && !empty($this->header)) {
// 发送状态码
http_response_code($this->code);
// 发送头部信息
foreach ($this->header as $name => $val) {
header($name . (!is_null($val) ? ':' . $val : ''));
if (!headers_sent()) {
if (!empty($this->header)) {
// 发送状态码
http_response_code($this->code);
// 发送头部信息
foreach ($this->header as $name => $val) {
header($name . (!is_null($val) ? ':' . $val : ''));
}
}
if ($this->cookie) {
$this->cookie->save();
}
}
if ($this->cookie) {
$this->cookie->save();
}
$this->sendData($data);

View File

@ -67,7 +67,7 @@ abstract class Make extends Command
protected function getPathName(string $name): string
{
$name = str_replace('app\\', '', $name);
$name = substr($name, 4);
return $this->app->getBasePath() . ltrim(str_replace('\\', '/', $name), '/') . '.php';
}

View File

@ -91,14 +91,13 @@ class RouteList extends Command
foreach ($routeList as $item) {
$item['route'] = $item['route'] instanceof \Closure ? '<Closure>' : $item['route'];
$row = [$item['rule'], $item['route'], $item['method'], $item['name']];
if ($this->input->hasOption('more')) {
$item = [$item['rule'], $item['route'], $item['method'], $item['name'], $item['domain'], json_encode($item['option']), json_encode($item['pattern'])];
} else {
$item = [$item['rule'], $item['route'], $item['method'], $item['name']];
array_push($row, $item['domain'], json_encode($item['option']), json_encode($item['pattern']));
}
$rows[] = $item;
$rows[] = $row;
}
if ($this->input->getOption('sort')) {

View File

@ -17,6 +17,7 @@ use League\Flysystem\Adapter\AbstractAdapter;
use League\Flysystem\Cached\CachedAdapter;
use League\Flysystem\Cached\Storage\Memory as MemoryStore;
use League\Flysystem\Filesystem;
use RuntimeException;
use think\Cache;
use think\File;
@ -91,6 +92,16 @@ abstract class Driver
return $path;
}
protected function concatPathToUrl($url, $path)
{
return rtrim($url, '/') . '/' . ltrim($path, '/');
}
public function url(string $path): string
{
throw new RuntimeException('This driver does not support retrieving URLs.');
}
/**
* 保存文件
* @param string $path 路径

View File

@ -41,4 +41,12 @@ class Local extends Driver
$permissions
);
}
public function url(string $path): string
{
if (isset($this->config['url'])) {
return $this->concatPathToUrl($this->config['url'], $path);
}
return parent::url($path);
}
}

View File

@ -139,7 +139,9 @@ class File implements LogHandlerInterface
try {
if (count($files) > $this->config['max_files']) {
set_error_handler(function ($errno, $errstr, $errfile, $errline) {});
unlink($files[0]);
restore_error_handler();
}
} catch (\Exception $e) {
//

View File

@ -14,6 +14,8 @@ namespace think\middleware;
use Closure;
use think\App;
use think\Config;
use think\Cookie;
use think\Lang;
use think\Request;
use think\Response;
@ -24,13 +26,14 @@ use think\Response;
class LoadLangPack
{
protected $app;
protected $lang;
protected $config;
public function __construct(App $app, Lang $lang)
public function __construct(App $app, Lang $lang, Config $config)
{
$this->app = $app;
$this->lang = $lang;
$this->app = $app;
$this->lang = $lang;
$this->config = $lang->getConfig();
}
/**
@ -43,19 +46,71 @@ class LoadLangPack
public function handle($request, Closure $next)
{
// 自动侦测当前语言
$langset = $this->lang->detect($request);
$langset = $this->detect($request);
if ($this->lang->defaultLangSet() != $langset) {
// 加载系统语言包
$this->lang->load([
$this->app->getThinkPath() . 'lang' . DIRECTORY_SEPARATOR . $langset . '.php',
]);
$this->app->LoadLangPack($langset);
$this->lang->switchLangSet($langset);
}
$this->lang->saveToCookie($this->app->cookie);
$this->saveToCookie($this->app->cookie, $langset);
return $next($request);
}
/**
* 自动侦测设置获取语言选择
* @access protected
* @param Request $request
* @return string
*/
protected function detect(Request $request): string
{
// 自动侦测设置获取语言选择
$langSet = '';
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']));
} elseif ($request->server('HTTP_ACCEPT_LANGUAGE')) {
// 自动侦测浏览器语言
$match = preg_match('/^([a-z\d\-]+)/i', $request->server('HTTP_ACCEPT_LANGUAGE'), $matches);
if ($match) {
$langSet = strtolower($matches[1]);
if (isset($this->config['accept_language'][$langSet])) {
$langSet = $this->config['accept_language'][$langSet];
}
}
}
if (empty($this->config['allow_lang_list']) || in_array($langSet, $this->config['allow_lang_list'])) {
// 合法的语言
$range = $langSet;
$this->lang->setLangSet($range);
} else {
$range = $this->lang->getLangSet();
}
return $range;
}
/**
* 保存当前语言到Cookie
* @access protected
* @param Cookie $cookie Cookie对象
* @param string $langSet 语言
* @return void
*/
protected function saveToCookie(Cookie $cookie, string $langSet)
{
if ($this->config['use_cookie']) {
$cookie->set($this->config['cookie_var'], $langSet);
}
}
}

View File

@ -12,6 +12,7 @@ declare (strict_types = 1);
namespace think\route;
use Psr\Http\Message\ResponseInterface;
use think\App;
use think\Container;
use think\Request;
@ -94,6 +95,12 @@ abstract class Dispatch
{
if ($data instanceof Response) {
$response = $data;
} elseif ($data instanceof ResponseInterface) {
$response = Response::create($data->getBody()->getContents(), 'html', $data->getStatusCode());
foreach ($data->getHeaders() as $header => $values) {
$response->header([$header => implode(", ", $values)]);
}
} elseif (!is_null($data)) {
// 默认自动识别响应输出类型
$type = $this->request->isJson() ? 'json' : 'html';

View File

@ -320,7 +320,7 @@ class Url
}
if (empty($pattern)) {
return [rtrim($url, '?/-'), $domain, $suffix];
return [rtrim($url, '?-'), $domain, $suffix];
}
$type = $this->route->config('url_common_param');
@ -331,11 +331,11 @@ class Url
$url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key, '<' . $key . '>'], $type ? (string) $vars[$key] : urlencode((string) $vars[$key]), $url);
$keys[] = $key;
$url = str_replace(['/?', '-?'], ['/', '-'], $url);
$result = [rtrim($url, '?/-'), $domain, $suffix];
$result = [rtrim($url, '?-'), $domain, $suffix];
} elseif (2 == $val) {
$url = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url);
$url = str_replace(['/?', '-?'], ['/', '-'], $url);
$result = [rtrim($url, '?/-'), $domain, $suffix];
$result = [rtrim($url, '?-'), $domain, $suffix];
} else {
$result = null;
$keys = [];

View File

@ -113,6 +113,13 @@ class Controller extends Dispatch
});
}
protected function parseActions($actions)
{
return array_map(function ($item) {
return strtolower($item);
}, is_string($actions) ? explode(",", $actions) : $actions);
}
/**
* 使用反射机制注册控制器中间件
* @access public
@ -128,30 +135,34 @@ class Controller extends Dispatch
$reflectionProperty->setAccessible(true);
$middlewares = $reflectionProperty->getValue($controller);
$action = $this->request->action(true);
foreach ($middlewares as $key => $val) {
if (!is_int($key)) {
if (isset($val['only']) && !in_array($this->request->action(true), array_map(function ($item) {
return strtolower($item);
}, is_string($val['only']) ? explode(",", $val['only']) : $val['only']))) {
continue;
} elseif (isset($val['except']) && in_array($this->request->action(true), array_map(function ($item) {
return strtolower($item);
}, is_string($val['except']) ? explode(',', $val['except']) : $val['except']))) {
continue;
} else {
$val = $key;
$middleware = $key;
$options = $val;
} elseif (isset($val['middleware'])) {
$middleware = $val['middleware'];
$options = $val['options'] ?? [];
} else {
$middleware = $val;
$options = [];
}
if (isset($options['only']) && !in_array($action, $this->parseActions($options['only']))) {
continue;
} elseif (isset($options['except']) && in_array($action, $this->parseActions($options['except']))) {
continue;
}
if (is_string($middleware) && strpos($middleware, ':')) {
$middleware = explode(':', $middleware);
if (count($middleware) > 1) {
$middleware = [$middleware[0], array_slice($middleware, 1)];
}
}
if (is_string($val) && strpos($val, ':')) {
$val = explode(':', $val);
if (count($val) > 1) {
$val = [$val[0], array_slice($val, 1)];
}
}
$this->app->middleware->controller($val);
$this->app->middleware->controller($middleware);
}
}
}

View File

@ -21,8 +21,6 @@ class EnvTest extends TestCase
$this->assertEquals('value1', $env->get('key1'));
$this->assertEquals('value2', $env->get('key2'));
$this->assertSame(['KEY1' => 'value1', 'KEY2' => 'value2'], $env->get());
}
public function testServerEnv()

View File

@ -194,6 +194,10 @@ class RouteTest extends TestCase
$this->createMiddleware()->mockery_getName() . ":params1:params2",
$this->createMiddleware(0)->mockery_getName() => ['except' => 'bar'],
$this->createMiddleware()->mockery_getName() => ['only' => 'bar'],
[
'middleware' => [$this->createMiddleware()->mockery_getName(), [new \stdClass()]],
'options' => ['only' => 'bar'],
],
];
$this->app->shouldReceive('parseClass')->with('controller', 'Foo')->andReturn($controller->mockery_getName());
@ -211,7 +215,8 @@ class RouteTest extends TestCase
$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('parseClass')->once()->with('controller', 'Foo')
->andReturn($controller->mockery_getName());
$this->app->shouldReceive('make')->with($controller->mockery_getName(), [], true)->andReturn($controller);
$request = $this->makeRequest('foo');

View File

@ -243,6 +243,29 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
}
}
$this->filter(function ($result, $options) {
// 关联查询
if (!empty($options['relation'])) {
$result->relationQuery($options['relation'], $options['with_relation_attr']);
}
// 预载入查询
if (!empty($options['with'])) {
$result->eagerlyResult($result, $options['with'], $options['with_relation_attr'], false, $options['with_cache'] ?? false);
}
// JOIN预载入查询
if (!empty($options['with_join'])) {
$result->eagerlyResult($result, $options['with_join'], $options['with_relation_attr'], true, $options['with_cache'] ?? false);
}
// 关联统计
if (!empty($options['with_count'])) {
foreach ($options['with_count'] as $val) {
$result->relationCount($this, (array) $val[0], $val[1], $val[2], false);
}
}
});
// 执行初始化操作
$this->initialize();
}
@ -260,11 +283,12 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
/**
* 创建新的模型实例
* @access public
* @param array $data 数据
* @param mixed $where 更新条件
* @param array $data 数据
* @param mixed $where 更新条件
* @param array $options 参数
* @return Model
*/
public function newInstance(array $data = [], $where = null): Model
public function newInstance(array $data = [], $where = null, array $options = []): Model
{
$model = new static($data);
@ -284,6 +308,11 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
$model->setUpdateWhere($where);
// 查询数据处理
foreach ($this->filter as $filter) {
call_user_func_array($filter, [$model, $options]);
}
$model->trigger('AfterRead');
return $model;
@ -970,21 +999,25 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
}
// ArrayAccess
#[\ReturnTypeWillChange]
public function offsetSet($name, $value)
{
$this->setAttr($name, $value);
}
#[\ReturnTypeWillChange]
public function offsetExists($name): bool
{
return $this->__isset($name);
}
#[\ReturnTypeWillChange]
public function offsetUnset($name)
{
$this->__unset($name);
}
#[\ReturnTypeWillChange]
public function offsetGet($name)
{
return $this->getAttr($name);
@ -1037,10 +1070,6 @@ abstract class Model implements JsonSerializable, ArrayAccess, Arrayable, Jsonab
return call_user_func_array(static::$macro[static::class][$method]->bindTo($this, static::class), $args);
}
if ('withattr' == strtolower($method)) {
return call_user_func_array([$this, 'withAttribute'], $args);
}
return call_user_func_array([$this->db(), $method], $args);
}

View File

@ -410,7 +410,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
* @return Traversable An instance of an object implementing <b>Iterator</b> or
* <b>Traversable</b>
*/
public function getIterator()
#[\ReturnTypeWillChange]
public function getIterator(): Traversable
{
return new ArrayIterator($this->items->all());
}
@ -421,7 +422,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
* @param mixed $offset
* @return bool
*/
public function offsetExists($offset)
#[\ReturnTypeWillChange]
public function offsetExists($offset): bool
{
return $this->items->offsetExists($offset);
}
@ -432,6 +434,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
* @param mixed $offset
* @return mixed
*/
#[\ReturnTypeWillChange]
public function offsetGet($offset)
{
return $this->items->offsetGet($offset);
@ -443,6 +446,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
* @param mixed $offset
* @param mixed $value
*/
#[\ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
$this->items->offsetSet($offset, $value);
@ -455,6 +459,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
* @return void
* @since 5.0.0
*/
#[\ReturnTypeWillChange]
public function offsetUnset($offset)
{
$this->items->offsetUnset($offset);
@ -498,6 +503,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
/**
* Specify data which should be serialized to JSON
*/
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->toArray();

View File

@ -106,6 +106,12 @@ abstract class BaseQuery
$name = Str::snake(substr($method, 5));
array_unshift($args, $name);
return call_user_func_array([$this, 'where'], $args);
} elseif ($this->model && in_array($method, ['hidden', 'visible', 'append'])) {
// 调用模型类方法
$this->model->filter(function ($model, $options) use ($method, $args) {
call_user_func_array([$model, $method], $args);
});
return $this;
} elseif ($this->model && method_exists($this->model, 'scope' . $method)) {
// 动态调用命名范围
$method = 'scope' . $method;
@ -137,7 +143,7 @@ abstract class BaseQuery
$query->name($this->name);
}
if (isset($this->options['json'])) {
if (!empty($this->options['json'])) {
$query->json($this->options['json'], $this->options['json_assoc']);
}
@ -278,7 +284,9 @@ abstract class BaseQuery
public function column($field, string $key = ''): array
{
$result = $this->connection->column($this, $field, $key);
$this->resultSet($result, false);
if (count($result) != count($result, 1)) {
$this->resultSet($result, false);
}
return $result;
}
@ -867,6 +875,7 @@ abstract class BaseQuery
{
$this->options['json'] = $json;
$this->options['json_assoc'] = $assoc;
return $this;
}
@ -1124,7 +1133,7 @@ abstract class BaseQuery
* 查找单条记录
* @access public
* @param mixed $data 查询数据
* @return array|Model|null|static
* @return array|Model|null|static|mixed
* @throws Exception
* @throws ModelNotFoundException
* @throws DataNotFoundException
@ -1178,7 +1187,7 @@ abstract class BaseQuery
$this->parseView($options);
}
foreach (['data', 'order', 'join', 'union'] as $name) {
foreach (['data', 'order', 'join', 'union', 'filter', 'json'] as $name) {
if (!isset($options[$name])) {
$options[$name] = [];
}

View File

@ -279,7 +279,7 @@ abstract class PDOConnection extends Connection
*/
protected function getFieldType(string $type): string
{
if (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) {
if (0 === stripos($type, 'set') || 0 === stripos($type, 'enum')) {
$result = 'string';
} elseif (preg_match('/(double|float|decimal|real|numeric)/is', $type)) {
$result = 'float';
@ -287,11 +287,11 @@ abstract class PDOConnection extends Connection
$result = 'int';
} elseif (preg_match('/bool/is', $type)) {
$result = 'bool';
} elseif (0 === strpos($type, 'timestamp')) {
} elseif (0 === stripos($type, 'timestamp')) {
$result = 'timestamp';
} elseif (0 === strpos($type, 'datetime')) {
} elseif (0 === stripos($type, 'datetime')) {
$result = 'datetime';
} elseif (0 === strpos($type, 'date')) {
} elseif (0 === stripos($type, 'date')) {
$result = 'date';
} else {
$result = 'string';
@ -1273,7 +1273,7 @@ abstract class PDOConnection extends Connection
$type = is_array($val) ? $val[1] : PDO::PARAM_STR;
if (self::PARAM_FLOAT == $type || PDO::PARAM_STR == $type) {
$value = '\'' . addslashes($value) . '\'';
$value = '\'' . addcslashes($value, "'") . '\'';
} elseif (PDO::PARAM_INT == $type && '' === $value) {
$value = '0';
}

View File

@ -94,4 +94,16 @@ class Sqlite extends Builder
return $key;
}
/**
* 设置锁机制
* @access protected
* @param Query $query 查询对象
* @param bool|string $lock
* @return string
*/
protected function parseLock(Query $query, $lock = false): string
{
return '';
}
}

View File

@ -51,42 +51,6 @@ trait ModelRelationQuery
return $this->model;
}
/**
* 设置需要隐藏的输出属性
* @access public
* @param array $hidden 需要隐藏的字段名
* @return $this
*/
public function hidden(array $hidden)
{
$this->options['hidden'] = $hidden;
return $this;
}
/**
* 设置需要输出的属性
* @access public
* @param array $visible 需要输出的属性
* @return $this
*/
public function visible(array $visible)
{
$this->options['visible'] = $visible;
return $this;
}
/**
* 设置需要追加输出的属性
* @access public
* @param array $append 需要追加的属性
* @return $this
*/
public function append(array $append)
{
$this->options['append'] = $append;
return $this;
}
/**
* 添加查询范围
* @access public
@ -187,7 +151,9 @@ trait ModelRelationQuery
$this->options['with_attr'][$name] = $callback;
}
return $this;
return $this->filter(function ($result) {
return $this->getResultAttr($result, $this->options['with_attr']);
}, 'with_attr');
}
/**
@ -418,23 +384,10 @@ trait ModelRelationQuery
return $this->model->toCollection();
}
// 检查动态获取器
if (!empty($this->options['with_attr'])) {
foreach ($this->options['with_attr'] as $name => $val) {
if (strpos($name, '.')) {
[$relation, $field] = explode('.', $name);
$withRelationAttr[$relation][$field] = $val;
unset($this->options['with_attr'][$name]);
}
}
}
$withRelationAttr = $withRelationAttr ?? [];
$withRelationAttr = $this->getWithRelationAttr();
foreach ($resultSet as $key => &$result) {
// 数据转换为模型对象
$this->resultToModel($result, $this->options, true, $withRelationAttr);
$this->resultToModel($result, $this->options, true);
}
if (!empty($this->options['with'])) {
@ -451,74 +404,74 @@ trait ModelRelationQuery
return $this->model->toCollection($resultSet);
}
/**
* 检查动态获取器
* @access protected
* @return array
*/
protected function getWithRelationAttr(): array
{
if (isset($this->options['with_relation_attr'])) {
return $this->options['with_relation_attr'];
}
$withRelationAttr = [];
if (!empty($this->options['with_attr'])) {
foreach ($this->options['with_attr'] as $name => $val) {
if (strpos($name, '.')) {
[$relation, $field] = explode('.', $name);
$withRelationAttr[$relation][$field] = $val;
unset($this->options['with_attr'][$name]);
}
}
}
$this->options['with_relation_attr'] = $withRelationAttr;
return $withRelationAttr;
}
/**
* 查询数据转换为模型对象
* @access protected
* @param array $result 查询数据
* @param array $options 查询参数
* @param bool $resultSet 是否为数据集查询
* @param array $withRelationAttr 关联字段获取器
* @return void
*/
protected function resultToModel(array &$result, array $options = [], bool $resultSet = false, array $withRelationAttr = []): void
protected function resultToModel(array &$result, array $options = [], bool $resultSet = false): void
{
// 动态获取器
if (!empty($options['with_attr']) && empty($withRelationAttr)) {
foreach ($options['with_attr'] as $name => $val) {
if (strpos($name, '.')) {
[$relation, $field] = explode('.', $name);
$withRelationAttr[$relation][$field] = $val;
unset($options['with_attr'][$name]);
}
}
}
$options['with_relation_attr'] = $this->getWithRelationAttr();
// JSON 数据处理
if (!empty($options['json'])) {
$this->jsonResult($result, $options['json'], $options['json_assoc'], $withRelationAttr);
$this->jsonResult($result, $options['json'], $options['json_assoc'], $options['with_relation_attr']);
}
$result = $this->model
->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options));
// 动态获取器
if (!empty($options['with_attr'])) {
$result->withAttribute($options['with_attr']);
foreach ($this->options['filter'] as $filter) {
$result = call_user_func($filter, $result);
}
// 输出属性控制
if (!empty($options['visible'])) {
$result->visible($options['visible']);
} elseif (!empty($options['hidden'])) {
$result->hidden($options['hidden']);
}
if (!empty($options['append'])) {
$result->append($options['append']);
}
// 关联查询
if (!empty($options['relation'])) {
$result->relationQuery($options['relation'], $withRelationAttr);
}
// 预载入查询
if (!$resultSet && !empty($options['with'])) {
$result->eagerlyResult($result, $options['with'], $withRelationAttr, false, $options['with_cache'] ?? false);
}
// JOIN预载入查询
if (!$resultSet && !empty($options['with_join'])) {
$result->eagerlyResult($result, $options['with_join'], $withRelationAttr, true, $options['with_cache'] ?? false);
}
// 关联统计
if (!empty($options['with_count'])) {
foreach ($options['with_count'] as $val) {
$result->relationCount($this, (array) $val[0], $val[1], $val[2], false);
}
}
$result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options), $options);
}
/**
* 查询软删除数据
* @access public
* @return Query
*/
public function withTrashed()
{
return $this->model ? $this->model->queryWithTrashed() : $this;
}
/**
* 只查询软删除数据
* @access public
* @return Query
*/
public function onlyTrashed()
{
return $this->model ? $this->model->queryOnlyTrashed() : $this;
}
}

View File

@ -26,6 +26,23 @@ use think\Model;
*/
trait ResultOperation
{
/**
* 设置数据处理
* @access public
* @param callable $filter 数据处理Callable
* @param string $index 索引(唯一)
* @return $this
*/
public function filter(callable $filter, string $index = null)
{
if ($index) {
$this->options['filter'][$index] = $filter;
} else {
$this->options['filter'][] = $filter;
}
return $this;
}
/**
* 是否允许返回空数据(或空模型)
* @access public
@ -62,11 +79,9 @@ trait ResultOperation
$this->jsonResult($result, $this->options['json'], true);
}
if (!empty($this->options['with_attr'])) {
$this->getResultAttr($result, $this->options['with_attr']);
foreach ($this->options['filter'] as $filter) {
$result = call_user_func($filter, $result);
}
$this->filterResult($result);
}
/**
@ -78,22 +93,8 @@ trait ResultOperation
*/
protected function resultSet(array &$resultSet, bool $toCollection = true): void
{
if (!empty($this->options['json'])) {
foreach ($resultSet as &$result) {
$this->jsonResult($result, $this->options['json'], true);
}
}
if (!empty($this->options['with_attr'])) {
foreach ($resultSet as &$result) {
$this->getResultAttr($result, $this->options['with_attr']);
}
}
if (!empty($this->options['visible']) || !empty($this->options['hidden'])) {
foreach ($resultSet as &$result) {
$this->filterResult($result);
}
foreach ($resultSet as &$result) {
$this->result($result);
}
// 返回Collection对象
@ -102,36 +103,14 @@ trait ResultOperation
}
}
/**
* 处理数据的可见和隐藏
* @access protected
* @param array $result 查询数据
* @return void
*/
protected function filterResult(&$result): void
{
$array = [];
if (!empty($this->options['visible'])) {
foreach ($this->options['visible'] as $key) {
$array[] = $key;
}
$result = array_intersect_key($result, array_flip($array));
} elseif (!empty($this->options['hidden'])) {
foreach ($this->options['hidden'] as $key) {
$array[] = $key;
}
$result = array_diff_key($result, array_flip($array));
}
}
/**
* 使用获取器处理数据
* @access protected
* @param array $result 查询数据
* @param array $withAttr 字段获取器
* @return void
* @return array
*/
protected function getResultAttr(array &$result, array $withAttr = []): void
protected function getResultAttr(array $result, array $withAttr = []): array
{
foreach ($withAttr as $name => $closure) {
$name = Str::snake($name);
@ -147,6 +126,8 @@ trait ResultOperation
$result[$name] = $closure($result[$name] ?? null, $result);
}
}
return $result;
}
/**
@ -170,7 +151,7 @@ trait ResultOperation
* 查找单条记录 不存在返回空数据(或者空模型)
* @access public
* @param mixed $data 数据
* @return array|Model|static
* @return array|Model|static|mixed
*/
public function findOrEmpty($data = null)
{
@ -242,7 +223,7 @@ trait ResultOperation
* 查找单条记录 如果不存在则抛出异常
* @access public
* @param array|string|Query|Closure $data 数据
* @return array|Model|static
* @return array|Model|static|mixed
* @throws ModelNotFoundException
* @throws DataNotFoundException
*/

View File

@ -157,7 +157,7 @@ class Collection extends BaseCollection
public function withAttr($name, $callback = null)
{
$this->each(function (Model $model) use ($name, $callback) {
$model->withAttribute($name, $callback);
$model->withAttr($name, $callback);
});
return $this;

View File

@ -106,6 +106,12 @@ trait Attribute
*/
private $withAttr = [];
/**
* 数据处理
* @var array
*/
private $filter = [];
/**
* 获取模型对象的主键
* @access public
@ -177,6 +183,24 @@ trait Attribute
return $this;
}
/**
* 设置模型数据处理
* @access public
* @param callable $filter 数据处理Callable
* @param string $index 索引(唯一)
* @return $this
*/
public function filter(callable $filter, string $index = null)
{
if ($index) {
$this->filter[$index] = $filter;
} else {
$this->filter[] = $filter;
}
return $this;
}
/**
* 获取实际的字段名
* @access protected
@ -633,11 +657,11 @@ trait Attribute
* @param callable $callback 闭包获取器
* @return $this
*/
public function withAttribute($name, callable $callback = null)
public function withAttr($name, callable $callback = null)
{
if (is_array($name)) {
foreach ($name as $key => $val) {
$this->withAttribute($key, $val);
$this->withAttr($key, $val);
}
} else {
$name = $this->getRealFieldName($name);

View File

@ -330,6 +330,7 @@ trait Conversion
}
// JsonSerializable
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->toArray();

View File

@ -55,6 +55,16 @@ trait SoftDelete
return $model->withTrashedData(true)->db();
}
/**
* 查询软删除数据
* @access public
* @return Query
*/
public function queryWithTrashed(): Query
{
return $this->withTrashedData(true)->db();
}
/**
* 是否包含软删除数据
* @access protected
@ -86,6 +96,23 @@ trait SoftDelete
return $model->db();
}
/**
* 只查询软删除数据
* @access public
* @return Query
*/
public function queryOnlyTrashed(): Query
{
$field = $this->getDeleteTimeField(true);
if ($field) {
return $this->db()
->useSoftDelete($field, $this->getWithTrashedExp());
}
return $this->db();
}
/**
* 获取软删除数据的查询条件
* @access protected
@ -152,12 +179,12 @@ trait SoftDelete
public static function destroy($data, bool $force = false): bool
{
// 传入空值包括空字符串和空数组的时候不会做任何的数据删除操作但传入0则是有效的
if(empty($data) && $data !== 0){
if (empty($data) && 0 !== $data) {
return false;
}
// 仅当强制删除时包含软删除数据
$model = (new static());
if($force){
if ($force) {
$model->withTrashedData(true);
}
$query = $model->db(false);

View File

@ -19,7 +19,6 @@ use think\db\Raw;
use think\Model;
use think\model\Pivot;
use think\model\Relation;
use think\Paginator;
/**
* 多对多关联类
@ -120,31 +119,6 @@ class BelongsToMany extends Relation
}
}
/**
* 合成中间表模型
* @access protected
* @param array|Collection|Paginator $models
*/
protected function hydratePivot(iterable $models)
{
foreach ($models as $model) {
$pivot = [];
foreach ($model->getData() as $key => $val) {
if (strpos($key, '__')) {
[$name, $attr] = explode('__', $key, 2);
if ('pivot' == $name) {
$pivot[$attr] = $val;
unset($model->$key);
}
}
}
$model->setRelation($this->pivotDataName, $this->newPivot($pivot));
}
}
/**
* 延迟获取关联数据
* @access public
@ -158,62 +132,9 @@ class BelongsToMany extends Relation
$closure($this->getClosureType($closure));
}
$result = $this->relation($subRelation)
return $this->relation($subRelation)
->select()
->setParent(clone $this->parent);
$this->hydratePivot($result);
return $result;
}
/**
* 重载select方法
* @access public
* @param mixed $data
* @return Collection
*/
public function select($data = null): Collection
{
$this->baseQuery();
$result = $this->query->select($data);
$this->hydratePivot($result);
return $result;
}
/**
* 重载paginate方法
* @access public
* @param int|array $listRows
* @param int|bool $simple
* @return Paginator
*/
public function paginate($listRows = null, $simple = false): Paginator
{
$this->baseQuery();
$result = $this->query->paginate($listRows, $simple);
$this->hydratePivot($result);
return $result;
}
/**
* 重载find方法
* @access public
* @param mixed $data
* @return Model
*/
public function find($data = null)
{
$this->baseQuery();
$result = $this->query->find($data);
if ($result && !$result->isEmpty()) {
$this->hydratePivot([$result]);
}
return $result;
}
/**
@ -673,6 +594,23 @@ class BelongsToMany extends Relation
$foreignKey = $this->foreignKey;
$localKey = $this->localKey;
$this->query->getModel()->filter(function ($result, $options) {
$pivot = [];
foreach ($result->getData() as $key => $val) {
if (strpos($key, '__')) {
[$name, $attr] = explode('__', $key, 2);
if ('pivot' == $name) {
$pivot[$attr] = $val;
unset($result->$key);
}
}
}
$result->setRelation($this->pivotDataName, $this->newPivot($pivot));
});
// 关联查询
if (null === $this->parent->getKey()) {
$condition = ['pivot.' . $localKey, 'exp', new Raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk())];