记一CI框架的$_POST编码问题
最近在做支付回调接口时,碰到一个编码问题,由于第三方支付平台异步通知post方式请求使用的是GBK编码,而我们项目服务端使用UTF-8编码开发,所以在做signature验证时,我们需要将post的数据进行编码转换
iconv("GBK//IGNORE", 'UTF-8', $_POST[$key]);
然而转换的结果竟然和原文并不一致,导致MD5后的签名和第三方通知过来的签名不匹配。于是决定脱离CI框架写一个简单的demo来获取第三方平台通知的数据内容是否正确,发现在原生php下,第三方平台post过来的数据是正常的,而在CI框架下,$_POST获取到数据的汉字编码字符却和原生PHP下$_POST获得的字符不一致。为了解决问题,尝试使用
$data = file_get_contents("php://input");
来获取原始数据,简单的将方法进行封装
function get_input_value() { static $content = null; if ($content === null) { parse_str(file_get_contents("php://input"), $content); } return $content; } function get_input($index) { $array = get_input_value(); if (isset($array[$index])) { return $array[$index]; } else { return null; } }
这样通过get_input('xxx')就能获取到post的数据,在CI框架中用这种方法代替$_POST获取post数据后,中文字符竟然被正确编码了。于是决定敲开CI的源码一探究竟。
在CI核心源码中搜索$_POST,果然不出所料的找到了线索,原来CI在加载CI_Input时对$_POST进行了一次编码,约在core/input.php的697行可见
// Clean UTF-8 if supported if (UTF8_ENABLED === TRUE) { $str = $this->uni->clean_string($str); }
继续深入到clean_string函数,在core/Utf8.php文件中可见
public function clean_string($str) { if ($this->is_ascii($str) === FALSE) { if (MB_ENABLED) { $str = mb_convert_encoding($str, 'UTF-8', 'UTF-8'); } elseif (ICONV_ENABLED) { $str = @iconv('UTF-8', 'UTF-8//IGNORE', $str); } } return $str; }
可以看到,当支持iconv时,会使用iconv函数对数据进行编码转换,UTF-8//IGNORE,会将遇到无法转换成UTF8编码的字符忽略。于是出现了$_POST到GBK数据时,字符缺失的现象,导致结果不准确。
故事到这里终于了却一桩心事。
码字很辛苦,转载请注明来自雨林寒舍的《记一CI框架的$_POST编码问题》
2016-12-22
编程语言
需要向博主学习的地方还有很多,很多,很多……