雖說是少見的一些屬性方法,但是可能還是有不少同學在日常的開發中使用過,這裏只是學習了可能相對來說我們用得比較少的一些 mysqli 的屬性或方法。就當是擴展一下自己的知識體系。
切換用戶
首先就是切換一下當前連接數據庫的用戶。
// 切換用戶
$mysqli->change_user('root2', "123", 'blog_test');
// 錯誤信息
$res = $mysqli->query( "SELECT * FROM zyblog_test_user");
var_dump($res); // bool(false)
var_dump($mysqli->error_list);
// array(1) {
// [0]=>
// array(3) {
// ["errno"]=>
// int(1142)
// ["sqlstate"]=>
// string(5) "42000"
// ["error"]=>
// string(78) "SELECT command denied to user 'root2'@'localhost' for table 'zyblog_test_user'"
// }
// }
使用 change_user() 方法就能夠方便地在 mysqli 對象已經實例化之後再來進行用戶的切換。在這段代碼中,我們的 root2 用戶沒有 blog_test 表的 SELECT 權限,所以返回的 query() 查詢結果是空的。通過打印 mysqli 的 error_list 屬性就可以看到當前的錯誤信息。
錯誤信息
緊接着上一段,對於執行語句的錯誤信息,我們可以通過幾個 mysqli 中的屬性來獲得。比如上文中的 error_list 。它裏面包含的就是所有的錯誤信息列表,並且是非常詳細的錯誤信息內容。不過我們還可以通過另外兩個屬性來獲得單獨的錯誤號和錯誤信息的字符串。
var_dump($mysqli->errno); // int(1142)
var_dump($mysqli->error); // string(78) "SELECT command denied to user 'root2'@'localhost' for table 'zyblog_test_user'"
errno 屬性裏面保存的就是數據庫的錯誤號,error 屬性中保存的是錯誤信息的詳細文本說明。這兩個錯誤信息內容都是在執行 SQL 語句之後數據庫返回的內容。
連接錯誤信息
當然,如果在連接數據庫的時候就產生了錯誤,我們也可以通過 connect_errno 和 connect_error 來獲得它們的錯誤號和錯誤信息內容。
$mysqli2 = @new mysqli("xxx", "root", "", "blog_test");
var_dump($mysqli2->connect_errno); // int(2002)
var_dump($mysqli2->connect_error); // string(90) "php_network_getaddresses: getaddrinfo failed: nodename nor servname provided, or not known"
在這段代碼中,我們使用了錯誤的 host 信息。如果不在實例化的時候加上錯誤抑制符,那麼在實例化的時候就會報出 PHP 的警告信息 Warning 。在這裏我們爲了測試輸出的清晰,加上了錯誤抑制符。
客戶端連接的統計數據
var_dump($mysqli->get_connection_stats());
// array(163) {
// ["bytes_sent"]=>
// string(3) "306"
// ["bytes_received"]=>
// string(3) "287"
// ["packets_sent"]=>
// string(2) "10"
// ["packets_received"]=>
// string(1) "6"
// ["protocol_overhead_in"]=>
// string(2) "24"
// ["protocol_overhead_out"]=>
// string(2) "40"
// ["bytes_received_ok_packet"]=>
// string(1) "0"
// ["bytes_received_eof_packet"]=>
// string(1) "0"
// ……
// ……
// ["bytes_received_real_data_normal"]=>
// string(1) "0"
// ["bytes_received_real_data_ps"]=>
// string(1) "0"
// }
通過 get_connection_stats() 方法,我們可以獲得當前數據庫連接的一些統計信息。返回的內容非常多,官方也沒有具體的每個字段的說明文檔。不過從字段名中我們還是可以看到一些有用的信息,比如 bytes_sent 字節發送的數量,bytes_received 字節接收的數量。
字符集
最近這些年,我們使用 PHP + MySQL 開發基本上都已經是統一地在使用 UTF-8 來作爲默認的字符集編碼了。不過在早些時候,包括 Discuz 、 DedeCMS 這些早期的開源建站程序都會提供一套 UTF-8 和一套 GBK 的源碼供大家使用。而 mysqli 則可以方便快捷地獲取及切換當前數據庫所使用的字符集。
獲取數據庫字符
// 獲取數據庫字符
var_dump($mysqli->character_set_name()); // string(4) "utf8"
character_set_name() 不要以爲看到一個 set 關鍵字就是設置或者修改什麼,這個方法是獲取當前的字符集信息的。
字符集詳細信息
var_dump($mysqli->get_charset());
// object(stdClass)#2 (8) {
// ["charset"]=>
// string(4) "utf8"
// ["collation"]=>
// string(15) "utf8_general_ci"
// ["dir"]=>
// string(0) ""
// ["min_length"]=>
// int(1)
// ["max_length"]=>
// int(3)
// ["number"]=>
// int(33)
// ["state"]=>
// int(1)
// ["comment"]=>
// string(13) "UTF-8 Unicode"
// }
我們也可以通過 get_charset() 獲取當前數據庫連接的詳細字符集信息。其中 charset 就是字符類型,我們這裏是 UTF-8 類型的,字符集是 utf8_general_ci ,這一套基本上就是我們現在開發時的標配了。
設置字符集
$mysqli->set_charset('gbk');
$mysqli->query("insert into zyblog_test_user(username, password, salt) values('GBK字符','dd','d')");
var_dump($mysqli->error); // string(65) "Incorrect string value: '\xAC\xA6' for column 'username' at row 1"
$mysqli->set_charset('utf8');
$mysqli->query("insert into zyblog_test_user(username, password, salt) values('UTF字符','dd','d')");
var_dump($mysqli->error);
echo $mysqli->insert_id, PHP_EOL;
通過 set_charset() 方法就可以設置當前數據庫連接的字符。在第一段代碼中我們將連接字符設置爲 gbk ,然後執行插入語句,直接就會返回字符不匹配的信息了。
特殊字符轉義
既然說到字符的問題了,我們順便多提一句關於 SQL 注入的問題。除了使用 預處理 功能來解決 SQL 注入之外,MySQLi 還爲我們提供了一個 real_escape_string() 方法,可以手工地解決SQL語句中的一些特殊符號問題。
$username = "aaa ' bbb";
$username = $mysqli->real_escape_string($username);
var_dump($username); // string(10) "aaa \' bbb"
使用這個方法可以說和 addslashes() 方法類似,不過它比 addslashes() 方法轉義的內容更多一些,它包括: NUL (ASCII 0),\n,\r,\,'," 和 Control-Z 這些字符。
線程操作
關於 MySQL 的線程問題,我們將來在深入學習並且刷 MySQL 文檔的時候再說(因爲現在我也不是很清楚~~)。在這裏,我們就先看看 mysqli 中關於 MySQL 線程的幾個屬性和方法吧。
var_dump($mysqli->thread_safe); // NULL
var_dump($mysqli->thread_id); // int(600)
$thread_id = $mysqli->thread_id;
$mysqli->kill($thread_id);
if (!$mysqli->query("insert into zyblog_test_user(username, password, salt) values('kill線程了','dd','d')")) {
var_dump($mysqli->error); // string(26) "MySQL server has gone away"
}
thread_safe 屬性是保存的當前數據庫連接是否是線程安全的,在我們的測試中返回的是 NULL 而不是正常的布爾值,這一塊將來我們學習到了再說。thread_id 屬性保存的是當前連接的線程ID,通過這個線程ID,我們就可以使用另外一個 kill() 方法來殺死當前的線程。其實就相當於關閉了當前這個 mysqli 對象的連接,這時我們再使用這個連接對象進行其它操作時就會出現 MySQL server has gone away 的提示信息了。
mysqli 對象
上面說的很多屬性其實我們可以直接通過打印 mysqli 對象就可以查看到。
var_dump($mysqli);
// object(mysqli)#1 (19) {
// ["affected_rows"]=>
// int(1)
// ["client_info"]=>
// string(79) "mysqlnd 5.0.12-dev - 20150407 - $Id: 7cc7cc96e675f6d72e5cf0f267f48e167c2abb23 $"
// ["client_version"]=>
// int(50012)
// ["connect_errno"]=>
// int(2002)
// ["connect_error"]=>
// string(90) "php_network_getaddresses: getaddrinfo failed: nodename nor servname provided, or not known"
// ["errno"]=>
// int(0)
// ["error"]=>
// string(0) ""
// ["error_list"]=>
// array(0) {
// }
// ["field_count"]=>
// int(0)
// ["host_info"]=>
// string(25) "Localhost via UNIX socket"
// ["info"]=>
// NULL
// ["insert_id"]=>
// int(59)
// ["server_info"]=>
// string(6) "8.0.17"
// ["server_version"]=>
// int(80017)
// ["stat"]=>
// string(139) "Uptime: 355128 Threads: 4 Questions: 35696 Slow queries: 0 Opens: 764 Flush tables: 3 Open tables: 636 Queries per second avg: 0.100"
// ["sqlstate"]=>
// string(5) "00000"
// ["protocol_version"]=>
// int(10)
// ["thread_id"]=>
// int(606)
// ["warning_count"]=>
// int(0)
// }
像是 host_info 顯示的是我們連接數據庫的 host 信息,server_info 是連接到的數據庫的版本信息,就像我們這裏使用的是 MySQL8 。stat 簡單的統計信息,裏面能看到有 4 個線程,沒有慢查詢等等。大家可以仔細地研究一下這個對象中的這些屬性,或許某些內容就是你在實際項目中所需要的。
總結
今天的文章學習的就是一些簡單的 mysqli 類所持有的屬性和方法。我們還沒有正式開始學習查詢之類的語句,不過從這些屬性方法就可以看出,相對於 PDO 來說,mysqli 提供的功能確實更加的全面一些。後面我們將繼續深入地學習和探索 mysqli 的各種方法和使用。
測試代碼:
參考文檔:
https://www.php.net/manual/zh/book.mysqli.php
各自媒體平臺均可搜索【硬核項目經理】