mysqli_set_charset和SET NAMES使用抉择及优劣分析


Posted in PHP onJanuary 13, 2013

最近公司组织了个PHP安全编程的培训, 其中涉及到一部分关于Mysql的”SET NAMES”和mysql_set_charset (mysqli_set_charset)的内容:
说到, 尽量使用mysqli_set_charset(mysqli:set_charset)而不是”SET NAMES”, 当然, 这个内容在PHP手册中也有叙及, 但是却没有解释为什么.

最近有好几个朋友问我这个问题, 到底为什么?
问的人多了, 我也就觉得可以写篇blog, 专门介绍下这部分的内容了.
首先, 很多人都不知道”SET NAMES”到底是做了什么,
我之前的文章深入MySQL字符集设置中, 曾经介绍过character_set_client/character_set_connection/character_set_results这三个MySQL的”环境变量”, 这里再简单介绍下,
这三个变量, 分别告诉MySQL服务器, 客户端的编码集, 在传输给MySQL服务器的时候的编码集, 以及期望MySQL返回的结果的编码集.
比如, 通过使用”SET NAMES utf8″, 就告诉服务器, 我用的是utf-8编码, 我希望你也给我返回utf-8编码的查询结果.

一般情况下, 使用”SET NAMES”就足够了, 也是可以保证正确的. 那么为什么手册又要说推荐使用mysqli_set_charset(PHP>=5.0.5)呢?
首先, 我们看看mysqli_set_charset到底做了什么(注意星号注释处, mysql_set_charset类似):

//php-5.2.11-SRC/ext/mysqli/mysqli_nonapi.c line 342 
PHP_FUNCTION(mysqli_set_charset) 
{ 
MY_MYSQL*mysql; 
zval*mysql_link; 
char *cs_name = NULL; 
unsigned int len; 
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis() 
, "Os", &mysql_link, mysqli_link_class_entry, &cs_name, &len) == FAILURE) { 
return; 
} 
MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL*, &mysql_link, "mysqli_link" 
, MYSQLI_STATUS_VALID); 
if (mysql_set_character_set(mysql->mysql, cs_name)) { 
//** 调用libmysql的对应函数 
RETURN_FALSE; 
} 
RETURN_TRUE; 
}

那mysql_set_character_set又做了什么呢?
//mysql-5.1.30-SRC/libmysql/client.c, line 3166: 
int STDCALLmysql_set_character_set(MYSQL*mysql, const char *cs_name) 
{ 
structcharset_info_st *cs; 
const char *save_csdir= charsets_dir; 
if (mysql->options.charset_dir) 
charsets_dir= mysql->options.charset_dir; 
if (strlen(cs_name) < MY_CS_NAME_SIZE && 
(cs= get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0)))) 
{ 
char buff[MY_CS_NAME_SIZE + 10]; 
charsets_dir= save_csdir; 
/* Skip execution of "SET NAMES" for pre-4.1 servers */ 
if (mysql_get_server_version(mysql) < 40100) 
return 0; 
sprintf(buff, "SET NAMES %s", cs_name); 
if (!mysql_real_query(mysql, buff, strlen(buff))) 
{ 
mysql->charset= cs; 
} 
} 
//以下省略

我们可以看到, mysqli_set_charset除了做了”SET NAMES”以外, 还多做了一步:
sprintf(buff, "SET NAMES %s", cs_name); 
if (!mysql_real_query(mysql, buff, strlen(buff))) 
{ 
mysql->charset= cs; 
}

而对于mysql这个核心结构的成员charset又有什么作用呢?
这就要说说mysql_real_escape_string()了, 这个函数和mysql_escape_string的区别就是, 它会考虑”当前”字符集. 那么这个当前字符集从哪里来呢?
对了, 你猜的没错, 就是mysql->charset.
mysql_real_string在判断宽字符集的字符的时候, 就根据这个成员变量来分别采用不同的策略, 比如如果是utf-8, 那么就会采用libmysql/ctype-utf8.c.
看个实例, 默认mysql连接字符集是latin-1, (经典的5c问题):
<?php 
$db = mysql_connect('localhost:3737', 'root' ,'123456'); 
mysql_select_db("test"); 
$a = "\x91\x5c";//"?\"的gbk编码, 低字节为5c, 也就是ascii中的"\" 
var_dump(addslashes($a)); 
var_dump(mysql_real_escape_string($a, $db)); 
mysql_query("set names gbk"); 
var_dump(mysql_real_escape_string($a, $db)); 
mysql_set_charset("gbk"); 
var_dump(mysql_real_escape_string($a, $db)); 
?>

