验证器

一.验证器定义

验证器的使用,我们必须先定义它,系统提供了一条命令直接生成想要的类;
php think make:validate User
这条命令会自动在应用目录下生成一个 validate 文件夹,并生成 User.php 类;
class User extends Validate
自动生成了两个属性:$rule 表示定义规则,$message 表示错误提示信息;

  1. protected $rule = [
  2. 'name' => 'require|max:20', //不得为空,不得大于 20 位
  3. 'price' => 'number|between:1,100', //必须是数值,1-100 之间
  4. 'email' => 'email' //邮箱格式要正确
  5. ];
  6. protected $message = [
  7. 'name.require' => '姓名不得为空',
  8. 'name.max' => '姓名不得大于 20 位',
  9. 'price.number' => '价格必须是数字',
  10. 'price.between' => '价格必须 1-100 之间',
  11. 'email' => '邮箱的格式错误'
  12. ];

如果不设置$message 定义的话,将提示默认的错误信息;
验证器定义好了之后,我们需要进行调用测试,创建一个 Verify.php 控制器;
$data = [
‘name’ => ‘蜡笔小新’,
‘price’ => 90,
‘email’ => ‘xiaoxin@163.com’,
];
$validate = new \app\validate\User();
if (!$validate->check($data)) {
dump($validate->getError());
}
控制器类还提供了一个更加方便验证的方法,可以更容易的进行编码;

  1. $result = $this->validate([
  2. 'name' => '蜡笔小新',
  3. 'price' => 90,
  4. 'email' => 'xiaoxin@163.com',
  5. ], '\app\validate\User');
  6. if ($result !== true) {
  7. dump($result);
  8. }

默认情况下,一旦有数据验证不符合规则,就立刻停止验证进行返回;
如果,我们要验证所有的错误信息,那需要手动开启批量验证;
protected $batchValidate = true;
默认情况下,验证失败后不会抛出异常,需要手动设置,如下:
protected $failException = true;
系统提供了常用的规则让开发者直接使用,也可以自行定义独有的特殊规则;
protected $rule = [
‘name’ => ‘require|max:20|checkName:李炎恢’,
];
//自定义规则,名称中不得是“李炎恢”
protected function checkName($value, $rule)
{
return $rule != $value ? true : ‘名称不得是“李炎恢”’;
}
对于自定义规则中,一共可以有五个参数,我们分别了解一下;
protected function checkName($value, $rule, $data, $field, $title)
{
dump($data); //所有数据信息
dump($field); //当前字段名
dump($title); //字段描述,没有就是字段名
}
如何设置字段描述,只要在字段名用|后设置即可:
‘name|用户名’ => ‘require|max:20|checkName:李炎恢’,

验证规则和错误消息

一.验证规则

在上一节验证器定义的时候,我们采用的字符串模式,也支持数组模式;

  1. protected $rule = [
  2. 'name' => [
  3. 'require',
  4. 'max' => 10,
  5. 'checkName' => '李炎恢'
  6. ],
  7. 'price' => [
  8. 'number',
  9. 'between' => '1,100'
  10. ],
  11. 'email' => 'email'
  12. ];

数组模式在验证规则很多很乱的情况下,更容易管理,可读性更高;
如果你想使用独立验证,就是手动调用验证类,而不是调用 User.php 验证类;
这种调用方式,一般来说,就是独立、唯一,并不共享的调用方式;

  1. $validate = new Validate();
  2. //$validate->rule('name', 'require|max:10');
  3. $validate->rule([
  4. 'name' => 'require|max:10',
  5. 'price' => 'number|between:1,100',
  6. 'email' => 'email'
  7. ]);

独立验证默认也是返回一条错误信息,如果要批量返回所有错误使用 batch();
$validate->batch()->check($data)
独立验支持对象化的定义方式,但不支持在属性方式的定义;
$validate = new Validate();
//$validate->rule(‘name’, ValidateRule::isRequire()->max(10));
$validate->rule([
‘name’ => ValidateRule::isRequire()->max(10),
‘price’ => ValidateRule::isNumber()->between(‘1,100’),【十天精品课堂系列】 主讲:李炎恢
‘email’ => ValidateRule::isEmail()
]);
独立验支持闭包的自定义方式,但不支持属性方式和多规则方式;
$validate = new Validate();
$validate->rule([
‘name’ => function ($value, $data) {
return $value != ‘’ ? true : ‘姓名不得为空’;
},
‘price’=> function ($value) {
return $value > 0 ? true : ‘价格不得小于零’;
}
]);

