thinkphp8使用指南

响应输出

2. 响应输出

  • 响应输出,有好几种:包括 returnjson()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");
    
  • 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"];
    
  • 默认是控制器全体方法有效,如果需要限制,还是 onlyexcept

    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");