HTML中单引号的转义符

就在今天(2016-11-12),一个正常运行了几个月的Web管理系统,突然出现了一个BUG,而且只针对个别用户,其他用户使用正常;排查了一下,发现问题就出在HTML的转义符上。

HTML的转义符

就算没查看过HTML的符号表,HTML写的多了,也自然会知道空格的转义符是 。其他一些常用的转义符还有:

显示 转义实体名称 转义实体编号
&qout; "
& & &
< < <
> > >

有些符号没有转义的实体名称,但一定有转义的实体编号,可以查看HTML ASCII 参考手册
这里先说明单引号'的转义符是&#39;, 下面再来陈述今天遇到的BUG。

还原现场

PHP开发者经常会把一些数据保存在HTML页面上,方便JS开发者调用,比如说,把用户的一些个人信息记录在页面上:

1
2
3
4
5
6
7
8
9
10
11
//php
<?php
$user = array(
"id" => 1
"name" => "坂本",
"work" => "贵干",
"addr" => "不详'",
);
?>
//html
<button class="btn user-btn" data-user='<?php echo json_encode($user); ?>' >查看信息</button>

这里可以看到,button标签的class的值用双引号包合,而data-user的值用单引号包合,因为PHP中的json_encode函数会产生双引号,如果data-user的值用双引号包合,碰上json_encode函数产生的双引号,就会提前闭合,保存的值就失真了。

但是,另一个问题也随之而来:数组useraddr属性的值是"不详'",这个值由用户填写,不知道有意还是无意,值中带有一个单引号,于是data-user的值还是被提前闭合而失真了。

这该怎么解决呢?

正则替换

当然是用正则匹配替换的方法啊!
一开始我以为单引号'的转义符是\',毕竟类C语言都是这样子,于是就写了一个正则替换,把'(不包括\')换成\'

1
preg_replace('/([^\\])\'/', '$1\\\'', json_encode($user));

运行一下,报错了。可能是因为优先级限制,'/[^\\]\'/'中先转义了],于是就报出中括号没闭合的错误。那就加上个括号(),区分一下:

1
preg_replace('/([^(\\)])\'/', '$1\\\'', json_encode($user));

哈,替换是成功了,可是\'中还是带有'data-user的值还是被提前闭合了……

解决办法

网上查了一下,才知道HTML中'的转义符根本不是\',而是&#39;,于是,最后的解决办法就是:

1
preg_replace('/\'/', '&#39;', json_encode($user));

把数据直接保存在页面标签的data属性上,这种实现方式经常会用到的吧,但是要注意转义一下单引号或者双引号:

1
preg_replace(array('/\'/', '/"/'), array('&#39;', '&#34;'), json_encode($user));

最后两句

最后说两句,只要是BUG,一定能百分百重现;BUG不针对谁,BUG针对所有人。