因为, “?\”的gbk编码低字节为5c, 也就是ascii中的”\”, 而因为除了mysql(i)_set_charset影响mysql->charset以外, 其他时刻mysql->charset都为默认值, 所以, 结果就是:
$ php -f 5c.php 
string(3) "?\\" 
string(3) "?\\" 
string(3) "?\\" 
string(2) "?\"大家现在很清楚了吧?
PHP 相关文章推荐
php adodb连接不同数据库
Mar 19 PHP
php实现简单洗牌算法
Jun 18 PHP
深入解析php中的foreach问题
Jun 30 PHP
thinkphp路由规则使用示例详解和伪静态功能实现(apache重写)
Feb 24 PHP
PHP中生成UUID自定义函数分享
Jun 10 PHP
PHP中的switch语句的用法实例详解
Oct 21 PHP
php简单截取字符串代码示例
Oct 19 PHP
PHP正则删除HTML代码中宽高样式的方法
Jun 12 PHP
thinkPHP中钩子的使用方法实例分析
Nov 16 PHP
自写的利用PDO对mysql数据库增删改查操作类
Feb 19 PHP
ThinkPHP框架中使用Memcached缓存数据的方法
Mar 31 PHP
Docker 安装 PHP并与Nginx的部署实例讲解
Feb 27 PHP
PHP读取xml方法介绍
Jan 12 #PHP
用PHP编写和读取XML的几种方式
Jan 12 #PHP
php图片的裁剪与缩放生成符合需求的缩略图
Jan 11 #PHP
浏览器预览PHP文件时顶部出现空白影响布局分析原因及解决办法
Jan 11 #PHP
php判断上传的Excel文件中是否有图片及PHPExcel库认识
Jan 11 #PHP
PHP中header和session_start前不能有输出原因分析
Jan 11 #PHP
PHP跨时区(UTC时间)应用解决方案
Jan 11 #PHP
You might like
PHPlet在Windows下的安装
2006/10/09 PHP
linux下使用ThinkPHP需要注意大小写导致的问题
2011/08/02 PHP
Laravel-添加后台模板AdminLte的实现方法
2019/10/08 PHP
javascript实现 在光标处插入指定内容
2007/05/25 Javascript
js中split函数的使用方法说明
2013/12/26 Javascript
JavaScript极简入门教程(二):对象和函数
2014/10/25 Javascript
jQuery+PHP实现动态数字展示特效
2015/03/14 Javascript
JavaScript控制网页平滑滚动到指定元素位置的方法
2015/04/17 Javascript
再次谈论React.js实现原生js拖拽效果引起的一系列问题
2016/04/03 Javascript
JavaScript表单验证开发
2016/11/23 Javascript
微信小程序项目实践之九宫格实现及item跳转功能
2018/07/19 Javascript
Bootbox将后台JSON数据填充Form表单的实例代码
2018/09/10 Javascript
vue  自定义组件实现通讯录功能
2018/09/30 Javascript
详解Vue iview IE浏览器不兼容报错(Iview Bable polyfill)
2019/01/07 Javascript
layUI的验证码功能及校验实例
2019/10/25 Javascript
ant-design-vue中tree增删改的操作方法
2020/11/03 Javascript
Vant 中的Toast设置全局的延迟时间操作
2020/11/04 Javascript
30分钟搭建Python的Flask框架并在上面编写第一个应用
2015/03/30 Python
用Python实现随机森林算法的示例
2017/08/24 Python
Python字符串的全排列算法实例详解
2019/01/07 Python
linux查找当前python解释器的位置方法
2019/02/20 Python
selenium+python自动化测试环境搭建步骤
2019/06/03 Python
pandas计数 value_counts()的使用
2019/06/24 Python
Pandas时间序列:重采样及频率转换方式
2019/12/26 Python
django自定义非主键自增字段类型详解(auto increment field)
2020/03/30 Python
TensorFlow实现模型断点训练,checkpoint模型载入方式
2020/05/26 Python
Python grequests模块使用场景及代码实例
2020/08/10 Python
美国女孩服装购物网站:Justice
2017/03/04 全球购物
管理部部长岗位职责
2013/12/05 职场文书
手机被没收检讨书
2014/02/22 职场文书
小学生我的梦想演讲稿
2014/08/21 职场文书
合法的离婚协议书范本
2014/10/23 职场文书
2014会计年终工作总结
2014/12/20 职场文书
2015学习委员工作总结范文
2015/04/03 职场文书
男方家长婚礼答谢词
2015/09/29 职场文书
人力资源部工作计划
2019/05/14 职场文书