2020年3月4日 星期三

物件導向 PHP 與 MySQL (MariaDB) - 1. 使用 MySQLi 進行連線與簡易的 CRUD 操作


使用 MySQLi 進行連線與簡易的 CRUD 操作

本文將介紹如何以物件導向的方式使用 MySQLi 延伸套件連結 MySQL (MariaDB) Server,並進行簡易的資料新增、查詢、更新、刪除等操作。


建立連線

PHP 可以使用 mysqli 類別建立與 MySQL Server 間的連線,要實例化一個 mysqli 物件需要依序提供:MySQL Server 的 IP、資料庫的使用者名稱、使用者密碼、資料庫的名稱、MySQL Server 的連接埠、以及要使用的插座 (Socket) 或是具名管道 (Named Pipe);後兩者一般都是使用預設值即可。

下列的程式碼為物件導向 PHP 使用 mysqli 來建立與 MySQL Server 間的連線:
$dbHost = 'localhost';
$dbUser = 'db_username';
$dbPassword = 'db_user_password';
$dbName = 'db_name';
$connect = new mysqli($dbHost, $dbUser, $dbPassword, $dbName);
if ($connect->connect_errno) {
    echo "Failed to connect to MySQL: (" . $connect->connect_errno
             . ") " . $connect->connect_error . PHP_EOL;
}
echo $connect->host_info . PHP_EOL;


CRUD

在建立了跟資料庫伺服器的連線後,就可以對選定的資料庫進行資料的建立 (Create)、讀取 (Read)、更新 (Update)、以及刪除 (Delete) 等操作;這四項資料操作取其首字母,而被簡稱為 CRUD。MySQL 是關聯式資料庫管理系統 (Relational DataBase Management System, RDBMS),使用結構化查詢語言 (Structured Query Language, SQL) 進行管理。SQL 使用資料操作語言 (Data Manipulation Language, DML) 來處理資料庫中資料表裡的資料,這些操作對應到 CRUD 分別是 INSERT、SELECT、UPDATE、以及 DELETE。

以 PHP 開發的應用中,查詢敘述通常是動態產生的 (例如,使用者在表單上輸入要查詢的資料),如果沒有妥善的處理查詢敘述,非常容易讓惡意的使用者以 SQL 隱碼攻擊 (SQL Injection) 的方式,破壞或是入侵資料庫系統。mysqli 提供了預備敘述,讓輸入參數化以避免 SQL 隱碼攻擊。

INSERT

下例會在資料表中新增資料,假設資料 (姓名、電子郵件信箱、住址、以及訊息等) 是由使用者輸入而取得:
$name = '許杰夫';
$email = 'jeff.hsu@gmail.com';
$zip = 10001;
$address = '台北市中正區中正路1號';
$score = 98.9;
$message = '資料新增';

$sql = "INSERT INTO `users` (`name`, `email`, `zip`, `address`, `score`, `message`) 
            VALUES (?, ?, ?, ?, ?, ?)";
$stmt = $connect->prepare($sql);
if (!$stmt) {    
    echo "Prepare failed: (" . $connect->errno . ") " . $connect->error;
}
$stmt->bind_param('ssisds', $name, $email, $zip, $address, $score, $message);
$stmt->execute();
$stmt->close();

為了避免 SQL 隱碼攻擊,需要:
1. 將查詢敘述參數化,mysqli 使用問號 ? 做為佔位符 (Placeholder) 來取代變數名稱;
2. 利用 mysqli::prepare 產生預備敘述 (建立 mysqli_stmt 物件);
3. 使用 mysqli_stmt::bind_param 將變數綁定到預備敘述作為參數,其中第一個參數為變數型別,分別以:i 代表整數、s 代表字串、d 代表雙精度浮點數、b 代表二進位大型物件;
4. 使用 mysqli_stmt::execute 執行綁定參數後的預備敘述;
5. 在查詢完成後使用 mysqli_stmt::close() 將查詢敘述關閉。

SELECT

下例會讀取資料表中第一筆到最後一筆的資料:
$id = $connect->insert_id;
$sql = "SELECT * FROM `users` WHERE `id` <= ?";
$stmt = $connect->prepare($sql);
if (!$stmt) {    
    echo "Prepare failed: (" . $connect->errno . ") " . $connect->error;
} 
$stmt->bind_param('i',$id);
$stmt->execute();

$stmt->bind_result($id, $name, $email, $zip, $address, $score, $message);
while($stmt->fetch()){
    echo $id . " , " . $name . " , " . $email . " , " . $zip
        . $address . " , " . $score . " , " . $message . PHP_EOL;
}
$stmt->close();


在本例中:
1. 使用了 mysqli::$insert_id,傳回最後查詢 (通常是 INSERT) 時自動生成的 ID (當資料表中有 AUTO_INCREMENT 欄位時);
2. 預備敘述的參數綁定與執行;
3. mysqli_stmt::bind_result 將預備敘述執行結果 (結果集合) 中的欄位名稱綁定到指定的變數;
4. mysqli_stmt::fetch 會將結果集合中的資料逐筆對應到由  mysqli_stmt::bind_result 綁定的變數;
5. 每執行一次 mysqli_stmt::fetch,結果集合就會少一筆記錄,直到結果集合成為空集合, mysqli_stmt::fetch 會傳回 false。
6. 在查詢完成後使用 mysqli_stmt::close() 將查詢敘述關閉。

除了上述的輸出方式之外,也可以透過 mysqli_stmt::get_result 將結果集合轉換為 mysqli_result 物件集合後,再使用 mysqli_result::fetch_object 以物件的方式輸出資料。如下列的程式:
$objSet = $stmt->get_result();
while($obj = $objSet->fetch_object()){
    echo $obj->id . " , " . $obj->name . " , " . $obj->email . " , " . $obj->zip
        . $obj->address . " , " . $obj->score . " , " . $obj->message , PHP_EOL;
}

要注意的是,在使用 mysqli_stmt::get_result 後,會將結果集合清空。

UPDATE

下例會更新資料表中的最後一筆新增的資料:
$name = '許班杰';
$email = 'benjamin@test.com';
 
$sql = "UPDATE `users` SET `name` = ?, `email` = ? WHERE `id` = ?";
$stmt = $connect->prepare($sql);
if (!$stmt) {    
    echo "Prepare failed: (" . $connect->errno . ") " 
            . $connect->error;
} 
$stmt->bind_param('ssi', $name, $email, $id);
$stmt->execute();
$stmt->close();


DELETE

下例會刪除資料表中的最後一筆新增的資料:
$sql = "DELETE FROM `users` WHERE `id` = ?";
$stmt = $connect->prepare($sql);
if (!$stmt) {    
    echo "Prepare failed: (" . $connect->errno . ") "
         . $connect->error;
} 
$stmt->bind_param('i',$id);
$stmt->execute();
$stmt->close();
$connect->close();

在所有的查詢工作結束後,記得要將連線關閉,釋放佔用的資源。

小結

本文介紹了如何使用 MySQLi 延伸套件 (包含 mysqli、mysqli_stmt、mysqli_result 等類別),以物件導向的方式操作 MySQL (MariaDB) 資料庫;並進行簡單的 CRUD 操作。使用MySQLi 最大的限制是只能夠對  MySQL (MariaDB) 資料庫進行操作,當資料庫系統轉換到其他的系統時,需要重新撰寫程式碼。在下文中將介紹 PHP Data Object (PDO) 延伸套件,可以避免這個問題。

沒有留言: