为何使用html_entity_decode后HTML实体仍被存入数据库?
咱先抓核心:你看到echo出来的SQL是对的,但实际存进数据库的还是原实体,问题出在字符串被二次转义了——你手动处理后的字符串,在发送给数据库的过程中又被转了一遍,导致实体又回来了。
具体可能的原因
1. 旧PHP版本的「魔术引号」在搞鬼
如果你的PHP版本是5.4之前的,magic_quotes_gpc这个配置可能是开启的。它会自动把GET/POST/COOKIE里的单引号转成\',要是你后续又做了转义或者数据库扩展再处理一次,就会把这个转义后的字符又变成'存起来。
2. 数据库操作时的自动转义
要是你没用预处理语句,而是直接拼接SQL的时候调用了mysqli_real_escape_string这类转义函数,那即使你已经用html_entity_decode把实体转成了单引号,转义函数又会把单引号转成\',如果中间还有其他过滤逻辑(比如不小心又调用了htmlentities),就会变成实体存进库。
3. 框架/第三方库的自动过滤
如果你用了PHP框架(比如Laravel、CodeIgniter),有些框架默认会对输入做XSS过滤或者自动转义,可能在你处理完字符串之后,又偷偷给你转成HTML实体了。
解决办法,一步一步来
先关魔术引号(旧版本PHP才需要)
打开php.ini找magic_quotes_gpc,改成Off。要是改不了php.ini,就在代码开头加这段:if (get_magic_quotes_gpc()) { $string = stripslashes($string); }注:PHP5.4及以后已经删掉这个功能了,新版本忽略就行。
用预处理语句(强烈推荐,还防SQL注入)
别再手动拼SQL了,用PDO或者mysqli的预处理语句,数据库会自动处理参数转义,根本不会出现这种问题:// PDO示例 $pdo = new PDO('mysql:host=localhost;dbname=你的数据库名;charset=utf8mb4', '用户名', '密码'); $stmt = $pdo->prepare("INSERT INTO database SET name = :name"); $stmt->bindParam(':name', $string); $stmt->execute();排查有没有二次转义的代码
检查你的代码里,是不是在html_entity_decode之后,又调用了htmlspecialchars或者htmlentities这类函数?要是有的话,把多余的编码去掉就行。确认数据库连接的字符集
确保你的数据库连接用的是utf8mb4这类正确的字符集,避免字符转换出问题:// mysqli示例设置字符集 $mysqli = new mysqli('localhost', '用户名', '密码', '你的数据库名'); $mysqli->set_charset('utf8mb4');
哦对了,为啥phpMyAdmin手动执行就没问题?因为phpMyAdmin是直接把你输入的SQL发给数据库,没经过PHP的那些自动转义或者过滤逻辑,所以能正确存进去。
内容的提问来源于stack exchange,提问作者user1203497

