升级核心框架

This commit is contained in:
tao 2022-01-07 14:43:42 +08:00
parent cd50418e62
commit 83ca563d1c
81 changed files with 2848 additions and 559 deletions

View File

@ -107,7 +107,8 @@ class Article extends Model
'user' => function ($query) {
$query->field('id,name,nickname,user_img,area_id,vip');
}
])->withCount(['comments'])->order('create_time', 'desc')->limit($num)->select();
])->withCount(['comments'])->order('create_time', 'desc')->limit($num)->select()->toArray();
Cache::tag('tagArtDetail')->set('arttop', $artTop, 60);
}
return $artTop;
@ -133,7 +134,7 @@ class Article extends Model
'user' => function($query){
$query->field('id,name,nickname,user_img,area_id,vip');
} ])
->withCount(['comments'])->where(['status'=>1,'is_top'=>0])->order('create_time','desc')->limit($num)->select();
->withCount(['comments'])->where(['status'=>1,'is_top'=>0])->order('create_time','desc')->limit($num)->select()->toArray();
Cache::tag('tagArt')->set('artlist',$artList,60);
}
return $artList;
@ -167,7 +168,7 @@ class Article extends Model
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getArtDetail(int $id)
public function getArtDetail(int $id, int $page)
{
$article = Cache::get('article_'.$id);
if(!$article){
@ -179,8 +180,8 @@ class Article extends Model
'user' => function($query){
$query->field('id,name,nickname,user_img,area_id,vip');
}
])->find($id);
Cache::tag('tagArtDetail')->set('article_'.$id,$article,3600);
])->withCount(['comments'])->find($id)->toArray();
Cache::tag('tagArtDetail')->set('article_'.$id, $article, 3600);
}
return $article;
}
@ -225,7 +226,7 @@ class Article extends Model
'list_rows' => 15,
'page' => $page,
'path' =>$url.'[PAGE]'.$suffix
]);
])->toArray();
break;
case 'hot':
@ -241,7 +242,7 @@ class Article extends Model
'list_rows' => 15,
'page' => $page,
'path' =>$url.'[PAGE]'.$suffix
]);
])->toArray();
break;
case 'top':
@ -257,7 +258,7 @@ class Article extends Model
'list_rows' => 15,
'page' => $page,
'path' =>$url.'[PAGE]'.$suffix
]);
])->toArray();
break;
case 'wait':
@ -273,7 +274,7 @@ class Article extends Model
'list_rows' => 15,
'page' => $page,
'path' =>$url.'[PAGE]'.$suffix
]);
])->toArray();
break;
default:
@ -289,7 +290,7 @@ class Article extends Model
'list_rows' => 15,
'page' => $page,
'path' =>$url.'[PAGE]'.$suffix
]);
])->toArray();
break;
}
Cache::tag('tagArtDetail')->set('arts'.$ename.$type.$page,$artList,600);

View File

@ -29,9 +29,9 @@ class Comment extends Model
}
//获取评论
public function getComment($id)
public function getComment($id, $page)
{
$comments = $this::where(['article_id'=>$id,'status'=>1])->order(['cai'=>'asc','create_time'=>'asc'])->paginate(10);
$comments = $this::with(['user'])->where(['article_id'=>$id,'status'=>1])->order(['cai'=>'asc','create_time'=>'asc'])->paginate(['list_rows'=>10, 'page'=>$page]);
return $comments;
}

View File

