6 Commits

Author SHA1 Message Date
fed4b76e28 Merge tag '8.1.3.3' into develop 2026-01-13 17:28:06 +08:00
2ee72e1363 Merge branch 'release/8.1.3.3' 2026-01-13 17:28:06 +08:00
5d6e2fc5e0 同步更新 2026-01-13 17:27:32 +08:00
45785dd33f Merge tag '8.1.3.2' into develop 2026-01-07 17:02:35 +08:00
50998e06f6 Merge branch 'release/8.1.3.2' 2026-01-07 17:02:34 +08:00
33d7d71dad 更新container 2026-01-07 17:02:11 +08:00
38 changed files with 1212 additions and 306 deletions

1
.gitignore vendored
View File

@@ -9,3 +9,4 @@ Thumbs.db
/.buildpath
/.project
.phpunit.result.cache
.php-cs-fixer.cache

48
.php-cs-fixer.dist.php Normal file
View File

@@ -0,0 +1,48 @@
<?php
use PhpCsFixer\Config;
use PhpCsFixer\Finder;
$finder = (new Finder())
->in(__DIR__)
;
return (new Config())
->setRules([
'@PhpCsFixer' => true,
'@PHP8x0Migration' => true,
'binary_operator_spaces' => [
'default' => 'align_single_space_minimal',
'operators' => [
'=>' => 'align_single_space_minimal_by_scope',
],
],
'blank_line_before_statement' => [
'statements' => [
'continue',
'declare',
'return',
'throw',
'try',
],
],
'concat_space' => [
'spacing' => 'one',
],
'global_namespace_import' => [
'import_classes' => true,
'import_constants' => false,
'import_functions' => false,
],
'ordered_class_elements' => [
'order' => [
'use_trait',
'case',
'constant',
'property',
'construct',
],
],
])
->setFinder($finder)
;

View File

@@ -25,17 +25,18 @@
"ext-ctype": "*",
"psr/log": "^1.0|^2.0|^3.0",
"psr/simple-cache": "^1.0|^2.0|^3.0",
"psr/http-message": "^1.0",
"psr/http-message": "^1.0|^2.0",
"topthink/think-orm": "^3.0|^4.0",
"topthink/think-helper": "^3.1",
"topthink/think-container": "^3.0",
"fixtopthink/think-container": "^3.0",
"topthink/think-validate": "^3.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.92",
"guzzlehttp/psr7": "^2.1.0",
"mikey179/vfsstream": "^1.6",
"mockery/mockery": "^1.2",
"phpunit/phpunit": "^9.5",
"guzzlehttp/psr7": "^2.1.0"
"phpunit/phpunit": "^9.5"
},
"autoload": {
"files": [],
@@ -54,6 +55,6 @@
"sort-packages": true
},
"scripts": {
"php-cs-fixer": "php-cs-fixer fix src/ --rules=@PER-CS2.0 --dry-run --diff"
"php-cs-fixer": "php-cs-fixer fix src/ --dry-run --diff"
}
}

View File

