php中set names与mysql_set_charset
今天看到大家在讨论,发现这是个很严重而又容易疏忽的问题,我以前也一直是用set names,遂记录下来,也提醒自己一把。
1.set names与mysql_set_charset有什么区别?
一般情况下, 使用”SET NAMES”就足够了, 也是可以保证正确的. 那么为什么手册又要说推荐使用 mysqli_set_charset(PHP>=5.0.5)呢。手册里面也没有明确说明。我们可以看下php扩展的源代码:
//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;
}
可以看到php的mysql扩展是直接调用了mysql的mysql_set_character_set函数,接下来看看mysql的代码
//mysql-5.1.30-SRC/libmysql/client.c, line 3166:
int STDCALL mysql_set_character_set(MYSQL *mysql, const char *cs_name)
{
struct charset_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;
}
}
//以下省略
可以看到,除了调用real_query设置set names,还设置了mysql的charset变量。
2.这样有什么影响?
mysql_real_escape_string会受到影响,它与mysql_escape_string的区别就 是, 它会考虑”当前”字符集。如果仅仅使用set names,mysql_real_escape_string可能会失效。
例子:
$mysqli = new mysqli("localhost", "user", "pass", "test", 3306);
/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$mysqli->query('SET NAMES gbk'); //使用set names设置字符集
$city = chr(0xbf).chr(0x5c); //0xbf5c是个有效的gbk字符,模拟用户输入
$city = $mysqli->real_escape_string ($city);//使用real_escape进行过滤
/* this query will fail, cause we didn't escape $city */
if (!$mysqli->query("INSERT into myCity(name) VALUES ('$city')")) {
print "INSERT into myCity (name) VALUES ('$city')\n";
printf("Error: %s\n", $mysqli->error);
}
var_dump($city);
var_dump($mysqli->client_encoding());
$mysqli->close();
3.解决方案
mysqli_set_charset函数对PHP和Mysql有版本要求,必须当mysql版本大于5,PHP版本大于5.0.5时,此函数才有效。至于另一个mysql_set_charset函数,则更要求PHP版本大于5.2.3时才能有效。对于mysql4.1以上版本,使用”SET character_set_client=binary;”
推荐使用mysql_set_charset设置字符集的方案,只有在环境不允许的情况下,我们才推荐使用第二种binary编码的方案。但是无论在什么情况下,都禁止使用”SET NAMES”来作为设置字符集的操作。
---------------------------------------------------------------
本站作品根据创作共同协议进行授权, 转载时请务必以超链接形式标明文章原始出处
原文地址:http://www.mirecle.com/2010/04/13/php-in-the-set-names-and-mysql_set_charset.html
---------------------------------------------------------------