模型验证不拦截字段,$visible、$hidden、allowField 才是字段白名单控制点;$visible 仅导出时裁剪输出,allowField 防写入需显式调用,验证器需配合_scene和validateScene(),控制器层须用input(['key'])主动截断。

模型验证本身不拦截字段,$visible、$hidden、allowField 这些才是真正的字段白名单控制点,它们作用时机和目标完全不同,混用或误以为“验证器能挡字段”是绝大多数人踩坑的起点。
模型转数组时只返回指定字段:用 $visible 声明式白名单
$visible 是模型类里最轻量的字段过滤机制,只在调用 toArray()、toJson() 等导出方法时生效,不影响查询 SQL。它不是“验证”,而是“输出裁剪”。
- 必须声明为
protected $visible = ['id', 'title', 'status'];,写成public或漏掉protected修饰符直接失效 - 字段名须与数据库列名完全一致(区分大小写),比如数据库是
user_name,就不能写成userName - 访问器字段(如定义了
getFullnameAttr)默认不受$visible控制,会照常出现在结果里;想让它受控,得改用append(['fullname'])显式追加 - 动态覆盖可用
$model->visible(['id', 'title'])->toJson(),但注意:这个调用只对当前链式操作生效,不影响后续调用
更新数据时只允许修改特定字段:allowField 必须显式传参
allowField 是防止字段被意外写入的核心防线,但它不会自动启用——你必须在 save() 或 update() 前主动调用,否则所有字段都可能被覆盖。
- 正确写法:
$model->allowField(['name', 'status'])->save($data);错误写法:$model->save($data)(没设白名单) -
allowField只对模型类有效,Db::name('user')->update($data)完全不认这个配置 - 字段名必须和数据库列名一致,JSON 字段、别名、虚拟属性不会被自动识别,需手动列入
- 配合
_strict = true可提前报错(如字段拼错),比静默覆盖更利于排查,但前提是字段列表写全了
验证器里字段白名单靠 _scene 配置 + 显式调用
_scene 不是开关,它只是个字段名列表,必须配合 validateScene() 或带 scene 参数的 validate() 才起效。直接调用 validate() 默认走 default 场景,_scene 配置形同虚设。
立即学习“PHP免费学习笔记(深入)”;
-
_scene = ['login' => ['username', 'password']]中的字段名必须和$rule键名完全一致(包括下划线、大小写) - 不能用
input('scene')动态取值传给validate(),否则可能绕过校验;应按接口硬编码,如登录接口固定写$user->validateScene('login') - 场景规则必须提前在
$rule里定义好,_scene只负责筛选,不补充规则。比如register场景需要唯一性检查,就得在$rule['username']里提前加上unique - 验证器不自动过滤数据,
$visible和allowField对验证器无影响;字段是否被接收,取决于你传给验证器的$data数组本身
控制器层字段白名单:用 input(['key1', 'key2']) 主动截断
验证器不拦字段,input() 无参调用会返回整个 $_GET 或 $_POST,这才是参数污染的源头。真正可控的白名单必须在控制器里做第一道截断。
- 别用
input('')或input(),而要用input(['id', 'name', 'status'])显式提取 - 若需兼容旧逻辑,可用
array_intersect_key($data, array_flip(['id', 'name']))强制过滤键名 -
input('id', '', 'intval')的第三个参数只处理值,不控制键名合法性;防 XSS 有用,防字段注入没用 - 路径式写法如
input('user.name')是嵌套提取语法,不是白名单机制,别混淆
字段白名单不是一层防护,而是分层动作:控制器截输入、验证器筛字段、模型控输出、更新时锁写入。最容易被忽略的是——这些机制全都不自动联动,每个环节都得手动配、手动调、手动传参。



















