首先思考, 爲何選擇PDOphp
PDO
是一個數據訪問抽象層(Database Access Abstraction Layer). 抽象是雙重的: 一個是衆所周知但不過重要的. 另外一個是模糊的可是是最重要的.
衆所周知 PDO
爲不一樣的數據庫提供了統一的接口. 雖然這個功能自己很龐大, 可是對於固定程序來講不是過於重要的事情, 基本全部的程序都是使用統一的後端數據庫. 儘管有一些謠言, 可是經過改變單行 PDO
配置來切換後端數據庫是不可能的-因爲不一樣的 SQL
風格(爲此, 須要使用像 DQL
這樣的平均查詢語言). 所以對於普通的 LAMP
開發者來講, 這一點是微不足道的, 而且對他而言, PDO只是熟悉的 mysql(i)_query()
函數的另外一個更復雜版本. 但實際上它不是, 它有豐富的其餘功能.PDO
不只抽象了數據庫API, 還抽象了基本操做, 不然必須在每一個應用程序中重複數百次, 使您的代碼很是WET. 不一樣於 mysql
和 mysqli
, 兩個都不能直接使用低級裸 APIs
(但僅做爲某些更高級別抽象層的構建材料), PDO
就是這樣的抽象. 雖然還是不完整的, 可是至少可用.
真正的PDO好處是:mysql
請注意, 儘管 PDO
是原生數據庫驅動程序中最好的, 但對於現代WEB應用程序來講, 請考慮將使用有查詢構建器的 ORM
或者與其餘更高抽象級別的庫一塊兒使用, 只是偶爾使用原生的PDO. 好的ORM好比 Doctrine
, Eloquent
, RedBean
和 Yii::AR
. Aura.SQL
是具備不少附加功能的使用PDO包裝器的一個很好的例子.
不管哪一種方式, 首先要了解基本工具是件好事. 那麼, 讓咱們開始吧:sql
PDO
有一個叫 DSN
的預想接方式. 它並不複雜-PDO須要你在三個不一樣的位置輸入不一樣的配置, 而不是一個簡單的選項列表.數據庫
database driver
, host
, db(schema) name
和 charset
, 以及不常使用的 port
和 unix_socket
設置 DSN
user_name
和 password
設置構造方法其中 DSN
是以分號分隔的字符串, 由 param=value
鍵值對組成, 從驅動程序名稱和冒號開始:後端
mysql:host=localhost;dbname=test;port=3306;charset=utf8mb4 driver^ ^colon ^param=value pair ^semicolon
注意, 遵循正確的格式是很是重要的- DSN中不能使用 空格, 引號, 和其餘的符號, 只能使用參數, 值和定界符. 就像手冊上展現的.數組
這裏有一個例子:安全
$host = '127.0.0.1'; $db = 'test'; $pass = 'root'; $charset = 'utf8mb4'; $dsn = "mysql:host={$host};dbnamej={$db};charset={$charset}"; $options = [ PDO::ATR_ERRMODE => PDO::ERRMODE_EXECPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]; try { $pdo = new \PDO($dsn, $user, $pass, $options); } catch (\PDOException $e) { }
設置了全部上述變量屬性, 咱們將在 $pdo
變量中獲得一個正確的 PDO
實例.
使用舊mysql擴展用戶重要通知oracle
mysql_*
函數, 能夠在代碼的任意位置使用, pdo
實例被存儲在一個變量中, 那就意味着只能在函數內部進行訪問. 所以, 必須經過函數參數傳遞或使用更高級的技術, 好比IOC容器.PDO
實例, 讓整個腳本使用.(適用於FPM模式)Query
運行SET NAMES
或者經過 PDO::MYSQL_ATTR_INIT_COMMAND
. 只有當PHP版本太低時(低於5.3.6), 纔可使用 SET NAMES
查詢, 而且關閉仿真模式.更多關於鏈接的內容能夠在 鏈接MySQL查看socket
使用 PDO
有兩種方式運行查詢. 若是查詢中沒有使用變量, 可使用 PDO::query
方法. 它會運行查詢並返回一個 PDOStatement
類的對象, 該類與 mysql_query
返回的資源大體相同, 特別時從中獲取實際記錄的操做:ide
$stmt = $pdo->query('SELECT name FROM users'); while ($row = $stmt->fetch()) { echo $row['name'] . "\n"; }
而且 query()
方法容許咱們使用一個整潔的方法鏈接 SELECT
查詢, 以下所示.
放棄熟悉的 mysql_query()
函數 並進入嚴格數據對象領域的主要緣由是 PDO
已經準備好了開箱即用的預處理語句. 若是要在語句中使用變量, 預處理語句是惟一正確運行的方式. 它如此重要的緣由在 The Hitchhiker's Guide to SQL Injection prevention.有詳細的解釋.
對於運行的查詢, 若是至少使用一個變量, 你必須使用佔位符替換它. 準備執行語句, 而後分別傳入變量執行.
長話短說, 它不像感受的那麼困難. 在大多數例子中, 你只須要使用函數 prepare
和 execute
.
首先, 須要修改查詢, 在使用變量的位置添加佔位符, 就像這樣
$sql = "SELECT * FROM users WHERE email = '{$email}' AND status = '{$status}'";
改成
$sql = "SELECT * FROM users where email = ? and status = ?";
或者
$sql = "SELECT * FROM users where email = :email AND status = :status";
注意 PDO
支持位置(?)和命名(:email)佔位符, 後者始終以冒號開始,而且只能使用字母, 數字和下劃線. 還須要注意 佔位符周圍不能使用引號 .
一個查詢使用了佔位符, 就必須使用PDO::prepare()
方法預處理. 這個方法返回一個和咱們上邊討論的相同的 PDOStatement
對象, 可是沒有綁定任何數據.
最後, 必須使用 PDOStatement
對象的 execute()
方法執行查詢, 而且經過數組形式傳遞參數. 以後, 就能夠從語句中獲得結果數據(若是適用).
$stmt = $pdo->prepare("SELECT * FROM users WHERES email = ? AND status = ?"); $stmt->execute([$email, $status]) $user = $stmt->fetch(); // or $stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email AND status = :status"); $stmt->execute(['email' => $email, 'status' => $status]); $user = $stmt->fetch();
能夠看到, 位置佔位符, 你須要提供一個索引數組. 命名佔位符, 須要提供一個關聯數組, 而且鍵要匹配查詢中的佔位符. 同一個查詢中不能混合位置佔位符和命名佔位符.
位置佔位符可讓你寫更簡短的代碼, 可是對參數順序是敏感的(必須於查詢中參數的順序一致). 雖然命名佔位符使代碼更冗長, 可是容許隨機參數綁定.
另外須要注意, 雖然存在廣泛的誤解, 可是數組鍵中 :
不是必須的.
執行後就可使用支持的方法獲取結果.
更多的例子能夠查看(respective article)[https://phpdelusions.net/pdo_...].
將數據傳入 execute()
(如上所示)方法中應被視爲默認的最方便的方式. 若是使用這個方法, 全部參數都將會綁定爲字符串(若是使用NULL值, 將會使用SQL NULL發送給查詢), 大多數時候都沒有問題.
可是, 有時候最好明確設置類型. 可能狀況以下:
bigint
boolean
必須綁定精確的操做數(爲了將 BIGINT
綁定爲 PDO::PARAM_INT
須要基於 mysqlnd
)這種狀況下, 必須使用顯式綁定, 能夠從 bindvalue()
和 bindParam()
兩個函數中選擇一個. 前者是推薦使用的, 它不像 bindParam()
具備必定的反作用.
瞭解哪些查詢部分可使用參數綁定哪些部分不能使用是很是重要的. 事實上, 這個列表是很是短的: 只有字符串和數字字面量能夠被綁定. 只要你的數據在查詢中能被表示爲數字或者帶引號的字符串, 就能夠被綁定. 其餘全部狀況你不能使用 PDO
預處理語句: 既不是標識符也不是逗號分隔列表, 或者是引用的文字字符串的一部分, 或者其餘任意查詢部分都不能使用預準備語句綁定
最多見的用例解決方案能夠在[本章的響應部分查看]()
有時候你可使用預處理屢次執行準備好的查詢, 比一次又一次執行相同的查詢快一點, 由於它只解析查詢一次. 若是能夠執行另外一個PHP實例中的預處理語句, 這個功能就是很是有用的, 可是事實並不是如此. 只會在同一個實例中重複相同的查詢, 這在常規的PHP腳本中不多使用到, 並限制了此功能用於重複插入和更新.
$data = [ 1 => 1000, 2 => 200, 3 => 200, ]; $stmt = $pdo->prepare('UPDATE users SET bonus = bonus + ? where id = ?'); foreach ($data as $id => $bonus) { $stmt->execute([$bonus, $id]); }
注意這個功能有點被高估了. 不只須要討論, 並且性能提高也不是很大 - 查詢解析有時候是
很快的. 並且只有在關閉仿真模式的時候才能帶來性能提高.
這些查詢沒有什麼特別之處, 對PDO來講他們都是同樣的. 運行哪一個查詢並不重要.
如上所示, 須要準備帶有佔位符的預處理查詢, 傳入變量並執行. DELETE
和 SELECT
的處理過程是基本相同的. 僅有的不一樣點是( DML
查詢不會返回任何數據), 你可使用鏈式方法, 調用 execute()
和 prepare()
.
$sql = "UPDATE users SET name = ? where id = ?"; $pdo->prepare($sql)->execute([$name, $id]);
然而, 你像得到影響行數, 代碼將和無聊的三行代碼相同:
$stmt = $pdo->prepare("DELETE FROM goods where category = ?"); $stmt->execute([$cat]); $deleted = $stmt->rowCount();
更多的例子能夠在respective article.找到.
咱們已經見過這個函數了, 如今讓咱們仔細看看. 它從數據庫獲取單行數據, 在結果集中移動內部指針, 所以, 對函數的後續調用將逐個返回全部行. 這個方法和 mysql_fetch_array()
大體相同但在工做模式稍微有點不一樣: 代替不少不一樣函數( mysql_fetch_assoc()
mysql_fetch_row
), 這個只有一個方法, 可是它的行爲能夠經過一個參數改變. 在 PDO
中有不少的獲取模式, 稍後咱們詳細討論, 這裏有一些簡單的實例:
PDO::FETCH_NUM
返回索引數組PDO::FETCH_ASSOC
返回關聯數組PDO::FETCH_BOTH
以上二者都包含PDO::FETCH_OBJ
返回對象PDO::FETCH_LAZY
容許三個(索引數組, 關聯數組, 對象)方法沒有內存開銷.從上面能夠看出, 這個必須在兩種狀況下使用:
當只須要一行時, 只獲取一行
$row = $stmt->fetch(PDO::FETCH_ASSOC);
將以關聯數組的方式從語句中獲取一行
另外一種有用的模式是 PDO::FETCH_CLASS
能夠建立一個特定類的對象
$news = $pdo->query("select * from news")->fetchAll(PDO::FETCH_CLASS, 'News');
將生成一個News類對象的數組, 而且經過返回值設置類屬性. 注意這個模式下:
__set
魔術方法__set
方法, 將會建立新屬性