bool mysqli_set_charset ( mysqli $link
, string $charset
)php
這應該是首選的用於改變字符編碼的方法,不建議使用 mysqli_query()執行SQL請求的SET NAMES ...(如 SET NAMES utf8)。html
用法:mysql
<?php $mysqli = new mysqli("localhost", "my_user", "my_password", "test"); /* check connection */ if (mysqli_connect_errno()) { printf("Connect failed: %s\n", mysqli_connect_error()); exit(); } /* change character set to utf8 */ if (!$mysqli->set_charset("utf8")) { printf("Error loading character set utf8: %s\n", $mysqli->error); } else { printf("Current character set: %s\n", $mysqli->character_set_name()); } $mysqli->close(); ?>
最近公司組織了個PHP安全編程的培訓, 其中涉及到一部分關於Mysql的」SET NAMES」和mysql_set_charset (mysqli_set_charset)的內容:sql
說到, 儘可能使用mysqli_set_charset(mysqli:set_charset)而不是」SET NAMES」, 固然, 這個內容在PHP手冊中也有敘及, 可是卻沒有解釋爲何.編程
最近有好幾個朋友問我這個問題, 到底爲何?api
問的人多了, 我也就以爲能夠寫篇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 9 if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis() 10 , "Os", &mysql_link, mysqli_link_class_entry, &cs_name, &len) == FAILURE) { 11 return; 12 } 13 MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL*, &mysql_link, "mysqli_link" 14 , MYSQLI_STATUS_VALID); 15 16 if (mysql_set_character_set(mysql->mysql, cs_name)) { 17 //** 調用libmysql的對應函數 18 RETURN_FALSE; 19 } 20 RETURN_TRUE; 21 }
那mysql_set_character_set又作了什麼呢?
1 //mysql-5.1.30-SRC/libmysql/client.c, line 3166: 2 int STDCALLmysql_set_character_set(MYSQL*mysql, const char *cs_name) 3 { 4 structcharset_info_st *cs; 5 const char *save_csdir= charsets_dir; 6 7 if (mysql->options.charset_dir) 8 charsets_dir= mysql->options.charset_dir; 9 10 if (strlen(cs_name) < MY_CS_NAME_SIZE && 11 (cs= get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0)))) 12 { 13 char buff[MY_CS_NAME_SIZE + 10]; 14 charsets_dir= save_csdir; 15 /* Skip execution of "SET NAMES" for pre-4.1 servers */ 16 if (mysql_get_server_version(mysql) < 40100) 17 return 0; 18 sprintf(buff, "SET NAMES %s", cs_name); 19 if (!mysql_real_query(mysql, buff, strlen(buff))) 20 { 21 mysql->charset= cs; 22 } 23 } 24 //如下省略
咱們能夠看到, 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 6 var_dump(addslashes($a)); 7 var_dump(mysql_real_escape_string($a, $db)); 8 9 mysql_query("set names gbk"); 10 var_dump(mysql_real_escape_string($a, $db)); 11 12 mysql_set_charset("gbk"); 13 var_dump(mysql_real_escape_string($a, $db)); 14 ?>
由於, 「憖」的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) "憖"
你們如今很清楚了吧?
轉載自:http://www.laruence.com/2010/04/12/1396.html