雲端教學課程
PDO - MySQL
PHP Data Objects
為什麼要使用PDO ?
現在鼓勵用 PDO 連 MySQL,主要的原因是 mysql_*
已經被 PHP 5.5+ 宣告為 deprecated:
還有 escape 的原因,mysql_escape_string() 不知道
MySQL 連線的 charset,對於 escape 會有影響。
PDO 提供了相似的界面,對於開發者比較友善。
在開始之前
- 打開localhost/phpmyadmin
- 設定資料庫編碼 utf8_unicode
- 建立blog資料庫及使用者
- 新增message資料表 ( id,時間,留言者,留言內容 )
- 在message資料表打上測試資料
連接資料庫
config.php
<?php
$config['db']['dsn']='mysql:host=localhost;dbname=blog;charset=utf8';
$config['db']['user'] = 'blog';
$config['db']['password'] = '';
$db = new PDO(
$config['db']['dsn'],
$config['db']['user'] ,
$config['db']['password'],
array(
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
)
);
Try catch
message.php
<?php
require 'config.php';
function some_logging_function($log){
echo 'LOG : ' . $log . '<br />';
}
try {
$db->query('hi'); //錯誤的查詢字串
} catch(PDOException $ex) {
echo "有錯誤發生!<br />"; //使用者訊息
some_logging_function($ex->getMessage());
}
查詢資料
<?php
function getData($db) { $stmt = $db->query("SELECT * FROM message"); return $stmt->fetchAll(PDO::FETCH_ASSOC);//回傳全部資料 } //then much later try { $data = getData($db);
var_dump($data);//印出資料
} catch(PDOException $ex) {
some_logging_function($ex->getMessage()); }
查詢資料 - foreach
<?php //model
$data = getData($db);
<!--View-->
<table>
<thead> <tr> <th>序號</th><th>時間</th><th>留言者</th><th>內容</th> </tr> </thead> <tbody>
<?php foreach( $data as $message ){ ?> <tr> <td><?php echo $message['message_id'];?></td> <td><?php echo $message['message_time'];?></td> <td><?php echo $message['messager'];?></td> <td><?php echo $message['message'];?></td> </tr>
<?php } ?> </tbody> </table>
資訊安全 - XSS、CSRF攻擊
記得把輸出的資料過濾,才不會在留言裡被塞程式碼。
攻擊示範 : 把這輸入到留言裡按送出。
<script type="text/javascript">
alert('HI!這是XSS攻擊!');
location = 'http://163.17.136.249';
</script>
防護方法 : strip_tags( ) 或 htmlspecialchars( )
<?php $str = "<a href=\"blah.htm\">Text</a>"; echo strip_tags($str); // Text
//http://www.php.net/manual/en/function.strip-tags.php
echo htmlspecialchars($str); // <a href=\"blah.htm\">Text</a>
//http://www.php.net/manual/en/function.htmlspecialchars.php
修改後的輸出
把不是由系統產生的都加上過濾。
<tbody>
<?php foreach( $data as $message ){ ?> <tr> <td><?php echo $message['message_id'];?></td> <td><?php echo $message['message_time'];?></td> <td><?php echo htmlspecialchars($message['messager']);?></td> <td><?php echo htmlspecialchars($message['message']);?></td> </tr>
<?php } ?> </tbody>
查詢資料 - rowCount()
Model
<?php
function getData($db) {
$stmt = $db->query("SELECT * FROM message");
return array(
'rows' => $stmt->fetchAll(PDO::FETCH_ASSOC),
'rowCount' => $stmt->rowCount()//回傳資料數
);
}
<table>
<caption>總共<?php echo $data['rowCount'];?>筆留言</caption>
<thead>
...
</thead>
<tbody>
...
</tbody>
</table>
新增資料 - lastInsertId()
message_add.php
<?php
require 'config.php';
$result = $db->exec("INSERT INTO message" .
"(`message_time`, `messager` ,`message`)" .
"VAULES(NOW(),'John', 'This is a message')");
$insertId = $db->lastInsertId();
echo '新增成功,id=' . $insertId;
SQL要自己打才會發現錯誤哦 ^_<
資訊安全 - SQL Injection攻擊
- 進到SQL的參數都要過濾過,嚴重資料庫都能被刪掉。
-
mysql_*時會用mysql_real_escape_string()過濾,
但會有編碼問題。 - 在PDO時我們會用prepare方法來過濾。
<?php
require 'config.php';
$messager = $_POST['messager'];
$message = $_POST['message'];
$result = $db->exec("INSERT INTO message" . "(`message_time`, `messager` ,`message`)" . "VAULES(NOW(),'$messager', '$message')");
帶參數 - Preparing
<?php
require 'config.php';
$messager = '鬼才';
$message = '測試資料123';
$stmt = $db->prepare("INSERT INTO message" . "(`message_time`, `messager` ,`message`)" . "VAULES(NOW(),:messager, :message)"); $stmt->execute(array(
':messager' => $messager,
':message' => $message
));
$insertId = $db->lastInsertId();
echo '新增成功,id=' . $insertId;
整理成函式
<?php
require 'config.php';
function new_message( $db ,$messager ,$message ){
$stmt = $db->prepare("INSERT INTO message" . "(`message_time`, `messager` ,`message`)" . "VAULES(NOW(),:messager, :message)"); $stmt->execute(array(
':messager' => $messager,
':message' => $message
));
return $db->lastInsertId();
}
echo '新增成功,id=' . new_message( $db ,'鬼才' ,'測試ABC');
新增表單
message.php
<form action="message_add.php" method="post">
<label for="messager">留言者</label> <input type="text" name="messager" id="messager" /> <br /> <label for="message">留言內容</label> <textarea name="message" id="message"></textarea> <br /> <input type="submit" /> <input type="reset" /> </form>
message_add.php
<?php
echo '新增成功,id=' . new_message( $db ,$_POST['messager'] ,$_POST['message']);
處理資料 - affected_rows
message_delete.php
<?php
require 'config.php';
function delete_user_message( $db ,$messager ,$message ){
$stmt = $db->prepare("UPDATE `message` SET " .
"`message` = :message " .
"WHERE `messager` = :messager");
$stmt->execute(array(
':messager' => $messager,
':message' => $message
));
return $stmt->rowCount();
}
echo '成功刪除' . delete_user_message($db,'鬼才','這是垃圾訊息') . '筆資料。';
處理資料 - 刪除特定留言 - 1
message.php
<!--View--> <form action="message_delete.php" method="post"> <button type="submit">確定刪除</button> <table> ... <tbody> <?php foreach( $data['rows'] as $message ){ ?> <tr> <td>
<label> <input type="checkbox" name="message_id[]" value="<?php echo $message['message_id'];?>" /> <?php echo $message['message_id'];?> </label>
</td> ... </tr> <?php } ?> </tbody> </table> </form>
處理資料 - 刪除特定留言 - 2
message_delete.php -1
<?php //model
... function delete_message( $db ,$message_id ,$message ){ try{ $stmt = $db->prepare("UPDATE `message` SET " . "`message` = :message " . "WHERE `message_id` = :message_id");
foreach($message_id as $id){ $stmt->execute(array( ':message_id' => $id, ':message' => $message )); } return 1; } catch(PDOException $ex) { return 0; } }
處理資料 - 刪除特定留言 - 3
message_delete.php -2
<?php //controller
if ( isset($_POST['messager']) ){ echo '成功刪除' . delete_user_message($db, $_POST['messager'], '這是垃圾訊息。') . '筆資料。';
} else if ( isset($_POST['message_id']) ){ if(delete_message($db, $_POST['message_id'], '這是垃圾訊息。')){ echo '成功刪除'; } else{ echo '失敗'; }
} else{ echo '參數錯誤。'; }
處理資料 - 刪除特定留言 - 4
message.php
<form action="message_delete.php" method="post">
<label for="messager">使用者</label>
<input type="text" value="" name="messager" id="messager" />
<input type="submit" value="刪除特定使用者留言" />
</form>
但是POST的參數不同,處理的function也不同。
- POST message_id 會刪除指定的留言。
- POST messager 會刪除指定使用者的留言。
交易 - Transactions
如果其中一條SQL錯誤,就把剛執行過的都還原回去。
<?php try { $db->beginTransaction(); $db->exec("SOME QUERY"); $stmt = $db->prepare("SOME OTHER QUERY?"); $stmt->execute(array($value)); $stmt = $db->prepare("YET ANOTHER QUERY??"); $stmt->execute(array($value2, $value3)); $db->commit();
} catch(PDOException $ex) { //Something went wrong rollback! $db->rollBack(); echo $ex->getMessage(); }
回家作業
- 會員註冊登入,每個會員一個留言版。
- 會員密碼用sha1加密或md5加密。
- 管理員身份刪除留言。
- 限制登入後才能留言。
- 研究MVC架構,
下週會做出一個跟無名小站一樣的BLOG。
題示 :
-
每個會員一個留言版可以在message新增一個
會員編號欄位。 -
會員登入功能另外建立一個會員資料表,
用SESSION記錄登入狀態。 - 參考程式碼及MVC觀念在下面。
參考資料
判斷會員登入
isset($_SESSION['user'])
會員登出 logout.php
unset($_SESSION['user'])
判斷管理員 ( 使用者名稱 = 留言版版主名稱)
function is_admin( $message_board ,$_SESSION['user']['member'] ){
if( $message_board == $_SESSION['user']['member'] ){
return 1;
}
else{
return 0;
}
}
參考資料
會員登入 login.php
<?php function login( $db ,$member ,$password){ $stmt = $db->prepare("SELECT * `member` WHERE " . "`member` = :member " . "AND `password` = :password"); $stmt->execute(array( ':member' => $member, /*拿加密過的密碼去比對資料庫已加密的密碼*/ ':password' => sha1($password) ));
if( $stmt->rowCount() == 1 ){//是否有符合的資料 $user = $stmt->fetchAll(PDO::FETCH_ASSOC); $_SESSION['user'] = $user[0];//把會員資料寫進SESSION裡 return 1;//登入成功 } else{ return 0;//登入失敗 } }
參考資料
MVC方式
index.php?page=頁面名稱 [&board=會員名稱]
- 頁面名稱 : 留言列表 、刪除留言、登入、登出、註冊。
- 會員名稱 : 在留言列表時用來指定瀏覽的留言版。
- Controller判斷現在是在那個會員的留言版。
- 呼叫Model取回留言版內的留言。
- Model收到呼叫後,用SQL對資料庫操作再回傳。
- Controller收到回傳的資料,呼叫View來顯示。
參考資料
MVC概念

PDO
By jackai
PDO
- 8,836