我們使用傳統的 mysql_connect 、mysql_query方法來連接查詢數據庫時,如果過濾不嚴就有SQL注入風險,導致網站被攻擊。
雖然可以用mysql_real_escape_string()函數過濾用戶提交的值,但是也有缺陷。
而使用PHP的PDO擴展的 prepare 方法,就可以避免sql injection 風險。
PDO(PHP Data Object) 是PHP5新加入的一個重大功能,因為在PHP 5以前的php4/php3都是一堆的數據庫擴展來跟各個數據庫的連接和處理.
如 php_mysql.dll。 PHP6中也將默認使用PDO的方式連接,mysql擴展將被作為輔助 。
官方:http://php.net/manual/en/book.pdo.php
1、使用PDO連接前需要先確認PDO擴展是否已經打開。
使用PDO擴展之前,先要啟用這個擴展,PHP.ini中。
去掉"extension=php_pdo.dll"前面的";"號,若要連接數據庫,還需要去掉與PDO相關的數據庫擴展前面的";"號。
(一般用的是php_pdo_mysql.dll),然后重啟Apache服務器即可。
extension=php_pdo.dll
extension=php_pdo_mysql.dll
2、PDO連接數據庫:
class MysqlPdo
{
private $config = [
// 數據庫類型
'type' => 'mysql',
// 服務器地址
'hostname' => '127.0.0.1',
// 數據庫名
'database' => 'test',
// 用戶名
'username' => 'root',
// 密碼
'password' => 'root',
// 端口
'hostport' => '3306',
// 數據庫編碼默認采用utf8
'charset' => 'utf8',
];
private $pdo;
public function __construct()
{
$dsn = "{$this->config['type']}:host={$this->config['hostname']};";
$dsn.= "dbname={$this->config['database']};charset={$this->config['charset']}";
$pdo = new PDO($dsn, $this->config['username'], $this->config['password']);
$pdo->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$this->pdo = $pdo;
unset($dsn);
}
public function select($sql = '')
{
$res = $this->pdo->query($sql);
$res = $res->fetchAll(PDO::FETCH_ASSOC);
return $res;
}
}
3、PDO設置屬性:
PDO有三種錯誤處理方式:
1、PDO::ERrmODE_SILENT 不顯示錯誤信息,只設置錯誤碼
2、PDO::ERrmODE_WARNING 顯示警告錯
3、PDO::ERrmODE_EXCEPTION 拋出異常
1 $pdo->setAttribute(\PDO::ATTR_ERrmODE, \PDO::ERrmODE_EXCEPTION);
1) :當設置為PDO::ERrmODE_SILENT時可以通過調用errorCode() 或errorInfo()來獲得錯誤信息,當然其他情況下也可以。
2) :因為不同數據庫對返回的字段名稱大小寫處理不同,所以PDO提供了PDO::ATTR_CASE設置項(包括PDO::CASE_LOWER,PDO::CASE_NATURAL,PDO::CASE_UPPER),來確定返回的字段名稱的大小寫。
3) :通過設置PDO::ATTR_ORACLE_NULLS類型(包括PDO::NULL_NATURAL,PDO::NULL_EmpTY_STRING,PDO::NULL_TO_STRING)來指定數據庫返回的NULL值在php中對應的數值。
4、PDO常用方法及其應用:
PDO::query() 主要是用於有記錄結果返回的操作,特別是SELECT操作
PDO::exec() 主要是針對沒有結果集合返回的操作,如INSERT、UPDATE等操作
PDO::prepare() 主要是預處理操作,需要通過$rs->execute()來執行預處理里面的SQL語句,這個方法可以綁定參數,功能比較強大(防止sql注入就靠這個)
PDO::lastInsertId() 返回上次插入操作,主鍵列類型是自增的最后的自增ID
PDOStatement::fetch() 是用來獲取一條記錄
PDOStatement::fetchAll() 是獲取所有記錄集到一個集合
PDOStatement::fetchColumn() 是獲取結果指定第一條記錄的某個字段,缺省是第一個字段
PDOStatement::rowCount() 主要是用於PDO::query()和PDO::prepare()進行DELETE、INSERT、UPDATE操作影響的結果集,對PDO::exec()方法和SELECT操作無效。
5、PDO操作實例:
【示例6,說明】,防sql注入操作:
1、使用PDO訪問MySQL數據庫時,真正的real prepared statements 默認情況下是不使用的。
為了解決這個問題,你必須禁用 prepared statements的仿真效果。
2、設置禁用prepared statements:$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
它會告訴PDO禁用模擬預處理語句,並使用 real parepared statements 。
這可以確保SQL語句和相應的值在傳遞到mysql服務器之前是不會被PHP解析的(禁止了所有可能的惡意SQL注入攻擊)。
雖然你可以配置文件中設置字符集的屬性(charset=utf8),但是需要格外注意的是,老版本的 PHP( < 5.3.6)在DSN中是忽略字符參數的。
但是我們需要注意以下幾種情況,PDO並不能幫助我們防范SQL注入的:
1、你不能讓占位符 ? 代替一組值,如:
select * from xc_company where id in( ? );
2、你不能讓占位符代替數據表名或列名,如:
select * from xc_company order by ?;
3、你不能讓占位符 ? 代替任何其他SQL語法,如:
select EXTRACT( ? from date) as times from xc_company;
查看PDO都有那些方法
echo '<pre>'; print_r(get_class_methods('PDO')); echo '</pre>';
結果:
Array ( [0] => __construct [1] => prepare [2] => beginTransaction [3] => commit [4] => rollBack [5] => inTransaction [6] => setAttribute [7] => exec [8] => query [9] => lastInsertId [10] => errorCode [11] => errorInfo [12] => getAttribute [13] => quote [14] => __wakeup [15] => __sleep [16] => getAvailableDrivers )
常用實例源碼:
<?php # 【PDO連接mysql數據庫】(數據庫名:lmgg ,賬戶:root,密碼:root) $pdo = new \PDO('mysql:host=localhost;dbname=lmgg;charset=utf8','root','root'); # 設置數據庫編碼為utf-8(防止亂碼)當然上面的連接設置了。 # 上面可設置也可不設置,有沒有charset=utf8都可以連接上數據庫 $pdo->exec('set names utf8'); # 【示例1:查詢數據】:查詢company表中的id和name字段。存到$data數組中去 $res = $pdo->query("select id,name from xc_company"); $data = []; # 1:FETCH_ASSOC 關聯數組形式返回 # 2:FETCH_NUM 數字索引數組形式返回 # 設置返回數據類型方法1: $res->setFetchMode(\PDO::FETCH_NUM); while($row = $res->fetch()){ $data[] = $row; } echo '<pre>'; print_r($data); echo '</pre>'; # 設置返回數據類型方法2: while($row = $res->fetch(\PDO::FETCH_ASSOC)){ $data[] = $row; } echo '<pre>'; print_r($data); echo '</pre>'; # 【示例2:添加數據】:添加數據到company表中,並返回數據在表中的ID是多少! $res = $pdo->exec("insert into xc_company(name) values('小川編程添加111')"); if($res){ echo '11添加成功數據ID為:'.$pdo->lastinsertid().'<br/>'; } $res = $pdo->query("insert into xc_company(name) values('小川編程添加222')"); if($res){ echo '22添加成功數據ID為:'.$pdo->lastinsertid().'<br/>'; } # 【示例3:更新數據】: $res = $pdo->exec("update xc_company set name='小川編程更新111' where id=26"); if($res){ echo '更新數據成功<br/>'; } $res = $pdo->query("update xc_company set name='小川編程更新222' where id=26"); if($res){ echo '成功更新數據【'.$res->rowCount().'】條<br/>'; } # 【示例4:刪除數據】: $res = $pdo->exec("delete from xc_company where id=38"); if($res){ echo '刪除數據成功<br/>'; } $res = $pdo->query("delete from xc_company where id=38"); if($res){ echo '成功刪除數據【'.$res->rowCount().'】條<br/>'; } # 【示例5:統計數據】:統計company表有多少條數據 $num = $pdo->query("select count(*) from xc_company"); echo '共有數據:【'.$num->fetchColumn().'】條'; # 【示例6:防sql注入】:實例,使用前先看文字說明,會理解的更透徹! # PDO連接mysql數據庫(數據庫名:lmgg ,賬戶:root,密碼:root) $pdo = new \PDO("mysql:host=localhost; dbname=lmgg", "root", "root"); # 禁用prepared statements的仿真效果 $pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false); $pdo->query("set names 'utf8'"); $sql="select * from xc_company where name = ?"; $res = $pdo->prepare($sql); $name = '小川編程'; $exeres = $res->execute(array($name)); $data = []; if($exeres){ while ($row = $res->fetch(\PDO::FETCH_ASSOC)){ $data[] = $row; } } echo '<pre>'; print_r($data); echo '</pre>'; $pdo = null; ?>
$dsn= "{$this->config['type']}:host={$this->config['hostname']};";
$dsn.= "dbname={$this->config['database']};charset={$this->config['charset']}";
$pdo = new PDO($dsn, $this->config['username'], $this->config['password']);