二.错误信息

上一节课,我们可以在属性定义错误信息,如果不定义讲使用默认错误信息;
如果使用默认错误信息,将字段设置描述信息,提示也会使用描述的信息;
protected $rule = [
‘name|姓名’ => ‘require|max:10’,
];
如果在控制器使用独立验证规则,也可以在控制器设置错误信息;
$validate->message([
‘name.require’ => ‘姓名不可以是空的呢’,
‘name.max’ => ‘姓名太长可不好’,
]);
你也可以直接把错误信息写入到语言包里,进行调用,语言包:lang/zh-cn.php;
‘name.require’ => ‘name_require’ //控制器设置
‘name_require’ => ‘name 不得为空’ //语言包设置

验证场景和路由认证

一.验证场景

有时,我们并不希望所有的字段都得到验证,比如修改更新时不验证邮箱;
可以在验证类 User.php 中,设置一个$scene 属性,用来限定场景验证;
protected $scene = [
‘insert’ => [‘name’, ‘price’, ‘email’],
‘edit’ => [‘name’, ‘price’],
];
上述中,insert 新增需要验证三个字段,而 edit 更新则只要验证两个字段;
在控制器端,验证时,根据不同的验证手段,绑定验证场景有下面两种方式:
$validate->scene(‘edit’)->check($data)
‘\app\validate\User.edit’
在验证类端,可以设置一个公共方法对场景的细节进行定义;
public function sceneEdit()
{
$edit = $this->only([‘name’, ‘price’]) //仅对两个字段验证
->remove(‘name’, ‘max’) //移出掉最大字段的限制
->append(‘price’, ‘require’); //增加一个不能为空的限制
return $edit;
}
注意:请不要对一个字段进行两个或以上的移出和添加,会被覆盖;
remove(‘name’, ‘xxx|yyy|zzz’)或 remove(‘name’, [‘xxx’, ‘yyy’, ‘zzz’]);
而不是
remove(‘name’, ‘xxx’)->remove(‘name’, ‘yyy’)->remove(‘name’, ‘zzz’);

二.路由验证

路由验证,即在路由的参数来调用验证类进行验证,和字段验证一模一样;
protected $rule = [
‘id’ => ‘number|between:1,10’
];
protected $message = [
‘id.between’ => ‘id 只能是 1-10 之间’,
‘id.number’ => ‘id 必须是数字’
];
Route::rule(‘read/:id’,’Verify/read’)
->validate(\app\validate\User::class, ‘eidt’);
如果不实用验证器类,也可以使用独立的验证方式,也可以使用对象化;
Route::rule(‘read/:id’,’Verify/read’)
->validate([
‘id’ => ‘number|between:1,10’,
‘email’ => \think\validate\ValidateRule::isEmail()
], ‘edit’, [
‘id.between’ => ‘id 限定在 1-10 之间’,
‘email’ => ‘邮箱格式错误’
], true);

验证静态调用和令牌

一.静态调用

静态调用,即使用 facade 模式进行调用验证,非常适合单个数据的验证;
引入 facade 中的 Validate 时,和 think\Validate 冲突,只需引入一个即可;
//验证邮箱是否合法
dump(Validate::isEmail(‘bnbbs@163.com’));
//验证是否为空
dump(Validate::isRequre(‘’));
//验证是否为数值
dump(Validate::isNumber(10));
静态调用返回的结果是 false 和 true,错误信息需要自行错误;
静态调用,也是支持多规则验证的,使用 checkRule()方法实现;
//验证数值合法性
dump(Validate::checkRule(10, ‘number|between:1,10’));
checkRule()不支持错误信息,需要自己实现,但支持对象化规则定义;
dump(Validate::checkRule(10, ValidateRule::isNumber()->between(‘1,10’)));

二.表单令牌

