在php代码里写sql句子,我喜欢用sprintf,这样看起来比较清晰,但是今天就遇到了一个隐藏很深的bug。
有这样一段代码:
public function update_catogery_ad($id, array $info){/*{{{*/
if (!$id || !$info){
return false;
}
$sql = null;
foreach($info as $a){
list($type, $key, $val) = $a;
switch($type){
case 'int':
$val = intval($val);
break;
case 'string':
$val = "'"._es($val)."'";
break;
}
$sql .= sprintf(" `%s` = $val,", _es($key)); #1
}
$sql = substr($sql, 0, -1);
$sql = sprintf("update %s set $sql where id = %d", $this->_get_catogery_ad(), intval($id)); #2
return $this->db->update($sql);
}/*}}}*/
它执行的是,根据传入的info数组中的字段,更新某表。在测试环境,运行的很好,但是上线之后,有同事就反映无法更新数据,也不报错。
出问题的info数组是:
$info = array(
‘a’ => ‘bbbb’,
‘c’ => ‘<area target=”_blank” alt=”" href=”http%3A%2F%2Fitry.try8.info%2Ftaobao%2Ftry%2F%3Fcatogery%3D%E4%BC%98%E8%B4%A8%E8%89%AF%E5%93%81″ coords=”0,100,180,170″ shape=”rect”>’,
);
这里字段a可以被正常更新,但是字段c就维持原样。
仔细观察这段数据,可以看到字段c的value中含有%!在sprintf里,它会被当作特殊字符,于是第一条sql句子会报warning:Too few arguments,由于页面上display_errors关闭了,所以没有看到任何提示,这段代码会默默的执行下去,完成整个功能,看起来就好像c字段被忽略掉一样。
于是动手修改第一条sql句子为:$sql .= sprintf(” `%s` = %s,”, _es($key), $val);
再次运行,直接报sql句子为空!这时发现第二条sql句子也有同样的问题。于是将第二条句子修改为:$sql = sprintf(“update %s set %s where id = %d”, $this->_get_catogery_ad(), $sql, intval($id));
这些人为的bug会隐藏的很深!得多加小心,所以,编程习惯很重要!在使用sprintf的时候,如果变量中可能带有特殊字符,则一定要放在arg里,不能直接写入format里。
Leave a Reply