PHP如何初始化PDO及原始SQL語句操作
目錄
PDO 例項
首先來看看一個 PDO 例項是如何初始化的。
$dns = 'mysql:host=localhost;dbname=blog_test;port=3306;charset=utf8'; $pdo = new PDO($dns,'root','');
普通情況下,我們直接例項化的時候傳遞構造引數就可以獲得一個 PDO 物件。這樣,我們就和資料庫建立了連線。如果連線失敗,也就是引數寫得有問題的時候,在例項化時直接就會報異常。
PDO 物件的引數包括 DNS 資訊、使用者名稱、密碼,另外還有一個引數就是可以設定 PDO 連線的一些屬性,我們將在後面看到它的使用。
dns 引數
PDO 構造引數的第一個引數是一個 DNS 字串。在這個字串中使用分號 ; 分隔不同的引數內容。它裡面可以定義的內容包括:
- DSN prefix,也就是我們要連線的資料庫型別,MySQL 資料庫一般都是直接使用 mysql: 這樣來定義即可。
- host,連線的地址,在這裡我們連線的是本地資料庫 localhost
- port,埠號,MySQL 預設為 3306 ,可以不寫
- dbname,要連線的資料庫名稱
- unix_socket,可以指定 MySQL 的 Unix Socket 檔案
- charset,連線的字符集
我們可以通過一個函式來檢視當前
print_r(PDO::getAvailableDrivers());exit; // Array // ( // [0] => dblib // [1] => mysql // [2] => odbc // [3] => pgsql // [4] => sqlite // )
PDO 物件屬性
PDO 構造引數的最後一個引數www.cppcns.com可以設定連線的一些屬性,如:
$pdo = new PDO($dns,'',[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); showPdoAttribute($pdo); // …… // PDO::ATTR_ERRMODE: 2 // ……
showPdoAttribute() 方法是我們自己封裝的一個展示所有連線屬性的函式。
// 顯示pdo連線屬性 function showPdoAttribute($pdo){ $attributes = array( "DRIVER_NAME","AUTOCOMMIT","ERRMODE","CASE","CLIENT_VERSION","CONNECTION_STATUS","ORACLE_NULLS","PERSISTENT","SERVER_INFO","SERVER_VERSION" ); foreach ($attributes as $val) { echo "PDO::ATTR_$val: "; echo $pdo->getAttribute(constant("PDO::ATTR_$val")) . "\n"; } }
在這個函式中,我們使用 PDO 例項的 getAttribute() 方法來獲取相應的屬性值。在沒有設定 PDO::ATTR_ERRMODE 時,它的預設值為 0 ,也就是 PDO::ERRMODE_SILENT 常量所對應的值。在上述程式碼中,我們將它設定為了 PDO::ERRMODE_EXCEPTION ,檢視屬性輸出的結果就變成了 2 。
除了在建構函式的引數中設定屬性外,我們也可以使用 PDO 例項的 setAttribute() 方法來設定 PDO 的屬性值。
pdo2 = new PDO($dns,[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); echo $pdo2->getAttribute(PDO::ATTR_DEFAULT_FETCH_MODE),PHP_EOL; // 4 // 設定屬性 $pdo2->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC); echo $pdo2->getAttribute(PDO::ATTR_DEFAULT_FETCH_MODE),PHP_EOL; // 2
在這段程式碼中,我們設定 PDO::ATTR_DEFAULT_FETCH_MODE 為 PDO::FETCH_ASSOC 。這樣,在使用這個 $pdo2 的連線進行查詢時,輸出的結果都會是以陣列鍵值對形式返回的內容。我們馬上就進入查詢方面相關函式的學習。
查詢語句
大多數情況下,使用 PDO 我們都會用它的預處理能力來編寫 SQL 語句,一來是效能更好,二來是更加安全。不過我們今天先不講預處理方面的問題,還是以最原始的直接操作 SQL 語句的方式學習相關的一些函式。
普通查詢及遍歷
// 普通查詢 - 遍歷1 $stmt = $pdo->query('select * from zyblog_test_user limit 5'); foreach ($stmt as $row) { var_dump($row); } // array(8) { // ["id"]=> // string(3) "204" // [0]=> // string(3) "204" // ["username"]=> // string(5) "three" // [1]=> // string(5) "three" // ["password"]=> // string(6) "123123" // [2]=> // string(6) "123123" // ["salt"]=> // string(3) "ccc" // [3]=> // string(3) "ccc" // } // …… // 普通查詢 - 遍歷2 $stmt = $pdo->query('select * from zyblog_test_user limit 5'); while ($row = $stmt->fetch()) { var_dump($row); } // array(8) { // ["id"]=> // string(3) "204" // [0]=> // string(3) "204" // ["username"]=> // string(5) "three" // [1]=> // string(5) "three" // ["password"]=> // string(6) "123123" // [2]=> // string(6) "123123" // ["salt"]=> // string(3) "ccc" // [3]=> // string(3) "ccc" // } // ……
PDO 例項的 query() 方法就是執行一條查詢語句,並返回一個 PDOStatement 物件。通過遍歷這個物件,就可以獲得查詢出來的資料結果集。
在程式碼中,我們使用了兩種方式來遍歷,其實它們的效果都是一樣的。在這裡,我們要關注的是返回的資料格式。可以看出,資料是以陣列格式返回的,並且是以兩種形式,一個是資料庫定義的鍵名,一個是以下標形式。
查詢結果集(陣列、物件)
其實大部分情況下,我們只需要資料庫鍵名的那種鍵值對形式的資料就可以了。這個有兩種方式,一是直接使用上文中我們定義好預設 PDO::ATTR_DEFAULT_FETCH_MODE 屬性的 $pdo2 連線,另一個就是在查詢的時候為 query() 方法指定屬性。
$stmt = $pdo2->query('select * from zyblog_test_user limit 5'); foreach ($stmt as www.cppcns.com$row) { var_dump($row); } // array(4) { // ["id"]=> // string(1) "5" // ["username"]=> // string(3) "two" // ["password"]=> // string(6) "123123" // ["salt"]=> // string(3) "bbb" // } // …… $stmt = $pdo->query('select * from zyblog_test_user limit 5',PDO::FETCH_ASSOC); foreach ($stmt as $row) { var_dump($row); } // array(4) { // ["id"]=> // string(1) "5" // ["username"]=> // string(3) "two" // ["password"]=> // string(6) "123123" // ["salt"]=> // string(3) "bbb" // } // ……
當然,我們也可以直接讓資料返回成物件的格式,同樣的也是使用預定義的常量來指定 query() 或者 PDO 例項連線的屬性就可以了。
$stmt = $pdo->query('select * from zyblog_test_user limit 5',PDO::FETCH_OBJ); foreach ($stmt as $row) { var_dump($row); } // object(stdClass)#4 (4) { // ["id"]=> // string(1) "5" // ["username"]=> // string(3) "two" // ["password"]=> // string(6) "123123" // ["salt"]=> // string(3) "bbb" // } // ……
查詢結果集(類)
上面返回物件形式的結果集中的物件是 stdClass 型別,也就是 PHP 的預設類型別。那麼我們是否可以自己定義一個類,然後在查詢完成後直接生成它的結果集呢?就是像是 ORM 框架一樣,完成資料到物件的對映。既然這麼說了,那當然是可以的啦,直接看程式碼。
class user { public $id; public $username; public $password; public $salt; public function __construct() { echo 'func_num_args: ' . func_num_args(),PHP_EOL; echo 'func_get_args: '; var_dump(func_get_args()); } } class user2 { } // 返回指定物件 $u = new user; $stmt = $pdo->query('select * from zyblog_test_user limit 5',PDO::FETCH_INTO,$u); foreach ($stmt as $row) { var_dump($row); } // object(user)#3 (4) { // ["id"]=> // string(1) "5" // ["username"]=> // string(3) "two" // ["password"]=> // string(6) "123123" // ["salt"]=> // string(3) "bbb" // } // …… // 空類測試 $u = new user2; $stmt = $pdo->query('select * from zyblog_test_user limit 5',$u); foreach ($stmt as $row) { var_dump($row); } // object(user2)#2 (4) { // ["id"]=> // string(1) "5www.cppcns.com" // ["username"]=> // string(3) "two" // ["password"]=> // string(6) "123123" // ["saltwww.cppcns.com"]=> // string(3) "bbb" // } // ……
在這段程式碼中,我們定義了兩個類,user 類有完整的和資料庫欄位對應的屬性,還定義了一個構造方法(後面會用到)。而 user2 類則是一個空的類。通過測試結果來看,類的屬性對於 PDO 來說並不重要。它會預設建立資料庫查詢到的欄位屬性,並將它賦值給物件。那麼假如我們定義了一個 const 常量屬性並給予相同的欄位名稱呢?大家可以自己嘗試一下。
對於 user 和 user2 來說,我們將它例項化了並傳遞給了 query() ,並且指定了結果集格式為 PDO::FETCH_INTO ,這樣就實現了獲取物件結果集的能力。但是 PDO 遠比你想象的強大,我們還可以直接用類模板來獲取查詢結果集。
// 根據類返回指定物件 $stmt = $pdo->query('select * from zyblog_test_user limit 5',PDO::FETCH_CLASS,'user',['x1','x2']); foreach ($stmt as $row) { var_dump($row); } // func_num_args: 2 // func_get_args: array(2) { // [0]=> // string(2) "x1" // [1]=> // string(2) "x2" // } // object(user)#4 (4) { // ["id"]=> // string(1) "5" // ["username"]=> // string(3) "two" // ["password"]=> // string(6) "123123" // ["salt"]=> // string(3) "bbb" // } // ……
query() 方法直接使用查詢結果集模式為 PDO::FETCH_CLASS ,並傳遞一個類模板的名稱,PDO 就會在當前程式碼中查詢有沒有對應的類模板,獲得的每個結果都會例項化一次。在這裡,我們又多了一個引數,最後一個引數是一個數組,並且給了兩個元素。估計有不少小夥伴已經看出來了,這個引數是傳遞給類的構造方法的。記住,使用這個模式,每個元素都會例項化一次,結果集中的每個元素都是新建立的類(object(user2)#3,#號後面的數字是不同的物件控制代碼id),而 PDO::FETCH_INTO 則是以引用的形式為每個元素賦值(object(user2)#3,#號後面的數字是相同的物件控制代碼id)。也就是說,我們使用 PDO::FETCH_INTO 模式的時候,修改一個元素的值,其它的元素也會跟著改變,如果使用一個數組去記錄遍歷的元素值,最後陣列的結果也會是相同的最後一個元素的內容。
$stmt = $pdo->query('select * from zyblog_test_user limit 5',$u); $resArr = []; foreach ($stmt as $row) { var_dump($row); $resArr[] = $row; } $resArr[0]->id = 55555; print_r($resArr); // Array // ( // [0] => user2 Object // ( // [id] => 55555 // [username] => two // [password] => 123123 // [salt] => bbb // ) // [1] => user2 Object // ( // [id] => 55555 // [username] => two // [password] => 123123 // [salt] => bbb // ) // [2] => user2 Object // ( // [id] => 55555 // [username] => two // [password] => 123123 // [salt] => bbb // ) // [3] => user2 Object // ( // [id] => 55555 // [username] => two // [password] => 123123 // [salt] => bbb // ) // [4] => user2 Object // ( // [id] => 55555 // [username] => two // [password] => 123123 // [salt] => bbb // ) // )
如何解決這個問題呢?最簡單的方式就是在陣列賦值的時候加個 clone 關鍵字唄!
查詢結果集(指定欄位)
最後輕鬆一點,我們看下 query() 方法還可以指定查詢的某一個欄位。
// 只返回第幾個欄位 $stmt = $pdo->query('select * from zyblog_test_user limit 5',PDO::FETCH_COLUMN,2); foreach ($stmt as $row) { var_dump($row); } // string(32) "bbff8283d0f90625015256b742b0e694" // string(6) "123123" // string(6) "123123" // string(6) "123123" // string(6) "123123"
增、刪、改操作
除了查詢之外的操作,我們也可以使用 exec() 方法來執行其他一些相應的 SQL 語句。
增加操作
$count = $pdo->exec("insert into zyblog_test_user(`username`,`password`,`salt`) value('akk','bkk','ckk')"); $id = $pdo->lastInsertId(); var_dump($count); // int(1) var_dump($id); // string(3) "205"
exec() 返回的是影響的行數,如果我們執行這一條 SQL ,返回的就是成功添加了一行資料。如果要獲得新增加資料的 id ,就要使用 lastInserId() 方法來獲取。
$count = $pdo->exec("insert into zyblog_test_user(`username`,'ckk','dkk')"); // Fatal error: Uncaught PDOException: SQLSTATE[21S01]: Insert value list does not match column list: 1136 Column count doesn't match value count at row 1
執行錯誤的 SQL 語句,就像根據 PDO::ATTR_ERRMODE 屬性的設定來返回錯誤資訊。我們在最上面的例項化 PDO 程式碼中指定了錯誤形式是異常處理模式,所以這裡直接就會報 PDOException 異常。
修改操作
// 正常更新 $count = $pdo->exec("update zyblog_test_user set `username`='aakk' where id='{$id}'"); var_dump($count); // int(1) // 資料不變更新 $count = $pdo->exec("update zyblog_test_user set `username`='aakk' wherVwMyrZMkke id='{$id}'"); var_dump($count); // int(0) // 條件錯誤更新 $count = $pdo->exec("update zyblog_test_user set `username`='aakk' where id='123123123123'"); var_dump($count); // int(0) echo '===============',PHP_EOL;
同樣的,在執行更新操作的時候,exec() 返回的也是受影響的行數。很多小夥伴會以這個進行判斷是否更新成功,但如果資料沒有修改,那麼它返回的將是 0 ,SQL 語句的執行是沒有問題的,邏輯上其實也沒有問題。比如我們在後臺打開了某條資料檢視,然後並不想更新任何內容就直接點了提交,這時候不應該出現更新失敗的提示。也就是說,在前端判斷更新操作的時候,需要判斷欄位是否都有改變,如果沒有改變的話那麼不應該提示更新失敗。這一點是業務邏輯上的考慮問題,如果你認為這樣也是更新失敗的話,那麼這麼報錯也沒有問題,一切以業務形式為主。
刪除操作
$count = $pdo->exec("delete from zyblog_test_user where id = '{$id}'"); var_dump($count); // int(1) // 條件錯誤刪除 $count = $pdo->exec("delete from zyblog_test_user where id = '5555555555'"); var_dump($count); // int(0)
刪除操作需要注意的問題和更新操作是一樣的,那就是同樣的 exec() 只是返回影響行數的問題,不過相對於更新操作來說,沒有受影響的行數那肯定是刪除失敗的,沒有資料被刪除。同樣的,這個失敗的提示也請根據業務情況來具體分析。
總結
不學不知道,一學嚇一跳吧,簡簡單的一個 PDO 的建立和語句執行竟然有這麼多的內容。對於我們的日常開發來說,掌握這些原理能夠避免很多莫名其妙的問題,比如上面 exec() 只是返回影響行數在業務開發中如何判斷操作是否成功的問題就很典型。好了,這只是第一篇,後面的學習不要落下了哦!
測試程式碼
以上就是PHP如何初始化PDO及原始SQL語句操作的詳細內容,更多關於PHP 初始化PDO及原始SQL語句操作的資料請關注我們其它相關文章!