Compare commits
2 Commits
45785dd33f
...
8.1.3.3
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ee72e1363 | |||
| 5d6e2fc5e0 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ Thumbs.db
|
||||
/.buildpath
|
||||
/.project
|
||||
.phpunit.result.cache
|
||||
.php-cs-fixer.cache
|
||||
48
.php-cs-fixer.dist.php
Normal file
48
.php-cs-fixer.dist.php
Normal 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)
|
||||
;
|
||||
@@ -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",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +42,9 @@ use think\validate\ValidateRuleSet;
|
||||
if (!function_exists('abort')) {
|
||||
/**
|
||||
* 抛出HTTP异常
|
||||
* @param integer|Response $code 状态码 或者 Response对象实例
|
||||
* @param string $message 错误信息
|
||||
* @param array $header 参数
|
||||
* @param int|Response $code 状态码 或者 Response对象实例
|
||||
* @param string $message 错误信息
|
||||
* @param array $header 参数
|
||||
*/
|
||||
function abort($code, string $message = '', array $header = [])
|
||||
{
|
||||
|
||||
@@ -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,9 +458,8 @@ class App extends Container
|
||||
public function initialize()
|
||||
{
|
||||
$this->initialized = true;
|
||||
|
||||
$this->beginTime = microtime(true);
|
||||
$this->beginMem = memory_get_usage();
|
||||
$this->beginTime = microtime(true);
|
||||
$this->beginMem = memory_get_usage();
|
||||
|
||||
// 加载环境变量
|
||||
if ($this->baseEnvName) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -191,9 +191,12 @@ class Event
|
||||
|
||||
if (empty($prefix) && $reflect->hasProperty('eventPrefix')) {
|
||||
$reflectProperty = $reflect->getProperty('eventPrefix');
|
||||
if(PHP_VERSION_ID < 80100) {
|
||||
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
// 8.0 版本时调用
|
||||
$reflectProperty->setAccessible(true);
|
||||
}
|
||||
|
||||
$prefix = $reflectProperty->getValue($observer);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
6
src/think/cache/Driver.php
vendored
6
src/think/cache/Driver.php
vendored
@@ -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
|
||||
|
||||
6
src/think/cache/TagSet.php
vendored
6
src/think/cache/TagSet.php
vendored
@@ -33,9 +33,9 @@ class TagSet
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|DateInterval|DateTimeInterface $expire 有效时间(秒)
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int|DateInterval|DateTimeInterface $expire 有效时间(秒)
|
||||
* @return bool
|
||||
*/
|
||||
public function set($name, $value, $expire = null): bool
|
||||
|
||||
6
src/think/cache/driver/Memcached.php
vendored
6
src/think/cache/driver/Memcached.php
vendored
@@ -120,9 +120,9 @@ class Memcached extends Driver
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|DateInterval|DateTimeInterface $expire 有效时间(秒)
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int|DateInterval|DateTimeInterface $expire 有效时间(秒)
|
||||
* @return bool
|
||||
*/
|
||||
public function set($name, $value, $expire = null): bool
|
||||
|
||||
6
src/think/cache/driver/Redis.php
vendored
6
src/think/cache/driver/Redis.php
vendored
@@ -133,9 +133,9 @@ class Redis extends Driver
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|DateInterval|DateTimeInterface $expire 有效时间(秒)
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int|DateInterval|DateTimeInterface $expire 有效时间(秒)
|
||||
* @return bool
|
||||
*/
|
||||
public function set($name, $value, $expire = null): bool
|
||||
|
||||
6
src/think/cache/driver/Wincache.php
vendored
6
src/think/cache/driver/Wincache.php
vendored
@@ -86,9 +86,9 @@ class Wincache extends Driver
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|DateInterval|DateTimeInterface $expire 有效时间(秒)
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int|DateInterval|DateTimeInterface $expire 有效时间(秒)
|
||||
* @return bool
|
||||
*/
|
||||
public function set($name, $value, $expire = null): bool
|
||||
|
||||
32
src/think/console/command/CommandCallable.php
Normal file
32
src/think/console/command/CommandCallable.php
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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') ?: '';
|
||||
$path = $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . ($dir ? $dir . DIRECTORY_SEPARATOR : '');
|
||||
if (!is_dir($path)) {
|
||||
$dirs = ((array) $input->getArgument('dir')) ?: $this->getDefaultDirs();
|
||||
|
||||
foreach ($dirs as $dir) {
|
||||
$path = $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . ($dir ? $dir . DIRECTORY_SEPARATOR : '');
|
||||
try {
|
||||
mkdir($path, 0755, true);
|
||||
} catch (\Exception $e) {
|
||||
// 创建失败
|
||||
$cache = $this->buildCache($dir);
|
||||
if (! is_dir($path)) {
|
||||
mkdir($path, 0755, true);
|
||||
}
|
||||
file_put_contents($path . 'config.php', $cache);
|
||||
} catch (Throwable $e) {
|
||||
$output->warning($e->getMessage());
|
||||
}
|
||||
}
|
||||
$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>");
|
||||
}
|
||||
|
||||
$output->info('Succeed!');
|
||||
}
|
||||
|
||||
public function loadConfig($dir = '')
|
||||
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");
|
||||
}
|
||||
|
||||
foreach ($files as $file) {
|
||||
$this->app->config->load($file, pathinfo($file, PATHINFO_FILENAME));
|
||||
// 使用 clone 防止多应用配置污染
|
||||
$config = clone $this->app->config;
|
||||
if (is_dir($path)) {
|
||||
$files = glob($path . '*' . $this->app->getConfigExt());
|
||||
foreach ($files as $file) {
|
||||
$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;
|
||||
}
|
||||
}
|
||||
44
src/think/console/command/optimize/Discoverable.php
Normal file
44
src/think/console/command/optimize/Discoverable.php
Normal 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;
|
||||
}
|
||||
}
|
||||
40
src/think/console/command/optimize/Optimize.php
Normal file
40
src/think/console/command/optimize/Optimize.php
Normal 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!');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
$path = $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . ($dir ? $dir . DIRECTORY_SEPARATOR : '');
|
||||
if (!is_dir($path)) {
|
||||
foreach ($dirs as $dir) {
|
||||
$path = $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . ($dir ? $dir . DIRECTORY_SEPARATOR : '');
|
||||
try {
|
||||
mkdir($path, 0755, true);
|
||||
} catch (\Exception $e) {
|
||||
// 创建失败
|
||||
$cache = $this->buildRouteCache($dir);
|
||||
if (! is_dir($path)) {
|
||||
mkdir($path, 0755, true);
|
||||
}
|
||||
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)
|
||||
@@ -53,7 +61,7 @@ class Route extends Command
|
||||
if ($fileinfo->getType() == 'file' && $fileinfo->getExtension() == 'php') {
|
||||
$groupName = str_replace('\\', '/', substr_replace($fileinfo->getPath(), '', 0, strlen($root)));
|
||||
if ($groupName) {
|
||||
$this->app->route->group($groupName, function() use ($fileinfo) {
|
||||
$this->app->route->group($groupName, function () use ($fileinfo) {
|
||||
include $fileinfo->getRealPath();
|
||||
});
|
||||
} else {
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,71 +39,38 @@ 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;
|
||||
$dirs = ((array) $input->getArgument('dir')) ?: $this->getDefaultDirs();
|
||||
foreach ($dirs as $dir) {
|
||||
$this->cacheModel($dir);
|
||||
}
|
||||
$class = '\\' . $namespace . '\\model\\' . pathinfo($file, PATHINFO_FILENAME);
|
||||
|
||||
if (!class_exists($class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->buildModelSchema($class);
|
||||
}
|
||||
} 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')) {
|
||||
try {
|
||||
/** @var \think\Model $model */
|
||||
$model = new $class;
|
||||
$connection = $model->db()->getConnection();
|
||||
if ($connection instanceof PDOConnection) {
|
||||
$table = $model->getTable();
|
||||
//预读字段信息
|
||||
$connection->getSchemaInfo($table, true);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
|
||||
$reflect = new ReflectionClass($class);
|
||||
if ($reflect->isAbstract() || ! $reflect->isSubclassOf('\think\Model')) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
/** @var \think\Model $model */
|
||||
$model = new $class;
|
||||
$connection = $model->db()->getConnection();
|
||||
if ($connection instanceof PDOConnection) {
|
||||
$table = $model->getTable();
|
||||
//预读字段信息
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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']) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -55,12 +55,12 @@ interface ModelRelationInterface
|
||||
/**
|
||||
* 关联统计
|
||||
* @access public
|
||||
* @param Model $result 模型对象
|
||||
* @param Closure $closure 闭包
|
||||
* @param string $aggregate 聚合查询方法
|
||||
* @param string $field 字段
|
||||
* @param string $name 统计字段别名
|
||||
* @return integer
|
||||
* @param Model $result 模型对象
|
||||
* @param Closure $closure 闭包
|
||||
* @param string $aggregate 聚合查询方法
|
||||
* @param string $field 字段
|
||||
* @param string|null $name 统计字段别名
|
||||
* @return int
|
||||
*/
|
||||
public function relationCount(Model $result, Closure $closure, string $aggregate = 'count', string $field = '*', ?string &$name = null);
|
||||
|
||||
@@ -78,10 +78,10 @@ interface ModelRelationInterface
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @param string $operator 比较操作符
|
||||
* @param int $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @return Query
|
||||
*/
|
||||
public function has(string $operator = '>=', int $count = 1, string $id = '*', string $joinType = 'INNER', ?Query $query = null): Query;
|
||||
|
||||
@@ -24,17 +24,17 @@ class ErrorException extends Exception
|
||||
{
|
||||
/**
|
||||
* 用于保存错误级别
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
protected $severity;
|
||||
|
||||
/**
|
||||
* 错误异常构造函数
|
||||
* @access public
|
||||
* @param integer $severity 错误级别
|
||||
* @param string $message 错误详细信息
|
||||
* @param string $file 出错文件路径
|
||||
* @param integer $line 出错行号
|
||||
* @param int $severity 错误级别
|
||||
* @param string $message 错误详细信息
|
||||
* @param string $file 出错文件路径
|
||||
* @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()
|
||||
{
|
||||
|
||||
@@ -270,7 +270,7 @@ class Handle
|
||||
* ErrorException则使用错误级别作为错误编码
|
||||
* @access protected
|
||||
* @param Throwable $exception
|
||||
* @return integer 错误编码
|
||||
* @return int 错误编码
|
||||
*/
|
||||
protected function getCode(Throwable $exception)
|
||||
{
|
||||
|
||||
@@ -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) 加载语言包
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -65,10 +65,10 @@ class Error
|
||||
/**
|
||||
* Error Handler
|
||||
* @access public
|
||||
* @param integer $errno 错误编号
|
||||
* @param string $errstr 详细错误信息
|
||||
* @param string $errfile 出错的文件
|
||||
* @param integer $errline 出错行号
|
||||
* @param int $errno 错误编号
|
||||
* @param string $errstr 详细错误信息
|
||||
* @param string $errfile 出错的文件
|
||||
* @param int $errline 出错行号
|
||||
* @throws ErrorException
|
||||
*/
|
||||
public function appError(int $errno, string $errstr, string $errfile = '', int $errline = 0): void
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ class File extends Response
|
||||
/**
|
||||
* 设置有效期
|
||||
* @access public
|
||||
* @param integer $expire 有效期
|
||||
* @param int $expire 有效期
|
||||
* @return $this
|
||||
*/
|
||||
public function expire(int $expire)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\route;
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -53,23 +63,23 @@ abstract class Dispatch
|
||||
|
||||
protected function autoResponse($data): Response
|
||||
{
|
||||
if($data instanceof Response) {
|
||||
if ($data instanceof Response) {
|
||||
$response = $data;
|
||||
} elseif($data instanceof ResponseInterface) {
|
||||
$response = Response::create((string)$data->getBody(), 'html', $data->getStatusCode());
|
||||
} elseif ($data instanceof ResponseInterface) {
|
||||
$response = Response::create((string) $data->getBody(), 'html', $data->getStatusCode());
|
||||
|
||||
foreach ($data->getHeaders() as $header => $values) {
|
||||
$response->header([$header => implode(", ", $values)]);
|
||||
}
|
||||
} elseif(!is_null($data)) {
|
||||
} elseif (!is_null($data)) {
|
||||
// 默认自动识别响应输出类型
|
||||
$type = $this->request->isJson() ? 'json' : 'html';
|
||||
$type = $this->request->isJson() ? 'json' : 'html';
|
||||
$response = Response::create($data, $type);
|
||||
} else {
|
||||
$data = ob_get_clean();
|
||||
|
||||
$content = false === $data ? '' : $data;
|
||||
$status = '' === $content && $this->request->isJson() ? 204 : 200;
|
||||
$content = false === $data ? '' : $data;
|
||||
$status = '' === $content && $this->request->isJson() ? 204 : 200;
|
||||
$response = Response::create($content, 'html', $status);
|
||||
}
|
||||
|
||||
@@ -78,7 +88,6 @@ abstract class Dispatch
|
||||
|
||||
/**
|
||||
* 检查路由后置操作
|
||||
*
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
@@ -87,8 +96,8 @@ abstract class Dispatch
|
||||
$option = $this->option;
|
||||
|
||||
// 添加中间件
|
||||
if(!empty($option['middleware'])) {
|
||||
if(isset($option['without_middleware'])) {
|
||||
if (!empty($option['middleware'])) {
|
||||
if (isset($option['without_middleware'])) {
|
||||
$middleware = !empty($option['without_middleware']) ? array_diff($option['middleware'], $option['without_middleware']) : [];
|
||||
} else {
|
||||
$middleware = $option['middleware'];
|
||||
@@ -96,12 +105,12 @@ abstract class Dispatch
|
||||
$this->app->middleware->import($middleware, 'route');
|
||||
}
|
||||
|
||||
if(!empty($option['append'])) {
|
||||
if (!empty($option['append'])) {
|
||||
$this->param = array_merge($this->param, $option['append']);
|
||||
}
|
||||
|
||||
// 绑定模型数据
|
||||
if(!empty($option['model'])) {
|
||||
if (!empty($option['model'])) {
|
||||
$this->createBindModel($option['model'], $this->param);
|
||||
}
|
||||
|
||||
@@ -112,14 +121,13 @@ abstract class Dispatch
|
||||
$this->request->setRoute($this->param);
|
||||
|
||||
// 数据自动验证
|
||||
if(isset($option['validate'])) {
|
||||
if (isset($option['validate'])) {
|
||||
$this->autoValidate($option['validate']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作的绑定参数
|
||||
*
|
||||
* @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)
|
||||
@@ -153,20 +159,20 @@ abstract class Dispatch
|
||||
$suffix = $this->rule->config('action_suffix');
|
||||
$action = $action . $suffix;
|
||||
|
||||
if(is_callable([$instance, $action])) {
|
||||
if (is_callable([$instance, $action])) {
|
||||
$vars = $this->getActionBindVars();
|
||||
try {
|
||||
$reflect = new ReflectionMethod($instance, $action);
|
||||
// 严格获取当前操作方法名
|
||||
$actionName = $reflect->getName();
|
||||
if($suffix) {
|
||||
if ($suffix) {
|
||||
$actionName = substr($actionName, 0, -strlen($suffix));
|
||||
}
|
||||
|
||||
$this->request->setAction($actionName);
|
||||
} catch (ReflectionException $e) {
|
||||
$reflect = new ReflectionMethod($instance, '__call');
|
||||
$vars = [$action, $vars];
|
||||
$vars = [$action, $vars];
|
||||
$this->request->setAction($action);
|
||||
}
|
||||
} else {
|
||||
@@ -182,47 +188,45 @@ abstract class Dispatch
|
||||
|
||||
/**
|
||||
* 使用反射机制注册控制器中间件
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param object $controller 控制器实例
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function registerControllerMiddleware($controller): void
|
||||
{
|
||||
$class = new ReflectionClass($controller);
|
||||
|
||||
if($class->hasProperty('middleware')) {
|
||||
if ($class->hasProperty('middleware')) {
|
||||
$reflectionProperty = $class->getProperty('middleware');
|
||||
if(PHP_VERSION_ID < 80100) {
|
||||
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
$reflectionProperty->setAccessible(true);
|
||||
}
|
||||
|
||||
$middlewares = $reflectionProperty->getValue($controller);
|
||||
$action = $this->request->action(true);
|
||||
$action = $this->request->action(true);
|
||||
|
||||
foreach ($middlewares as $key => $val) {
|
||||
if(!is_int($key)) {
|
||||
if (!is_int($key)) {
|
||||
$middleware = $key;
|
||||
$options = $val;
|
||||
} elseif(isset($val['middleware'])) {
|
||||
$options = $val;
|
||||
} elseif (isset($val['middleware'])) {
|
||||
$middleware = $val['middleware'];
|
||||
$options = $val['options'] ?? [];
|
||||
$options = $val['options'] ?? [];
|
||||
} else {
|
||||
$middleware = $val;
|
||||
$options = [];
|
||||
$options = [];
|
||||
}
|
||||
|
||||
if(isset($options['only']) && !in_array($action, $this->parseActions($options['only']))) {
|
||||
if (isset($options['only']) && !in_array($action, $this->parseActions($options['only']))) {
|
||||
continue;
|
||||
} elseif(isset($options['except']) && in_array($action, $this->parseActions($options['except']))) {
|
||||
} elseif (isset($options['except']) && in_array($action, $this->parseActions($options['except']))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(is_string($middleware) && str_contains($middleware, ':')) {
|
||||
if (is_string($middleware) && str_contains($middleware, ':')) {
|
||||
$middleware = explode(':', $middleware);
|
||||
if(count($middleware) > 1) {
|
||||
if (count($middleware) > 1) {
|
||||
$middleware = [$middleware[0], array_slice($middleware, 1)];
|
||||
}
|
||||
}
|
||||
@@ -241,26 +245,23 @@ abstract class Dispatch
|
||||
|
||||
/**
|
||||
* 路由绑定模型实例
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @param array $bindModel 绑定模型
|
||||
* @param array $matches 路由变量
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function createBindModel(array $bindModel, array $matches): void
|
||||
{
|
||||
foreach ($bindModel as $key => $val) {
|
||||
if($val instanceof \Closure) {
|
||||
if ($val instanceof \Closure) {
|
||||
$result = $this->app->invokeFunction($val, $matches);
|
||||
} else {
|
||||
$fields = explode('&', $key);
|
||||
|
||||
if(is_array($val)) {
|
||||
if (is_array($val)) {
|
||||
[$model, $exception] = $val;
|
||||
} else {
|
||||
$model = $val;
|
||||
$model = $val;
|
||||
$exception = true;
|
||||
}
|
||||
|
||||
@@ -268,7 +269,7 @@ abstract class Dispatch
|
||||
$match = true;
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if(!isset($matches[$field])) {
|
||||
if (!isset($matches[$field])) {
|
||||
$match = false;
|
||||
break;
|
||||
} else {
|
||||
@@ -276,12 +277,12 @@ abstract class Dispatch
|
||||
}
|
||||
}
|
||||
|
||||
if($match) {
|
||||
if ($match) {
|
||||
$result = $model::where($where)->failException($exception)->find();
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($result)) {
|
||||
if (!empty($result)) {
|
||||
// 注入容器
|
||||
$this->app->instance($result::class, $result);
|
||||
}
|
||||
@@ -290,11 +291,8 @@ abstract class Dispatch
|
||||
|
||||
/**
|
||||
* 验证数据
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @param array $option
|
||||
*
|
||||
* @return void
|
||||
* @throws \think\exception\ValidateException
|
||||
*/
|
||||
@@ -302,7 +300,7 @@ abstract class Dispatch
|
||||
{
|
||||
[$validate, $scene, $message, $batch] = $option;
|
||||
|
||||
if(is_array($validate)) {
|
||||
if (is_array($validate)) {
|
||||
// 指定验证规则
|
||||
$v = new Validate();
|
||||
$v->rule($validate);
|
||||
@@ -312,7 +310,7 @@ abstract class Dispatch
|
||||
|
||||
$v = new $class();
|
||||
|
||||
if(!empty($scene)) {
|
||||
if (!empty($scene)) {
|
||||
$v->scene($scene);
|
||||
}
|
||||
}
|
||||
@@ -336,14 +334,26 @@ 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->app = Container::pull('app');
|
||||
$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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
@@ -218,7 +218,7 @@ class Url
|
||||
$controller = empty($path) ? $controller : array_pop($path);
|
||||
$url = $controller . '/' . $action;
|
||||
$auto = $this->route->getName('__think_auto_route__');
|
||||
if (!empty($auto) && !strpos($controller,'.')) {
|
||||
if (!empty($auto) && !strpos($controller, '.')) {
|
||||
$module = empty($path) ? $request->layer() : array_pop($path);
|
||||
$url = $module . '/' . $url;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -20,7 +20,7 @@ class Cache implements SessionHandlerInterface
|
||||
/** @var CacheInterface */
|
||||
protected $handler;
|
||||
|
||||
/** @var integer */
|
||||
/** @var int */
|
||||
protected $expire;
|
||||
|
||||
/** @var string */
|
||||
|
||||
243
src/think/traits/DomainHandler.php
Normal file
243
src/think/traits/DomainHandler.php
Normal 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;
|
||||
}
|
||||
}
|
||||
213
src/think/traits/HttpMethodHandler.php
Normal file
213
src/think/traits/HttpMethodHandler.php
Normal 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;
|
||||
}
|
||||
}
|
||||
233
src/think/traits/UrlHandler.php
Normal file
233
src/think/traits/UrlHandler.php
Normal 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'];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user