最近公司组织了个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类似):

  1. //php-5.2.11-SRC/ext/mysqli/mysqli_nonapi.c line 342
  2. PHP_FUNCTION(mysqli_set_charset)
  3. {
  4. MY_MYSQL *mysql;
  5. zval *mysql_link;
  6. char *cs_name = NULL;
  7. unsigned int len;
  8. if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis()
  9. , "Os", &mysql_link, mysqli_link_class_entry, &cs_name, &len) == FAILURE)
  10. {        return;    }
  11. MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL*, &mysql_link, "mysqli_link" , MYSQLI_STATUS_VALID);
  12. if (mysql_set_character_set(mysql->mysql, cs_name))
  13. {
  14. //** 调用libmysql的对应函数
  15. RETURN_FALSE;
  16. }
  17. RETURN_TRUE;
  18. }

那mysql_set_character_set又做了什么呢?

  1. //mysql-5.1.30-SRC/libmysql/client.c, line 3166:
  2. int STDCALL mysql_set_character_set(MYSQL *mysql, const char *cs_name)
  3. {
  4. struct charset_info_st *cs;
  5. const char *save_csdir= charsets_dir;
  6. if (mysql->options.charset_dir)
  7. charsets_dir= mysql->options.charset_dir;
  8. if (strlen(cs_name) < MY_CS_NAME_SIZE &&
  9. (cs= get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0))))
  10. {
  11. char buff[MY_CS_NAME_SIZE + 10];
  12. charsets_dir= save_csdir;
  13. /* Skip execution of "SET NAMES" for pre-4.1 servers */
  14. if (mysql_get_server_version(mysql) < 40100)
  15. return 0;
  16. sprintf(buff, "SET NAMES %s", cs_name);
  17. if (!mysql_real_query(mysql, buff, strlen(buff)))
  18. {
  19. mysql->charset= cs;
  20. }
  21. }  //以下省略

我们可以看到, mysqli_set_charset除了做了”SET NAMES”以外, 还多做了一步:

  1. sprintf(buff, "SET NAMES %s", cs_name);
  2. if (!mysql_real_query(mysql, buff, strlen(buff)))
  3. {
  4. mysql->charset= cs;
  5. }

而对于mysql这个核心结构的成员charset又有什么作用呢?

这就要说说mysql_real_escape_string()了, 这个函数和mysql_escape_string的区别就是, 它会考虑”当前”字符集. 那么这个当前字符集从哪里来呢?

对了, 你猜的没错, 就是mysql->charset.

mysql_real_string在判断宽字符集的字符的时候, 就根据这个成员变量来分别采用不同的策略, 比如如果是utf-8, 那么就会采用libmysql/ctype-utf8.c.

看个实例, 默认mysql连接字符集是latin-1, (经典的5c问题):

  1. <?php
  2. $db = mysql_connect('localhost:3737', 'root' ,'123456');
  3. mysql_select_db("test");
  4. $a = "\x91\x5c";//"慭"的gbk编码, 低字节为5c, 也就是ascii中的"\"
  5. var_dump(addslashes($a));
  6. var_dump(mysql_real_escape_string($a, $db));
  7. mysql_query("set names gbk");
  8. var_dump(mysql_real_escape_string($a, $db));
  9. mysql_set_charset("gbk");
  10. var_dump(mysql_real_escape_string($a, $db));
  11. ?>

因为, “慭”的gbk编码低字节为5c, 也就是ascii中的”\”, 而因为除了mysql(i)_set_charset影响mysql->charset以外, 其他时刻mysql->charset都为默认值, 所以, 结果就是:

  1. $ php -f 5c.php
  2. string(3) "慭\"
  3. string(3) "慭\"
  4. string(3) "慭\"
  5. string(2) "慭

大家现在很清楚了吧?

转载于:https://blog.51cto.com/jiedushi/482071

