hi
昨天又213了,雖然有室友3點多才睡覺的客觀影響,但是昨晚不想學東西是本質原因。今天搞起。打算3、4天之內,學完PDO和AJAX這兩個,還望大家沒事兒來罵罵我,免的我又偷懶。
1、PDO
二、PDO對象的使用(二)
2.2 錯誤信息
errorCode()——錯誤號;
errorInfo()——錯誤信息;
舉個栗子
<?php
/*
* PDO錯誤信息
*/
$pdo=new PDO('mysql:host=localhost;dbname=imooc','root','');
$pdo->exec('use imooc_pdo');
$resultd=$pdo->exec('delete from user where id=13');
var_dump($resultd);
$insert='insert user(username,password,email) values("Knga","'.md5('king').'","[email protected]")';
$result1=$pdo->exec($insert);
var_dump($result1);
if ($result1==false) {
echo "出錯了";
echo $pdo->errorCode();
print_r($pdo->errorInfo());
}
看一下錯誤信息
Array ( [0] => 23000 [1] => 1062 [2] => Duplicata du champ 'Knga' pour la clef 'username' )
0為錯誤類型,1062是代碼,2是錯誤信息;(這裡是由於username設置為了unique鍵,但是id號是還在增長的其實)。
2.3 query()實現查詢
執行一條語句,返回一個PDOstatement對象。
--舉個栗子
<?php
/*
* PDOquery
*/
$pdo=new PDO('mysql:host=localhost;dbname=imooc','root','');
$pdo->exec('use imooc_pdo');
$insert='select * from user';
$result1=$pdo->query($insert);
var_dump($result1); //查看statement對象
foreach ($result1 as $row){ //查看輸出結果(根據返回情況)
print_r($row);
}
if ($result1==false) {
echo "出錯了";
echo $pdo->errorCode();
print_r($pdo->errorInfo());
}
如果sql語句有問題的話,statement對象是false,然後後面的輸出也是錯誤信息;
如果sql語句正確,但查詢的內容是不存在的,那麼statement對象沒問題,然後輸出為空。
當然這樣會好看一些:
foreach ($result1 as $row){ //查看輸出結果(根據返回情況)
// print_r($row);echo "<br/>";
echo '編號:'.$row['id'];echo "<br/>";
echo '用戶名:'.$row['username'];echo "<br/>";
echo '密碼:'.$row['password'];echo "<br/>";
echo '郵箱:'.$row['email'];echo "<br/>";
echo "<hr/>";
}
當然,query執行增刪改都是沒問題的。
2.4 prepare()和execute()方法實現查詢
推薦使用的查詢方法,可以實現條件查詢。
prepare()——准備要執行的SQL語句,返回PDOstatement對象;
execute()——執行一條預處理語句,返回true或false;
所以上面是一對。
--舉個例子
<?php
/*
* PDOprepare&execute方法
*/
$pdo=new PDO('mysql:host=localhost;dbname=imooc','root','');
$pdo->exec('use imooc_pdo');
$insert='select * from user where username="king"';
$result=$pdo->prepare($insert);
var_dump($result);
$result1=$result->execute();//執行是對預處理語句
var_dump($result1);
print_r($result->fetchAll());//對statement對象才能有結果輸出
if ($result1==false) {
echo "出錯了";
echo $pdo->errorCode();
print_r($pdo->errorInfo());
}
這裡要小心預處理這種特殊情況,分清楚對象到底是誰就好辦了。
--選取輸出形式
要關聯數組輸出或者全部或者索引數組,有參數和方法兩種不同的方法。
<?php
header('content-type:text/html;charset=utf-8');
try{
$pdo=new PDO('mysql:host=localhost;dbname=imooc','root','root');
$sql='select * from user';
$stmt=$pdo->prepare($sql);
$res=$stmt->execute();
// if($res){
// while($row=$stmt->fetch(PDO::FETCH_ASSOC)){//僅需要關聯數組輸出
// print_r($row);
// echo '<hr/>';
// }
// }
// $rows=$stmt->fetchAll(PDO::FETCH_ASSOC);
// print_r($rows);
echo '<hr/>';
$stmt->setFetchMode(PDO::FETCH_ASSOC); //同樣的實現效果,用這個方法也可以,設置默認模式
//var_dump($stmt);
$rows=$stmt->fetchAll();
print_r($rows);
}catch(PDOException $e){
echo $e->getMessage();
}
一般的我們都是想要索引數組的。
2.5 設置數據庫連接屬性
setAttribute()——設置數據庫連接屬性;
getAttribute()——得到數據庫連接屬性;
--舉個例子
$pdo=new PDO('mysql:host=localhost;dbname=imooc','root','');
echo "自動提交".$pdo->getAttribute(PDO::ATTR_AUTOCOMMIT);echo "<hr/>";
//記住pdo是個對象,所以得到屬性,你懂的。然後它內部是有很多設定好的屬性值的,這就是我們得到屬性的前提。
echo "默認的錯誤處理模式:".$pdo->getAttribute(PDO::ATTR_ERRMODE);echo "<hr/>";
$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, 0);
echo "自動提交".$pdo->getAttribute(PDO::ATTR_AUTOCOMMIT);echo "<hr/>";
然後試著得到一大波屬性信息:
$attrArr=array(
'AUTOCOMMIT','ERRMODE','CASE','PERSISTENT','SERVER_INFO','SERVER_VERSION'
);
foreach ($attrArr as $attr){
echo "PDO::ATTR_$attr: ";
echo $pdo->getAttribute(constant("PDO::ATTR_$attr"))."<br/>";
}
有些是沒有的,會有錯誤信息,沒什麼關系。
三、PDOstatement對象的使用
3.1 quote()方法防止SQL注入
--SQL注入
首先舉個例子說明這個簡單的SQL注入(其實我也不是很懂——百度一下http://baike.baidu.com/link?url=jiMtgmTeePlWAqdAntWbk-wB8XKP8xS3ZOViJE9IVSToLP_iT2anuUaPdMEM0b-VDknjolQ8BdxN8ycNLohup_)
所謂SQL注入,就是通過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字符串,最終達到欺騙服務器執行惡意的SQL命令。
所以也就是要有表單,然後需要跟數據庫進行查詢數據等等,然後通過惡意的運用規則上的漏洞,得到大量的,而不是頁面所希望的數據。栗子如下:
例子為登錄的情況——登錄需要有用戶名密碼等,需要與數據庫中的信息進行比對;
首先是登錄頁面
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html>
<title>登錄</title>
</head>
<body>
<form action='doAction.php' method='post'>
用戶名:<input type='text' name='username'/><br/>
密碼:<input type='password' name='password'/><br/>
<input type='submit' value='登錄'/>
</form>
</body>
</html>
注意這裡出現了表單。然後是php文件:
<?php
header('content-type:text/html;charset=utf-8');
$username=$_POST['username'];
$password=$_POST['password'];
try {
$pdo=new PDO('mysql:host=localhost;dbname=imooc','root','');
$pdo->exec('use imooc_pdo');
$sql="select * from user where username='{$username}' and password='{$password}'";
$stmt=$pdo->query($sql);
echo $stmt->rowCount();//顯示結果集statement對象中的行數
} catch (PDOException $e) {
echo $e->getMessage();
}
然後浏覽器中打開login.html,輸入數據庫中有的username和password,點擊登陸,會得到1;
若輸入錯誤的信息,一般會得到0;
注意,若輸入諸如用戶名為'or 1=1#,密碼隨意,就會輕松得到數據庫的所有數據。這是由於sql語句本身的規則造成的。
所以需要過濾用戶輸入的信息,不要相信用戶的所有操作。
--應對方法
echo $pdo->quote($username);
寫這麼一句,再用上述的作弊代碼,輸出會多出單引號,以及自動加上\:
'\'or 1=1#'
但這麼做的話,$username的調用,會自動加上引號,所以下面的sql語句就要跟著變動:
$username=$pdo->quote($username);
$pdo->exec('use imooc_pdo');
$sql="select * from user where username={$username} and password='{$password}'";
簡單的說就是把用戶名上個套,對於有數據庫的情況,似乎都要防這個。
但是不建議使用這種手段——建議使用prepare+execute的預處理手段。
3.2 預處理語句中占位符的使用
很好的防止注入;而且一次編譯即可,多次執行,減小系統的開銷;
--占位符:(命名參數)(推薦)
<?php
header('content-type:text/html;charset=utf-8');
$username=$_POST['username'];
$password=$_POST['password'];
try {
$pdo=new PDO('mysql:host=localhost;dbname=imooc','root','');
$pdo->exec('use imooc_pdo');
$sql="select * from user where username=:username and password=:$password";
$stmt=$pdo->prepare($sql);
$stmt->execute(array(":username"=>$username,":password"=>$password));
//$stmt=$pdo->query($sql);
echo $stmt->rowCount();//顯示結果集statement對象中的行數
} catch (PDOException $e) {
echo $e->getMessage();
}
對應的sql語句,對應的執行,需要傳遞的參數也要對應的上。
--占位符?
$sql="select * from user where username=? and password=?";
$stmt=$pdo->prepare($sql);
$stmt->execute(array($username,$password));
感覺?方式要簡單一點,就三個點——sql語句中輸入占位符+預處理+執行(傳遞多個數據用array)。
3.3 bindParam()方法綁定參數
把一個參數綁定到變量名。
<?php
/*
* 綁定參數
*/
header('content-type:text/html;charset=utf-8');
try {
$pdo=new PDO('mysql:host=localhost;dbname=imooc','root','');
$pdo->exec('use imooc_pdo');
$sql="insert user(username,password,email) values(:username,:password,:email)";
$stmt=$pdo->prepare($sql);
$username="Wid";$password="123";$email="[email protected]"; //定義參數
$stmt->bindParam(":username", $username,PDO::PARAM_STR);
$stmt->bindParam(":password",$password);
$stmt->bindParam(":email",$email);
$stmt->execute();
$res=$pdo->query("select * from user");
foreach ($res as $row){ //查看輸出結果(根據返回情況)
// print_r($row);echo "<br/>";
echo '編號:'.$row['id'];echo "<br/>";
echo '用戶名:'.$row['username'];echo "<br/>";
echo '密碼:'.$row['password'];echo "<br/>";
echo '郵箱:'.$row['email'];echo "<br/>";
echo "<hr/>";
}
} catch (PDOException $e) {
echo $e->getMessage();
}
其實就是為了不用每次更改sql語句來執行略重復的操作。
當然還可以換個占位符
// $sql="insert user(username,password,email) values(?,?,?)";
// $stmt->bindParam(1,$username);
所以,總之,實際上:占位符會比較清楚,?會混淆。
3.4 bindValue()實現綁定參數
把值綁定到參數中。
<?php
/*
* 綁定參數
*/
header('content-type:text/html;charset=utf-8');
try {
$pdo=new PDO('mysql:host=localhost;dbname=imooc','root','');
$pdo->exec('use imooc_pdo');
$sql="insert user(username,password,email) values(:username,:password,:email)";
// $sql="insert user(username,password,email) values(?,?,?)";
$stmt=$pdo->prepare($sql);
//假設email參數不變
$stmt->bindValue(":email", '[email protected]');
$username="Wade";$password="123";
$stmt->bindParam(":username", $username,PDO::PARAM_STR);
$stmt->bindParam(":password",$password);
$stmt->execute();
$res=$pdo->query("select * from user");
foreach ($res as $row){ //查看輸出結果(根據返回情況)
// print_r($row);echo "<br/>";
echo '編號:'.$row['id'];echo "<br/>";
echo '用戶名:'.$row['username'];echo "<br/>";
echo '密碼:'.$row['password'];echo "<br/>";
echo '郵箱:'.$row['email'];echo "<br/>";
echo "<hr/>";
}
} catch (PDOException $e) {
echo $e->getMessage();
}
應用場景就是當某個值固定不變的時候,就可以固定變量的參數值。
3.5 bindColumn()方法綁定參數
將綁定一列到php對象。
$pdo=new PDO('mysql:host=localhost;dbname=imooc','root','');
$pdo->exec('use imooc_pdo');
$sql="select * from user";
$stmt=$pdo->prepare($sql);
$stmt->execute();
//控制輸出
$stmt->bindColumn(2, $username);
$stmt->bindColumn(3,$password);
$stmt->bindColumn(4,$email);
while ($stmt->fetch(PDO::FETCH_BOUND)){
echo '用戶名:'.$username.'-密碼:'.$password.'-郵箱:'.$email.'<hr/>';
}
這裡的用法就是對輸出結果進行控制,利於輸出格式的調控。
當然,可以看看結果集中到底有幾列,然後每一列是什麼:
echo '結果集中的列數:'.$stmt->columnCount().'<hr/>';
print_r($stmt->getColumnMeta(2));
3.6 fetchColumn()從結果集中取一列
上述的getColumnMeta()方法實際上在PHP該版本中是個實驗的函數,可能會在將來的版本中消失。
$stmt->execute();
print_r($stmt->fetchColumn(3));
需要注意該方法很蛋疼的地方在於會每執行一次,指針向下一位,所以只需要指定第幾列,但並不知道在哪一行。
3.7 debugDumpParams()打印一條預處理語句
在bindParam中測試這個方法:
$stmt->debugDumpParams();
結果是一大堆:
SQL: [71] insert user(username,password,email) values(:username,:password,:email) Params: 3 Key: Name: [9] :username paramno=-1 name=[9] ":username" is_param=1 param_type=2 Key: Name: [9] :password paramno=-1 name=[9] ":password" is_param=1 param_type=2 Key: Name: [6] :email paramno=-1 name=[6] ":email" is_param=1 param_type=2
也就是說會給出預處理時的詳細情況。
很明顯就是為了Debug而生的方法。
3.8 nextRowset()方法取出所有結果集
用於比如,mysql的存儲過程(看我之前mysql的博文就有),一下子取出很多結果集,然後對集進行操作。
實際上是指針一步步下移就好了。
例子我懶了,不想敲了。。。。
雖然沒寫很多,就這樣吧。
過兩天想去復查一下腳,雖然還在痛,不知道還敢不敢活血了。。。。