SQL预处理已经不是什么新鲜的东西了,它可以很好的防sql注入。而ThinkPHP直到3.1中才在其orm里增加对其的支持。相关介绍在手册里的《14.3 防止SQL注入》这一节。
使用环境
where(), query(), execute()
使用方式
- 变长参数
1 | "id=%d and username='%s' and xx='%f'",$id,$username,$xx |
- 数组参数
1 | "id=%d and username='%s' and xx='%f'",array($id,$username,$xx) |
实现
接下来以query()在3.0及3.1的实现来看一下:
ThinkPHP 3.0(Lib\Core\Model.class.php):
1 | public function query($sql,$parse=false) { |
ThinkPHP 3.1(Lib\Core\Model.class.php):
1 | public function query($sql,$parse=false) { |
通过对比可以发现有两处变化:
- query()
添加了对$parse参数的判断,如果$parse不是布尔型且不是数据,那么肯定就是实现中的第一种方式(变长参数),这时用func_get_args接收参数并用array_shift把这些参数拼成一个数组。
由此可见变长参数的内部实现也是转换成数组方式,所以建议使用数组参数方式(少了两次函数的开销)
- parseSql()
1 | if(is_array($parse)) $sql = vsprintf($sql,$parse); |
如果$parse是数组,用vsprintf()函数来完成$sql的预处理。
vsprintf()是什么呢?
它在手册里的定义是”把格式化字符串写入变量中”,即本例中的%d,%s和%f替换成$id,$username和$xx,这些以%开头转换字符如下:
%% - 返回百分比符号
%b - 二进制数
%c - 依照 ASCII 值的字符
%d - 带符号十进制数
%e - 可续计数法(比如 1.5e+3)
%u - 无符号十进制数
%f - 浮点数(local settings aware)
%F - 浮点数(not local settings aware)
%o - 八进制数
%s - 字符串
%x - 十六进制数(小写字母)
%X - 十六进制数(大写字母)