响应输出
2. 响应输出
响应输出,有好几种:包括 return、json() 和 view() 等等;
默认输出方式是以 html 格式输出,如果你发起 json 请求,则输出 json;
而背后是 response 对象,可以用 response() 输出达到相同的效果;
$data = "Hello,TP8!"; // 等同于 return $data; return response($data);
response()方法可以设置第二参数,状态码,或调用 code()方法;
// 参数二,发送状态码 return response($data, 201); //或 return response($data)->code(201); // json()和view()均支持状态码
3. 重定向
使用 redirect()方法可以实现页面重定向,需要 return 执行;
// 首页 return redirect("/"); // 访问路由页面,外加状态码 return redirect("details/5", 303); // 访问url生成的地址 return redirect(url("Index/index"));
还支持session跳转和记住上一次地址的跳转,实战时再研究;
39. Session和Cookie
1. Session
TP8不支持原生 $ SESSION 的获取方式,也不支持 session开头的函数,默认是关闭的;
在使用 Session 之前,需要开启初始化,在中间件文件 middleware.php;
// 去掉注释,开始Session \think\middleware\SessionInit::class
使用 ::set() 和 ::get() 方法去设置 Session 的存取:
// 设置session use Session::set("user", "Mr.Lee"); // 获取session use echo Session::get("user"); // 获取不存在的session时,参数二默认值 echo Session::get("abc", "不存在"); // 获取全部session,数组形式 dump(Session::all());
::has() 判断是否赋值,::delete() 删除,::pull() 取值后删除,::clear() 清空整个 session;
// 判断是否存在 Session::has("user"); // 删除 Session::delete("user"); // 取值后删除,不存在返回null Session::pull("user"); // 清空所有 Session::clear("");
Request 对象可以直接对Session进行存取,自行查看手册,这里再罗列常用的助手函数:
//赋值 session("user", "Mr.Wang"); //has 判断 session("?user"); //delete 删除 session("user", null); //清理全部 session(null); //输出 echo session("user");
2. Cookie
Cookie 是客户端存储,默认情况下是开启初始化的,在 config/cookie.php;
// 设置cookie user value 过期时间,不设过期时间,则为临时关闭浏览器后自动删除 Cookie::set("user", "Mr.Lee", 3600); // 获取cookie,注意,会慢一拍 echo Cookie::get("user"); // 获取全部cookie dump(Cookie::get()); // 永久保持 Cookie::forever("user", "Mr.Lee");
::has() 判断是否存在,::delete() 删除 cookie;
Cookie::has("user"); Cookie::delete("user");
助手函数,更加方便操作:
echo cookie("user"); //输出 cookie("user", "Mr.Lee", 3600); //设置 cookie("user", null); //删除
40. 缓存功能
1. 常规方法
系统内置了很多类型的缓存,除了 File,其它均需要结合相关产品;
我们这里主要演示 File 文本缓存,其它的需要学习相关产品,比如Redis将单独出;
配置文件 cache.php 进行缓存配置,默认生成在 runtime/cache 目录;
// 生成缓存,和cookie一样的设置,但注意,过期时间不设,将是永久,和cookie有区别 Cache::set("user", "Mr.Lee", 3600); // 读取缓存,无数据返回null echo Cache::get("user"); // 判断是否存在 echo Cache::has("user"); // 删除缓存 Cache::delete("user"); // 先获取,再删掉,没有值返回null Cache::pull("user"); // 清空缓存 Cache::clear();
::inc() 和 ::dec() 实现缓存数据的自增和自减操作:
// 创建数值缓存 Cache::set("num", 1); // 默认自增+1 Cache::inc("num"); // 参数二自增值 Cache::inc("num", 3); echo Cache::get("num");
::push() 实现缓存的数组数据追加的功能:
// 数组缓存 Cache::set("arr", [1, 2, 3]); // 追加数据 Cache::push("arr", 4); halt(Cache::get("arr"));
2. 助手函数
最常用的创建和读取缓存的助手函数:
//设置缓存 cache("user", "Mr.Lee", 3600); //输出缓存 echo cache("user"); //删除指定缓存 cache("user", null);
41. 验证器的使用
1. 定义方式
验证器的使用,我们必须先定义它,系统提供了一条命令直接生成想要的类;
// 生成一个 validate 文件夹,并生成 User.php 验证类; // 自动生成了两个属性:$rule 表示定义规则,$message 表示错误提示信息; php think make:validate User
// 规则 protected $rule = [ "name" => "require|max:20", //不得为空,不得大于 20 位 "price" => "number|between:1,100", //必须是数值,1-100 之间 "email" => "email" //邮箱格式要正确 ]; // 提示,不设置则默认错误提示 protected $message = [ "name.require" => "姓名不得为空", "name.max" => "姓名不得大于 20 位", "price.number" => "价格必须是数字", "price.between" => "价格必须 1-100 之间", "email.email" => "邮箱的格式错误" ];
验证器定义好了之后,我们需要进行调用测试,创建一个 Verify.php 控制器;
public function vali() { try { validate(User::class)->check([ // 可以将姓名设置空,或大于20位 // 将邮箱写错,来测试 "name" => "Mr.Lee", "email" => "123163.com" ]); } catch (ValidateException $e) { dump($e->getError()); } }
默认情况下,出现一个错误就会停止后面字段的验证,我们也可以设置批量验证;
validate(User::class)->batch(true)...
手册提供了大量内置的规则,查看 手册 -> 验证 -> 内置规则,但我们也可以自定义规则:
// 过滤:李炎恢 这三个字 protected $rule = [ "name" => "require|max:20|checkName:李炎恢" ] // 自定义规则checkName protected function checkName($value, $rule) { // 判断不等返回true,否则返回错误提示 return $value != $rule ? true : "名字是违禁词!"; } // 规则函数五个参数 protected function checkName($value, $rule, $data, $field, $title) { dump($data); //所有数据信息 dump($field); //当前字段名 dump($title); //字段描述,没有就是字段名 } // 设置字段描述 protected $rule = [ "name|用户名" ]
2. 规则.错误信息
当规则体量较大时,可以采用数组的方式,让规则的可读性变高;
// 只不过,体量小时,反而感觉乱 protected $rule = [ "name" => [ "require", "max" => 20, "checkName" => "李炎恢" ], "email" => "email" ];
较少的验证时,或想只在控制器独立验证,不想创建验证类,也是支持的;
// 规则 $validate = Validate::rule([ "name" => "require|max:20", "email" => "email" ]); // 验证 $result = $validate->check([ "name" => "李炎恢", "email" => "123@163.com" ]); // 判断 if (!$result) { dump($validate->getError()); }
42. 验证场景.内置规则
1. 验证场景
验证场景设置,即特定的场景下是否进行验证,独立验证不存在场景验证;
举一个简单的例子,新增数据需要验证用户名,而修改更新时不验证用户名;
可以在验证类 User.php 中,设置一个$scene 属性,用来限定场景验证;
// 验证场景 protected $scene = [ "insert" => ["name", "email"], // 新增验证name和email "edit" => ["email"] // 修改验证email ];
在控制器端,验证时,根据不同的验证手段,绑定相关场景进行验证即可;
validate(User::class)->scene("edit")
在验证类端,可以设置一个公共方法对场景的细节进行定义,方法名为 scene+场景;
// 除了only,还支持移出remove规则和append增加规则,请查看手册 public function sceneEdit() { return $this->only(["email"]); }
2. 内置规则
验证器提供了大量的内置规则:格式验证、长度区间、字段比较以及其它等验证;
格式验证类:(更多可查看手册 验证 -> 验证规则)
// 规则 $validate = Validate::rule([ "id" => "number", ]); // 验证 $result = $validate->check([ "id" => "abc", ]); // 判断 if (!$result) { dump($validate->getError()); }
可以使用静态 facade 模式单一验证来快捷对字段数据进行判断:
php // 格式为Validate::is+Rule,返回 true 或 false dump(Validate::isNumber(10));
# 43. Token表单令牌
1. 设置Token
表单令牌就是在表单中增加一个隐藏字段,随机生成一串字符,确定不是伪造;
这种随机产生的字符和服务器的 session(开启)进行对比,通过则是合法表单;
// 随机生成一个Token,并交给模板 public function index() { return view("index", [ "token" => $this->request->buildToken("__token__", "sha1") ]); }
// 表单插入一个隐藏域,value是token <input type="hidden" name="__token__" value="<?php echo $token;?>"> // 至于Aajx提交,不方便测试,等实战时再演练,具体参看 手册 -> 验证 -> 表单令牌
public function save() { // 可以看到 token 一并写入了session echo $this->request->post("__token__")."<br>"; echo session("__token__"); }
2. 验证Token
一般来说提交跳转有两种形式:路由模式和常规模式,先看下常规模式;
// 验证__token__是否通过 $check = $this->request->checkToken("__token__"); // 不通过,这里手动抛出指定异常即可 if ($check === false) { throw new ValidateException("无效令牌!"); }
路由模式,需要开启路由,并设置路由:
// Token测试 Route::rule("v/i", "Verify/index"); Route::rule("v/s", "Verify/save")->token();
当然也支持验证器模式,具体如下:
protected $rule = [ 'name' => 'require|max:25|token' }
44. 上传文件
1. 文件上传
如果要实现上传功能,首先需要建立一个上传表单:
// 载入模板 public function up() { return View::engine("php")->fetch("up"); } // 接受上传数据 public function save() {}
<form action="/file/save" enctype="multipart/form-data" method="post"> <input type="file" name="image"> <input type="submit" value="上传"> </form>
使用 Request::file() 或 request()->file 来获取上传数据:
// 获取上传数据 halt(Request::file("image")); // 或 halt(request()->file("image")); // 上传到本地服务器,参数1是设置的分类目录 $saveName = Filesystem::putFile("topic", $file); halt($saveName); // 生成的规则还支持另外两种方式:md5 和 sha1; Filesystem::putFile("topic", $file, "sha1");
在 config/filesystem.php 里可以修改上次规则和目录;
如果要批量上传,表单改成数组形式即可:
<form action="/verify/upload" method="post" enctype="multipart/form-data"> <input type="file" name="image[]"> <input type="file" name="image[]"> <input type="file" name="image[]"> <input type="submit" value="上传"> </form>
// 获取表单上传数据 $files = Request::file("image"); // 批量 $saveNames = []; foreach ($files as $file) { $saveNames[] = Filesystem::putFile("topic", $file); } halt($saveNames);
2. 上传验证
如果要对上传的内容进行验证,比如后缀、大小等,直接用验证器:
手册上本身给出的例子是批量上传验证,我这里写一个单一上传验证;
// 获取表单上传数据 $file = Request::file("image"); // 独立验证规则,也可以写到验证类里 $validate = Validate::rule([ // 10240000 等于 1.28MB "image" => "file|fileExt:jpg,png,gif|fileSize:10240000" ]); // 验证上传 $result = $validate->check([ "image" => $file ]); // 判断 if ($result) { $saveName = Filesystem::putFile("topic", $file); halt($saveName); } else { dump($validate->getError()); }
45. 验证码
1. 生成验证码
验证码功能不是系统内置的功能了,需要通过 composer 引入进来;
// 安装后使用需要开启 session composer require topthink/think-captcha
引入进来之后,我们在模版中,验证一下验证码是否能正常显示;
<div><?php echo captcha_img() ?></div> <div><img src="{:captcha_src()}" alt="captcha" /></div>
验证码不显示图片原因是伪静态问题,无法加在404错误:
- 开启路由即可;
- 避免麻烦,可以取消强制路由;
// 强制路由时,需要设置访问路由,非强制不需要 //Route::rule("v/c", "Verify/code");
手动验证码,需要手动设置点击刷新:
<div><img src="<?php echo captcha_src() ?>" alt="" onclick='this.src="/captcha.html?"+Math.random();'></div>
在 config/captcha.php 文件中, 可以配置验证码的各种参数:
//验证码位数 'length' => 4, // 是否使用中文验证码 'useZh' => true, // 是否使用算术验证码 'math' => false,
手册提供了自建验证码,具体参看:手册 -> 扩展库 -> 验证码;
2. 验证码验证
直接利用验证器功能,对验证码进行验证即可;
// 获取表单值 $code = $this->request->param("code"); // 规则 $validate = Validate::rule([ "captcha|验证码" => "require|captcha" ]); // 验证 $result = $validate->check([ "captcha" => $code ]); // 判断 if (!$result) { dump($validate->getError()); }
手册上提供了继承基类后依赖注入的方法,就是将规则和验证合二为一:
// 通过依赖入驻,需要继承BaseController $this->validate([ "captcha" => $code ], [ "captcha|验证码" => "require|captcha" ]);
极简方案 -> 助手函数:
// 助手函数 if (!captcha_check(input("post.code"))) { // 抛异常 throw new ValidateException("验证码错误"); //return "验证码错误!"; }
46. 图像处理
1. 生成图像
图像处理功能不是系统内置的功能了,需要通过 composer 引入进来;
composer require topthink/think-image
为了更方便的测试,我们在 public/Image.png 放置一张图片,这样不用考虑 url 了;
$image = Image::open("image.png"); //图片宽度 echo $image->width(); //图片高度 echo $image->height(); //图片类型 echo $image->type(); //图片 mime echo $image->mime(); //图片大小 dump($image->size());
使用 crop() 方法可以裁剪图片,并使用 save() 方法保存到指定路径;
// 裁剪图片,默认从左上角裁,并保存到指定位置 $image->crop(550, 400)->save("crop1.png");
使用 thumb() 方法,可以生成缩略图,配合 save() 把缩略图保存下来;
// 生成缩略图 $image->thumb(550, 400)->save("thumb1.png");
// 手册上并没有Image这个组件功能,应该是没更新,PHP8.2会报错,修改源码 // 256行,添加 (int) $img = imagecreatetruecolor((int)$width, (int)$height); // 269行,添加 (int),如果设置参数3,x,y,w,h都需要 (int) imagecopyresampled($img, $this->im, 0, 0, $x, $y, (int)$width, (int)$height, $w, $h);
2. 图像参数
thumb() 方法拥有第三个参数,可以追踪进去查看;
/* 缩略图相关常量定义 */ const THUMB_SCALING = 1; //常量,标识缩略图等比例缩放类型 const THUMB_FILLED = 2; //常量,标识缩略图缩放后填充类型 const THUMB_CENTER = 3; //常量,标识缩略图居中裁剪类型 const THUMB_NORTHWEST = 4; //常量,标识缩略图左上角裁剪类型 const THUMB_SOUTHEAST = 5; //常量,标识缩略图右下角裁剪类型 const THUMB_FIXED = 6; //常量,标识缩略图固定尺寸缩放类型
$image->thumb(550, 400, 3)->save("thumb1.png");
使用 rotate()方法,可以旋转图片,默认是 90 度,参数可以设置;
$image->rotate(180)->save("rotate1.png");
save(‘路径’,[‘类型’,’质量’,’是否隔行扫描’]),追踪到方法查看;
save($pathname, $type = null, $quality = 80, $interlace = true)
water()方法,可以给图片增加一个图片水印,默认位置为右下角,可看源码常量;
// 加水印 $image->water("Logo.png")->save("water1.png");
text()方法,可以给图片增加一个文字水印;
$image->text('Mr.Lee',getcwd().'/1.ttf',20,'#ffffff')->save('text1.png');
47. 分页处理
1. 简易分页
不管是数据库操作还是模型操作,都使用 paginate()方法来实现;
// 数据集 $list = User::paginate(3); // 单独分页 $page = $list->render(); return view("index", [ "list" => $list, "page" => $page ]);
<table border="1"> <tr> <th>id</th> <th>姓名</th> <th>年龄</th> <th>性别</th> </tr> <?php foreach ($list as $user) {?> <tr> <td><?=$user["id"]?></td> <td><?=$user["name"]?></td> <td><?=$user["age"]?></td> <td><?=$user["gender"]?></td> </tr> <?php }?> </table> <div><?=$page?></div>
可以通过浏览器的源码检查,或者使用输出html实体的方式来了解 page 构造,方便设计 css;
<div><?=htmlentities($page)?></div>
paginate() 可以限定总记录数,比如,限定总记录数只有 10 条的页码;
->paginate(5, 10);
可以设置分页的页码为简洁分页,就是没有 1,2,3,4 这种,只有上下页;
->paginate(5, true);
2. 分页参数
如果想自行制作个性化分页,或获取分页中的各种参数,这里也提供了:
| 参数 | 描述 |
| :———-: | :————-: |
| list_rows | 每页数量 |
| page | 当前页 |
| path | url路径 |
| query | url额外参数 |
| fragment | url锚点 |
| var_page | 分页变量 |$list = User::paginate([ "list_rows" => 3, "var_page" => "p", ]);
分页提供了 total() 方法,来获取所有总记录:
// 获取总记录 $total = $list->total();
3. 其它分页
重写 Paginate 类,复制 Bootstrap 类,开启重写:
// 路径:vendor/topthink/think-orm/src/paginator/driver/Bootstrap.php // 开启:app/common/Bootstrap.php, provider.php return [ 'think\Paginator' => 'app\common\Bootstrap' ]; // 复制后,修改上一页和下一页中文即可 protected function getPreviousButton(string $text = '上一页'): string
手册 提供了 大数据分页 :PaginateX() 方法,用于百万级提高性能的。嗯!
48. 中间件入门
1. 定义中间件
中间件的主要用于拦截和过滤 HTTP 请求,并进行相应处理;
这些请求的功能可以是 URL 重定向、权限验证等等;
为了进一步了解中间件的用法,我们首先定义一个基础的中间件;
可以通过命令行模式,在应用目录下生成一个中间件文件和文件夹;
php think make:middleware Check
public function handle($request, \Closure $next) { // 拦截请求 if ($request->param("name") == "index") { return redirect("../"); } // 继续往执行 return $next($request); }
然后将这个中间件进行注册,在应用目录下创建 middleware.php 中间件配置;
// 注册中间件 app\middleware\Check::class
中间件的入口执行方法必须是:handle()方法,第一参数请求,第二参数是闭包;
- 业务代码判断请求的 name 如果等于 index,就拦截住,执行中间件,跳转到首页;
- 但如果请求的 name 是 lee,那需要继续往下执行才行,不能被拦死;
- 那么就需要$next($request)把这个请求去调用回调函数;
- 中间件 handle()方法规定需要返回 response 对象,才能正常使用;
- 而$next($request)执行后,就是返回的 response 对象;
- 为了测试拦截后,无法继续执行,可以 return response()助手函数测试;
2. 前后置中间件
将$next($request)放在方法底部的方式,属于前置中间件;
前置中间件就是请求阶段来进行拦截验证,比如登录判断、跳转、权限等;
而后置中间件就是请求完毕之后再进行验证,比如写入日志等等;
public function handle($request, \Closure $next) { //中间件代码,前置 return $next($request); }
public function handle($request, \Closure $next) { $response = $next($request); //中间件代码,后置 return $response; }
```php
// 先执行内容,再执行中间件
$response = $next($request);
// 拦截请求
if ($request->param(“name”) == “index”)
{return redirect("../");
}
return $response;
```# 49. 中间件操作
1. 路由中间件
创建一个给路由使用的中间件,判断路由的 ID 值实现相应的验证;
php think make:middleware Auth
路由方法提供了一个 middleware() 方法,让指定的路由采用指定的中间件;
// 限定这个路由采用了中间件 Route::rule('/', 'Index/index')->middleware(\app\middleware\Auth::class);
// 导入后,可以省略 use app\middleware\Auth; use app\middleware\Check; // 路由采用多个中间件 Route::rule('/', 'Index/index')->middleware([Auth::class, Check::class]);
也可以在 config/middleware.php 配置文件加中,配置别名支持;
// 别名或分组 'alias' => [ "Auth" => \app\middleware\Auth::class, "Check" => \app\middleware\Check::class, ],
// 当然,Route::group() 路由分组也支持 Route::rule('/', 'Index/index')->middleware(["Auth", "Check"]);
2. 控制器中间件
如果不用路由,怎么用局部化的中间件呢?当然也可以直接在控制器上定义;
// 这里是别名方式,和路由一样,另外两种均支持 protected $middleware = ["Auth", "Check"];
默认是控制器全体方法有效,如果需要限制,还是 only 和 except;
protected $middleware = [ "Auth" => ["only" => ["hello"]], "Check" => ["only" => ["index"]] ];
50. 助手函数和工具库
1. 助手函数
前面,我们已经学习了不少实用的助手函数,这里再总结一些:手册 -> 附录 -> 助手函数
| 助手函数 | 说明 |
| ———— | —————————————————— |
| env | 支持 Env:: 模式,环境变量 |
| config | 支持 Config:: 模式,配置信息 |
| app_path | 一组六个,自行参考手册,当前应用目录 |// 获取所有环境变量信息 halt(env()); // 获取HOST return env("DB_HOST"); // 获取所有配置信息 halt(config()); // 获取默认应用名 return config("app.default_app"); // 当前应用目录 return app_path();
2. 助手工具库
实用助手工具库,需要安装扩展:手册 -> 扩展库 -> think助手工具库
composer require topthink/think-helper
// 将字母转换为小写 return Str::lower("ABCDEFG");