回调函数必须定义在控制器中,因CodeIgniter的form_validation仅在当前控制器查找callback_开头的public方法;若放模型或库中会静默失效,且跨域调用、HMVC模块未绑定实例均导致method_exists返回false。

回调函数必须定义在控制器里,不能放模型或库中
CodeIgniter 的 form_validation 类只会在当前控制器实例中查找以 callback_ 开头的 public 方法。如果你把回调写在模型、自定义库或独立 PHP 文件里,set_rules('email', '邮箱', 'callback_check_email') 会静默失效——既不报错,也不执行逻辑,run() 可能返回 TRUE,但实际没校验。
常见错误包括:
- 在模型中定义
public function check_email($str),然后在控制器里写callback_check_email→ 找不到方法 - 用
get_instance()->check_email($str)试图跨作用域调用 → 破坏生命周期,$this->db等可能为null - 在 HMVC 模块的库中写回调,但没重载
CI_Form_validation或绑定$ci实例 →method_exists($this, 'callback_xxx')返回false
回调签名和参数传递必须严格匹配框架解析规则
Form_validation 解析规则字符串(如 'required|callback_player_check[p1,p2]')时,会按固定格式拆解:callback_ 后是方法名,方括号内是**字符串形式的参数**,且只支持一个参数(即使写成 [a,b,c],整个也会作为单个字符串传入)。
所以正确写法是:
$this->form_validation->set_rules('p1', '球员1', 'callback_player_conflict[p2,p3,p4,p5]');- 对应回调:
public function callback_player_conflict($str, $other_fields_str) - 在回调里用
explode(',', $other_fields_str)拆出字段名,再用$this->input->post($field)获取值
别指望传数组或对象——框架底层只做字符串切分,多参数需手动解析或改用 JSON 字符串(如 [{"field":"p2","msg":"已选"}])。
set_message() 必须在 return FALSE 前调用,且键名要和规则中 callback_ 后一致
这是最常被忽略的细节。form_validation 只有在回调返回 FALSE **且** 已通过 set_message() 注册了对应键的消息时,才会把错误渲染进 validation_errors()。
错误示范:
-
$this->form_validation->set_message('player_conflict', '该球员已被选择');写在回调开头 → 消息注册了,但后续逻辑可能 return TRUE,消息不触发 -
set_message('callback_player_conflict', ...)→ 键名多了callback_,不匹配规则中的player_conflict,消息丢弃 - 在回调里调用
redirect()或exit→ 验证流程中断,run()不会走到错误收集阶段
正确顺序只有一条路径:if (冲突) { $this->form_validation->set_message('player_conflict', '提示'); return FALSE; } else { return TRUE; }
跨字段校验要用 $this->input->post(),但别重复取当前字段值
回调函数的第一个参数 $str 就是当前字段经过 trim、xss_clean 等前置规则处理后的值,**不要**再用 $this->input->post('p1') 重取——它可能未过滤,也可能因 POST 数据被清空而返回空。
但校验“p1 是否等于 p2”这类跨字段逻辑时,必须用 $this->input->post() 获取其他字段:
-
$p1 = $str;(当前字段,已过滤) -
$p2 = $this->input->post('p2');(其他字段,未过滤,需自行trim()或验证) - 比较前建议统一处理:
if (trim($p1) === trim($p2)) { ... }
注意:如果其他字段也参与验证(如 p2 有 required 规则),那它们的值一定存在;但如果没设规则,$this->input->post('p2') 可能为 null 或空字符串,需判空。


















