就在今天(2016-11-12),一个正常运行了几个月的Web管理系统,突然出现了一个BUG,而且只针对个别用户,其他用户使用正常;排查了一下,发现问题就出在HTML的转义符上。
HTML的转义符
就算没查看过HTML的符号表,HTML写的多了,也自然会知道空格的转义符是
。其他一些常用的转义符还有:
显示 | 转义实体名称 | 转义实体编号 |
---|---|---|
“ | &qout; | " |
& | & | & |
< | < | < |
> | > | > |
… | … | … |
有些符号没有转义的实体名称,但一定有转义的实体编号,可以查看HTML ASCII 参考手册。
这里先说明单引号'
的转义符是'
, 下面再来陈述今天遇到的BUG。
还原现场
PHP开发者经常会把一些数据保存在HTML页面上,方便JS开发者调用,比如说,把用户的一些个人信息记录在页面上:
1 | //php |
这里可以看到,button
标签的class
的值用双引号包合,而data-user
的值用单引号包合,因为PHP中的json_encode
函数会产生双引号,如果data-user
的值用双引号包合,碰上json_encode
函数产生的双引号,就会提前闭合,保存的值就失真了。
但是,另一个问题也随之而来:数组user
的addr
属性的值是"不详'"
,这个值由用户填写,不知道有意还是无意,值中带有一个单引号,于是data-user
的值还是被提前闭合而失真了。
这该怎么解决呢?
正则替换
当然是用正则匹配替换的方法啊!
一开始我以为单引号'
的转义符是\'
,毕竟类C语言都是这样子,于是就写了一个正则替换,把'
(不包括\'
)换成\'
:
1 | preg_replace('/([^\\])\'/', '$1\\\'', json_encode($user)); |
运行一下,报错了。可能是因为优先级限制,'/[^\\]\'/'
中先转义了]
,于是就报出中括号没闭合的错误。那就加上个括号()
,区分一下:
1 | preg_replace('/([^(\\)])\'/', '$1\\\'', json_encode($user)); |
哈,替换是成功了,可是\'
中还是带有'
,data-user
的值还是被提前闭合了……
解决办法
网上查了一下,才知道HTML中'
的转义符根本不是\'
,而是'
,于是,最后的解决办法就是:
1 | preg_replace('/\'/', ''', json_encode($user)); |
把数据直接保存在页面标签的data
属性上,这种实现方式经常会用到的吧,但是要注意转义一下单引号或者双引号:
1 | preg_replace(array('/\'/', '/"/'), array(''', '"'), json_encode($user)); |
最后两句
最后说两句,只要是BUG,一定能百分百重现;BUG不针对谁,BUG针对所有人。