@@ -42,7 +42,7 @@ use think\validate\ValidateRuleSet;
if (!function_exists('abort')) {
/**
* 抛出HTTP异常
* @param integer|Response $code 状态码 或者 Response对象实例
* @param int|Response $code 状态码 或者 Response对象实例
* @param string $message 错误信息
* @param array $header 参数
*/

View File

@@ -43,7 +43,7 @@ class App extends Container
* 核心框架版本
* @deprecated 已经废弃 请改用version()方法
*/
const VERSION = '8.0.0';
public const VERSION = '8.0.0';
/**
* 应用调试模式
@@ -71,7 +71,7 @@ class App extends Container
/**
* 应用内存初始占用
* @var integer
* @var int
*/
protected $beginMem;
@@ -427,7 +427,7 @@ class App extends Container
/**
* 获取应用初始内存占用
* @access public
* @return integer
* @return int
*/
public function getBeginMem(): int
{
@@ -458,7 +458,6 @@ class App extends Container
public function initialize()
{
$this->initialized = true;
$this->beginTime = microtime(true);
$this->beginMem = memory_get_usage();

View File

@@ -28,6 +28,7 @@ use think\console\command\make\Service;
use think\console\command\make\Subscribe;
use think\console\command\make\Validate;
use think\console\command\optimize\Config;
use think\console\command\optimize\Optimize;
use think\console\command\optimize\Route;
use think\console\command\optimize\Schema;
use think\console\command\RouteList;
@@ -70,6 +71,7 @@ class Console
'make:listener' => Listener::class,
'make:service' => Service::class,
'make:subscribe' => Subscribe::class,
'optimize' => Optimize::class,
'optimize:config' => Config::class,
'optimize:route' => Route::class,
'optimize:schema' => Schema::class,
@@ -785,5 +787,4 @@ class Console
return $namespaces;
}
}

View File

@@ -135,7 +135,7 @@ class Cookie
* @access public
* @param string $name cookie名称
* @param string $value cookie值
* @param mixed $option 可选参数 可能会是 null|integer|string
* @param mixed $option 可选参数 可能会是 null|int|string
* @return void
*/
public function forever(string $name, string $value = '', $option = null): void

View File

@@ -191,9 +191,12 @@ class Event
if (empty($prefix) && $reflect->hasProperty('eventPrefix')) {
$reflectProperty = $reflect->getProperty('eventPrefix');
if (PHP_VERSION_ID < 80100) {
// 8.0 版本时调用
$reflectProperty->setAccessible(true);
}
$prefix = $reflectProperty->getValue($observer);
}

View File

@@ -17,6 +17,9 @@ use ArrayAccess;
use think\facade\Lang;
use think\file\UploadedFile;
use think\route\Rule;
use think\traits\UrlHandler;
use think\traits\DomainHandler;
use think\traits\HttpMethodHandler;
/**
* 请求管理类
@@ -24,6 +27,7 @@ use think\route\Rule;
*/
class Request implements ArrayAccess
{
use UrlHandler, DomainHandler, HttpMethodHandler;
/**
* 兼容PATH_INFO获取
* @var array
@@ -36,35 +40,6 @@ class Request implements ArrayAccess
*/
protected $varPathinfo = 's';
/**
* 请求类型
* @var string
*/
protected $varMethod = '_method';
/**
* 表单ajax伪装变量
* @var string
*/
protected $varAjax = '_ajax';
/**
* 表单pjax伪装变量
* @var string
*/
protected $varPjax = '_pjax';
/**
* 域名根
* @var string
*/
protected $rootDomain = '';
/**
* 特殊域名根标识 用于识别com.cn org.cn 这种
* @var array
*/
protected $domainSpecialSuffix = ['com', 'net', 'org', 'edu', 'gov', 'mil', 'co', 'info'];
/**
* HTTPS代理标识
@@ -84,59 +59,7 @@ class Request implements ArrayAccess
*/
protected $proxyServerIpHeader = ['HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP'];
/**
* 请求类型
* @var string
*/
protected $method;
/**
* 域名(含协议及端口)
* @var string
*/
protected $domain;
/**
* HOST含端口
* @var string
*/
protected $host;
/**
* 子域名
* @var string
*/
protected $subDomain;
/**
* 泛域名
* @var string
*/
protected $panDomain;
/**
* 当前URL地址
* @var string
*/
protected $url;
/**
* 基础URL
* @var string
*/
protected $baseUrl;
/**
* 当前执行的文件
* @var string
*/
protected $baseFile;
/**
* 访问的ROOT地址
* @var string
*/
protected $root;
/**
* pathinfo
@@ -435,7 +358,7 @@ class Request implements ArrayAccess
*/
public function subDomain(): string
{
if (is_null($this->subDomain)) {
if ($this->subDomain === '') {
// 获取当前主域名
$rootDomain = $this->rootDomain();
@@ -681,7 +604,7 @@ class Request implements ArrayAccess
* 获取当前请求的时间
* @access public
* @param bool $float 是否使用浮点类型
* @return integer|float
* @return int|float
*/
public function time(bool $float = false)
{
@@ -1682,7 +1605,7 @@ class Request implements ArrayAccess
* @param string $ip IP地址
* @param string $type IP地址类型 (ipv4, ipv6)
*
* @return boolean
* @return bool
*/
public function isValidIP(string $ip, string $type = ''): bool
{
@@ -2191,7 +2114,7 @@ class Request implements ArrayAccess
* 检测中间传递数据的值
* @access public
* @param string $name 名称
* @return boolean
* @return bool
*/
public function __isset(string $name): bool
{

View File

@@ -38,7 +38,7 @@ abstract class Response
/**
* 状态码
* @var integer
* @var int
*/
protected $code = 200;
@@ -277,7 +277,7 @@ abstract class Response
/**
* 发送HTTP状态
* @access public
* @param integer $code 状态码
* @param int $code 状态码
* @return $this
*/
public function code(int $code)
@@ -406,7 +406,7 @@ abstract class Response
/**
* 获取状态码
* @access public
* @return integer
* @return int
*/
public function getCode(): int
{

View File

@@ -36,13 +36,13 @@ abstract class Driver implements CacheHandlerInterface
/**
* 缓存读取次数
* @var integer
* @var int
*/
protected $readTimes = 0;
/**
* 缓存写入次数
* @var integer
* @var int
*/
protected $writeTimes = 0;
@@ -61,7 +61,7 @@ abstract class Driver implements CacheHandlerInterface
/**
* 获取有效期
* @access protected
* @param integer|DateInterval|DateTimeInterface $expire 有效期
* @param int|DateInterval|DateTimeInterface $expire 有效期
* @return int
*/
protected function getExpireTime(int | DateInterval | DateTimeInterface $expire): int

View File

@@ -35,7 +35,7 @@ class TagSet
* @access public
* @param string $name 缓存变量名
* @param mixed $value 存储数据
* @param integer|DateInterval|DateTimeInterface $expire 有效时间(秒)
* @param int|DateInterval|DateTimeInterface $expire 有效时间(秒)
* @return bool
*/
public function set($name, $value, $expire = null): bool

View File

@@ -122,7 +122,7 @@ class Memcached extends Driver
* @access public
* @param string $name 缓存变量名
* @param mixed $value 存储数据
* @param integer|DateInterval|DateTimeInterface $expire 有效时间(秒)
* @param int|DateInterval|DateTimeInterface $expire 有效时间(秒)
* @return bool
*/
public function set($name, $value, $expire = null): bool

View File

@@ -135,7 +135,7 @@ class Redis extends Driver
* @access public
* @param string $name 缓存变量名
* @param mixed $value 存储数据
* @param integer|DateInterval|DateTimeInterface $expire 有效时间(秒)
* @param int|DateInterval|DateTimeInterface $expire 有效时间(秒)
* @return bool
*/
public function set($name, $value, $expire = null): bool

View File

@@ -88,7 +88,7 @@ class Wincache extends Driver
* @access public
* @param string $name 缓存变量名
* @param mixed $value 存储数据
* @param integer|DateInterval|DateTimeInterface $expire 有效时间(秒)
* @param int|DateInterval|DateTimeInterface $expire 有效时间(秒)
* @return bool
*/
public function set($name, $value, $expire = null): bool

View File

@@ -0,0 +1,32 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2025 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\command;
use think\console\Command;
use think\console\Input;
/**
* @mixin Command
*/
trait CommandCallable
{
/**
* @param class-string<Command> $class
*/
private function callCommand(string $class): Command
{
return tap(app($class, newInstance: true), function ($command) {
$command->setApp($this->app);
$command->run(new Input([]), clone $this->output);
});
}
}

View File

@@ -10,13 +10,17 @@
// +----------------------------------------------------------------------
namespace think\console\command\optimize;
use InvalidArgumentException;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\Output;
use Throwable;
class Config extends Command
{
use Discoverable;
protected function configure()
{
$this->setName('optimize:config')
@@ -26,39 +30,54 @@ class Config extends Command
protected function execute(Input $input, Output $output)
{
// 加载配置文件
$dir = $input->getArgument('dir') ?: '';
$dirs = ((array) $input->getArgument('dir')) ?: $this->getDefaultDirs();
foreach ($dirs as $dir) {
$path = $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . ($dir ? $dir . DIRECTORY_SEPARATOR : '');
if (!is_dir($path)) {
try {
$cache = $this->buildCache($dir);
if (! is_dir($path)) {
mkdir($path, 0755, true);
} catch (\Exception $e) {
// 创建失败
}
}
$file = $path . 'config.php';
$config = $this->loadConfig($dir);
$content = '<?php ' . PHP_EOL . 'return ' . var_export($config, true) . ';';
if (file_put_contents($file, $content)) {
$output->writeln("<info>Succeed!</info>");
} else {
$output->writeln("<error>config build fail</error>");
file_put_contents($path . 'config.php', $cache);
} catch (Throwable $e) {
$output->warning($e->getMessage());
}
}
public function loadConfig($dir = '')
$output->info('Succeed!');
}
private function buildCache(?string $dir = null): string
{
$configPath = $this->app->getRootPath() . ($dir ? 'app' . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR : '') . 'config' . DIRECTORY_SEPARATOR;
$files = [];
if (is_dir($configPath)) {
$files = glob($configPath . '*' . $this->app->getConfigExt());
$path = $this->app->getRootPath() . ($dir ? 'app' . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR : '') . 'config' . DIRECTORY_SEPARATOR;
if (! is_dir($path)) {
throw new InvalidArgumentException("{$path} directory does not exist");
}
// 使用 clone 防止多应用配置污染
$config = clone $this->app->config;
if (is_dir($path)) {
$files = glob($path . '*' . $this->app->getConfigExt());
foreach ($files as $file) {
$this->app->config->load($file, pathinfo($file, PATHINFO_FILENAME));
$config->load($file, pathinfo($file, PATHINFO_FILENAME));
}
}
return $this->app->config->get();
return '<?php ' . PHP_EOL . 'return ' . var_export($config->get(), true) . ';';
}
/**
* 获取默认目录名
* @return array<int, ?string>
*/
private function getDefaultDirs(): array
{
// 包含全局应用配置目录
$dirs = [null];
if ($this->isInstalledMultiApp()) {
$dirs = array_merge($dirs, $this->discoveryMultiAppDirs('config'));
}
return $dirs;
}
}

View File

@@ -0,0 +1,44 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\console\command\optimize;
use Composer\InstalledVersions;
use DirectoryIterator;
trait Discoverable
{
/**
* 判断是否安装 topthink/think-multi-app
*/
private function isInstalledMultiApp(): bool
{
return InstalledVersions::isInstalled('topthink/think-multi-app');
}
/**
* 发现多应用程序目录
* @return string[]
*/
private function discoveryMultiAppDirs(string $directoryName): array
{
$dirs = [];
foreach (new DirectoryIterator($this->app->getAppPath()) as $item) {
if (! $item->isDir() || $item->isDot()) {
continue;
}
$path = $item->getRealPath() . DIRECTORY_SEPARATOR . $directoryName . DIRECTORY_SEPARATOR;
if (is_dir($path)) {
$dirs[] = $item->getFilename();
}
}
return $dirs;
}
}

View File

@@ -0,0 +1,40 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace think\console\command\optimize;
use think\console\Command;
use think\console\command\CommandCallable;
use think\console\Input;
use think\console\Output;
class Optimize extends Command
{
use CommandCallable;
protected function configure()
{
$this->setName('optimize')
->setDescription('Build cache.');
}
protected function execute(Input $input, Output $output)
{
$commands = [
Config::class,
Route::class,
Schema::class,
];
foreach ($commands as $class) {
$command = $this->callCommand($class);
$this->output->info($command->getName() . ' run succeed!');
}
}
}

View File

@@ -11,14 +11,18 @@
namespace think\console\command\optimize;
use DirectoryIterator;
use InvalidArgumentException;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\Output;
use think\event\RouteLoaded;
use Throwable;
class Route extends Command
{
use Discoverable;
protected function configure()
{
$this->setName('optimize:route')
@@ -28,18 +32,22 @@ class Route extends Command
protected function execute(Input $input, Output $output)
{
$dir = $input->getArgument('dir') ?: '';
$dirs = ((array) $input->getArgument('dir')) ?: $this->getDefaultDirs();
foreach ($dirs as $dir) {
$path = $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . ($dir ? $dir . DIRECTORY_SEPARATOR : '');
if (!is_dir($path)) {
try {
$cache = $this->buildRouteCache($dir);
if (! is_dir($path)) {
mkdir($path, 0755, true);
} catch (\Exception $e) {
// 创建失败
}
file_put_contents($path . 'route.php', $cache);
} catch (Throwable $e) {
$output->warning($e->getMessage());
}
}
file_put_contents($path . 'route.php', $this->buildRouteCache($dir));
$output->writeln('<info>Succeed!</info>');
$output->info('Succeed!');
}
protected function scanRoute($path, $root, $autoGroup)
@@ -73,6 +81,9 @@ class Route extends Command
// 路由检测
$autoGroup = $this->app->route->config('route_auto_group');
$path = $this->app->getRootPath() . ($dir ? 'app' . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR : '') . 'route' . DIRECTORY_SEPARATOR;
if (! is_dir($path)) {
throw new InvalidArgumentException("{$path} directory does not exist");
}
$this->scanRoute($path, $path, $autoGroup);
@@ -83,4 +94,17 @@ class Route extends Command
return '<?php ' . PHP_EOL . 'return ' . var_export($rules, true) . ';';
}
/**
* 获取默认目录名
* @return array<int, ?string>
*/
private function getDefaultDirs(): array
{
// 判断是否使用多应用模式
// 如果使用了则扫描 app 目录
// 否则返回 null让其扫描根目录的 route 目录
return $this->isInstalledMultiApp()
? $this->discoveryMultiAppDirs('route')
: [null];
}
}

View File

@@ -11,15 +11,23 @@
namespace think\console\command\optimize;
use Exception;
use InvalidArgumentException;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ReflectionClass;
use SplFileInfo;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\db\PDOConnection;
use Throwable;
class Schema extends Command
{
use Discoverable;
protected function configure()
{
$this->setName('optimize:schema')
@@ -31,59 +39,28 @@ class Schema extends Command
protected function execute(Input $input, Output $output)
{
$dir = $input->getArgument('dir') ?: '';
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 (!str_contains($table, '.')) {
$dbName = $connection->getConfig('database');
try {
if ($table = $input->hasOption('table')) {
$this->cacheTable($table, $input->getOption('connection'));
} else {
[$dbName, $table] = explode('.', $table);
}
if ($table == '*') {
$table = $connection->getTables($dbName);
}
$this->buildDataBaseSchema($connection, (array) $table, $dbName);
} else {
if ($dir) {
$appPath = $this->app->getBasePath() . $dir . DIRECTORY_SEPARATOR;
$namespace = 'app\\' . $dir;
} else {
$appPath = $this->app->getBasePath();
$namespace = 'app';
}
$path = $appPath . 'model';
$list = is_dir($path) ? scandir($path) : [];
foreach ($list as $file) {
if (str_starts_with($file, '.')) {
continue;
}
$class = '\\' . $namespace . '\\model\\' . pathinfo($file, PATHINFO_FILENAME);
if (!class_exists($class)) {
continue;
}
$this->buildModelSchema($class);
$dirs = ((array) $input->getArgument('dir')) ?: $this->getDefaultDirs();
foreach ($dirs as $dir) {
$this->cacheModel($dir);
}
}
} catch (Throwable $e) {
return $output->error($e->getMessage());
}
$output->writeln('<info>Succeed!</info>');
$output->info('Succeed!');
}
protected function buildModelSchema(string $class): void
{
$reflect = new \ReflectionClass($class);
if (!$reflect->isAbstract() && $reflect->isSubclassOf('\think\Model')) {
$reflect = new ReflectionClass($class);
if ($reflect->isAbstract() || ! $reflect->isSubclassOf('\think\Model')) {
return;
}
try {
/** @var \think\Model $model */
$model = new $class;
@@ -94,8 +71,6 @@ class Schema extends Command
$connection->getSchemaInfo($table, true);
}
} catch (Exception $e) {
}
}
}
@@ -106,4 +81,81 @@ class Schema extends Command
$connection->getSchemaInfo("{$dbName}.{$table}", true);
}
}
/**
* 缓存表
*/
private function cacheTable(string $table, ?string $connectionName = null): void
{
$connection = $this->app->db->connect($connectionName);
if (! $connection instanceof PDOConnection) {
throw new InvalidArgumentException('only PDO connection support schema cache!');
}
if (str_contains($table, '.')) {
[$dbName, $table] = explode('.', $table);
} else {
$dbName = $connection->getConfig('database');
}
if ($table == '*') {
$table = $connection->getTables($dbName);
}
$this->buildDataBaseSchema($connection, (array) $table, $dbName);
}
/**
* 缓存模型
*/
private function cacheModel(?string $dir = null): void
{
if ($dir) {
$modelDir = $this->app->getAppPath() . $dir . DIRECTORY_SEPARATOR . 'model' . DIRECTORY_SEPARATOR;
$namespace = 'app\\' . $dir;
} else {
$modelDir = $this->app->getAppPath() . 'model' . DIRECTORY_SEPARATOR;
$namespace = 'app';
}
if (! is_dir($modelDir)) {
throw new InvalidArgumentException("{$modelDir} directory does not exist");
}
/** @var SplFileInfo[] $iterator */
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($modelDir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $fileInfo) {
$relativePath = substr($fileInfo->getRealPath(), strlen($modelDir));
if (! str_ends_with($relativePath, '.php')) {
continue;
}
// 去除 .php
$relativePath = substr($relativePath, 0, -4);
$class = '\\' . $namespace . '\\model\\' . str_replace('/', '\\', $relativePath);
if (! class_exists($class)) {
continue;
}
$this->buildModelSchema($class);
}
}
/**
* 获取默认目录名
* @return array<int, ?string>
*/
private function getDefaultDirs(): array
{
// 包含默认的模型目录
$dirs = [null];
if ($this->isInstalledMultiApp()) {
$dirs = array_merge($dirs, $this->discoveryMultiAppDirs('model'));
}
return $dirs;
}
}

View File

@@ -212,7 +212,7 @@ class Descriptor
*/
protected function describeConsole(Console $console, array $options = [])
{
$describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
$describedNamespace = $options['namespace'] ?? null;
$description = new ConsoleDescription($console, $describedNamespace);
if (isset($options['raw_text']) && $options['raw_text']) {

View File

@@ -120,11 +120,11 @@ class Console
]);
for ($i = 0, $count = count($trace); $i < $count; ++$i) {
$class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
$type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
$class = $trace[$i]['class'] ?? '';
$type = $trace[$i]['type'] ?? '';
$function = $trace[$i]['function'];
$file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
$line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
$file = $trace[$i]['file'] ?? 'n/a';
$line = $trace[$i]['line'] ?? 'n/a';
$this->write(sprintf(' %s%s%s() at <info>%s:%s</info>', $class, $type, $function, $file, $line), true, Output::OUTPUT_NORMAL, $stderr);
}

View File

@@ -59,8 +59,8 @@ interface ModelRelationInterface
* @param Closure $closure 闭包
* @param string $aggregate 聚合查询方法
* @param string $field 字段
* @param string $name 统计字段别名
* @return integer
* @param string|null $name 统计字段别名
* @return int
*/
public function relationCount(Model $result, Closure $closure, string $aggregate = 'count', string $field = '*', ?string &$name = null);
@@ -79,7 +79,7 @@ interface ModelRelationInterface
* 根据关联条件查询当前模型
* @access public
* @param string $operator 比较操作符
* @param integer $count 个数
* @param int $count 个数
* @param string $id 关联表的统计字段
* @param string $joinType JOIN类型
* @return Query

View File

@@ -24,17 +24,17 @@ class ErrorException extends Exception
{
/**
* 用于保存错误级别
* @var integer
* @var int
*/
protected $severity;
/**
* 错误异常构造函数
* @access public
* @param integer $severity 错误级别
* @param int $severity 错误级别
* @param string $message 错误详细信息
* @param string $file 出错文件路径
* @param integer $line 出错行号
* @param int $line 出错行号
*/
public function __construct(int $severity, string $message, string $file, int $line)
{
@@ -48,7 +48,7 @@ class ErrorException extends Exception
/**
* 获取错误级别
* @access public
* @return integer 错误级别
* @return int 错误级别
*/
final public function getSeverity()
{

View File

@@ -270,7 +270,7 @@ class Handle
* ErrorException则使用错误级别作为错误编码
* @access protected
* @param Throwable $exception
* @return integer 错误编码
* @return int 错误编码
*/
protected function getCode(Throwable $exception)
{

View File

@@ -36,7 +36,7 @@ use think\Facade;
* @method static string getConfigPath() 获取应用配置目录
* @method static string getConfigExt() 获取配置后缀
* @method static float getBeginTime() 获取应用开启时间
* @method static integer getBeginMem() 获取应用初始内存占用
* @method static int getBeginMem() 获取应用初始内存占用
* @method static \think\App initialize() 初始化应用
* @method static bool initialized() 是否初始化过
* @method static void loadLangPack(string $langset) 加载语言包

View File

@@ -1,4 +1,5 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
@@ -38,7 +39,7 @@ use think\route\Rule;
* @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 int|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) 设置请求类型
@@ -81,7 +82,7 @@ use think\route\Rule;
* @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 bool isValidIP(string $ip, string $type = '') 检测是否是合法的IP地址
* @method static string ip2bin(string $ip) 将IP地址转换为二进制字符串
* @method static bool isMobile() 检测是否使用手机访问
* @method static string scheme() 当前URL地址中的scheme参数
@@ -116,7 +117,7 @@ use think\route\Rule;
* @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 __isset(string $name) 检测中间传递数据的值
* @method static bool offsetExists(mixed $name)
* @method static mixed offsetGet(mixed $name)
* @method static mixed offsetSet(mixed $name, $value)

View File

@@ -65,10 +65,10 @@ class Error
/**
* Error Handler
* @access public
* @param integer $errno 错误编号
* @param int $errno 错误编号
* @param string $errstr 详细错误信息
* @param string $errfile 出错的文件
* @param integer $errline 出错行号
* @param int $errline 出错行号
* @throws ErrorException
*/
public function appError(int $errno, string $errstr, string $errfile = '', int $errline = 0): void

View File

@@ -132,6 +132,9 @@ class File implements LogHandlerInterface
try {
if (count($files) > $this->config['max_files']) {
set_error_handler(fn() => null);
usort($files, function($a, $b) {
return filemtime($a) - filemtime($b);
});
unlink($files[0]);
restore_error_handler();
}

View File

@@ -91,7 +91,7 @@ class File extends Response
/**
* 设置有效期
* @access public
* @param integer $expire 有效期
* @param int $expire 有效期
* @return $this
*/
public function expire(int $expire)

View File

@@ -28,9 +28,20 @@ use think\Validate;
*/
abstract class Dispatch
{
/**
* 控制器名
* @var string
*/
protected $controller;
/**
* 操作名
* @var string
*/
protected $actionName;
/**
* 应用对象
*
* @var App
*/
protected $app;
@@ -41,7 +52,6 @@ abstract class Dispatch
/**
* 执行路由调度
*
* @access public
* @return Response
*/
@@ -78,7 +88,6 @@ abstract class Dispatch
/**
* 检查路由后置操作
*
* @access protected
* @return void
*/
@@ -119,7 +128,6 @@ abstract class Dispatch
/**
* 获取操作的绑定参数
*
* @access protected
* @return array
*/
@@ -135,11 +143,9 @@ abstract class Dispatch
/**
* 执行中间件调度
*
* @access public
*
* @param object $controller 控制器实例
*
* @param object $instance 控制器实例
* @param string $action
* @return void
*/
protected function responseWithMiddlewarePipeline($instance, $action)
@@ -182,11 +188,8 @@ abstract class Dispatch
/**
* 使用反射机制注册控制器中间件
*
* @access public
*
* @param object $controller 控制器实例
*
* @return void
*/
protected function registerControllerMiddleware($controller): void
@@ -195,6 +198,7 @@ abstract class Dispatch
if ($class->hasProperty('middleware')) {
$reflectionProperty = $class->getProperty('middleware');
if (PHP_VERSION_ID < 80100) {
$reflectionProperty->setAccessible(true);
}
@@ -241,12 +245,9 @@ abstract class Dispatch
/**
* 路由绑定模型实例
*
* @access protected
*
* @param array $bindModel 绑定模型
* @param array $matches 路由变量
*
* @return void
*/
protected function createBindModel(array $bindModel, array $matches): void
@@ -290,11 +291,8 @@ abstract class Dispatch
/**
* 验证数据
*
* @access protected
*
* @param array $option
*
* @return void
* @throws \think\exception\ValidateException
*/
@@ -336,13 +334,25 @@ abstract class Dispatch
abstract public function exec();
public function __sleep()
public function __serialize(): array
{
return ['rule', 'dispatch', 'param', 'controller', 'actionName'];
return [
'rule' => $this->rule,
'dispatch' => $this->dispatch,
'param' => $this->param,
'controller' => $this->controller,
'actionName' => $this->actionName,
];
}
public function __wakeup()
public function __unserialize(array $data): void
{
$this->rule = $data['rule'];
$this->dispatch = $data['dispatch'];
$this->param = $data['param'];
$this->controller = $data['controller'];
$this->actionName = $data['actionName'];
$this->app = Container::pull('app');
$this->request = $this->app->request;
}

View File

@@ -1030,13 +1030,29 @@ abstract class Rule
return call_user_func_array([$this, 'setOption'], $args);
}
public function __sleep()
public function __serialize(): array
{
return ['name', 'rule', 'route', 'method', 'vars', 'option', 'pattern'];
return [
'name' => $this->name,
'rule' => $this->rule,
'route' => $this->route,
'method' => $this->method,
'vars' => $this->vars,
'option' => $this->option,
'pattern' => $this->pattern,
];
}
public function __wakeup()
public function __unserialize(array $data): void
{
$this->name = $data['name'];
$this->rule = $data['rule'];
$this->route = $data['route'];
$this->method = $data['method'];
$this->vars = $data['vars'];
$this->option = $data['option'];
$this->pattern = $data['pattern'];
$this->router = Container::pull('route');
}

View File

@@ -368,7 +368,7 @@ class Url
}
if ($url) {
$checkName = isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : '');
$checkName = $name ?? $url . (isset($info['query']) ? '?' . $info['query'] : '');
$checkDomain = $domain && is_string($domain) ? $domain : null;
$rule = $this->route->getName($checkName, $checkDomain);

View File

@@ -20,7 +20,7 @@ class Cache implements SessionHandlerInterface
/** @var CacheInterface */
protected $handler;
/** @var integer */
/** @var int */
protected $expire;
/** @var string */

View File

@@ -0,0 +1,243 @@
<?php
declare (strict_types=1);
namespace think\traits;
/**
* 域名处理 trait
*/
trait DomainHandler
{
/**
* 域名(含协议及端口)
* @var string
*/
protected $domain;
/**
* 域名根
* @var string
*/
protected $rootDomain = '';
/**
* 子域名
* @var string
*/
protected $subDomain = '';
/**
* 泛域名
* @var string
*/
protected $panDomain = '';
/**
* 特殊域名根标识 用于识别com.cn org.cn 这种
* @var array
*/
protected $domainSpecialSuffix = ['com', 'net', 'org', 'edu', 'gov', 'mil', 'co', 'info'];
/**
* 设置当前域名
* @access public
* @param string $domain 域名
* @return $this
*/
public function setDomain(string $domain)
{
$this->domain = $domain;
return $this;
}
/**
* 获取当前域名
* @access public
* @param bool $port 是否需要包含端口
* @return string
*/
public function domain(bool $port = false): string
{
if (!$this->domain) {
$this->domain = $this->scheme() . '://' . $this->host();
}
return $port ? $this->domain : rtrim($this->domain, ':');
}
/**
* 设置域名根
* @access public
* @param string $domain 域名根
* @return $this
*/
public function setRootDomain(string $domain)
{
$this->rootDomain = $domain;
return $this;
}
/**
* 获取域名根
* @access public
* @return string
*/
public function rootDomain(): string
{
if (!$this->rootDomain) {
$item = explode('.', $this->host());
$count = count($item);
$suffix = $this->config('app.domain_suffix');
if ($suffix && in_array($item[$count - 2], $this->domainSpecialSuffix)) {
$this->rootDomain = $item[$count - 3] . '.' . $item[$count - 2] . '.' . $item[$count - 1];
} elseif ($suffix) {
$this->rootDomain = $item[$count - 2] . '.' . $item[$count - 1];
} else {
$this->rootDomain = $item[$count - 2] . '.' . $item[$count - 1];
}
}
return $this->rootDomain;
}
/**
* 设置子域名
* @access public
* @param string $domain 子域名
* @return $this
*/
public function setSubDomain(string $domain)
{
$this->subDomain = $domain;
return $this;
}
/**
* 获取子域名
* @access public
* @return string
*/
public function subDomain(): string
{
if (!$this->subDomain) {
if ($this->isCli()) {
return '';
}
$rootDomain = $this->rootDomain();
if ($rootDomain) {
$this->subDomain = rtrim(strstr($this->host(), $rootDomain, true), '.');
} else {
$this->subDomain = '';
}
}
return $this->subDomain;
}
/**
* 设置泛域名
* @access public
* @param string $domain 泛域名
* @return $this
*/
public function setPanDomain(string $domain)
{
$this->panDomain = $domain;
return $this;
}
/**
* 获取泛域名
* @access public
* @return string
*/
public function panDomain(): string
{
if (!$this->panDomain) {
if ($this->isCli()) {
return '';
}
$rootDomain = $this->rootDomain();
if ($rootDomain) {
$this->panDomain = '*' . $rootDomain;
} else {
$this->panDomain = '';
}
}
return $this->panDomain;
}
/**
* 获取当前HOST
* @access public
* @param bool $strict 是否严格模式
* @return string
*/
public function host(bool $strict = true): string
{
if ($this->server('HTTP_X_FORWARDED_HOST')) {
$host = $this->server('HTTP_X_FORWARDED_HOST');
} elseif ($this->server('HTTP_HOST')) {
$host = $this->server('HTTP_HOST');
} else {
$host = $this->server('SERVER_NAME') . ($this->server('SERVER_PORT') == '80' ? '' : ':' . $this->server('SERVER_PORT'));
}
return true === $strict && strpos($host, ':') ? strstr($host, ':', true) : $host;
}
/**
* 获取当前请求的协议
* @access public
* @return string
*/
public function scheme(): string
{
return $this->isSsl() ? 'https' : 'http';
}
/**
* 当前是否SSL
* @access public
* @return bool
*/
public function isSsl(): bool
{
if ($this->server('HTTPS') && ('1' == $this->server('HTTPS') || 'on' == strtolower($this->server('HTTPS')))) {
return true;
} elseif ('https' == $this->server('REQUEST_SCHEME')) {
return true;
} elseif ('443' == $this->server('SERVER_PORT')) {
return true;
} elseif ('https' == $this->server('HTTP_X_FORWARDED_PROTO')) {
return true;
}
return false;
}
/**
* 获取当前请求的端口
* @access public
* @return int
*/
public function port(): int
{
return $this->server('SERVER_PORT') ?? 80;
}
/**
* 获取当前请求的远程端口
* @access public
* @return int
*/
public function remotePort(): int
{
return $this->server('REMOTE_PORT') ?? 0;
}
}

View File

@@ -0,0 +1,213 @@
<?php
declare (strict_types=1);
namespace think\traits;
/**
* HTTP方法处理 trait
*/
trait HttpMethodHandler
{
/**
* 请求类型
* @var string
*/
protected $varMethod = '_method';
/**
* 表单ajax伪装变量
* @var string
*/
protected $varAjax = '_ajax';
/**
* 表单pjax伪装变量
* @var string
*/
protected $varPjax = '_pjax';
/**
* 请求类型
* @var string
*/
protected $method;
/**
* 获取请求类型
* @access public
* @param bool $origin 是否获取原始请求类型
* @return string
*/
public function method(bool $origin = false): string
{
if ($origin) {
return $this->server('REQUEST_METHOD') ?: 'GET';
} elseif (!$this->method) {
if (isset($_POST[$this->varMethod])) {
$this->method = strtoupper($_POST[$this->varMethod]);
$this->{$this->method}($_POST);
} elseif ($this->server('HTTP_X_HTTP_METHOD_OVERRIDE')) {
$this->method = $this->server('HTTP_X_HTTP_METHOD_OVERRIDE');
} else {
$this->method = $this->server('REQUEST_METHOD') ?: 'GET';
}
}
return $this->method;
}
/**
* 是否为GET请求
* @access public
* @return bool
*/
public function isGet(): bool
{
return $this->method() == 'GET';
}
/**
* 是否为POST请求
* @access public
* @return bool
*/
public function isPost(): bool
{
return $this->method() == 'POST';
}
/**
* 是否为PUT请求
* @access public
* @return bool
*/
public function isPut(): bool
{
return $this->method() == 'PUT';
}
/**
* 是否为DELTE请求
* @access public
* @return bool
*/
public function isDelete(): bool
{
return $this->method() == 'DELETE';
}
/**
* 是否为HEAD请求
* @access public
* @return bool
*/
public function isHead(): bool
{
return $this->method() == 'HEAD';
}
/**
* 是否为PATCH请求
* @access public
* @return bool
*/
public function isPatch(): bool
{
return $this->method() == 'PATCH';
}
/**
* 是否为OPTIONS请求
* @access public
* @return bool
*/
public function isOptions(): bool
{
return $this->method() == 'OPTIONS';
}
/**
* 是否为CLI
* @access public
* @return bool
*/
public function isCli(): bool
{
return PHP_SAPI == 'cli';
}
/**
* 是否为CGI
* @access public
* @return bool
*/
public function isCgi(): bool
{
return str_starts_with(PHP_SAPI, 'cgi');
}
/**
* 是否为ajax请求
* @access public
* @param bool $ajax true 获取原始ajax请求
* @return bool
*/
public function isAjax(bool $ajax = false): bool
{
$result = $this->server('HTTP_X_REQUESTED_WITH', '', 'strtolower') === 'xmlhttprequest';
if (true === $ajax) {
return $result;
}
return $this->param($this->varAjax) ? true : $result;
}
/**
* 是否为pjax请求
* @access public
* @param bool $pjax true 获取原始pjax请求
* @return bool
*/
public function isPjax(bool $pjax = false): bool
{
$result = !empty($this->server('HTTP_X_PJAX'));
if (true === $pjax) {
return $result;
}
return $this->param($this->varPjax) ? true : $result;
}
/**
* 是否为JSON请求
* @access public
* @return bool
*/
public function isJson(): bool
{
return false !== strpos($this->type(), 'json');
}
/**
* 是否为手机访问
* @access public
* @return bool
*/
public function isMobile(): bool
{
if ($this->server('HTTP_VIA') && stristr($this->server('HTTP_VIA'), 'wap')) {
return true;
} elseif ($this->server('HTTP_ACCEPT') && strpos(strtoupper($this->server('HTTP_ACCEPT')), 'VND.WAP.WML')) {
return true;
} elseif ($this->server('HTTP_X_WAP_PROFILE') || $this->server('HTTP_PROFILE')) {
return true;
} elseif ($this->server('HTTP_USER_AGENT') && preg_match('/(blackberry|configuration\/cldc|hp |hp-|htc |htc-|htc_|htc-|iemobile|kindle|midp|mmp|mobile|novarra|o2 |opera mini|opera mobi|palm|palmos|pocket|portalmmm|proxynet|sharp-|sharp t-mobile|sonyericsson |sonyericsson|symbian|symbianos|up.browser|up.link|vodafone|wap |webos|windows ce|windows phone|xda |xda_)/i', $this->server('HTTP_USER_AGENT'))) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,233 @@
<?php
declare (strict_types=1);
namespace think\traits;
/**
* URL处理 trait
*/
trait UrlHandler
{
/**
* 当前URL地址
* @var string
*/
protected $url;
/**
* 当前URL地址 不含QUERY_STRING
* @var string
*/
protected $baseUrl;
/**
* 当前执行的文件 SCRIPT_NAME
* @var string
*/
protected $baseFile;
/**
* URL访问根地址
* @var string
*/
protected $root;
/**
* 设置当前请求的URL
* @access public
* @param string $url URL地址
* @return $this
*/
public function setUrl(string $url)
{
$this->url = $url;
return $this;
}
/**
* 获取当前请求URL
* @access public
* @param bool $complete 是否包含完整域名
* @return string
*/
public function url(bool $complete = false): string
{
if ($this->url) {
$url = $this->url;
} elseif ($this->server('HTTP_X_REWRITE_URL')) {
$url = $this->server('HTTP_X_REWRITE_URL');
} elseif ($this->server('REQUEST_URI')) {
$url = $this->server('REQUEST_URI');
} elseif ($this->server('ORIG_PATH_INFO')) {
$url = $this->server('ORIG_PATH_INFO') . (!empty($this->server('QUERY_STRING')) ? '?' . $this->server('QUERY_STRING') : '');
} elseif (isset($_SERVER['argv'][1])) {
$url = $_SERVER['argv'][1];
} else {
$url = '';
}
return $complete ? $this->domain() . $url : $url;
}
/**
* 设置当前URL 不含QUERY_STRING
* @access public
* @param string $url URL地址
* @return $this
*/
public function setBaseUrl(string $url)
{
$this->baseUrl = $url;
return $this;
}
/**
* 获取当前URL 不含QUERY_STRING
* @access public
* @param bool $complete 是否包含完整域名
* @return string
*/
public function baseUrl(bool $complete = false): string
{
if (!$this->baseUrl) {
$str = $this->url();
$this->baseUrl = str_contains($str, '?') ? strstr($str, '?', true) : $str;
}
return $complete ? $this->domain() . $this->baseUrl : $this->baseUrl;
}
/**
* 获取当前执行的文件 SCRIPT_NAME
* @access public
* @param bool $complete 是否包含完整域名
* @return string
*/
public function baseFile(bool $complete = false): string
{
if (!$this->baseFile) {
$url = '';
if (!$this->isCli()) {
$script_name = basename($this->server('SCRIPT_FILENAME'));
if (basename($this->server('SCRIPT_NAME')) === $script_name) {
$url = $this->server('SCRIPT_NAME');
} elseif (basename($this->server('PHP_SELF')) === $script_name) {
$url = $this->server('PHP_SELF');
} elseif (basename($this->server('ORIG_SCRIPT_NAME')) === $script_name) {
$url = $this->server('ORIG_SCRIPT_NAME');
} elseif (($pos = strpos($this->server('PHP_SELF'), '/' . $script_name)) !== false) {
$url = substr($this->server('SCRIPT_NAME'), 0, $pos) . '/' . $script_name;
} elseif ($this->server('DOCUMENT_ROOT') && str_starts_with($this->server('SCRIPT_FILENAME'), $this->server('DOCUMENT_ROOT'))) {
$url = str_replace('\\', '/', str_replace($this->server('DOCUMENT_ROOT'), '', $this->server('SCRIPT_FILENAME')));
}
}
$this->baseFile = $url;
}
return $complete ? $this->domain() . $this->baseFile : $this->baseFile;
}
/**
* 设置URL访问根地址
* @access public
* @param string $url URL地址
* @return $this
*/
public function setRoot(string $url)
{
$this->root = $url;
return $this;
}
/**
* 获取URL访问根地址
* @access public
* @param bool $complete 是否包含完整域名
* @return string
*/
public function root(bool $complete = false): string
{
if (!$this->root) {
$file = $this->baseFile();
if ($file && !str_starts_with($this->url(), $file)) {
$file = str_replace('\\', '/', dirname($file));
}
$this->root = rtrim($file, '/');
}
return $complete ? $this->domain() . $this->root : $this->root;
}
/**
* 获取URL访问根地址
* @access public
* @return string
*/
public function rootUrl(): string
{
$base = $this->root();
$root = '' === $base ? dirname($this->baseUrl()) : $base;
return $root;
}
/**
* 获取当前请求URL的pathinfo信息含URL后缀
* @access public
* @return string
*/
public function pathinfo(): string
{
if (isset($_SERVER['PATH_INFO'])) {
return ltrim($_SERVER['PATH_INFO'], '/');
}
$url = $this->url();
$base = $this->rootUrl();
if ($base && str_starts_with($url, $base)) {
$url = substr($url, strlen($base));
}
return ltrim($url, '/');
}
/**
* 获取当前请求URL的pathinfo信息(不含URL后缀)
* @access public
* @return string
*/
public function path(): string
{
$pathinfo = $this->pathinfo();
$suffix = $this->config('url_html_suffix');
if (false === $suffix) {
return $pathinfo;
}
return preg_replace('/\.(' . $suffix . ')$/i', '', $pathinfo);
}
/**
* 获取URL后缀
* @access public
* @return string
*/
public function ext(): string
{
return pathinfo($this->pathinfo(), PATHINFO_EXTENSION);
}
/**
* 获取当前请求的时间
* @access public
* @param bool $float 是否使用浮点数
* @return float|int
*/
public function time(bool $float = false)
{
return $float ? $_SERVER['REQUEST_TIME_FLOAT'] : $_SERVER['REQUEST_TIME'];
}
}