表单令牌就是在表单中增加一个隐藏字段,随机生成一串字符,确定不是伪造;
这种随机产生的字符和服务器的 session 进行对比,通过则是合法表单;
首先,创建一个带有令牌的表单,在 See 控制器下的 vali 方法模版实现;





为了验证系统内部的机制,可以通过打印测试出内部的构造;
//打印出生成的 token 随机数
echo Request::token();
//打印出保存到 session 的 token
echo Session::get(‘token‘);【十天精品课堂系列】 主讲:李炎恢
验证器部分,只要使用内置规则 token 即可验证,具体流程如下:
$data = [
‘user’ => input(‘post.user’),
token‘ => input(‘post.token‘),
];
$validate = new \think\Validate();
$validate->rule([
‘user’ => ‘require|token’,
]);
if (!$validate->batch()->check($data)) {
dump($validate->getError());
}

独立验证和内置规则

一.独立验证

之前,已经了解了一些独立验证的地方,系统还提供了 make 方法实现独立验证;
只不过这个方法,并且在 TP6 就废弃了,所以,简单了解一下即可;

  1. $data = [
  2. 'name' => '',
  3. 'email' => 'bnbbs163.com'
  4. ];
  5. $validate = Validate::make([
  6. 'name' => 'require|max:25',
  7. 'email' => 'email'
  8. ]);
  9. if (!$validate->batch()->check($data)) {
  10. dump($validate->getError());
  11. }

内置规则
内置的规则内容较多,并且严格区分大小写,这里按照类别一一列出;
静态方法支持两种形式,比如::number()或者 isNumber()均可;
而 require 是 PHP 保留字,那么就必须用 isRequire()或 must();
格式验证类:
‘field’ => ‘require’, //不得为空::isRequire 或::must
‘field’ => ‘number’, //是否是纯数字,非负数非小数点
‘field’ => ‘integer’, //是否是整数
‘field’ => ‘float’, //是否是浮点数
‘field’ => ‘boolean’, //是否是布尔值,或者 bool
‘field’ => ‘email’, //是否是 email
‘field’ => ‘array’, //是否是数组
‘field’ => ‘accepted’, //是否是“yes”“no”“1”这三个值
‘field’ => ‘date’, //是否是有效日期
‘field’ => ‘alpha’, //是否是纯字母
‘field’ => ‘alphaNum’, //是否是字母和数字
‘field’ => ‘alphaDash’, //是否是字母和数字以及-(下划线和破折号)
‘field’ => ‘chs’, //是否是纯汉字
‘field’ => ‘chsAlpha’, //是否是汉字字母
‘field’ => ‘chsAlphaNum’, //是否是汉字字母数字
‘field’ => ‘chsDash’, //是否是汉字字母数字以及
-(下划线和破折号)
‘field’ => ‘cntrl’, //是否是控制字符(换行、缩进、空格)
‘field’ => ‘graph’, //是否是可打印字符(空格除外)
‘field’ => ‘print’, //是否是可打印字符(包含空格)
‘field’ => ‘lower’, //是否是小写字符
‘field’ => ‘upper’, //是否是大写字符
‘field’ => ‘space’, //是否是空白字符
‘field’ => ‘xdigit’, //是否是十六进制
‘field’ => ‘activeUrl’, //是否是有效域名或 IP 地址
‘field’ => ‘url’, //是否是有效 URL 地址
‘field’ => ‘ip’, //是否是有效 IP(支持 ipv4,ipv6)
‘field’ => ‘dateFormat:Y-m-d’, //是否是指定日期格式
‘field’ => ‘mobile’, //是否是有效手机
‘field’ => ‘idCard’, //是否是有效身份证
‘field’ => ‘macAddr’, //是否是有效 MAC 地址
‘field’ => ‘zip’, //是否是有效邮编
长度和区间验证类:
‘field’ => ‘in:1,2,3’, //是否是有某个值
‘field’ => ‘notIn:1,2,3’, //是否是没有某个值
‘field’ => ‘between:1,100’, //是否是在区间中
‘field’ => ‘notBetween:1,100’, //是否是不在区间中
‘field’ => ‘length:2,20’, //是否字符长度在范围中
‘field’ => ‘length:4’, //是否字符长度匹配
‘field’ => ‘max:20’, //是否字符最大长度
‘field’ => ‘min:5’, //是否字符最小长度
//length、max、min 也可以判断数组长度和 File 文件大小
‘field’ => ‘after:2020-1-1’, //是否在指定日期之后
‘field’ => ‘before:2020-1-1’, //是否在指定日期之前
//是否在当前操作是否在某个有效期内
‘field’ => ‘expire:2019-1-1,2020-1-1’,
//验证当前请求的 IP 是否在某个范围之间,
‘field’ => ‘allowIp:221.221.78.1, 192.168.0.1’,
//验证当前请求的 IP 是否被禁用
‘field’ => ‘denyIp:221.221.78.1, 127.0.0.1’,
字段比较类:
‘field’ => ‘confirm:password’, //是否和另一个字段匹配
‘field’ => ‘differnet:password’,//是否和另一个字段不匹配
‘field’ => ‘eq:100’, //是否等于某个值,=、same 均可
‘field’ => ‘gt:100’, //是否大于某个值,支持>【十天精品课堂系列】 主讲:李炎恢
‘field’ => ‘egt:100’, //是否大于等于某个值,支持>=
‘field’ => ‘lt:100’, //是否小于某个值,支持<
‘field’ => ‘elt:100’, //是否小于等于某个值,支持<=
//比较方式也支持字段比较,比如:’field’=>’lt:price’
其它验证类:
‘field’ => ‘\d{6}’, //正则表达式验证
‘field’ => ‘regex:\d{6}’, //正则表达式验证
‘field’ => ‘file’, //判断是否是上传文件
‘field’ => ‘image:150,150,gif’, //判断图片(参数可选)
‘field’ => ‘fileExt:jpg,txt’, //判断文件允许的后缀
‘field’ => ‘fileMime:text/html’,//判断文件允许的文件类型
‘field’ => ‘fileSize:2048’, //判断文件允许的字节大小
‘field’ => ‘behavior:\app...’, //判断行为验证
‘field’ => ‘unique:user’, //验证 field 字段的值是否在 user 表
‘field’ => ‘requireWith:account’//当 account 有值时,requireWidth 必须