@ -34,7 +34,7 @@ class Slider extends Model
{
$sliders = Cache::get('slider'.$type);
if(!$sliders){
$sliders = $this::where(['slid_status'=>1,'delete_time'=>0,'slid_type'=>$type])->whereTime('slid_over','>=',time())->select();
$sliders = $this::where(['slid_status'=>1,'delete_time'=>0,'slid_type'=>$type])->whereTime('slid_over','>=',time())->select()->toArray();
Cache::set('slider'.$type,$sliders,3600);
}
return $sliders;

View File

@ -29,27 +29,35 @@ class Article extends BaseController
// 抛出 HTTP 异常
throw new \think\exception\HttpException(404, '请求异常');
}
$page = Request::param('page') ? Request::param('page') : 1;
//获取分类ID
$ename = Request::param('ename');
$type = Request::param('type') ?? 'all';
$tpl = Db::name('cate')->where('ename',$ename)->value('detpl');
//分页伪静态
$str = Request::baseUrl(); //不带参数在url
//$str = Request::baseUrl(); //不带参数在url
$path = Request::pathinfo();
$str = '/'.app('http')->getName().'/'.$path;
//halt($str);
$patterns = "/\d+/"; //数字正则
preg_match($patterns,$str,$arr); //正则查询页码出现在位置
$p = preg_match($patterns,$str,$arr); //正则查询页码出现在位置
//检测route配置中是否设置了伪静态后缀
$suffix = Config::get('route.url_html_suffix') ? '.'.Config::get('route.url_html_suffix') : '/';
if(Config::get('route.url_html_suffix')){
//伪静态有后缀
if(isset($arr[0])){
$page = $arr[0];
$url = strstr($str,$arr[0],true);
} else {
$page = 1;
$url = strstr($str,'.html',true).'/';
$url = strstr($str,'.html',true);
}
} else {
//伪静态后缀false
if(isset($arr[0])){
$page = $arr[0];
@ -59,10 +67,12 @@ class Article extends BaseController
$url = $str.'/';
}
}
//分类列表
$article = new ArticleModel();
$artList = $article->getCateList($ename,$type,$page,$url,$suffix);
$count = $artList->total();
//$count = $artList->total();
// 热议文章
$artHot = $article->getArtHot(10);
@ -74,30 +84,38 @@ class Article extends BaseController
//分类钻展赞助
$ad_comm = $ad->getSliderList(6);
View::assign(['type'=>$type,'artList'=>$artList,'artHot'=>$artHot,'ad_cateImg'=>$ad_cateImg,'ad_comm'=>$ad_comm,'jspage'=>'jie','count'=>$count,'page'=>$page,'url'=>$url]);
View::assign(['type'=>$type,'artList'=>$artList,'artHot'=>$artHot,'ad_cateImg'=>$ad_cateImg,'ad_comm'=>$ad_comm,'jspage'=>'jie','url'=>$url]);
return View::fetch('article/'.$tpl.'/cate');
}
//文章详情页
public function detail($id)
{
$page = input('page') ? input('page') : 1;
$article = new ArticleModel();
$artDetail = $article->getArtDetail($id);
if(is_null($artDetail)){
$id = input('id');
$artStu = Db::name('article')->field('id')->where(['status'=>1,'delete_time'=>0])->find($id);
if(is_null($artStu)){
// 抛出 HTTP 异常
throw new \think\exception\HttpException(404, '异常消息');
}
$arId = $artDetail->cate->id;
//输出内容
$page = input('page') ? input('page') : 1;
$article = new ArticleModel();
$artDetail = $article->getArtDetail($id,$page);
$arId = $artDetail['cate_id'];
$tpl = Db::name('cate')->where('id',$arId)->value('detpl');
$comments = $artDetail->comments()->where('status',1)->order(['cai'=>'asc','create_time'=>'asc'])->paginate(['list_rows'=>10, 'page'=>$page]);
//$comment = new \app\common\model\Comment();
//$comments = $comment->getComment($id);
//dump($comments);
$count = $comments->total();
$artDetail->inc('pv')->update();
$download = $artDetail['upzip'] ? download($artDetail['upzip'],'file') : '';
//$count = $comments->total();
//$artDetail->inc('pv')->update();
//浏览pv
Db::name('article')->where('id',$id)->inc('pv')->update();
$pv = Db::name('article')->field('pv')->where('id',$id)->value('pv');
$download = $artDetail->upzip ? download($artDetail->upzip,'file') : '';
//评论
$comment = new Comment;
$comments = $comment->getComment($id, $page);
// 热议文章
$artHot = $article->getArtHot(10);
@ -108,7 +126,7 @@ class Article extends BaseController
//分类钻展赞助
$ad_comm = $ad->getSliderList(7);
View::assign(['article'=>$artDetail,'pv'=>$pv,'comments'=>$comments,'artHot'=>$artHot,'ad_art'=>$ad_artImg,'ad_comm'=>$ad_comm,$download,'count'=>$count,'page'=>$page,'jspage'=>'jie']);
View::assign(['article'=>$artDetail,'pv'=>$pv,'artHot'=>$artHot,'ad_art'=>$ad_artImg,'ad_comm'=>$ad_comm,$download,'page'=>$page,'comments'=>$comments,'jspage'=>'jie']);
return View::fetch('article/'.$tpl.'/detail');
}

View File

@ -13,8 +13,10 @@ use think\facade\Route;
Route::get('captcha/[:config]','\\think\\captcha\\CaptchaController@index');
Route::rule('/', 'index'); // 首页访问路由
Route::group(function () {
Route::get('jie/:id', 'article/detail');
Route::get('column/<ename?>/<type?>/<page?>','article/cate')
Route::get('jie/:id', 'article/detail');
//Route::get('column/<ename>','article/cate');
//Route::get('column/<ename>/<type>','article/cate');
Route::get('column/<ename>/[:type]/[:page]','article/cate')
->pattern([
'ename' => '\w+',
'page' => '\d+',

View File

@ -12,8 +12,9 @@ class Index extends BaseController
// 检测是否安装过
protected function initialize(){
if(file_exists('./install.lock')){
echo "<script>alert('已经成功安装了TaoLer社区系统安装系统已锁定。如需重新安装请删除根目录下的install.lock文件')</script>";
die();
//echo "<script>alert('已经成功安装了TaoLer社区系统安装系统已锁定。如需重新安装请删除根目录下的install.lock文件')</script>";
//die();
return response("<script>alert('已经成功安装了TaoLer社区系统安装系统已锁定。如需重新安装请删除根目录下的install.lock文件')</script>");
}
}
@ -144,7 +145,7 @@ class Index extends BaseController
<?php
return [
// 默认使用的数据库连接配置
'default' => 'mysql',
'default' => env('database.driver', 'mysql'),
// 自定义时间查询规则
'time_query_rule' => [],
// 自动写入时间戳字段
@ -156,43 +157,44 @@ return [
// 数据库连接配置信息
'connections' => [
'mysql' => [
// 数据库类型
'type' => 'mysql',
// 服务器地址
'hostname' => '{$data['DB_HOST']}',
// 数据库名
'database' => '{$data['DB_NAME']}',
// 用户名
'username' => '{$data['DB_USER']}',
// 密码
'password' => '{$data['DB_PWD']}',
// 端口
'hostport' => '{$data['DB_PORT']}',
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => 'utf8',
// 数据库表前缀
'prefix' => '{$data['DB_PREFIX']}',
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 是否需要断线重连
'break_reconnect' => false,
// 监听SQL
'trigger_sql' => true,
// 开启字段缓存
'fields_cache' => false,
// 字段缓存路径
'schema_cache_path' => app()->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR,
],
// 数据库类型
'type' => env('database.type', 'mysql'),
// 服务器地址
'hostname' => env('database.hostname', '{$data['DB_HOST']}'),
// 数据库名
'database' => env('database.database', '{$data['DB_NAME']}'),
// 用户名
'username' => env('database.username', '{$data['DB_USER']}'),
// 密码
'password' => env('database.password', '{$data['DB_PWD']}'),
// 端口
'hostport' => env('database.hostport', '{$data['DB_PORT']}'),
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => 'utf8',
// 数据库表前缀
'prefix' => env('database.prefix', '{$data['DB_PREFIX']}'),
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 是否需要断线重连
'break_reconnect' => false,
// 监听SQL
'trigger_sql' => env('app_debug', true),
// 开启字段缓存
'fields_cache' => false,
// 字段缓存路径
//'schema_cache_path' => app()->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR,
],
// 更多的数据库配置信息
],
];
php;

View File

@ -21,13 +21,12 @@
</tr>
<tr>
<td>php版本</td>
<td>&gt;7.1</td>
<td>&gt;7.2.5</td>
<td>
<?php echo PHP_VERSION ?>
</td>
<?php $php_version=explode('.', PHP_VERSION); ?>
<td class="<?php if(($php_version['0']>=7) && $php_version['1']>1)echo 'yes'; ?>">
<?php if (($php_version['0']>=7) && $php_version['1']>1): ?>
<td class="<?php if(version_compare(PHP_VERSION, '7.2.5', '>='))echo 'yes'; ?>">
<?php if (version_compare(PHP_VERSION, '7.2.5', '>=')): ?>
<?php else: ?> ×
<?php endif ?>
</td>

100
composer.lock generated
View File

@ -531,6 +531,65 @@
},
"time": "2021-11-05T16:50:12+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"support": {
"source": "https://github.com/php-fig/http-message/tree/master"
},
"time": "2016-08-06T14:39:51+00:00"
},
{
"name": "psr/log",
"version": "1.1.4",
@ -767,16 +826,16 @@
},
{
"name": "topthink/framework",
"version": "v6.0.9",
"version": "v6.0.10",
"source": {
"type": "git",
"url": "https://github.com/top-think/framework.git",
"reference": "0b5fb453f0e533de3af3a1ab6a202510b61be617"
"reference": "d9cadb6971ae92ff85ba5f2be77a40b0ad5718fb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/framework/zipball/0b5fb453f0e533de3af3a1ab6a202510b61be617",
"reference": "0b5fb453f0e533de3af3a1ab6a202510b61be617",
"url": "https://api.github.com/repos/top-think/framework/zipball/d9cadb6971ae92ff85ba5f2be77a40b0ad5718fb",
"reference": "d9cadb6971ae92ff85ba5f2be77a40b0ad5718fb",
"shasum": "",
"mirrors": [
{
@ -792,12 +851,14 @@
"league/flysystem-cached-adapter": "^1.0",
"php": ">=7.2.5",
"psr/container": "~1.0",
"psr/http-message": "^1.0",
"psr/log": "~1.0",
"psr/simple-cache": "^1.0",
"topthink/think-helper": "^3.1.1",
"topthink/think-orm": "^2.0"
},
"require-dev": {
"guzzlehttp/psr7": "^2.1.0",
"mikey179/vfsstream": "^1.6",
"mockery/mockery": "^1.2",
"phpunit/phpunit": "^7.0"
@ -832,9 +893,9 @@
],
"support": {
"issues": "https://github.com/top-think/framework/issues",
"source": "https://github.com/top-think/framework/tree/v6.0.9"
"source": "https://github.com/top-think/framework/tree/v6.0.10"
},
"time": "2021-07-22T03:24:49+00:00"
"time": "2021-12-31T09:14:28+00:00"
},
{
"name": "topthink/think-captcha",
@ -897,16 +958,16 @@
},
{
"name": "topthink/think-helper",
"version": "v3.1.5",
"version": "v3.1.6",
"source": {
"type": "git",
"url": "https://github.com/top-think/think-helper.git",
"reference": "f98e3ad44acd27ae85a4d923b1bdfd16c6d8d905"
"reference": "769acbe50a4274327162f9c68ec2e89a38eb2aff"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/think-helper/zipball/f98e3ad44acd27ae85a4d923b1bdfd16c6d8d905",
"reference": "f98e3ad44acd27ae85a4d923b1bdfd16c6d8d905",
"url": "https://api.github.com/repos/top-think/think-helper/zipball/769acbe50a4274327162f9c68ec2e89a38eb2aff",
"reference": "769acbe50a4274327162f9c68ec2e89a38eb2aff",
"shasum": "",
"mirrors": [
{
@ -918,6 +979,9 @@
"require": {
"php": ">=7.1.0"
},
"require-dev": {
"phpunit/phpunit": "^9.5"
},
"type": "library",
"autoload": {
"psr-4": {
@ -940,9 +1004,9 @@
"description": "The ThinkPHP6 Helper Package",
"support": {
"issues": "https://github.com/top-think/think-helper/issues",
"source": "https://github.com/top-think/think-helper/tree/v3.1.5"
"source": "https://github.com/top-think/think-helper/tree/v3.1.6"
},
"time": "2021-06-21T06:17:31+00:00"
"time": "2021-12-15T04:27:55+00:00"
},
{
"name": "topthink/think-multi-app",
@ -1000,16 +1064,16 @@
},
{
"name": "topthink/think-orm",
"version": "v2.0.45",
"version": "v2.0.47",
"source": {
"type": "git",
"url": "https://github.com/top-think/think-orm.git",
"reference": "3dcf9af447b048103093840833e8c74ab849152f"
"reference": "e69151fba9dd21f86e392a0ae208825904d6d49a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/think-orm/zipball/3dcf9af447b048103093840833e8c74ab849152f",
"reference": "3dcf9af447b048103093840833e8c74ab849152f",
"url": "https://api.github.com/repos/top-think/think-orm/zipball/e69151fba9dd21f86e392a0ae208825904d6d49a",
"reference": "e69151fba9dd21f86e392a0ae208825904d6d49a",
"shasum": "",
"mirrors": [
{
@ -1055,9 +1119,9 @@
],
"support": {
"issues": "https://github.com/top-think/think-orm/issues",
"source": "https://github.com/top-think/think-orm/tree/v2.0.45"
"source": "https://github.com/top-think/think-orm/tree/v2.0.47"
},
"time": "2021-11-30T14:31:05+00:00"
"time": "2021-12-31T06:12:13+00:00"
},
{
"name": "topthink/think-template",

View File

@ -15,4 +15,6 @@ return [
'httponly' => false,
// 是否使用 setcookie
'setcookie' => true,
// samesite 设置,支持 'strict' 'lax'
'samesite' => '',
];

View File

@ -1,7 +1,7 @@
<?php
return [
// 默认使用的数据库连接配置
'default' => 'mysql',
'default' => env('database.driver', 'mysql'),
// 自定义时间查询规则
'time_query_rule' => [],
// 自动写入时间戳字段
@ -13,42 +13,43 @@ return [
// 数据库连接配置信息
'connections' => [
'mysql' => [
// 数据库类型
'type' => 'mysql',
// 服务器地址
'hostname' => '',
// 数据库名
'database' => '',
// 用户名
'username' => '',
// 密码
'password' => '',
// 端口
'hostport' => '3306',
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => 'utf8',
// 数据库表前缀
'prefix' => 'tao_',
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 是否需要断线重连
'break_reconnect' => false,
// 监听SQL
'trigger_sql' => true,
// 开启字段缓存
'fields_cache' => false,
// 字段缓存路径
'schema_cache_path' => app()->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR,
],
// 数据库类型
'type' => env('database.type', 'mysql'),
// 服务器地址
'hostname' => env('database.hostname', ''),
// 数据库名
'database' => env('database.database', ''),
// 用户名
'username' => env('database.username', ''),
// 密码
'password' => env('database.password', ''),
// 端口
'hostport' => env('database.hostport', ''),
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => 'utf8',
// 数据库表前缀
'prefix' => env('database.prefix', 'tao_'),
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 是否需要断线重连
'break_reconnect' => false,
// 监听SQL
'trigger_sql' => env('app_debug', true),
// 开启字段缓存
'fields_cache' => false,
// 字段缓存路径
//'schema_cache_path' => app()->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR,
],
// 更多的数据库配置信息
],
];

View File

@ -11,8 +11,8 @@ return array(
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'223fa6f9b46fbe5d6b44c5ff847bfceb' => $vendorDir . '/taoser/think-addons/src/helper.php',
'1cfd2761b63b0a29ed23657ea394cb2d' => $vendorDir . '/topthink/think-captcha/src/helper.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'd421242fd42b2ea6cd13f802bcf18a6e' => $baseDir . '/extend/taoler/com/form.php',
);

View File

@ -22,6 +22,7 @@ return array(
'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'),

View File

@ -12,9 +12,9 @@ class ComposerStaticInit1b32198725235c8d6500c87262ef30c2
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
'223fa6f9b46fbe5d6b44c5ff847bfceb' => __DIR__ . '/..' . '/taoser/think-addons/src/helper.php',
'1cfd2761b63b0a29ed23657ea394cb2d' => __DIR__ . '/..' . '/topthink/think-captcha/src/helper.php',
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
'd421242fd42b2ea6cd13f802bcf18a6e' => __DIR__ . '/../..' . '/extend/taoler/com/form.php',
);
@ -52,6 +52,7 @@ class ComposerStaticInit1b32198725235c8d6500c87262ef30c2
array (
'Psr\\SimpleCache\\' => 16,
'Psr\\Log\\' => 8,
'Psr\\Http\\Message\\' => 17,
'Psr\\Container\\' => 14,
'Psr\\Cache\\' => 10,
'PHPMailer\\PHPMailer\\' => 20,
@ -136,6 +137,10 @@ class ComposerStaticInit1b32198725235c8d6500c87262ef30c2
array (
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
),
'Psr\\Http\\Message\\' =>
array (
0 => __DIR__ . '/..' . '/psr/http-message/src',
),
'Psr\\Container\\' =>
array (
0 => __DIR__ . '/..' . '/psr/container/src',

View File

@ -542,6 +542,68 @@
},
"install-path": "../psr/container"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"version_normalized": "1.0.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.3.0"
},
"time": "2016-08-06T14:39:51+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"support": {
"source": "https://github.com/php-fig/http-message/tree/master"
},
"install-path": "../psr/http-message"
},
{
"name": "psr/log",
"version": "1.1.4",
@ -1147,17 +1209,17 @@
},
{
"name": "topthink/framework",
"version": "v6.0.9",
"version_normalized": "6.0.9.0",
"version": "v6.0.10",
"version_normalized": "6.0.10.0",
"source": {
"type": "git",
"url": "https://github.com/top-think/framework.git",
"reference": "0b5fb453f0e533de3af3a1ab6a202510b61be617"
"reference": "d9cadb6971ae92ff85ba5f2be77a40b0ad5718fb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/framework/zipball/0b5fb453f0e533de3af3a1ab6a202510b61be617",
"reference": "0b5fb453f0e533de3af3a1ab6a202510b61be617",
"url": "https://api.github.com/repos/top-think/framework/zipball/d9cadb6971ae92ff85ba5f2be77a40b0ad5718fb",
"reference": "d9cadb6971ae92ff85ba5f2be77a40b0ad5718fb",
"shasum": "",
"mirrors": [
{
@ -1173,17 +1235,19 @@
"league/flysystem-cached-adapter": "^1.0",
"php": ">=7.2.5",
"psr/container": "~1.0",
"psr/http-message": "^1.0",
"psr/log": "~1.0",
"psr/simple-cache": "^1.0",
"topthink/think-helper": "^3.1.1",
"topthink/think-orm": "^2.0"
},
"require-dev": {
"guzzlehttp/psr7": "^2.1.0",
"mikey179/vfsstream": "^1.6",
"mockery/mockery": "^1.2",
"phpunit/phpunit": "^7.0"
},
"time": "2021-07-22T03:24:49+00:00",
"time": "2021-12-31T09:14:28+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -1215,7 +1279,7 @@
],
"support": {
"issues": "https://github.com/top-think/framework/issues",
"source": "https://github.com/top-think/framework/tree/v6.0.9"
"source": "https://github.com/top-think/framework/tree/v6.0.10"
},
"install-path": "../topthink/framework"
},
@ -1279,17 +1343,17 @@
},
{
"name": "topthink/think-helper",
"version": "v3.1.5",
"version_normalized": "3.1.5.0",
"version": "v3.1.6",
"version_normalized": "3.1.6.0",
"source": {
"type": "git",
"url": "https://github.com/top-think/think-helper.git",
"reference": "f98e3ad44acd27ae85a4d923b1bdfd16c6d8d905"
"reference": "769acbe50a4274327162f9c68ec2e89a38eb2aff"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/think-helper/zipball/f98e3ad44acd27ae85a4d923b1bdfd16c6d8d905",
"reference": "f98e3ad44acd27ae85a4d923b1bdfd16c6d8d905",
"url": "https://api.github.com/repos/top-think/think-helper/zipball/769acbe50a4274327162f9c68ec2e89a38eb2aff",
"reference": "769acbe50a4274327162f9c68ec2e89a38eb2aff",
"shasum": "",
"mirrors": [
{
@ -1301,7 +1365,10 @@
"require": {
"php": ">=7.1.0"
},
"time": "2021-06-21T06:17:31+00:00",
"require-dev": {
"phpunit/phpunit": "^9.5"
},
"time": "2021-12-15T04:27:55+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -1325,7 +1392,7 @@
"description": "The ThinkPHP6 Helper Package",
"support": {
"issues": "https://github.com/top-think/think-helper/issues",
"source": "https://github.com/top-think/think-helper/tree/v3.1.5"
"source": "https://github.com/top-think/think-helper/tree/v3.1.6"
},
"install-path": "../topthink/think-helper"
},
@ -1384,17 +1451,17 @@
},
{
"name": "topthink/think-orm",
"version": "v2.0.45",
"version_normalized": "2.0.45.0",
"version": "v2.0.47",
"version_normalized": "2.0.47.0",
"source": {
"type": "git",
"url": "https://github.com/top-think/think-orm.git",
"reference": "3dcf9af447b048103093840833e8c74ab849152f"
"reference": "e69151fba9dd21f86e392a0ae208825904d6d49a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/think-orm/zipball/3dcf9af447b048103093840833e8c74ab849152f",
"reference": "3dcf9af447b048103093840833e8c74ab849152f",
"url": "https://api.github.com/repos/top-think/think-orm/zipball/e69151fba9dd21f86e392a0ae208825904d6d49a",
"reference": "e69151fba9dd21f86e392a0ae208825904d6d49a",
"shasum": "",
"mirrors": [
{
@ -1414,7 +1481,7 @@
"require-dev": {
"phpunit/phpunit": "^7|^8|^9.5"
},
"time": "2021-11-30T14:31:05+00:00",
"time": "2021-12-31T06:12:13+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -1442,7 +1509,7 @@
],
"support": {
"issues": "https://github.com/top-think/think-orm/issues",
"source": "https://github.com/top-think/think-orm/tree/v2.0.45"
"source": "https://github.com/top-think/think-orm/tree/v2.0.47"
},
"install-path": "../topthink/think-orm"
},

View File

@ -5,7 +5,7 @@
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '13037f7440f2dcce2cd78297df9d6a675744ecad',
'reference' => 'f35436abc461b29e7056f63f10302c84aeb2bfbc',
'name' => 'taoser/taoler',
'dev' => true,
),
@ -82,6 +82,15 @@
'reference' => '513e0666f7216c7459170d56df27dfcefe1689ea',
'dev_requirement' => false,
),
'psr/http-message' => array(
'pretty_version' => '1.0.1',
'version' => '1.0.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-message',
'aliases' => array(),
'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
'dev_requirement' => false,
),
'psr/log' => array(
'pretty_version' => '1.1.4',
'version' => '1.1.4.0',
@ -142,7 +151,7 @@
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '13037f7440f2dcce2cd78297df9d6a675744ecad',
'reference' => 'f35436abc461b29e7056f63f10302c84aeb2bfbc',
'dev_requirement' => false,
),
'taoser/think-addons' => array(
@ -164,12 +173,12 @@
'dev_requirement' => false,
),
'topthink/framework' => array(
'pretty_version' => 'v6.0.9',
'version' => '6.0.9.0',
'pretty_version' => 'v6.0.10',
'version' => '6.0.10.0',
'type' => 'library',
'install_path' => __DIR__ . '/../topthink/framework',
'aliases' => array(),
'reference' => '0b5fb453f0e533de3af3a1ab6a202510b61be617',
'reference' => 'd9cadb6971ae92ff85ba5f2be77a40b0ad5718fb',
'dev_requirement' => false,
),
'topthink/think-captcha' => array(
@ -182,12 +191,12 @@
'dev_requirement' => false,
),
'topthink/think-helper' => array(
'pretty_version' => 'v3.1.5',
'version' => '3.1.5.0',
'pretty_version' => 'v3.1.6',
'version' => '3.1.6.0',
'type' => 'library',
'install_path' => __DIR__ . '/../topthink/think-helper',
'aliases' => array(),
'reference' => 'f98e3ad44acd27ae85a4d923b1bdfd16c6d8d905',
'reference' => '769acbe50a4274327162f9c68ec2e89a38eb2aff',
'dev_requirement' => false,
),
'topthink/think-multi-app' => array(
@ -200,12 +209,12 @@
'dev_requirement' => false,
),
'topthink/think-orm' => array(
'pretty_version' => 'v2.0.45',
'version' => '2.0.45.0',
'pretty_version' => 'v2.0.47',
'version' => '2.0.47.0',
'type' => 'library',
'install_path' => __DIR__ . '/../topthink/think-orm',
'aliases' => array(),
'reference' => '3dcf9af447b048103093840833e8c74ab849152f',
'reference' => 'e69151fba9dd21f86e392a0ae208825904d6d49a',
'dev_requirement' => false,
),
'topthink/think-template' => array(

36
vendor/psr/http-message/CHANGELOG.md vendored Normal file
View File

@ -0,0 +1,36 @@
# Changelog
All notable changes to this project will be documented in this file, in reverse chronological order by release.
## 1.0.1 - 2016-08-06
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- Updated all `@return self` annotation references in interfaces to use
`@return static`, which more closelly follows the semantics of the
specification.
- Updated the `MessageInterface::getHeaders()` return annotation to use the
value `string[][]`, indicating the format is a nested array of strings.
- Updated the `@link` annotation for `RequestInterface::withRequestTarget()`
to point to the correct section of RFC 7230.
- Updated the `ServerRequestInterface::withUploadedFiles()` parameter annotation
to add the parameter name (`$uploadedFiles`).
- Updated a `@throws` annotation for the `UploadedFileInterface::moveTo()`
method to correctly reference the method parameter (it was referencing an
incorrect parameter name previously).
## 1.0.0 - 2016-05-18
Initial stable release; reflects accepted PSR-7 specification.

19
vendor/psr/http-message/LICENSE vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2014 PHP Framework Interoperability Group
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

13
vendor/psr/http-message/README.md vendored Normal file
View File

@ -0,0 +1,13 @@
PSR Http Message
================
This repository holds all interfaces/classes/traits related to
[PSR-7](http://www.php-fig.org/psr/psr-7/).
Note that this is not a HTTP message implementation of its own. It is merely an
interface that describes a HTTP message. See the specification for more details.
Usage
-----
We'll certainly need some stuff in here.

26
vendor/psr/http-message/composer.json vendored Normal file
View File

@ -0,0 +1,26 @@
{
"name": "psr/http-message",
"description": "Common interface for HTTP messages",
"keywords": ["psr", "psr-7", "http", "http-message", "request", "response"],
"homepage": "https://github.com/php-fig/http-message",
"license": "MIT",
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"require": {
"php": ">=5.3.0"
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
}
}

View File

@ -0,0 +1,187 @@
<?php
namespace Psr\Http\Message;
/**
* HTTP messages consist of requests from a client to a server and responses
* from a server to a client. This interface defines the methods common to
* each.
*
* Messages are considered immutable; all methods that might change state MUST
* be implemented such that they retain the internal state of the current
* message and return an instance that contains the changed state.
*
* @link http://www.ietf.org/rfc/rfc7230.txt
* @link http://www.ietf.org/rfc/rfc7231.txt
*/
interface MessageInterface
{
/**
* Retrieves the HTTP protocol version as a string.
*
* The string MUST contain only the HTTP version number (e.g., "1.1", "1.0").
*
* @return string HTTP protocol version.
*/
public function getProtocolVersion();
/**
* Return an instance with the specified HTTP protocol version.
*
* The version string MUST contain only the HTTP version number (e.g.,
* "1.1", "1.0").
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* new protocol version.
*
* @param string $version HTTP protocol version
* @return static
*/
public function withProtocolVersion($version);
/**
* Retrieves all message header values.
*
* The keys represent the header name as it will be sent over the wire, and
* each value is an array of strings associated with the header.
*
* // Represent the headers as a string
* foreach ($message->getHeaders() as $name => $values) {
* echo $name . ": " . implode(", ", $values);
* }
*
* // Emit headers iteratively:
* foreach ($message->getHeaders() as $name => $values) {
* foreach ($values as $value) {
* header(sprintf('%s: %s', $name, $value), false);
* }
* }
*
* While header names are not case-sensitive, getHeaders() will preserve the
* exact case in which headers were originally specified.
*
* @return string[][] Returns an associative array of the message's headers. Each
* key MUST be a header name, and each value MUST be an array of strings
* for that header.
*/
public function getHeaders();
/**
* Checks if a header exists by the given case-insensitive name.
*
* @param string $name Case-insensitive header field name.
* @return bool Returns true if any header names match the given header
* name using a case-insensitive string comparison. Returns false if
* no matching header name is found in the message.
*/
public function hasHeader($name);
/**
* Retrieves a message header value by the given case-insensitive name.
*
* This method returns an array of all the header values of the given
* case-insensitive header name.
*
* If the header does not appear in the message, this method MUST return an
* empty array.
*
* @param string $name Case-insensitive header field name.
* @return string[] An array of string values as provided for the given
* header. If the header does not appear in the message, this method MUST
* return an empty array.
*/
public function getHeader($name);
/**
* Retrieves a comma-separated string of the values for a single header.
*
* This method returns all of the header values of the given
* case-insensitive header name as a string concatenated together using
* a comma.
*
* NOTE: Not all header values may be appropriately represented using
* comma concatenation. For such headers, use getHeader() instead
* and supply your own delimiter when concatenating.
*
* If the header does not appear in the message, this method MUST return
* an empty string.
*
* @param string $name Case-insensitive header field name.
* @return string A string of values as provided for the given header
* concatenated together using a comma. If the header does not appear in
* the message, this method MUST return an empty string.
*/
public function getHeaderLine($name);
/**
* Return an instance with the provided value replacing the specified header.
*
* While header names are case-insensitive, the casing of the header will
* be preserved by this function, and returned from getHeaders().
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* new and/or updated header and value.
*
* @param string $name Case-insensitive header field name.
* @param string|string[] $value Header value(s).
* @return static
* @throws \InvalidArgumentException for invalid header names or values.
*/
public function withHeader($name, $value);
/**
* Return an instance with the specified header appended with the given value.
*
* Existing values for the specified header will be maintained. The new
* value(s) will be appended to the existing list. If the header did not
* exist previously, it will be added.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* new header and/or value.
*
* @param string $name Case-insensitive header field name to add.
* @param string|string[] $value Header value(s).
* @return static
* @throws \InvalidArgumentException for invalid header names or values.
*/
public function withAddedHeader($name, $value);
/**
* Return an instance without the specified header.
*
* Header resolution MUST be done without case-sensitivity.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that removes
* the named header.
*
* @param string $name Case-insensitive header field name to remove.
* @return static
*/
public function withoutHeader($name);
/**
* Gets the body of the message.
*
* @return StreamInterface Returns the body as a stream.
*/
public function getBody();
/**
* Return an instance with the specified message body.
*
* The body MUST be a StreamInterface object.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return a new instance that has the
* new body stream.
*
* @param StreamInterface $body Body.
* @return static
* @throws \InvalidArgumentException When the body is not valid.
*/
public function withBody(StreamInterface $body);
}

View File

@ -0,0 +1,129 @@
<?php
namespace Psr\Http\Message;
/**
* Representation of an outgoing, client-side request.
*
* Per the HTTP specification, this interface includes properties for
* each of the following:
*
* - Protocol version
* - HTTP method
* - URI
* - Headers
* - Message body
*
* During construction, implementations MUST attempt to set the Host header from
* a provided URI if no Host header is provided.
*
* Requests are considered immutable; all methods that might change state MUST
* be implemented such that they retain the internal state of the current
* message and return an instance that contains the changed state.
*/
interface RequestInterface extends MessageInterface
{
/**
* Retrieves the message's request target.
*
* Retrieves the message's request-target either as it will appear (for
* clients), as it appeared at request (for servers), or as it was
* specified for the instance (see withRequestTarget()).
*
* In most cases, this will be the origin-form of the composed URI,
* unless a value was provided to the concrete implementation (see
* withRequestTarget() below).
*
* If no URI is available, and no request-target has been specifically
* provided, this method MUST return the string "/".
*
* @return string
*/
public function getRequestTarget();
/**
* Return an instance with the specific request-target.
*
* If the request needs a non-origin-form request-target e.g., for
* specifying an absolute-form, authority-form, or asterisk-form
* this method may be used to create an instance with the specified
* request-target, verbatim.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* changed request target.
*
* @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
* request-target forms allowed in request messages)
* @param mixed $requestTarget
* @return static
*/
public function withRequestTarget($requestTarget);
/**
* Retrieves the HTTP method of the request.
*
* @return string Returns the request method.
*/
public function getMethod();
/**
* Return an instance with the provided HTTP method.
*
* While HTTP method names are typically all uppercase characters, HTTP
* method names are case-sensitive and thus implementations SHOULD NOT
* modify the given string.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* changed request method.
*
* @param string $method Case-sensitive method.
* @return static
* @throws \InvalidArgumentException for invalid HTTP methods.
*/
public function withMethod($method);
/**
* Retrieves the URI instance.
*
* This method MUST return a UriInterface instance.
*
* @link http://tools.ietf.org/html/rfc3986#section-4.3
* @return UriInterface Returns a UriInterface instance
* representing the URI of the request.
*/
public function getUri();
/**
* Returns an instance with the provided URI.
*
* This method MUST update the Host header of the returned request by
* default if the URI contains a host component. If the URI does not
* contain a host component, any pre-existing Host header MUST be carried
* over to the returned request.
*
* You can opt-in to preserving the original state of the Host header by
* setting `$preserveHost` to `true`. When `$preserveHost` is set to
* `true`, this method interacts with the Host header in the following ways:
*
* - If the Host header is missing or empty, and the new URI contains
* a host component, this method MUST update the Host header in the returned
* request.
* - If the Host header is missing or empty, and the new URI does not contain a
* host component, this method MUST NOT update the Host header in the returned
* request.
* - If a Host header is present and non-empty, this method MUST NOT update
* the Host header in the returned request.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* new UriInterface instance.
*
* @link http://tools.ietf.org/html/rfc3986#section-4.3
* @param UriInterface $uri New request URI to use.
* @param bool $preserveHost Preserve the original state of the Host header.
* @return static
*/
public function withUri(UriInterface $uri, $preserveHost = false);
}

View File

@ -0,0 +1,68 @@
<?php
namespace Psr\Http\Message;
/**
* Representation of an outgoing, server-side response.
*
* Per the HTTP specification, this interface includes properties for
* each of the following:
*
* - Protocol version
* - Status code and reason phrase
* - Headers
* - Message body
*
* Responses are considered immutable; all methods that might change state MUST
* be implemented such that they retain the internal state of the current
* message and return an instance that contains the changed state.
*/
interface ResponseInterface extends MessageInterface
{
/**
* Gets the response status code.
*
* The status code is a 3-digit integer result code of the server's attempt
* to understand and satisfy the request.
*
* @return int Status code.
*/
public function getStatusCode();
/**
* Return an instance with the specified status code and, optionally, reason phrase.
*
* If no reason phrase is specified, implementations MAY choose to default
* to the RFC 7231 or IANA recommended reason phrase for the response's
* status code.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* updated status and reason phrase.
*
* @link http://tools.ietf.org/html/rfc7231#section-6
* @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
* @param int $code The 3-digit integer result code to set.
* @param string $reasonPhrase The reason phrase to use with the
* provided status code; if none is provided, implementations MAY
* use the defaults as suggested in the HTTP specification.
* @return static
* @throws \InvalidArgumentException For invalid status code arguments.
*/
public function withStatus($code, $reasonPhrase = '');
/**
* Gets the response reason phrase associated with the status code.
*
* Because a reason phrase is not a required element in a response
* status line, the reason phrase value MAY be null. Implementations MAY
* choose to return the default RFC 7231 recommended reason phrase (or those
* listed in the IANA HTTP Status Code Registry) for the response's
* status code.
*
* @link http://tools.ietf.org/html/rfc7231#section-6
* @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
* @return string Reason phrase; must return an empty string if none present.
*/
public function getReasonPhrase();
}

View File

@ -0,0 +1,261 @@
<?php
namespace Psr\Http\Message;
/**
* Representation of an incoming, server-side HTTP request.
*
* Per the HTTP specification, this interface includes properties for
* each of the following:
*
* - Protocol version
* - HTTP method
* - URI
* - Headers
* - Message body
*
* Additionally, it encapsulates all data as it has arrived to the
* application from the CGI and/or PHP environment, including:
*
* - The values represented in $_SERVER.
* - Any cookies provided (generally via $_COOKIE)
* - Query string arguments (generally via $_GET, or as parsed via parse_str())
* - Upload files, if any (as represented by $_FILES)
* - Deserialized body parameters (generally from $_POST)
*
* $_SERVER values MUST be treated as immutable, as they represent application
* state at the time of request; as such, no methods are provided to allow
* modification of those values. The other values provide such methods, as they
* can be restored from $_SERVER or the request body, and may need treatment
* during the application (e.g., body parameters may be deserialized based on
* content type).
*
* Additionally, this interface recognizes the utility of introspecting a
* request to derive and match additional parameters (e.g., via URI path
* matching, decrypting cookie values, deserializing non-form-encoded body
* content, matching authorization headers to users, etc). These parameters
* are stored in an "attributes" property.
*
* Requests are considered immutable; all methods that might change state MUST
* be implemented such that they retain the internal state of the current
* message and return an instance that contains the changed state.
*/
interface ServerRequestInterface extends RequestInterface
{
/**
* Retrieve server parameters.
*
* Retrieves data related to the incoming request environment,
* typically derived from PHP's $_SERVER superglobal. The data IS NOT
* REQUIRED to originate from $_SERVER.
*
* @return array
*/
public function getServerParams();
/**
* Retrieve cookies.
*
* Retrieves cookies sent by the client to the server.
*
* The data MUST be compatible with the structure of the $_COOKIE
* superglobal.
*
* @return array
*/
public function getCookieParams();
/**
* Return an instance with the specified cookies.
*
* The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST
* be compatible with the structure of $_COOKIE. Typically, this data will
* be injected at instantiation.
*
* This method MUST NOT update the related Cookie header of the request
* instance, nor related values in the server params.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* updated cookie values.
*
* @param array $cookies Array of key/value pairs representing cookies.
* @return static
*/
public function withCookieParams(array $cookies);
/**
* Retrieve query string arguments.
*
* Retrieves the deserialized query string arguments, if any.
*
* Note: the query params might not be in sync with the URI or server
* params. If you need to ensure you are only getting the original
* values, you may need to parse the query string from `getUri()->getQuery()`
* or from the `QUERY_STRING` server param.
*
* @return array
*/
public function getQueryParams();
/**
* Return an instance with the specified query string arguments.
*
* These values SHOULD remain immutable over the course of the incoming
* request. They MAY be injected during instantiation, such as from PHP's
* $_GET superglobal, or MAY be derived from some other value such as the
* URI. In cases where the arguments are parsed from the URI, the data
* MUST be compatible with what PHP's parse_str() would return for
* purposes of how duplicate query parameters are handled, and how nested
* sets are handled.
*
* Setting query string arguments MUST NOT change the URI stored by the
* request, nor the values in the server params.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* updated query string arguments.
*
* @param array $query Array of query string arguments, typically from
* $_GET.
* @return static
*/
public function withQueryParams(array $query);
/**
* Retrieve normalized file upload data.
*
* This method returns upload metadata in a normalized tree, with each leaf
* an instance of Psr\Http\Message\UploadedFileInterface.
*
* These values MAY be prepared from $_FILES or the message body during
* instantiation, or MAY be injected via withUploadedFiles().
*
* @return array An array tree of UploadedFileInterface instances; an empty
* array MUST be returned if no data is present.
*/
public function getUploadedFiles();
/**
* Create a new instance with the specified uploaded files.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* updated body parameters.
*
* @param array $uploadedFiles An array tree of UploadedFileInterface instances.
* @return static
* @throws \InvalidArgumentException if an invalid structure is provided.
*/
public function withUploadedFiles(array $uploadedFiles);
/**
* Retrieve any parameters provided in the request body.
*
* If the request Content-Type is either application/x-www-form-urlencoded
* or multipart/form-data, and the request method is POST, this method MUST
* return the contents of $_POST.
*
* Otherwise, this method may return any results of deserializing
* the request body content; as parsing returns structured content, the
* potential types MUST be arrays or objects only. A null value indicates
* the absence of body content.
*
* @return null|array|object The deserialized body parameters, if any.
* These will typically be an array or object.
*/
public function getParsedBody();
/**
* Return an instance with the specified body parameters.
*
* These MAY be injected during instantiation.
*
* If the request Content-Type is either application/x-www-form-urlencoded
* or multipart/form-data, and the request method is POST, use this method
* ONLY to inject the contents of $_POST.
*
* The data IS NOT REQUIRED to come from $_POST, but MUST be the results of
* deserializing the request body content. Deserialization/parsing returns
* structured data, and, as such, this method ONLY accepts arrays or objects,
* or a null value if nothing was available to parse.
*
* As an example, if content negotiation determines that the request data
* is a JSON payload, this method could be used to create a request
* instance with the deserialized parameters.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* updated body parameters.
*
* @param null|array|object $data The deserialized body data. This will
* typically be in an array or object.
* @return static
* @throws \InvalidArgumentException if an unsupported argument type is
* provided.
*/
public function withParsedBody($data);
/**
* Retrieve attributes derived from the request.
*
* The request "attributes" may be used to allow injection of any
* parameters derived from the request: e.g., the results of path
* match operations; the results of decrypting cookies; the results of
* deserializing non-form-encoded message bodies; etc. Attributes
* will be application and request specific, and CAN be mutable.
*
* @return array Attributes derived from the request.
*/
public function getAttributes();
/**
* Retrieve a single derived request attribute.
*
* Retrieves a single derived request attribute as described in
* getAttributes(). If the attribute has not been previously set, returns
* the default value as provided.
*
* This method obviates the need for a hasAttribute() method, as it allows
* specifying a default value to return if the attribute is not found.
*
* @see getAttributes()
* @param string $name The attribute name.
* @param mixed $default Default value to return if the attribute does not exist.
* @return mixed
*/
public function getAttribute($name, $default = null);
/**
* Return an instance with the specified derived request attribute.
*
* This method allows setting a single derived request attribute as
* described in getAttributes().
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* updated attribute.
*
* @see getAttributes()
* @param string $name The attribute name.
* @param mixed $value The value of the attribute.
* @return static
*/
public function withAttribute($name, $value);
/**
* Return an instance that removes the specified derived request attribute.
*
* This method allows removing a single derived request attribute as
* described in getAttributes().
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that removes
* the attribute.
*
* @see getAttributes()
* @param string $name The attribute name.
* @return static
*/
public function withoutAttribute($name);
}

View File

@ -0,0 +1,158 @@
<?php
namespace Psr\Http\Message;
/**
* Describes a data stream.
*
* Typically, an instance will wrap a PHP stream; this interface provides
* a wrapper around the most common operations, including serialization of
* the entire stream to a string.
*/
interface StreamInterface
{
/**
* Reads all data from the stream into a string, from the beginning to end.
*
* This method MUST attempt to seek to the beginning of the stream before
* reading data and read the stream until the end is reached.
*
* Warning: This could attempt to load a large amount of data into memory.
*
* This method MUST NOT raise an exception in order to conform with PHP's
* string casting operations.
*
* @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
* @return string
*/
public function __toString();
/**
* Closes the stream and any underlying resources.
*
* @return void
*/
public function close();
/**
* Separates any underlying resources from the stream.
*
* After the stream has been detached, the stream is in an unusable state.
*
* @return resource|null Underlying PHP stream, if any
*/
public function detach();
/**
* Get the size of the stream if known.
*
* @return int|null Returns the size in bytes if known, or null if unknown.
*/
public function getSize();
/**
* Returns the current position of the file read/write pointer
*
* @return int Position of the file pointer
* @throws \RuntimeException on error.
*/
public function tell();
/**
* Returns true if the stream is at the end of the stream.
*
* @return bool
*/
public function eof();
/**
* Returns whether or not the stream is seekable.
*
* @return bool
*/
public function isSeekable();
/**
* Seek to a position in the stream.
*
* @link http://www.php.net/manual/en/function.fseek.php
* @param int $offset Stream offset
* @param int $whence Specifies how the cursor position will be calculated
* based on the seek offset. Valid values are identical to the built-in
* PHP $whence values for `fseek()`. SEEK_SET: Set position equal to
* offset bytes SEEK_CUR: Set position to current location plus offset
* SEEK_END: Set position to end-of-stream plus offset.
* @throws \RuntimeException on failure.
*/
public function seek($offset, $whence = SEEK_SET);
/**
* Seek to the beginning of the stream.
*
* If the stream is not seekable, this method will raise an exception;
* otherwise, it will perform a seek(0).
*
* @see seek()
* @link http://www.php.net/manual/en/function.fseek.php
* @throws \RuntimeException on failure.
*/
public function rewind();
/**
* Returns whether or not the stream is writable.
*
* @return bool
*/
public function isWritable();
/**
* Write data to the stream.
*
* @param string $string The string that is to be written.
* @return int Returns the number of bytes written to the stream.
* @throws \RuntimeException on failure.
*/
public function write($string);
/**
* Returns whether or not the stream is readable.
*
* @return bool
*/
public function isReadable();
/**
* Read data from the stream.
*
* @param int $length Read up to $length bytes from the object and return
* them. Fewer than $length bytes may be returned if underlying stream
* call returns fewer bytes.
* @return string Returns the data read from the stream, or an empty string
* if no bytes are available.
* @throws \RuntimeException if an error occurs.
*/
public function read($length);
/**
* Returns the remaining contents in a string
*
* @return string
* @throws \RuntimeException if unable to read or an error occurs while
* reading.
*/
public function getContents();
/**
* Get stream metadata as an associative array or retrieve a specific key.
*
* The keys returned are identical to the keys returned from PHP's
* stream_get_meta_data() function.
*
* @link http://php.net/manual/en/function.stream-get-meta-data.php
* @param string $key Specific metadata to retrieve.
* @return array|mixed|null Returns an associative array if no key is
* provided. Returns a specific key value if a key is provided and the
* value is found, or null if the key is not found.
*/
public function getMetadata($key = null);
}

View File

@ -0,0 +1,123 @@
<?php
namespace Psr\Http\Message;
/**
* Value object representing a file uploaded through an HTTP request.
*
* Instances of this interface are considered immutable; all methods that
* might change state MUST be implemented such that they retain the internal
* state of the current instance and return an instance that contains the
* changed state.
*/
interface UploadedFileInterface
{
/**
* Retrieve a stream representing the uploaded file.
*
* This method MUST return a StreamInterface instance, representing the
* uploaded file. The purpose of this method is to allow utilizing native PHP
* stream functionality to manipulate the file upload, such as
* stream_copy_to_stream() (though the result will need to be decorated in a
* native PHP stream wrapper to work with such functions).
*
* If the moveTo() method has been called previously, this method MUST raise
* an exception.
*
* @return StreamInterface Stream representation of the uploaded file.
* @throws \RuntimeException in cases when no stream is available or can be
* created.
*/
public function getStream();
/**
* Move the uploaded file to a new location.
*
* Use this method as an alternative to move_uploaded_file(). This method is
* guaranteed to work in both SAPI and non-SAPI environments.
* Implementations must determine which environment they are in, and use the
* appropriate method (move_uploaded_file(), rename(), or a stream
* operation) to perform the operation.
*
* $targetPath may be an absolute path, or a relative path. If it is a
* relative path, resolution should be the same as used by PHP's rename()
* function.
*
* The original file or stream MUST be removed on completion.
*
* If this method is called more than once, any subsequent calls MUST raise
* an exception.
*
* When used in an SAPI environment where $_FILES is populated, when writing
* files via moveTo(), is_uploaded_file() and move_uploaded_file() SHOULD be
* used to ensure permissions and upload status are verified correctly.
*
* If you wish to move to a stream, use getStream(), as SAPI operations
* cannot guarantee writing to stream destinations.
*
* @see http://php.net/is_uploaded_file
* @see http://php.net/move_uploaded_file
* @param string $targetPath Path to which to move the uploaded file.
* @throws \InvalidArgumentException if the $targetPath specified is invalid.
* @throws \RuntimeException on any error during the move operation, or on
* the second or subsequent call to the method.
*/
public function moveTo($targetPath);
/**
* Retrieve the file size.
*
* Implementations SHOULD return the value stored in the "size" key of
* the file in the $_FILES array if available, as PHP calculates this based
* on the actual size transmitted.
*
* @return int|null The file size in bytes or null if unknown.
*/
public function getSize();
/**
* Retrieve the error associated with the uploaded file.
*
* The return value MUST be one of PHP's UPLOAD_ERR_XXX constants.
*
* If the file was uploaded successfully, this method MUST return
* UPLOAD_ERR_OK.
*
* Implementations SHOULD return the value stored in the "error" key of
* the file in the $_FILES array.
*
* @see http://php.net/manual/en/features.file-upload.errors.php
* @return int One of PHP's UPLOAD_ERR_XXX constants.
*/
public function getError();
/**
* Retrieve the filename sent by the client.
*
* Do not trust the value returned by this method. A client could send
* a malicious filename with the intention to corrupt or hack your
* application.
*
* Implementations SHOULD return the value stored in the "name" key of
* the file in the $_FILES array.
*
* @return string|null The filename sent by the client or null if none
* was provided.
*/
public function getClientFilename();
/**
* Retrieve the media type sent by the client.
*
* Do not trust the value returned by this method. A client could send
* a malicious media type with the intention to corrupt or hack your
* application.
*
* Implementations SHOULD return the value stored in the "type" key of
* the file in the $_FILES array.
*
* @return string|null The media type sent by the client or null if none
* was provided.
*/
public function getClientMediaType();
}

View File

@ -0,0 +1,323 @@
<?php
namespace Psr\Http\Message;
/**
* Value object representing a URI.
*
* This interface is meant to represent URIs according to RFC 3986 and to
* provide methods for most common operations. Additional functionality for
* working with URIs can be provided on top of the interface or externally.
* Its primary use is for HTTP requests, but may also be used in other
* contexts.
*
* Instances of this interface are considered immutable; all methods that
* might change state MUST be implemented such that they retain the internal
* state of the current instance and return an instance that contains the
* changed state.
*
* Typically the Host header will be also be present in the request message.
* For server-side requests, the scheme will typically be discoverable in the
* server parameters.
*
* @link http://tools.ietf.org/html/rfc3986 (the URI specification)
*/
interface UriInterface
{
/**
* Retrieve the scheme component of the URI.
*
* If no scheme is present, this method MUST return an empty string.
*
* The value returned MUST be normalized to lowercase, per RFC 3986
* Section 3.1.
*
* The trailing ":" character is not part of the scheme and MUST NOT be
* added.
*
* @see https://tools.ietf.org/html/rfc3986#section-3.1
* @return string The URI scheme.
*/
public function getScheme();
/**
* Retrieve the authority component of the URI.
*
* If no authority information is present, this method MUST return an empty
* string.
*
* The authority syntax of the URI is:
*
* <pre>
* [user-info@]host[:port]
* </pre>
*
* If the port component is not set or is the standard port for the current
* scheme, it SHOULD NOT be included.
*
* @see https://tools.ietf.org/html/rfc3986#section-3.2
* @return string The URI authority, in "[user-info@]host[:port]" format.
*/
public function getAuthority();
/**
* Retrieve the user information component of the URI.
*
* If no user information is present, this method MUST return an empty
* string.
*
* If a user is present in the URI, this will return that value;
* additionally, if the password is also present, it will be appended to the
* user value, with a colon (":") separating the values.
*
* The trailing "@" character is not part of the user information and MUST
* NOT be added.
*
* @return string The URI user information, in "username[:password]" format.
*/
public function getUserInfo();
/**
* Retrieve the host component of the URI.
*
* If no host is present, this method MUST return an empty string.
*
* The value returned MUST be normalized to lowercase, per RFC 3986
* Section 3.2.2.
*
* @see http://tools.ietf.org/html/rfc3986#section-3.2.2
* @return string The URI host.
*/
public function getHost();
/**
* Retrieve the port component of the URI.
*
* If a port is present, and it is non-standard for the current scheme,
* this method MUST return it as an integer. If the port is the standard port
* used with the current scheme, this method SHOULD return null.
*
* If no port is present, and no scheme is present, this method MUST return
* a null value.
*
* If no port is present, but a scheme is present, this method MAY return
* the standard port for that scheme, but SHOULD return null.
*
* @return null|int The URI port.
*/
public function getPort();
/**
* Retrieve the path component of the URI.
*
* The path can either be empty or absolute (starting with a slash) or
* rootless (not starting with a slash). Implementations MUST support all
* three syntaxes.
*
* Normally, the empty path "" and absolute path "/" are considered equal as
* defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
* do this normalization because in contexts with a trimmed base path, e.g.
* the front controller, this difference becomes significant. It's the task
* of the user to handle both "" and "/".
*
* The value returned MUST be percent-encoded, but MUST NOT double-encode
* any characters. To determine what characters to encode, please refer to
* RFC 3986, Sections 2 and 3.3.
*
* As an example, if the value should include a slash ("/") not intended as
* delimiter between path segments, that value MUST be passed in encoded
* form (e.g., "%2F") to the instance.
*
* @see https://tools.ietf.org/html/rfc3986#section-2
* @see https://tools.ietf.org/html/rfc3986#section-3.3
* @return string The URI path.
*/
public function getPath();
/**
* Retrieve the query string of the URI.
*
* If no query string is present, this method MUST return an empty string.
*
* The leading "?" character is not part of the query and MUST NOT be
* added.
*
* The value returned MUST be percent-encoded, but MUST NOT double-encode
* any characters. To determine what characters to encode, please refer to
* RFC 3986, Sections 2 and 3.4.
*
* As an example, if a value in a key/value pair of the query string should
* include an ampersand ("&") not intended as a delimiter between values,
* that value MUST be passed in encoded form (e.g., "%26") to the instance.
*
* @see https://tools.ietf.org/html/rfc3986#section-2
* @see https://tools.ietf.org/html/rfc3986#section-3.4
* @return string The URI query string.
*/
public function getQuery();
/**
* Retrieve the fragment component of the URI.
*
* If no fragment is present, this method MUST return an empty string.
*
* The leading "#" character is not part of the fragment and MUST NOT be
* added.
*
* The value returned MUST be percent-encoded, but MUST NOT double-encode
* any characters. To determine what characters to encode, please refer to
* RFC 3986, Sections 2 and 3.5.
*
* @see https://tools.ietf.org/html/rfc3986#section-2
* @see https://tools.ietf.org/html/rfc3986#section-3.5
* @return string The URI fragment.
*/
public function getFragment();
/**
* Return an instance with the specified scheme.
*
* This method MUST retain the state of the current instance, and return
* an instance that contains the specified scheme.
*
* Implementations MUST support the schemes "http" and "https" case
* insensitively, and MAY accommodate other schemes if required.
*
* An empty scheme is equivalent to removing the scheme.
*
* @param string $scheme The scheme to use with the new instance.
* @return static A new instance with the specified scheme.
* @throws \InvalidArgumentException for invalid or unsupported schemes.
*/
public function withScheme($scheme);
/**
* Return an instance with the specified user information.
*
* This method MUST retain the state of the current instance, and return
* an instance that contains the specified user information.
*
* Password is optional, but the user information MUST include the
* user; an empty string for the user is equivalent to removing user
* information.
*
* @param string $user The user name to use for authority.
* @param null|string $password The password associated with $user.
* @return static A new instance with the specified user information.
*/
public function withUserInfo($user, $password = null);
/**
* Return an instance with the specified host.
*
* This method MUST retain the state of the current instance, and return
* an instance that contains the specified host.
*
* An empty host value is equivalent to removing the host.
*
* @param string $host The hostname to use with the new instance.
* @return static A new instance with the specified host.
* @throws \InvalidArgumentException for invalid hostnames.
*/
public function withHost($host);
/**
* Return an instance with the specified port.
*
* This method MUST retain the state of the current instance, and return
* an instance that contains the specified port.
*
* Implementations MUST raise an exception for ports outside the
* established TCP and UDP port ranges.
*
* A null value provided for the port is equivalent to removing the port
* information.
*
* @param null|int $port The port to use with the new instance; a null value
* removes the port information.
* @return static A new instance with the specified port.
* @throws \InvalidArgumentException for invalid ports.
*/
public function withPort($port);
/**
* Return an instance with the specified path.
*
* This method MUST retain the state of the current instance, and return
* an instance that contains the specified path.
*
* The path can either be empty or absolute (starting with a slash) or
* rootless (not starting with a slash). Implementations MUST support all
* three syntaxes.
*
* If the path is intended to be domain-relative rather than path relative then
* it must begin with a slash ("/"). Paths not starting with a slash ("/")
* are assumed to be relative to some base path known to the application or
* consumer.
*
* Users can provide both encoded and decoded path characters.
* Implementations ensure the correct encoding as outlined in getPath().
*
* @param string $path The path to use with the new instance.
* @return static A new instance with the specified path.
* @throws \InvalidArgumentException for invalid paths.
*/
public function withPath($path);
/**
* Return an instance with the specified query string.
*
* This method MUST retain the state of the current instance, and return
* an instance that contains the specified query string.
*
* Users can provide both encoded and decoded query characters.
* Implementations ensure the correct encoding as outlined in getQuery().
*
* An empty query string value is equivalent to removing the query string.
*
* @param string $query The query string to use with the new instance.
* @return static A new instance with the specified query string.
* @throws \InvalidArgumentException for invalid query strings.
*/
public function withQuery($query);
/**
* Return an instance with the specified URI fragment.
*
* This method MUST retain the state of the current instance, and return
* an instance that contains the specified URI fragment.
*
* Users can provide both encoded and decoded fragment characters.
* Implementations ensure the correct encoding as outlined in getFragment().
*
* An empty fragment value is equivalent to removing the fragment.
*
* @param string $fragment The fragment to use with the new instance.
* @return static A new instance with the specified fragment.
*/
public function withFragment($fragment);
/**
* Return the string representation as a URI reference.
*
* Depending on which components of the URI are present, the resulting
* string is either a full URI or relative reference according to RFC 3986,
* Section 4.1. The method concatenates the various components of the URI,
* using the appropriate delimiters:
*
* - If a scheme is present, it MUST be suffixed by ":".
* - If an authority is present, it MUST be prefixed by "//".
* - The path can be concatenated without delimiters. But there are two
* cases where the path has to be adjusted to make the URI reference
* valid as PHP does not allow to throw an exception in __toString():
* - If the path is rootless and an authority is present, the path MUST
* be prefixed by "/".
* - If the path is starting with more than one "/" and no authority is
* present, the starting slashes MUST be reduced to one.
* - If a query is present, it MUST be prefixed by "?".
* - If a fragment is present, it MUST be prefixed by "#".
*
* @see http://tools.ietf.org/html/rfc3986#section-4.1
* @return string
*/
public function __toString();
}

2
vendor/services.php vendored
View File

@ -1,5 +1,5 @@
<?php
// This file is automatically generated at:2021-12-30 16:35:53
// This file is automatically generated at:2022-01-02 21:09:33
declare (strict_types = 1);
return array (
0 => 'taoser\\addons\\Service',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,6 +26,17 @@ class Env implements ArrayAccess
*/
protected $data = [];
/**
* 数据转换映射
* @var array
*/
protected $convert = [
'true' => true,
'false' => false,
'off' => false,
'on' => true,
];
public function __construct()
{
$this->data = $_ENV;
@ -39,7 +50,7 @@ class Env implements ArrayAccess
*/
public function load(string $file): void
{
$env = parse_ini_file($file, true) ?: [];
$env = parse_ini_file($file, true, INI_SCANNER_RAW) ?: [];
$this->set($env);
}
@ -57,9 +68,14 @@ class Env implements ArrayAccess
}
$name = strtoupper(str_replace('.', '_', $name));
if (isset($this->data[$name])) {
return $this->data[$name];
$result = $this->data[$name];
if (is_string($result) && isset($this->convert[$result])) {
return $this->convert[$result];
}
return $result;
}
return $this->getEnv($name, $default);
@ -159,21 +175,25 @@ class Env implements ArrayAccess
}
// ArrayAccess
#[\ReturnTypeWillChange]
public function offsetSet($name, $value): void
{
$this->set($name, $value);
}
#[\ReturnTypeWillChange]
public function offsetExists($name): bool
{
return $this->__isset($name);
}
#[\ReturnTypeWillChange]
public function offsetUnset($name)
{
throw new Exception('not support: unset');
}
#[\ReturnTypeWillChange]
public function offsetGet($name)
{
return $this->get($name);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,32 @@
<?php
namespace think\tests;
use GuzzleHttp\Psr7\Response;
use Mockery;
use PHPUnit\Framework\TestCase;
use think\Request;
use think\route\Dispatch;
use think\route\Rule;
class DispatchTest extends TestCase
{
public function testPsr7Response()
{
$request = Mockery::mock(Request::class);
$rule = Mockery::mock(Rule::class);
$dispatch = new class($request, $rule, '') extends Dispatch {
public function exec()
{
return new Response(200, ['framework' => ['tp', 'thinkphp'], 'psr' => 'psr-7'], '123');
}
};
$response = $dispatch->run();
$this->assertInstanceOf(\think\Response::class, $response);
$this->assertEquals('123', $response->getContent());
$this->assertEquals('tp, thinkphp', $response->getHeader('framework'));
$this->assertEquals('psr-7', $response->getHeader('psr'));
}
}

View File

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

View File

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

View File

@ -0,0 +1,36 @@
name: CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
phpcs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup PHP environment
uses: shivammathur/setup-php@v2
- name: Install dependencies
run: composer install
- name: PHPCSFixer check
run: composer check-style
phpunit:
strategy:
matrix:
php_version: [7.3, 7.4, 8.0]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup PHP environment
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php_version }}
coverage: xdebug
- name: Install dependencies
run: composer install
- name: PHPUnit check
run: ./vendor/bin/phpunit --coverage-text

View File

@ -0,0 +1,36 @@
name: PHP Composer
on:
push:
branches: [ 3.0 ]
pull_request:
branches: [ 3.0 ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Validate composer.json and composer.lock
run: composer validate --strict
- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v2
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-php-
- name: Install dependencies
run: composer install --prefer-dist --no-progress
# Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit"
# Docs: https://getcomposer.org/doc/articles/scripts.md
- name: Run test suite
run: composer test

View File

@ -1,3 +1,4 @@
/vendor/
/.idea/
composer.lock
composer.lock
.phpunit.result.cache

View File

@ -2,6 +2,8 @@
基于PHP7.1+
[![PHP Composer](https://github.com/larvatecn/think-helper/actions/workflows/php.yml/badge.svg)](https://github.com/larvatecn/think-helper/actions/workflows/php.yml)
> 以下类库都在`\\think\\helper`命名空间下
## Str

View File

@ -10,7 +10,10 @@
],
"require": {
"php": ">=7.1.0"
},
},
"require-dev": {
"phpunit/phpunit": "^9.5"
},
"autoload": {
"psr-4": {
"think\\": "src"
@ -18,5 +21,16 @@
"files": [
"src/helper.php"
]
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests"
}
},
"scripts": {
"test": "./vendor/bin/phpunit --colors"
},
"scripts-descriptions": {
"test": "Run all tests."
}
}

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
>
<testsuites>
<testsuite name="Application Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">./src</directory>
</include>
</coverage>
</phpunit>

View File

@ -20,6 +20,7 @@ use JsonSerializable;
use think\contract\Arrayable;
use think\contract\Jsonable;
use think\helper\Arr;
use Traversable;
/**
* 数据集管理类
@ -142,7 +143,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
public function intersect($items, string $indexKey = null)
{
if ($this->isEmpty() || is_scalar($this->items[0])) {
return new static(array_diff($this->items, $this->convertToArray($items)));
return new static(array_intersect($this->items, $this->convertToArray($items)));
}
$intersect = [];
@ -579,16 +580,19 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
}
// ArrayAccess
public function offsetExists($offset)
#[\ReturnTypeWillChange]
public function offsetExists($offset) : bool
{
return array_key_exists($offset, $this->items);
}
#[\ReturnTypeWillChange]
public function offsetGet($offset)
{
return $this->items[$offset];
}
#[\ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
@ -598,24 +602,27 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
}
}
#[\ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->items[$offset]);
}
//Countable
public function count()
public function count(): int
{
return count($this->items);
}
//IteratorAggregate
public function getIterator()
#[\ReturnTypeWillChange]
public function getIterator(): Traversable
{
return new ArrayIterator($this->items);
}
//JsonSerializable
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->toArray();
@ -627,7 +634,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
* @param integer $options json参数
* @return string
*/
public function toJson(int $options = JSON_UNESCAPED_UNICODE) : string
public function toJson(int $options = JSON_UNESCAPED_UNICODE): string
{
return json_encode($this->toArray(), $options);
}

View File

@ -179,7 +179,7 @@ class Str
}
if (!ctype_lower($value)) {
$value = preg_replace('/\s+/u', '', $value);
$value = preg_replace('/\s+/u', '', ucwords($value));
$value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value));
}

View File

@ -0,0 +1,342 @@
<?php
namespace Tests;
use stdClass;
use think\Collection;
use think\helper\Arr;
class ArrTest extends TestCase
{
public function testAdd()
{
$array = Arr::add(['name' => 'ThinkPHP'], 'price', 100);
$this->assertSame(['name' => 'ThinkPHP', 'price' => 100], $array);
}
public function testCrossJoin()
{
// Single dimension
$this->assertSame(
[[1, 'a'], [1, 'b'], [1, 'c']],
Arr::crossJoin([1], ['a', 'b', 'c'])
);
// Square matrix
$this->assertSame(
[[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']],
Arr::crossJoin([1, 2], ['a', 'b'])
);
// Rectangular matrix
$this->assertSame(
[[1, 'a'], [1, 'b'], [1, 'c'], [2, 'a'], [2, 'b'], [2, 'c']],
Arr::crossJoin([1, 2], ['a', 'b', 'c'])
);
// 3D matrix
$this->assertSame(
[
[1, 'a', 'I'], [1, 'a', 'II'], [1, 'a', 'III'],
[1, 'b', 'I'], [1, 'b', 'II'], [1, 'b', 'III'],
[2, 'a', 'I'], [2, 'a', 'II'], [2, 'a', 'III'],
[2, 'b', 'I'], [2, 'b', 'II'], [2, 'b', 'III'],
],
Arr::crossJoin([1, 2], ['a', 'b'], ['I', 'II', 'III'])
);
// With 1 empty dimension
$this->assertSame([], Arr::crossJoin([], ['a', 'b'], ['I', 'II', 'III']));
$this->assertSame([], Arr::crossJoin([1, 2], [], ['I', 'II', 'III']));
$this->assertSame([], Arr::crossJoin([1, 2], ['a', 'b'], []));
// With empty arrays
$this->assertSame([], Arr::crossJoin([], [], []));
$this->assertSame([], Arr::crossJoin([], []));
$this->assertSame([], Arr::crossJoin([]));
// Not really a proper usage, still, test for preserving BC
$this->assertSame([[]], Arr::crossJoin());
}
public function testDivide()
{
list($keys, $values) = Arr::divide(['name' => 'ThinkPHP']);
$this->assertSame(['name'], $keys);
$this->assertSame(['ThinkPHP'], $values);
}
public function testDot()
{
$array = Arr::dot(['foo' => ['bar' => 'baz']]);
$this->assertSame(['foo.bar' => 'baz'], $array);
$array = Arr::dot([]);
$this->assertSame([], $array);
$array = Arr::dot(['foo' => []]);
$this->assertSame(['foo' => []], $array);
$array = Arr::dot(['foo' => ['bar' => []]]);
$this->assertSame(['foo.bar' => []], $array);
}
public function testExcept()
{
$array = ['name' => 'ThinkPHP', 'price' => 100];
$array = Arr::except($array, ['price']);
$this->assertSame(['name' => 'ThinkPHP'], $array);
}
public function testExists()
{
$this->assertTrue(Arr::exists([1], 0));
$this->assertTrue(Arr::exists([null], 0));
$this->assertTrue(Arr::exists(['a' => 1], 'a'));
$this->assertTrue(Arr::exists(['a' => null], 'a'));
$this->assertFalse(Arr::exists([1], 1));
$this->assertFalse(Arr::exists([null], 1));
$this->assertFalse(Arr::exists(['a' => 1], 0));
}
public function testFirst()
{
$array = [100, 200, 300];
$value = Arr::first($array, function ($value) {
return $value >= 150;
});
$this->assertSame(200, $value);
$this->assertSame(100, Arr::first($array));
$this->assertSame('default', Arr::first([], null, 'default'));
$this->assertSame('default', Arr::first([], function () {
return false;
}, 'default'));
}
public function testLast()
{
$array = [100, 200, 300];
$last = Arr::last($array, function ($value) {
return $value < 250;
});
$this->assertSame(200, $last);
$last = Arr::last($array, function ($value, $key) {
return $key < 2;
});
$this->assertSame(200, $last);
$this->assertSame(300, Arr::last($array));
}
public function testFlatten()
{
// Flat arrays are unaffected
$array = ['#foo', '#bar', '#baz'];
$this->assertSame(['#foo', '#bar', '#baz'], Arr::flatten(['#foo', '#bar', '#baz']));
// Nested arrays are flattened with existing flat items
$array = [['#foo', '#bar'], '#baz'];
$this->assertSame(['#foo', '#bar', '#baz'], Arr::flatten($array));
// Flattened array includes "null" items
$array = [['#foo', null], '#baz', null];
$this->assertSame(['#foo', null, '#baz', null], Arr::flatten($array));
// Sets of nested arrays are flattened
$array = [['#foo', '#bar'], ['#baz']];
$this->assertSame(['#foo', '#bar', '#baz'], Arr::flatten($array));
// Deeply nested arrays are flattened
$array = [['#foo', ['#bar']], ['#baz']];
$this->assertSame(['#foo', '#bar', '#baz'], Arr::flatten($array));
// Nested arrays are flattened alongside arrays
$array = [new Collection(['#foo', '#bar']), ['#baz']];
$this->assertSame(['#foo', '#bar', '#baz'], Arr::flatten($array));
// Nested arrays containing plain arrays are flattened
$array = [new Collection(['#foo', ['#bar']]), ['#baz']];
$this->assertSame(['#foo', '#bar', '#baz'], Arr::flatten($array));
// Nested arrays containing arrays are flattened
$array = [['#foo', new Collection(['#bar'])], ['#baz']];
$this->assertSame(['#foo', '#bar', '#baz'], Arr::flatten($array));
// Nested arrays containing arrays containing arrays are flattened
$array = [['#foo', new Collection(['#bar', ['#zap']])], ['#baz']];
$this->assertSame(['#foo', '#bar', '#zap', '#baz'], Arr::flatten($array));
}
public function testFlattenWithDepth()
{
// No depth flattens recursively
$array = [['#foo', ['#bar', ['#baz']]], '#zap'];
$this->assertSame(['#foo', '#bar', '#baz', '#zap'], Arr::flatten($array));
// Specifying a depth only flattens to that depth
$array = [['#foo', ['#bar', ['#baz']]], '#zap'];
$this->assertSame(['#foo', ['#bar', ['#baz']], '#zap'], Arr::flatten($array, 1));
$array = [['#foo', ['#bar', ['#baz']]], '#zap'];
$this->assertSame(['#foo', '#bar', ['#baz'], '#zap'], Arr::flatten($array, 2));
}
public function testGet()
{
$array = ['products.item' => ['price' => 100]];
$this->assertSame(['price' => 100], Arr::get($array, 'products.item'));
$array = ['products' => ['item' => ['price' => 100]]];
$value = Arr::get($array, 'products.item');
$this->assertSame(['price' => 100], $value);
// Test null array values
$array = ['foo' => null, 'bar' => ['baz' => null]];
$this->assertNull(Arr::get($array, 'foo', 'default'));
$this->assertNull(Arr::get($array, 'bar.baz', 'default'));
// Test null key returns the whole array
$array = ['foo', 'bar'];
$this->assertSame($array, Arr::get($array, null));
// Test $array is empty and key is null
$this->assertSame([], Arr::get([], null));
$this->assertSame([], Arr::get([], null, 'default'));
}
public function testHas()
{
$array = ['products.item' => ['price' => 100]];
$this->assertTrue(Arr::has($array, 'products.item'));
$array = ['products' => ['item' => ['price' => 100]]];
$this->assertTrue(Arr::has($array, 'products.item'));
$this->assertTrue(Arr::has($array, 'products.item.price'));
$this->assertFalse(Arr::has($array, 'products.foo'));
$this->assertFalse(Arr::has($array, 'products.item.foo'));
$array = ['foo' => null, 'bar' => ['baz' => null]];
$this->assertTrue(Arr::has($array, 'foo'));
$this->assertTrue(Arr::has($array, 'bar.baz'));
$array = ['foo', 'bar'];
$this->assertFalse(Arr::has($array, null));
$this->assertFalse(Arr::has([], null));
$array = ['products' => ['item' => ['price' => 100]]];
$this->assertTrue(Arr::has($array, ['products.item']));
$this->assertTrue(Arr::has($array, ['products.item', 'products.item.price']));
$this->assertTrue(Arr::has($array, ['products', 'products']));
$this->assertFalse(Arr::has($array, ['foo']));
$this->assertFalse(Arr::has($array, []));
$this->assertFalse(Arr::has($array, ['products.item', 'products.price']));
$this->assertFalse(Arr::has([], [null]));
}
public function testIsAssoc()
{
$this->assertTrue(Arr::isAssoc(['a' => 'a', 0 => 'b']));
$this->assertTrue(Arr::isAssoc([1 => 'a', 0 => 'b']));
$this->assertTrue(Arr::isAssoc([1 => 'a', 2 => 'b']));
$this->assertFalse(Arr::isAssoc([0 => 'a', 1 => 'b']));
$this->assertFalse(Arr::isAssoc(['a', 'b']));
}
public function testOnly()
{
$array = ['name' => 'ThinkPHP', 'price' => 100, 'orders' => 10];
$array = Arr::only($array, ['name', 'price']);
$this->assertSame(['name' => 'ThinkPHP', 'price' => 100], $array);
}
public function testPrepend()
{
$array = Arr::prepend(['one', 'two', 'three', 'four'], 'zero');
$this->assertSame(['zero', 'one', 'two', 'three', 'four'], $array);
$array = Arr::prepend(['one' => 1, 'two' => 2], 0, 'zero');
$this->assertSame(['zero' => 0, 'one' => 1, 'two' => 2], $array);
}
public function testPull()
{
$array = ['name' => 'ThinkPHP', 'price' => 100];
$name = Arr::pull($array, 'name');
$this->assertSame('ThinkPHP', $name);
$this->assertSame(['price' => 100], $array);
// Only works on first level keys
$array = ['i@example.com' => 'Joe', 'jack@localhost' => 'Jane'];
$name = Arr::pull($array, 'i@example.com');
$this->assertSame('Joe', $name);
$this->assertSame(['jack@localhost' => 'Jane'], $array);
// Does not work for nested keys
$array = ['emails' => ['i@example.com' => 'Joe', 'jack@localhost' => 'Jane']];
$name = Arr::pull($array, 'emails.i@example.com');
$this->assertNull($name);
$this->assertSame(['emails' => ['i@example.com' => 'Joe', 'jack@localhost' => 'Jane']], $array);
}
public function testRandom()
{
$randomValue = Arr::random(['foo', 'bar', 'baz']);
$this->assertContains($randomValue, ['foo', 'bar', 'baz']);
$randomValues = Arr::random(['foo', 'bar', 'baz'], 1);
$this->assertIsArray($randomValues);
$this->assertCount(1, $randomValues);
$this->assertContains($randomValues[0], ['foo', 'bar', 'baz']);
$randomValues = Arr::random(['foo', 'bar', 'baz'], 2);
$this->assertIsArray($randomValues);
$this->assertCount(2, $randomValues);
$this->assertContains($randomValues[0], ['foo', 'bar', 'baz']);
$this->assertContains($randomValues[1], ['foo', 'bar', 'baz']);
}
public function testSet()
{
$array = ['products' => ['item' => ['price' => 100]]];
Arr::set($array, 'products.item.price', 200);
Arr::set($array, 'goods.item.price', 200);
$this->assertSame(['products' => ['item' => ['price' => 200]], 'goods' => ['item' => ['price' => 200]]], $array);
}
public function testWhere()
{
$array = [100, '200', 300, '400', 500];
$array = Arr::where($array, function ($value, $key) {
return is_string($value);
});
$this->assertSame([1 => '200', 3 => '400'], $array);
}
public function testWhereKey()
{
$array = ['10' => 1, 'foo' => 3, 20 => 2];
$array = Arr::where($array, function ($value, $key) {
return is_numeric($key);
});
$this->assertSame(['10' => 1, 20 => 2], $array);
}
public function testForget()
{
$array = ['products' => ['item' => ['price' => 100]]];
Arr::forget($array, null);
$this->assertSame(['products' => ['item' => ['price' => 100]]], $array);
$array = ['products' => ['item' => ['price' => 100]]];
Arr::forget($array, []);
$this->assertSame(['products' => ['item' => ['price' => 100]]], $array);
$array = ['products' => ['item' => ['price' => 100]]];
Arr::forget($array, 'products.item');
$this->assertSame(['products' => []], $array);
$array = ['products' => ['item' => ['price' => 100]]];
Arr::forget($array, 'products.item.price');
$this->assertSame(['products' => ['item' => []]], $array);
$array = ['products' => ['item' => ['price' => 100]]];
Arr::forget($array, 'products.final.price');
$this->assertSame(['products' => ['item' => ['price' => 100]]], $array);
$array = ['shop' => ['cart' => [150 => 0]]];
Arr::forget($array, 'shop.final.cart');
$this->assertSame(['shop' => ['cart' => [150 => 0]]], $array);
$array = ['products' => ['item' => ['price' => ['original' => 50, 'taxes' => 60]]]];
Arr::forget($array, 'products.item.price.taxes');
$this->assertSame(['products' => ['item' => ['price' => ['original' => 50]]]], $array);
$array = ['products' => ['item' => ['price' => ['original' => 50, 'taxes' => 60]]]];
Arr::forget($array, 'products.item.final.taxes');
$this->assertSame(['products' => ['item' => ['price' => ['original' => 50, 'taxes' => 60]]]], $array);
$array = ['products' => ['item' => ['price' => 50], null => 'something']];
Arr::forget($array, ['products.amount.all', 'products.item.price']);
$this->assertSame(['products' => ['item' => [], null => 'something']], $array);
// Only works on first level keys
$array = ['i@example.com' => 'Joe', 'i@thinkphp.com' => 'Jane'];
Arr::forget($array, 'i@example.com');
$this->assertSame(['i@thinkphp.com' => 'Jane'], $array);
// Does not work for nested keys
$array = ['emails' => ['i@example.com' => ['name' => 'Joe'], 'jack@localhost' => ['name' => 'Jane']]];
Arr::forget($array, ['emails.i@example.com', 'emails.jack@localhost']);
$this->assertSame(['emails' => ['i@example.com' => ['name' => 'Joe']]], $array);
}
public function testWrap()
{
$string = 'a';
$array = ['a'];
$object = new stdClass();
$object->value = 'a';
$this->assertSame(['a'], Arr::wrap($string));
$this->assertSame($array, Arr::wrap($array));
$this->assertSame([$object], Arr::wrap($object));
}
}

View File

@ -0,0 +1,70 @@
<?php
namespace Tests;
use think\Collection;
class CollectionTest extends TestCase
{
public function testMerge()
{
$c = new Collection(['name' => 'Hello']);
$this->assertSame(['name' => 'Hello', 'id' => 1], $c->merge(['id' => 1])->all());
}
public function testFirst()
{
$c = new Collection(['name' => 'Hello', 'age' => 25]);
$this->assertSame('Hello', $c->first());
}
public function testLast()
{
$c = new Collection(['name' => 'Hello', 'age' => 25]);
$this->assertSame(25, $c->last());
}
public function testToArray()
{
$c = new Collection(['name' => 'Hello', 'age' => 25]);
$this->assertSame(['name' => 'Hello', 'age' => 25], $c->toArray());
}
public function testToJson()
{
$c = new Collection(['name' => 'Hello', 'age' => 25]);
$this->assertSame(json_encode(['name' => 'Hello', 'age' => 25]), $c->toJson());
$this->assertSame(json_encode(['name' => 'Hello', 'age' => 25]), (string) $c);
$this->assertSame(json_encode(['name' => 'Hello', 'age' => 25]), json_encode($c));
}
public function testSerialize()
{
$c = new Collection(['name' => 'Hello', 'age' => 25]);
$sc = serialize($c);
$c = unserialize($sc);
$this->assertSame(['name' => 'Hello', 'age' => 25], $c->all());
}
public function testGetIterator()
{
$c = new Collection(['name' => 'Hello', 'age' => 25]);
$this->assertInstanceOf(\ArrayIterator::class, $c->getIterator());
$this->assertSame(['name' => 'Hello', 'age' => 25], $c->getIterator()->getArrayCopy());
}
public function testCount()
{
$c = new Collection(['name' => 'Hello', 'age' => 25]);
$this->assertCount(2, $c);
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace Tests;
use think\helper\Str;
class StrTest extends TestCase
{
public function testCamel()
{
$this->assertSame('fooBar', Str::camel('FooBar'));
$this->assertSame('fooBar', Str::camel('FooBar'));
$this->assertSame('fooBar', Str::camel('foo_bar'));
$this->assertSame('fooBar', Str::camel('_foo_bar'));
$this->assertSame('fooBar', Str::camel('_foo_bar_'));
}
public function testStudly()
{
$this->assertSame('FooBar', Str::studly('fooBar'));
$this->assertSame('FooBar', Str::studly('_foo_bar'));
$this->assertSame('FooBar', Str::studly('_foo_bar_'));
$this->assertSame('FooBar', Str::studly('_foo_bar_'));
}
public function testSnake()
{
$this->assertSame('think_p_h_p_framework', Str::snake('ThinkPHPFramework'));
$this->assertSame('think_php_framework', Str::snake('ThinkPhpFramework'));
$this->assertSame('think php framework', Str::snake('ThinkPhpFramework', ' '));
$this->assertSame('think_php_framework', Str::snake('Think Php Framework'));
$this->assertSame('think_php_framework', Str::snake('Think Php Framework '));
// ensure cache keys don't overlap
$this->assertSame('think__php__framework', Str::snake('ThinkPhpFramework', '__'));
$this->assertSame('think_php_framework_', Str::snake('ThinkPhpFramework_', '_'));
$this->assertSame('think_php_framework', Str::snake('think php Framework'));
$this->assertSame('think_php_frame_work', Str::snake('think php FrameWork'));
// prevent breaking changes
$this->assertSame('foo-bar', Str::snake('foo-bar'));
$this->assertSame('foo-_bar', Str::snake('Foo-Bar'));
$this->assertSame('foo__bar', Str::snake('Foo_Bar'));
$this->assertSame('żółtałódka', Str::snake('ŻółtaŁódka'));
}
public function testTitle()
{
$this->assertSame('Welcome Back', Str::title('welcome back'));
}
public function testRandom()
{
$this->assertIsString(Str::random(10));
}
public function testUpper()
{
$this->assertSame('USERNAME', Str::upper('username'));
$this->assertSame('USERNAME', Str::upper('userNaMe'));
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace Tests;
use PHPUnit\Framework\TestCase as BaseTestCase;
/**
* Class TestCase
* @author Tongle Xu <xutongle@gmail.com>
*/
class TestCase extends BaseTestCase
{
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -361,9 +361,7 @@ trait WhereQuery
$field = $this->options['via'] . '.' . $field;
}
if ($field instanceof Raw) {
return $this->whereRaw($field, is_array($op) ? $op : [], $logic);
} elseif ($strict) {
if ($strict) {
// 使用严格模式查询
if ('=' == $op) {
$where = $this->whereEq($field, $condition);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,7 +17,7 @@
</ul>
</div>
<ul class="question-list">
{volist name="artList" id="art"}
{volist name="artList['data']" id="art"}
<li>
{if ($art.jie == 1)}
<div class="que-sta-jie">
@ -32,7 +32,7 @@
{/if}
<div class="que-body">
<h2>
<a href="{:url('article/detail',['id' => $art.id])}" style="color:'{$art.title_color}';">{$art.title}</a>
<a href="{:url('article/detail',['id' => $art.id])}" style="color:{$art.title_color};">{$art.title}</a>
</h2>
<div class="que-user-info">
<a href="{:url('user/home',['id'=>$art.user.id])}" class="que-avatar">
@ -94,9 +94,9 @@
//执行一个laypage实例
laypage.render({
elem: 'pages' //注意,这里的 test1 是 ID不用加 # 号
,count: {$count} //数据总数,从服务端得到
,limit: 15
,curr : {$page}
,count: "{$artList['total']}" //数据总数,从服务端得到
,limit: "{$artList['per_page']}"
,curr : "{$artList['current_page']}"
//获取起始页
,jump: function(obj, first){

View File

@ -12,7 +12,7 @@
<div class="fly-panel detail-box">
{//标题}
<div class="title layui-clear">
<h1 style="color:'{$article.title_color}';">{$article.title}</h1>
<h1 style="color:{$article.title_color};">{$article.title}</h1>
{if ($article.jie == 0)}
<span class="layui-btn layui-btn-xs" style="background-color: #FF5722;">{:lang('no finished')}</span>
{else /}
@ -30,7 +30,7 @@
</a>
<span class="user-post-time" data="{$article.create_time}" style="padding-top: 5px;">2019-12-01</span>
<span class="user-questions-right">
<i class="iconfont" title="{:lang('reply')}">&#xe60c;</i>{$comments->count()}<i class="iconfont" title="浏览">&#xe60b;</i>{$pv}
<i class="iconfont" title="{:lang('reply')}">&#xe60c;</i>{$article.comments_count}<i class="iconfont" title="浏览">&#xe60b;</i>{$pv}
</span>
</div>
@ -88,9 +88,9 @@
{/if}
{//评论内容}
<div class="fly-panel detail-box" id="flyReply">
<span style="font-size:18px;">评论 {$comments->count()}</span>
<span style="font-size:18px;">评论 {$article.comments_count}</span>
<ul class="jieda" id="jieda">
{volist name="comments" id="vo" empty= ""}
{volist name="comments" id="vo" empty= ""}
<li data-id="{$vo.id}" class="jieda-daan">
<a name="item-1111111111"></a>
<div class="detail-about detail-about-reply">
@ -104,7 +104,7 @@
{if condition="$article.user.id eq $vo.user.id"}<span>({:lang('poster')})</span>{/if}
</div>
<div class="detail-hits"><span class="post-time" data="{$vo.create_time}"></span></div>
{if $vo.cai == 1}<i class="iconfont icon-caina" title="最佳答案"></i>{/if}
{if $vo.cai == 1}<i class="iconfont icon-caina" title="最佳答案"></i>{/if}
</div>
<div class="detail-body jieda-body photos">{$vo.content}</div>
<div class="jieda-reply">
@ -112,19 +112,19 @@
<i class="iconfont icon-zan"></i><em>{$vo.zan}</em>
</span>
<span type="reply"><i class="iconfont icon-svgmoban53"></i>{:lang('reply')}</span>
{//评论编辑删除采纳权限}
{//评论编辑删除采纳权限}
<div class="jieda-admin">
{if ((session('user_id') == $vo.user.id) && (getLimtTime($vo.create_time) < 2)) OR ($user.auth ?? '')}
{if ((session('user_id') == $vo.user.id) && (getLimtTime($vo.create_time) < 2)) OR ($user.auth ?? '')}
<span type="edit">{:lang('edit')}</span>
<span type="del">{:lang('delete')}</span>
{/if}
{if ($vo.cai == 0) && ((session('user_id') == $article.user_id) OR ($user.auth ?? '')) /}<span class="jieda-accept" type="accept">{:lang('accept')}</span>{/if}
{/if}
{if ($vo.cai == 0) && ((session('user_id') == $article.user_id) OR ($user.auth ?? '')) /}<span class="jieda-accept" type="accept">{:lang('accept')}</span>{/if}
</div>
</div>
</li>
{/volist}
</ul>
<div style="text-align: center">{$comments|raw}</div>
<div style="text-align: center" id="pages"></div>
</div>
</div>
@ -285,7 +285,7 @@ layui.use(['fly', 'face','colorpicker','plyr', 'laypage'], function(){
//执行一个laypage实例
laypage.render({
elem: 'pages' //注意,这里的 test1 是 ID不用加 # 号
,count: {$count} //数据总数,从服务端得到
,count: "{$article.comments_count}" //数据总数,从服务端得到
,limit: 10
,curr : {$page}
@ -294,8 +294,10 @@ layui.use(['fly', 'face','colorpicker','plyr', 'laypage'], function(){
//首次不执行
if(!first){
var page = obj.curr;
var url = "{:url('article/detail',['id'=>$article.id])}"
$.post(url,{"page":page},function(){
console.log(page);
var url = "{:url('article/detail',['id'=>$article.id])}";
var id = "{$article.id}";
$.post("{:url('article/detail')}",{"page":page,"id":id},function(){
window.location.href = url + '?page=' + page + '#flyReply';
});
}

View File

@ -12,14 +12,14 @@
{include file="public/filter" /}
<ul class="fly-list">
{volist name="artList" id="art"}
{volist name="artList['data']" id="art"}
<li>
<a href="{:url('user/home',['id'=>$art.user.id])}" class="fly-avatar">
<a href="{:url('user/home',['id'=>$art.user_id])}" class="fly-avatar">
<img src="{$art.user.user_img}" alt="{$art.user.name}">
</a>
<h2>
{if config('taoler.config.cate_show') == 1}<a class="layui-badge">{:cookie('think_lang') == 'en-us' ? $art.cate.ename : $art.cate.catename}</a>{/if}
<a href="{:url('article/detail',['id' => $art.id])}" style="color:{$art.title_color};">{$art.title}</a>
<a href="{:url('article/detail',['id' => $art.id])}" style="color: {$art['title_color']};">{$art.title}</a>
</h2>
<div class="fly-list-info">
<a href="{:url('user/home',['id'=>$art.user.id])}" link>
@ -95,19 +95,20 @@
//执行一个laypage实例
laypage.render({
elem: 'pages' //注意,这里的 test1 是 ID不用加 # 号
,count: {$count} //数据总数,从服务端得到
,limit: 15
,curr :{$page}
,count: "{$artList['total']}" //数据总数,从服务端得到
,limit: "{$artList['per_page']}"
,curr : "{$artList['current_page']}"
//获取起始页
,jump: function(obj, first){
//obj包含了当前分页的所有参数比如
//console.log(obj.curr); //得到当前页,以便向服务端请求对应页的数据。
console.log(obj.curr); //得到当前页,以便向服务端请求对应页的数据。
//console.log(obj.limit); //得到每页显示的条数
//首次不执行
if(!first){
//do something
//window.location.href= url+obj.curr+'&limit='+obj.limit+'#collection'; //跳转
window.location.href = '{$url}'+obj.curr+'.html'; //跳转
//var page = obj.curr ? obj.curr : 1;
window.location.href = '{$url}'+ obj.curr +'.html'; //跳转
}
}
});

View File

@ -10,14 +10,14 @@
<div class="layui-col-md9">
<div class="fly-panel" style="margin-bottom: 0;">
<ul class="fly-list">
{volist name="artList" id="art"}
{volist name="artList['data']" id="art"}
<li>
<a href="{:url('user/home',['id'=>$art.user.id])}" class="fly-avatar">
<img src="{$art.user.user_img}" alt="{$art.user.name}">
</a>
<h2>
{if config('taoler.config.cate_show') == 1}<a class="layui-badge">{:cookie('think_lang') == 'en-us' ? $art.cate.ename : $art.cate.catename}</a>{/if}
<a href="{:url('article/detail',['id' => $art.id])}" style="color:'{$art.title_color}';">{$art.title}</a>
<a href="{:url('article/detail',['id' => $art.id])}" style="color:{$art.title_color};">{$art.title}</a>
</h2>
<div class="fly-list-info">
<a href="{:url('user/home',['id'=>$art.user.id])}" link>
@ -86,9 +86,9 @@
//执行一个laypage实例
laypage.render({
elem: 'pages' //注意,这里的 test1 是 ID不用加 # 号
,count: {$count} //数据总数,从服务端得到
,limit: 15
,curr : {$page}
,count: "{$artList['total']}" //数据总数,从服务端得到
,limit: "{$artList['per_page']}"
,curr : "{$artList['current_page']}"
//获取起始页
,jump: function(obj, first){

View File

@ -11,7 +11,7 @@
<div class="layui-col-md9 content detail">
<div class="fly-panel detail-box">
{//标题}
<h1 style="color:'{$article.title_color}';">{$article.title}</h1>
<h1 style="color:{$article.title_color};">{$article.title}</h1>
<div class="detail_qrcode layui-hide-xs" onclick="PhoneDown();" id="mobile"></div>
{//图标}
<div class="fly-detail-info">
@ -20,7 +20,7 @@
{if ($article.is_hot == 1)}<span class="layui-badge layui-bg-red">{:lang('hot')}</span>{/if}
<span id="LAY_jieAdmin" data-id="{$article['id']}"></span>
<span class="fly-list-nums">
<a href="#comment"><i class="iconfont" title="{:lang('reply')}">&#xe60c;</i>{$comments->count()}</a><i class="iconfont" title="浏览">&#xe60b;</i>{$pv}
<a href="#comment"><i class="iconfont" title="{:lang('reply')}">&#xe60c;</i>{$article.comments_count}</a><i class="iconfont" title="浏览">&#xe60b;</i>{$pv}
</span>
</div>
{//作者}
@ -75,13 +75,13 @@
</div>
{//评论}
<div class="fly-panel detail-box" id="flyReply">
<span style="font-size:18px;">评论 {$comments->count()}</span>
<span style="font-size:18px;">评论 {$article.comments_count}</span>
<ul class="jieda" id="jieda">
{volist name="comments" id="vo" empty= ""}
<li data-id="{$vo.id}" class="jieda-daan">
<a name="item-1111111111"></a>
<div class="detail-about detail-about-reply">
<a class="fly-avatar" href="{:url('user/home',['id'=>$vo.user.id])}">
<a class="fly-avatar" href="{:url('user/home',['id'=>$vo.user_id])}">
<img src="{$vo.user.user_img}" alt=" "><i class="iconfont icon-renzheng" title="认证信息"></i>
</a>
<div class="fly-detail-user">
@ -262,7 +262,7 @@ layui.use(['fly', 'face','colorpicker','plyr', 'laypage'], function(){
//执行一个laypage实例
laypage.render({
elem: 'pages' //注意,这里的 test1 是 ID不用加 # 号
,count: {$count} //数据总数,从服务端得到
,count: "{$article.comments_count}" //数据总数,从服务端得到
,limit: 10
,curr : {$page}

View File

@ -35,7 +35,7 @@
</ul>
<div style="text-align: center">
<div class="laypage-main">
<a href="{:url('article/cate',['ename'=>'all'])}" class="laypage-next">{:lang('more post')}</a>
<a href="{:url('article/cate',['ename'=>'all','page'=>1])}" class="laypage-next">{:lang('more post')}</a>
</div>
</div>
</div>