很多使用php寫的舊系統,連結mysql用的方式為mysql_connect()、mysql_xxxxxx.....,但php從5.5.0就不再對mysql_的擴充提供支援了,雖然還能在之後像5.6.x的版本使用,但是速度及極可能發生sql injection的低安全性是mysql_擴充應該被淘汰的主因,而且以現在的趨勢,php7會成為之後的開發主流,而mysql_擴充則是完完全全在php7被拿掉!
於是一部分人轉用了mysqli_擴充,因轉換的速度非常快,不用在原有的架構做太多的變動,基本上除了一些呼叫的變數不一樣位置以外,幾乎加個「i」再微調幾下就好了,mysqli的確比mysql速度還要快,安全性也要更高,但兩者共同有一個缺點:如果專案需要使用不同的資料庫(Ex. mysql、sqlite3、......),那就必須要引入並使用不同的資料庫擴充語法和介面,想想就蠻麻煩的吧。
所以進到了今天的重頭戲:PDO,PDO是一個抽象層(abstract layer),這個抽象層就建立在我們寫的php和php連接到資料庫的方式之間,不過雖然PDO支援不同的資料庫之間(上面提及的( mysql 、sqlite3 ),但送入執行的sql語法一樣是我們自己寫的,意思是如果有些sql語法是特定資料庫才提供的特性,那麼嚴格來說PDO還是沒有完美的提供一個統整的解決方案,以現階段來說,在專案裡面還是需要調整sql語法使其支援所有使用的資料庫。
以下來示範實作:
建立好了連結,接著建好資料:
最簡單取值的方式:
結果:Chih, Hong
此外,PDO擴充還提供了一個很方便的「消毒」方式,即使前端已經過濾了使用者輸入,但是關係到資料庫的安全,後端接到前端丟過來的值時,還是一樣要做消毒,以下是PDO的消毒實作:
先在原本的sql條件加了個佔位符,也就是:last_name,接著prepare之後,就能安全地在用bindValue去綁定任何值。
結果:Chih, Hong
bindValue()其實有三個參數,第三個參數如果沒設定的話,PDO會假定資料為String,而如果現在要查詢的條件為整數、布林值或NULL,那我們必須要改變一點寫法:
結果:Chih, Hong
這裡有個細節要小心,bindValue裡的佔位符變數和sql語法裡的佔位變數大小寫不一樣的話,是不會通過的,PDO會拋出Exception告訴你佔位符尚有變數未設定,意思是說,如果sql裡寫的是:id,bindValue設定的是:Id,就會出錯,一定要特別注意。
出錯訊息:
接著介紹最後一個PDO也很容易實現的特點:交易機制
專案上線之前,勢必要把天災人禍考慮進去,萬一機房進水、虛擬主機商跑路或是停電種種狀況發生,當sql進到一半,可能一連串資料有的進了資料庫,有的卻沒進去,結果最後資料對不起來,整個一團亂,所以需要一個機制去控制資料,讓他們同時成功,或是同時失敗,范逸臣突然拍我肩膀說,講這麼多幹嘛,總之就一句話:
「 留下來,或是我跟你走!」
好的,接下來實作交易機制:
首先把資料庫調整一下
假設我跟阿范(突然很熟?!)都各自有100,某天我夾自助餐的時候,不小心夾了120,糟糕,趕快跟阿范借20,所以這裡我們兩筆資料同時要做變動,我將會多20,而阿范會少20,阿范轉給我20的時候,如果系統有突發狀況,我沒收到20,他卻已經扣了20,就會非常的麻煩,還好系統有交易機制:
系統使用了beginTransaction及commit,將這組動作包裝成單一的資料庫交易,確定這兩筆會同時成功,或是同時失敗。
結果:
終於可以吃便當了!
參考資料:
http://php.net/manual/en/intro.mysql.php
http://php.net/manual/en/intro.mysqli.php
http://stackoverflow.com/questions/23134769/pdostatementexecute-sqlstatehy093-invalid-parameter-number-parameter-wa
https://www.tenlong.com.tw/items/9863477788?item_id=1007177
http://www.books.com.tw/products/0010556526
於是一部分人轉用了mysqli_擴充,因轉換的速度非常快,不用在原有的架構做太多的變動,基本上除了一些呼叫的變數不一樣位置以外,幾乎加個「i」再微調幾下就好了,mysqli的確比mysql速度還要快,安全性也要更高,但兩者共同有一個缺點:如果專案需要使用不同的資料庫(Ex. mysql、sqlite3、......),那就必須要引入並使用不同的資料庫擴充語法和介面,想想就蠻麻煩的吧。
所以進到了今天的重頭戲:PDO,PDO是一個抽象層(abstract layer),這個抽象層就建立在我們寫的php和php連接到資料庫的方式之間,不過雖然PDO支援不同的資料庫之間(上面提及的( mysql 、sqlite3 ),但送入執行的sql語法一樣是我們自己寫的,意思是如果有些sql語法是特定資料庫才提供的特性,那麼嚴格來說PDO還是沒有完美的提供一個統整的解決方案,以現階段來說,在專案裡面還是需要調整sql語法使其支援所有使用的資料庫。
以下來示範實作:
try { $host = '127.0.0.1'; $dbname = 'dbname'; $username = 'username'; $password = 'password'; $dsn = 'mysql:host=' . $host . ';dbname=' . $dbname; $pdo = new PDO($dsn, $username, $password); } catch (PDOException $e) { echo "錯誤訊息:" . $e->getMessage(), PHP_EOL; exit; }
建立好了連結,接著建好資料:
$sql = " select * from test"; $result_query = $pdo->query($sql); $result_fetch = $result_query->fetchAll(); echo $result_fetch[0]['first_name'], PHP_EOL;
結果:Chih, Hong
此外,PDO擴充還提供了一個很方便的「消毒」方式,即使前端已經過濾了使用者輸入,但是關係到資料庫的安全,後端接到前端丟過來的值時,還是一樣要做消毒,以下是PDO的消毒實作:
$sql = ' select * from test where last_name = :last_name'; $statement = $pdo->prepare($sql); // 假設用POST接到值是'Chen'$post_lastname = 'Chen'; $statement->bindValue(':last_name', $post_lastname); $statement->execute(); $result_fetch = $statement->fetchAll(); echo $result_fetch[0]['first_name'], PHP_EOL;
先在原本的sql條件加了個佔位符,也就是:last_name,接著prepare之後,就能安全地在用bindValue去綁定任何值。
結果:Chih, Hong
bindValue()其實有三個參數,第三個參數如果沒設定的話,PDO會假定資料為String,而如果現在要查詢的條件為整數、布林值或NULL,那我們必須要改變一點寫法:
$sql = ' select * from test where id = :id'; $statement = $pdo->prepare($sql); // 假設用POST接到值是1 $post_id = 1; $statement->bindValue(':id', $post_id, PDO::PARAM_INT); $statement->execute(); $result_fetch = $statement->fetchAll(); echo $result_fetch[0]['first_name'], PHP_EOL;
結果:Chih, Hong
這裡有個細節要小心,bindValue裡的佔位符變數和sql語法裡的佔位變數大小寫不一樣的話,是不會通過的,PDO會拋出Exception告訴你佔位符尚有變數未設定,意思是說,如果sql裡寫的是:id,bindValue設定的是:Id,就會出錯,一定要特別注意。
出錯訊息:
接著介紹最後一個PDO也很容易實現的特點:交易機制
專案上線之前,勢必要把天災人禍考慮進去,萬一機房進水、虛擬主機商跑路或是停電種種狀況發生,當sql進到一半,可能一連串資料有的進了資料庫,有的卻沒進去,結果最後資料對不起來,整個一團亂,所以需要一個機制去控制資料,讓他們同時成功,或是同時失敗,范逸臣突然拍我肩膀說,講這麼多幹嘛,總之就一句話:
「 留下來,或是我跟你走!」
好的,接下來實作交易機制:
首先把資料庫調整一下
$sql1 = ' update test set money = money - :money where last_name = :last_name'; $sql2 = ' update test set money = money + :money where last_name = :last_name'; $statement1 = $pdo->prepare($sql1); $statement2 = $pdo->prepare($sql2); // 開始交易 $pdo->beginTransaction(); // 假設匯款的是 Fan $from_lastname = 'Fan'; $withdrawal = 20; $statement1->bindParam(':money', $withdrawal, PDO::PARAM_INT); $statement1->bindParam(':last_name', $from_lastname, PDO::PARAM_STR); $statement1->execute(); // 假設收款的是 我 $to_lastname = 'Chen'; $deposit = 20; $statement2->bindParam(':money', $deposit, PDO::PARAM_INT); $statement2->bindParam(':last_name', $to_lastname, PDO::PARAM_STR); $statement2->execute(); // 承認交易 $pdo->commit();
系統使用了beginTransaction及commit,將這組動作包裝成單一的資料庫交易,確定這兩筆會同時成功,或是同時失敗。
結果:
終於可以吃便當了!
參考資料:
http://php.net/manual/en/intro.mysql.php
http://php.net/manual/en/intro.mysqli.php
http://stackoverflow.com/questions/23134769/pdostatementexecute-sqlstatehy093-invalid-parameter-number-parameter-wa
https://www.tenlong.com.tw/items/9863477788?item_id=1007177
http://www.books.com.tw/products/0010556526
沒有留言:
張貼留言