说坑有点过分,更多的是自己写代码的时候,没有考虑全面,导致自己踩坑了。
案例复现
$source='CSDN科技';$source=rtrim($source,"】");
发现经过
rtrim
处理的字符串无法入库,检查后发现数据库报错信息:
General error: 1366 Incorrect string value: ‘xxxxxxx’ for column xxxxx
其实看到这个报错,就能立马意识到问题所在:字符串被不正确的截取,导致数据库驱动无法识别为正确的UTF8编码的文本。
而导致这问题的根本原因是
rtrim
以及一系列的方法是二进制安全的。
我们先把最后一个字符
技
和
】
的字节码打印出来:
var_dump(unpack('C*','技'));var_dump(unpack('C*','】'));//结果如下array(3){[1]=>int(230)[2]=>int(138)[3]=>int(128)}//技array(3){[1]=>int(227)[2]=>int(128)[3]=>int(145)}//】
其中的端倪:
技
的最后一个字节和
】
的第二个字节相同。
trim
方法是二进制安全的,会把
】
当做三个
字符
去递归处理原始文本,导致
技
的最后一个字节被截取,只剩两个字节,已经不是原来的
技
了,无法识别。
也就是说,只要原始字符串的尾部字节(包括递归处理后)包含在
trim
的第二个参数内,都会被依次递归处理。
下面这段代码跟我们最开始写的那段没有本质区别。
$source='anlige';$source=rtrim($source,"aeb");
解决方案
封装三个
mb
开头方法,去处理多字节编码的文本,逻辑比较暴力,代码有待优化。
注意:封装的
$char
参数是单个字符,有兴趣也可以修改成递归处理多个字符。
if(!function_exists('mb_ltrim')){functionmb_ltrim($str,$char){if(empty($str))return'';while(mb_substr($str,0,1)==$char){$str=mb_substr($str,1);}return$str;}}if(!function_exists('mb_rtrim')){functionmb_rtrim($str,$char){if(empty($str))return'';while(mb_substr($str,-1,1)==$char){$str=mb_substr($str,0,-1);}return$str;}}if(!function_exists('mb_trim')){functionmb_trim($str,$char){returnmb_rtrim(mb_ltrim($str,$char),$char);}}
总结
1、PHP大部分方法都是二进制安全的,因此在使用的时候要注意。
2、并不是二进制安全的方法都会遇到这个问题,主要是
trim
系列的特殊处理方式:根据第二个参数传递的值,递归处理首尾字符。
3、遇到问题不可怕,能迅速想到问题的来源和解决方案才最重要的。
版权归原作者 Anlige 所有, 如有侵权,请联系我们删除。