前言
本文為《現代 PHP》一書的學習筆記。
環境
- Windows 10
- Wnmp 3.1.0
資料庫
PHP 應用程式以各種資料庫來儲存資料,例如 MySQL
、PostgreSQL
、SQLite
等。MySQL
使用了 mysqli
擴充,添加了許多 mysqli_*()
函式到 PHP 中。
PDO 擴充
PDO(PHP Data Objects)是一組 PHP 類別,可以使用單一使用者介面和許多不同的 SQL 資料庫溝通,讓資料庫的實作方法被抽象化出來。
即使 PDO 提供單一介面給不同資料庫,仍然需要撰寫 SQL 陳述式。建議在使用 PDO 時撰寫 ANSI/ISO SQL,避免在切換資料庫系統時導致 SQL 無法使用。
資料庫連線和 DSN
在 PHP 中實例化 PDO 類別,利用 PDO 實體可以建立一個 PHP 和資料庫之間的連線。
PDO 類別建構式接受一個字串參數,稱作 DSN
或資料來源名稱,DSN
的連線字串大致上包含了以下:
- 主機名稱(
host
) - 資料庫名稱(
dbname
) - 埠號碼(
port
) - 字元組(
charset
)
範例 5-18:PDO 建構式
1 | try { |
DSN
以mysql:
開頭,在「:
」後指定host
、dbname
、port
和charset
。
資料庫認證資訊
絕對不要在 PHP 檔案中寫入資料庫認證資訊,應該要把資料庫認證資訊移到根目錄之下的部署檔,在需要時引用到 PHP 檔案。
絕對不要版本控制資料庫認證資訊,應該要寫入
.gitignore
檔以忽略版本控制。
接下來的範例,其目錄結構如下:
1 | |- settings.php |
settings.php
檔如下
1 | $settings = [ |
範例 5-19:外部設定的 PDO 建構式
1 | require 'settings.php'; |
預備陳述式
初學的開發者可能會建立一個 SQL 陳述式如下:
1 | $sql = sprintf( |
- 這提供了一個好方法讓駭客入侵資料庫,幸好 PDO 擴充引入了預備陳述式和範圍(
bound
)參數,讓消毒使用者輸入變得非常簡單。
以 PDO 實體的 prepare()
方法獲取預備陳述式的物件,此方法接受一個 SQL 陳述式字串作為第一個參數,並回傳一個 PDOStatement
實體。
1 | $sql = 'SELECT id FROM users WHERE email = :email'; |
:email
作為名稱占位符,可以安全地綁定任何値。
範例 5-20:帶有電子郵件位址的預備陳述式
1 | $sql = 'SELECT id FROM users WHERE email = :email'; |
bindValue()
方法的第三個參數是資料型態,沒有的話會假設資料為字串型態。
範例 5-21:帶有 ID 的預備陳述式
1 | $sql = 'SELECT email FROM users WHERE id = :id'; |
- 使用
PDO::PARAM_INT
常數作為bindValue()
方法的第三個參數。
其他的 PDO 常數還有:
PDO::PARAM_BOOL
PDO::PARAM_NULL
PDO::PARAM_INT
PDO::PARAM_STR
查詢結果
使用預備陳述式的 execute()
方法執行 SQL 陳述式,包括 INSERT
、UPDATE
或 DELETE
陳述式。
使用 fetch()
、fetchAll()
、fetchColumn
和 fetchObject()
方法,可以擷取查詢結果。
範例 5-22:預備陳述式的結果作為關聯陣列
1 | $statement->execute(); |
fetch()
方法適合用在記憶體無法完全容納整個查詢結果的時候。
以下常數皆可作為 fetch()
方法的第一個參數:
PDO::FETCH_ASSOC
會回傳以資料庫欄位名稱作為索引鍵的陣列PDO::FETCH_NUM
會回傳以數字作為索引鍵的陣列PDO::FETCH_BOTH
是PDO::FETCH_ASSOC
和PDO::FETCH_NUM
的結合PDO::FETCH_OBJ
會回傳物件,屬性為資料庫欄位名稱
如果處理較小的資料集,並且百分之百確定記憶體可以容納整個查詢結果,則可以使用 fetchAll()
方法擷取所有查詢結果。
範例 5-23:預備陳述式擷取整個結果
1 | $statement->execute(); |
如果只要查詢結果中的一個欄位,可以使用預備陳述式的 fetchColumn()
方法,其唯一參數是目標欄位的索引値。
範例 5-24:預備陳述式擷取單一欄位單一列
1 | $statement->execute(); |
可以使用預備陳述式的 fetchObject()
方法擷取查詢結果作為一個物件。
範例 5-25:預備陳述式擷取列作為物件
1 | $statement->execute(); |
交易
「交易」是一組自動執行的資料庫陳述式,同時執行成功或同時執行失敗的 SQL 查詢,可有效提升效能。
交易要使用 PDO 實體的 beginTransaction()
和 commit()
方法包覆 SQL 陳述式,如果交易中的任何查詢失敗,所有交易中的查詢都不會被執行。
範例 5-26:不使用交易的資料庫查詢
1 | $stmtSubtract = $pdo->prepare(' |
- 如果從第一個帳戶提款後,發生斷電,這筆帳款將不會存入第二個帳戶。
以下範例將提款和存款包裝成單一的資料庫交易,確保兩個動作會同時執行成功,或是同時執行失敗。
範例 5-27:以交易使用資料庫查詢
1 | $stmtSubtract = $pdo->prepare(' |
參考資料
- Josh Lockhart(2015)。現代 PHP。台北市:碁峯資訊。