session

一.Session

Session 第一次调用时,会按照 config/session.php 进行初始化;
return [
// SESSION 前缀
‘prefix’ => ‘think’,
// 是否自动开启 SESSION
‘auto_start’ => true,
];
从上面截取配置内容可以了解到,名称前缀定义了 think,并且自动开启;
Session 是服务器的内容存储,而相对的客户端的是 Cookie,基础知识不再赘述;
系统也支持通过方法调用的形式进行初始化,比如::init()方法;
Session::init([
‘prefix’ => ‘tp’,
‘auto_start’ => true,
]);
所有配置参数,如下表:
image.png
直接使用::set()和::get()方法去设置 Session 的存取;
Session::set(‘user’, ‘Mr.Lee’);
echo Session::get(‘user’);
dump(Request::session());
echo $_SESSION[‘think’][‘user’];
::set()赋值中,第三个参数可以设置前缀,即 Session 作用域;
Session::set(‘user’, ‘Mr.Lee’, ‘tp’);
::get()取值时,第二参数可强调作用域,不写则默认当前作用域;
Session::get(‘user’, ‘tp’);
::has()判断当前作用域 session 是否赋值,第二参数可以指定作用域;
Session::has(‘user’);
Session::has(‘user’, ‘tp’);
::delete()删除,默认删除当前作用域的值,第二参数可以指定作用域;
Session::delete(‘user’);
Session::delete(‘user’, ‘tp’);
::prefix()方法,可以指定当前作用域;
Session::prefix(‘tp’);
::pull()方法,取出当前的值,并删除掉这个 session,不存在返回 null;
echo Session::pull(‘user’);
::clear()方法,清除当前作用域的 session,可指定作用域;
Session::clear(‘’);
Session::clear(‘think’);
::flash()方法,设置闪存数据,只请求一次有效的情况,再请求会失效;
Session::flash(‘user’,’Mr.Lee’);
::flush()方法,可以清理闪存数据的有效 session;
Session::flush();

