在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