深入理解SET NAMES和mysql(i)_set_charset的区别相关推荐

  1. orcle与mysql的区别_Orcle与MySQL的SQL语法区别:

    Orcle与MySQL的SQL语法区别: 1.在Oracle中用 select * from all_users显示所有的用户,而在MySQL中显示所有数据 库的命令是 show databases ...

  2. mysql库与oracle库的区别_开源数据库Oracle与MySQL的SQL语法区别

    Oracle数据库与MySQL数据库的区别是本文我们主要要介绍的内容,接下来我们就开始介绍这部分内容,希望能够对您有所帮助. Oracle与MySQL的SQL语法区别: 1.在Oracle中用sele ...

  3. mssql和mysql那个好_mssql与mysql的有什么区别?哪个更好用?

    mssql与mysql的有什么区别?哪个更好用?MySQL可以说是MSSQL的简化版本.理念相同,但MySQL的实现比MSSQL的需求低.MySQL是一个免费的.开放源代码的SQL数据库,所以免费的M ...

  4. mysql declare 赋值_sql server和mysql变量赋值的区别 以及 MySql Declare(转)

    sql server和mysql都是我们经常用到的数据库系统,下面就为您介绍sql server和mysql变量赋值的区别,希望对您能有所启迪. sql server中变量要先申明后赋值: 局部变量用 ...

  5. mysql版本 时间_【MySQL】MySQL版本时间线和MySQL各版本的区别

    MySQL各版本的区别 https://yq.aliyun.com/articles/607474 http://blog.sina.com.cn/s/blog_62b37bfe0101he5t.ht ...

  6. MySQL与Oracle的区别

    MySQL与Oracle的区别 1.收费区别 MySQL是轻量型数据库,开源免费.Oracle是收费的而且价格非常高: 2.实例区别 MySQL一个实例可以操作多个库,而Oracle一个实例只能对应一 ...

  7. mariadb mysql版本对应_弹指神通MariaDB——MariaDB与MySQL各版本的区别

    1 MariaDB的性质 MariaDB从MySQL衍生而来,作为完全开源的一款数据库产品.关于MariaDB更详细的信息其实是在MariaDB.org中.在MariaDB download中也做了详 ...

  8. sql数据库和mysql有什么区别_【数据库】MySQL和sql的区别有哪些 - 收获啦

    sql是什么? sql,全称:结构化查询语言(Structured Query Language),是一种用于操作数据库的语言,它是用于所有数据库的基本语言.虽然,不同数据库之间存在较小的语法更改,但 ...

  9. mysql declare 赋值_sql server和mysql变量赋值的区别 以及 MySql Declare

    sql server和mysql都是我们经常用到的数据库系统,下面就为您介绍sql server和mysql变量赋值的区别,希望对您能有所启迪. sql server中变量要先申明后赋值: 局部变量用 ...

最新文章

  1. spring包自动扫描声明
  2. 共建网络安全 共享网络文明 2015福建网络安全日开幕在即
  3. gperf的学习笔记(一)
  4. HWS计划 decryption 寒假逆向生涯(17/100)
  5. JQUERY的split
  6. Android 自定义注解(Annotation)
  7. scrapy使用代理报错keyerror: proxy_爬虫Scrapy框架-Crawlspider链接提取器与规则解析器...
  8. 换行符‘\n’和回车符‘\r’
  9. 计算机中年级排名怎么操作,智学网如何查看年级排名 智学网看年级排名方法【详解】...
  10. 2021.03.14.浩楠卷子
  11. 解决java poi导出excel2003不能超过65536行的问题
  12. Objective-C 的 self 和 super 详解
  13. 设计模式之美 - 64状态模式
  14. RDD的两种操作(Transformation和Action)
  15. vb6启动时提示安装VS2008 SP1
  16. 111 进程的创建和结束
  17. XUI 熟练使用之(一) ----------- 将 XUI 引入项目
  18. Frontend Development
  19. 谷歌浏览器怎样把网页全部内容保存为.mhtml文件?
  20. ArcGIS利用Model批量裁剪

热门文章

  1. php 登录 linux服务器,如何实现linux系统远程登录到linux服务器
  2. Win64 驱动内核编程-20.UnHook SSDT
  3. 【Groovy】Groovy 方法调用 ( 使用 对象名.@成员名 访问 Groovy 对象成员 )
  4. 【错误记录】p7zip 交叉编译 Android 版本 NDK 报错 ( error: case value evaluates to -2 , which cannot be narrowed )
  5. 【运筹学】单纯形法总结 ( 单纯形法原理 | 单纯形法流程 | 单纯形表 | 计算检验数 | 最优解判定 | 入基变量 | 出基变量 | 方程组同解变换 ) ★★★
  6. 【Android 应用开发】Google 官方 EasyPermissions 权限申请库 ( 权限申请原理对话框操作回调接口 | 永久拒绝权限后引导设用户置权限 )
  7. 大龄开发人员如何破局
  8. redis as session_handler
  9. 《java语法实例2~15章》
  10. DEDE利用Ajax实现调用当前登录会员的信息简要说明