二.二维和助手函数

二维操作,就是对象和数组的调用方式,如下:
// 赋值(当前作用域)
Session::set(‘obj.user’,’Mr.Lee’);
// 判断(当前作用域)是否赋值
Session::has(‘obj.user’);【十天精品课堂系列】 主讲:李炎恢
// 取值(当前作用域)
Session::get(‘obj.user’);
// 删除(当前作用域)
Session::delete(‘obj.user’);
助手函数,更加方便操作,如下:
//赋值
session(‘user’, ‘Mr.Wang’);
//带作用域赋值
session(‘user’, ‘Mr.Lee’, ‘tp’);
//has 判断
session(‘?user’);
//delete 删除
session(‘user’, null);
//清理
session(null);
//带作用域清理
session(null, ‘tp’);
//带作用域输出
echo session(‘user’, ‘’, ‘tp’);
//输出
echo session(‘user’);

Cookie

一.Cookie

Cookie 是客户端存储,无须手动初始化,配置文件 cookie.php 中会自行初始化;
return [
// cookie 名称前缀
‘prefix’ => ‘’,
// cookie 保存时间
‘expire’ => 0,
// cookie 保存路径
‘path’ => ‘/‘,
// 是否使用 setcookie
‘setcookie’ => true,
];
当然,也可以自行手动初始化,也可以单独设置 cookie 前缀;
Cookie::init([
‘prefix’ => ‘think‘,
‘expire’ => 3600,
‘path’ => ‘/‘
]);
Cookie::prefix(‘tp
‘);
::set()方法,创建一个最基本的 cookie,可以设置前缀、过期时间、数组等;
Cookie::set(‘user’, ‘Mr.Lee’);
Cookie::set(‘user’, ‘Mr.Lee’, 3600);
Cookie::set(‘user’, ‘Mr.Lee’, [
‘prefix’ => ‘think‘,
‘expire’ => 3600
]);
::forever()方法,永久保存 Cookie(就是十年的意思);
Cookie::forever(‘user’, ‘Mr.Lee’);【十天精品课堂系列】 主讲:李炎恢
::has()方法,判断是否赋值,第二参数可设置前缀;
Cookie::has(‘user’);
Cookie::has(‘user’,’think
‘);
::get()取值时,第二参数可设置前缀;
Cookie::get(‘user’);
Cookie::get(‘user’,’think‘);
::delete()删除,第二参数可以设置前缀;
Cookie::delete(‘user’);
Cookie::delete(‘user’, ‘tp
‘);
::clear()方法,清空 Cookie,这里必须指定前缀,否则无法清空;
Cookie::clear(‘think_’);

二.助手函数

助手函数,更加方便操作,如下:
//初始化
cookie([
‘prefix’ => ‘tp‘,
‘expire’ => 3600,
]);
//输出
echo cookie(‘user’);
//设置
cookie(‘user’, ‘Mr.Lee’, 3600);
//删除
cookie(‘user’, null);
//清除
cookie(null, ‘tp
‘);

多语音

一.配置多语言

如果要开启多语言切换功能,需要在 app.php 配置文件中开启;
// 是否开启多语言
‘langswitch_on’ => true,
默认应用目录会调用 application\lang 目录下的语言包,我们创建三个;
//错误信息,zh-cn.php
return [
‘require_name’ => ‘用户名不得为空!’,
‘email_error’ => ‘邮箱地址不正确!’,
];
//error message,en-us.php
return [
‘require_name’ => ‘The user name cannot be empty!’,
‘email_error’ => ‘Incorrect email address!’,
];
//エラーメッセージ, ja-jp.php
return [
‘require_name’ => ‘ユーザ名は空ではいけません!’,
‘email_error’ => ‘メールアドレスが間違っています!’,
];
以上三个语言包,会根据条件自动加载,如果不在指定的目录则需要::load();
Lang::load( ‘../application/common/lang/xx-xx.php’);
系统默认会指定:zh-cn 这个语言包,我们通过::get()来输出错误信息;
Lang::get(‘email_error’);
通过 URL 方式来切换语言,?lang=en-us 即可,也可以在公共文件设置 cookie;
\think\facade\Cookie::prefix(‘think
‘);
\think\facade\Cookie::set(‘var’, ‘en-us’);【十天精品课堂系列】 主讲:李炎恢
在模版中调用语言信息,可以用$Think.lang.xxx 或{:lang(‘xxx’)};
助手函数:lang(‘email_error’);
可以在公共文件,设置语言包限定列表,如下:
Lang::setAllowLangList([‘zh-cn’,’en-us’,’ja-jp’]);
当开启限定列表后,默认语言又可以设置了。

分页功能

一.分页功能

不管是数据库操作还是模型操作,都使用 paginate()方法来实现;
//查找 user 表所有数据,每页显示 5 条
$list = Db::name(‘user’)->paginate(5);
return json($list);
通过生成的数据列表,可以得到分页必须的参数变量,具体如下;
total(总条数)
per_page(每页条数)
current_page(当前页码)
last_page(最终页码)
创建一个静态模版页面,并使用{volist}标签遍历列表;









{volist name=’list’ id=’user’}







{/volist}
编号 姓名 性别 邮箱 价格
{$user.id} {$user.username} {$user.gender} {$user.email} {$user.price}

分页功能还提供了一个固定方式,实现分页按钮,只需要设置相应的 CSS 即可;
{$list|raw}【十天精品课堂系列】 主讲:李炎恢

.pagination {
list-style: none;
margin: 0;
padding: 0;
}
.pagination li {
display: inline-block;
padding: 20px;
}
也可以单独赋值分页的模版变量;
// 获取分页显示
$page = $list->render();
$this->assign(‘page’, $page);
{$page|raw}
也可以单独获取到总记录数量;
$total = $list->total();
可以限定总记录数,比如,限定总记录数只有 10 条;
->paginate(5, 10);
如果你使用模型方式分页,则可以通过获取器修改字段值,而分页本身也可以;
->each(function ($item, $key) {
$item[‘gender’] = ‘【’.$item[‘gender’].’】’;
return $item;
});
也可以设置分页的页码为简洁分页,就是没有 1,2,3,4 这种,只有上下页;
->paginate(5, true);

上传功能

一.上传功能

1如果要实现上传功能,首先需要建立一个上传表单,具体如下:

enctype=”multipart/form-data” method=”post”>



创建一个控制器 upload.php,用于处理上传文件;
class Upload
{
public function index()
{
//获取表单的上传数据
$file = Request::file(‘image’);
//移动到应用目录 uploads 下
$info = $file->move(‘../application/uploads’);
//判断上传信息
if ($info) {
//输出上传信息
echo $info->getExtension();
echo ‘
‘;
echo $info->getSaveName();
echo ‘
‘;
echo $info->getFileName();
} else {
//输出错误信息
echo $file->getError();
}
}
}
批量上传,使用 image[]作为名称,并使用 foreach()遍历上传;
enctype=”multipart/form-data” method=”post”>
【十天精品课堂系列】 主讲:李炎恢




public function uploads()
{
//获取表单的上传数据
$files = Request::file(‘image’);
foreach ($files as $file) {
//移动到应用目录 uploads 下
$info = $file->move(‘../application/uploads’);
//判断上传信息
if ($info) {
//输出上传信息
echo $info->getExtension();
echo ‘
‘;
echo $info->getSaveName();
echo ‘
‘;
echo $info->getFileName();
} else {
//输出错误信息
echo $file->getError();
}
}
}
上传文件,可以通过 validate()方法进行验证,包括大小限定、后缀限定等;
$info = $file->validate([
‘size’ => 102400,
‘ext’ => ‘jpg,gif,png’,
//‘type’ => ‘text/html’
])->move(‘../application/uploads’);
默认情况下,上传的文件是以日期和微秒生成的方式:date;
生成的规则还支持另外两种方式:md5 和 sha1;
$file->rule(‘md5’)->move(‘../application/uploads’);
也可以通过传递一个方法或函数来自定义命名,比如使用 uniqid();
$file->rule(‘uniqid’); //uniqid()产生一个微秒时间生成一个唯一的 ID
在 move()方法的,第二参数设置为空字符串,可以表示按原本的名称保存;
$file->move(‘../application/uploads’, ‘’);