PDO数据库抽象层总结
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
PDO数据库抽象层总结
PDO(PHP Data Objects)是⼀种在PHP⾥连接数据库的使⽤接⼝。
PDO与mysqli曾经被建议⽤来取代原本PHP在⽤的mysql相关函数,基于数据库使⽤的安全性,因为后者⽋缺对于SQL注⼊的防护。
PDO的出现让PHP达到了⼀个新的⾼度。
PDO扩展类库为PHP访问数据库定义了⼀个轻量级、⼀致性的接⼝,它提供了⼀个数据访问抽象层,这样,⽆论使⽤什么数据库,都可以通过⼀致的函数执⾏查询和获取数据,这⼤⼤简化了数据库的操作,并能够屏蔽不同数据库之间的差异。
使⽤ PDO 可以很⽅便地进⾏跨数据库程序的开发,以及不同数据库间的移植,是将来PHP在数据库处理⽅⾯的主要发展⽅向。
⼀、pod安装:
php5.1以上的源代码包环境中,向configure命令中添加如下代码:
--with-pdo-MySQL=/url/local/MySQL //其中“/url/local/MySQL”为MySQL安装⽬录
⼆、php.ini 开启 PDO
extension = php_pdo.so
#下⾯开启其中之⼀
extension = php_pdo_MySQL.so #使⽤ MySQL 驱动程序
;extension = php_pdo_mssql.so #使⽤ SQL server 驱动程序
;extension = php_pdo_odbc.so #使⽤ ODBC 驱动程序
;extension = php_pdo_ocl.so #使⽤ oracle 驱动程序
三、PDO 连接数据库:
<?php
/**
* 连接 Oracle 数据库⽰例 -------------------------------------------------------------
*/
try {
//第⼀个参数是 DSN 字符串加载 Oracle 数据库,第⼀参数指定数据库,第⼆个参数指定字符集
$dbh = new PDO("OCI:dbname=accounts;charset=UTF-8", "userName", "password");
} catch(PDOException $e) {
exit('数据库连接失败' . $e->getMessage());
}
/**
* 连接 MySQL 数据库⽰例 -------------------------------------------------------------
*/
try {
//第⼀个参数是 DSN 字符串加载 mysql 数据库,DSN 的第⼀参数指定数据库,第⼆个参数指定地址
//"mysql:dbname=DBtest;host=127.0.0.1" 就是 DSN 字符串,“mysql”就是驱动且必须⼩写,“dbname”指数据库,“host”指地址,DSN字符串内不能有空格。
$dbh = new PDO("mysql:dbname=DBtest;host=127.0.0.1", "username", "password");
} catch(PDOException $e) {
exit('数据库连接失败' . $e->getMessage());
}
/**
* 将参数放⼊⽂件 --------------------------------------------------------------------------
*/
try {
//只要将⽂件/usr/local/dbconnect中的DSN驱动改变,就可以在多个数据库系统之间切换,但要确保该⽂件由负责执⾏PHP脚本的⽤户所拥有,⽽且此⽤户拥有必要的权限。
$dbh = new PDO("uri:file:///usr/local/dbconnect", "webuser", "password");
} catch(PDOException $e) {
exit('数据库连接失败' . $e->getMessage());
}
>
只要在php.ini⽂件中把DSN信息赋给⼀个名为pdo.dsn.aliasname的配置参数,就可以在PHP服务器的配置⽂件中维护DSN信息,这⾥aliasname是后⾯将提供给构造函数的DSN别名。
⽰例:
[PDO]
pdo.dsn.oraclepdo = "OCI:dbname=//localhost:1521/mysqldb;charset=UTF-8";
四、PDO 操作数据库:
1.⽰例数据库:
//结构:
CREATE table contactInfo (
uid mediumint(8) unsigned NOT NULL AUTO_INCREMENT, #联系⼈ID
name varchar(50) NOT NULL, #姓名
departmentId char(3) NOT NULL, #部门编号
address varchar(80) NOT NULL, #联系地址
phone varchar(20), #联系电话
email varchar(100), #联系⼈的电⼦邮箱
PRIMARY KEY(uid) #设置⽤户ID为主键
);
//插⼊的⽰例数据:
INSERT INTO contactInfo (name, departmentId, address, phone, email) VALUES ('孙先⽣', 'D01', '北京市海淀区', '1580168001', 'shunsir@');
INSERT INTO contactInfo (name, departmentId, address, phone, email) VALUES ('猪先⽣', 'D02', '北京市朝阳区', '1580168002', 'zhusir@');
INSERT INTO contactInfo (name, departmentId, address, phone, email) VALUES ('沙先⽣', 'D03', '北京市东城区', '1580168003', 'shasir@');
INSERT INTO contactInfo (name, departmentId, address, phone, email) VALUES ('唐先⽣', 'D01', '北京市西城区', '1580168004', 'tangsir@');
INSERT INTO contactInfo (name, departmentId, address, phone, email) VALUES ('⽩先⽣', 'D01', '北京市昌平区', '1580168005', 'baisir@');
2.当执⾏INSERT、UPDATE和DELETE等没有结果集的查询时,使⽤PDO对象中的exec()⽅法。
该⽅法成功执⾏后,将返回受影响的⾏数。
注意,该⽅法不能⽤于SELECT查询。
<?php
try {
$dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
} catch (PDOException $e) {
exit('数据库连接失败' . $e->getMessage());
}
$query = 'UPDATE contactInfo SET phone = 138******** WHERE name = "孙先⽣"';
//使⽤ exec() ⽅法可以执⾏INSERT、UPDATE和DELETE等操作
$affected = $dbh->exec($query);
if($affected){
echo '数据表 contactInfo 受影响的⾏数为' . $affected;
} else {
print_r($dbh->errorInfo());
}
>
3.当执⾏返回结果集的SELECT查询,或者所影响的⾏数⽆关紧要时,应当使⽤PDO对象中的query()⽅法。
<?php
$dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$query = 'SELECT name, phone, email FROM contactInfo WHERE departmentId = "D01"';
try {
//执⾏ SELECT 查询,并返回 PDOStatement 对象
$pdostatement = $dbh->query($query);
echo '⼀共从表中获取到' . $pdostatement->rowCount() . '条数据:<br>';
foreach($pdostatement as$row){
echo$row['name'] . ' ';
echo$row['phone'] . ' ';
echo$row['email'] . '<br>';
}
} catch (PDOException $e) {
echo$e->getMessage();
}
>
4.可以使⽤PDO过滤⼀些特殊字符,以防⼀些能引起SQL注⼊的代码混⼊。
我们在PDO中使⽤quote()⽅法实现,⽰例如下:
<?php
$dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$query = 'SELECT * FROM users WHERE login = "' . $dbh->quote($_POST['user']) . ' AND password = ' . $dbh->quote($_POST['pass']);
>
五、PDO对预处理语句的⽀持
SQL注⼊的最⼤原因是SQL语⾔的命令和数据混编,造成数据中被⾮法植⼊命令,特别是SQL语⾔的拼接是SQL注⼊的⼀⼤危害来源。
PDO 预处理语句使SQL语⾔的命令和数据分离,能有效减少SQL注⼊的危害,但没⼈打包票说是绝对有效的,因为技术每时每刻都在进步,就像发明SQL语⾔的⼈没有意识到还有SQL注⼊这个⿁⼀样。
1.使⽤命名参数作为占位符的INSERT查询。
<?php
try {
$dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
} catch (PDOException $e) {
exit('数据库连接失败' . $e->getMessage());
}
$query = 'INSERT INTO contactInfo (name, address, phone) VALUES ( :name, :address, :phone)';
$stmt = $dbh->prerare($query);
$stmt->bindParam(':name', $name);
$stmt->bindParam(':address', $address);
$stmt->bindParam(':phone', $phone);
$name = '齐天⼤圣';
$address = '花果⼭⽔帘洞';
$phone = '158********';
$stmt->execute(); //执⾏绑定参数后的 SQL 语句
$name = '猪⼋戒';
$address = '⾼⽼庄';
$phone = '158********';
$stmt->execute(); //再次执⾏绑定参数后的 SQL 语句
>
2.使⽤ ? 号数作为占位符的INSERT查询。
<?php
try {
$dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
} catch (PDOException $e) {
exit('数据库连接失败' . $e->getMessage());
}
$query = 'INSERT INTO contactInfo (name, address, phone) VALUES ( ?, ?, ?)';
$stmt = $dbh->prerare($query);
$stmt->bindParam(1, $name); //1是第⼀个?号
$stmt->bindParam(2, $address); //2是第⼆个?号
$stmt->bindParam(3, $phone); //3是第三个?号
$name = '齐天⼤圣';
$address = '花果⼭⽔帘洞';
$phone = '158********';
$stmt->execute(); //执⾏绑定参数后的 SQL 语句
$name = '猪⼋戒';
$address = '⾼⽼庄';
$phone = '158********';
$stmt->execute(); //再次执⾏绑定参数后的 SQL 语句
>
3.预处理查询在执⾏中替换输⼊参数的⽅式。
此语法能够省去对$stmt->bindParam()的调⽤。
此⽅法要在 execute() 中传⼊关联数组。
<?php
try {
$dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
} catch (PDOException $e) {
exit('数据库连接失败' . $e->getMessage());
}
$query = 'INSERT INTO contactInfo (name, address, phone) VALUES ( :name, :address, :phone);
$stmt->execute(":name"=>"齐天⼤圣", ":address"=>"花果⼭⽔帘洞", ":phone"=>"158********");
$stmt->execute(":name"=>"猪⼋戒", ":address"=>"⾼⽼庄", ":phone"=>"158********");
>
4.如果使⽤的是问号(?)参数,则需要传递⼀个索引数组,数组中每个值的位置都要对应每个问号参数。
<?php
try {
$dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
} catch (PDOException $e) {
exit('数据库连接失败' . $e->getMessage());
}
$query = 'INSERT INTO contactInfo (name, address, phone) VALUES ( ?, ?, ?)';
$stmt->execute("齐天⼤圣", "花果⼭⽔帘洞", "158********");
$stmt->execute("猪⼋戒", "⾼⽼庄", "158********");
>
5.使⽤ fetch() 查询数据库
<?php
try {
$dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
} catch (PDOException $e) {
exit('数据库连接失败' . $e->getMessage());
}
$query = 'SELECT uid, name, address, phone, email FROM contactInfo';
echo <<<Eof
<table border="1" align="center" width="90%">
<caption><h1>联系⼈信息表</h1></caption>
<tr bgcolor="#ccc">
<th>UID</th>
<th>姓名</th>
<th>地址</th>
<th>电话</th>
<th>电⼦邮箱</th>
Eof;
//使⽤ query() ⽅式查询数据库,建议⽤ prepare() 和 execute() 预处理语句执⾏查询
$stmt = $dbh->query($query);
while(list($uid, $name, $address, $phone, $email) = $stmt->fetch(PDO::FETCH_NUM)){
echo '<tr>';
echo '<td>' . $uid . '</td>';
echo '<td>' . $name . '</td>';
echo '<td>' . $address . '</td>';
echo '<td>' . $phone . '</td>';
echo '<td>' . $email . '</td>';
echo '</tr>';
}
echo <<<Eof
<table>
Eof;
>
6.fetchAll()⽅法与fetch()⽅法类似,但是该⽅法只需要调⽤⼀次就可以获取结果集中的所有⾏,并赋给返回的⼆维数组。
使⽤fetchAll()⽅法代替fetch()⽅法,在很⼤程度上是出于⽅便的考虑。
然⽽,使⽤fetchAll()⽅法处理特别⼤的结果集时,会给数据库服务器资源和⽹络带宽带来很⼤的负担。
<?php
try {
$dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
} catch (PDOException $e) {
exit('数据库连接失败' . $e->getMessage());
}
$query = 'SELECT uid, name, address, phone, email FROM contactInfo';
echo <<<Eof
<table border="1" align="center" width="90%">
<caption><h1>联系⼈信息表</h1></caption>
<tr bgcolor="#ccc">
<th>UID</th>
<th>姓名</th>
<th>地址</th>
<th>电话</th>
<th>电⼦邮箱</th>
Eof;
$stmt = $dbh->prepare($query);
$stmt->execute();
$allRows = $stmt->fetchall(PDO::FETCH_ASSOC);
while($allRows as$row){
echo '<tr>';
echo '<td>' . $row['uid'] . '</td>';
echo '<td>' . $row['name'] . '</td>';
echo '<td>' . $row['address'] . '</td>';
echo '<td>' . $row['phone'] . '</td>';
echo '<td>' . $row['email'] . '</td>';
echo '</tr>';
}
echo <<<Eof
<table>
Eof;
/* 以下是 fetchall() ⽅法中使⽤两个特别参数的⽰例 */
$stmt->execute();
$rows = $stmt->fetchall(PDO::FETCH_COLUMN, 1);
echo '所有联系⼈的性名:';
print_r($rows);
>
7.bindColumn() ⽅法,使⽤该⽅法可以将⼀个列和⼀个指定的变量名绑定,这样在每次使⽤fetch()⽅法获取各⾏记录时,会⾃动将相应的列值赋给该变量,但前提是 fetch()⽅法的第⼀个参数必须设置为 PDO::FETCH_BOTH的值。
<?php
try {
$dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
exit('数据库连接失败' . $e->getMessage());
}
$query = 'SELECT uid, name, phone, email FROM contactInfo WHERE departmentId = "D01"';
try {
$stmt = $dbh->prepare($query);
$stmt->execute();
$stmt->bindColumn(1, $uid); //通过列位置偏移量绑定变量 $uid
$stmt->bindColumn(2, $name); //通过列位置偏移量绑定变量 $name
$stmt->bindColumn('phone', $phone); //通过列名称绑定变量 $phone
$stmt->bindColumn('email', $email); //通过列名称绑定变量 $email
while($stmt->fetchall(PDO::FETCH_BOUND)){
echo$uid . ' ' . $name . ' ' . $phone . ' ' . $email . '<br>';
}
} catch (PDOException $e) {
echo$e->getMessage();
}
>
8.在进⾏项⽬开发时,有时需要在数据库中存储“⼤型”数据。
⼤型对象可以是⽂本数据,也可以是⼆进制数据形式的图⽚、视频等。
PDO 允许在 bindParam()或 bindColumn()调⽤中通过使⽤PDO::PARAM_LOB类型代码来使⽤⼤型数据类型。
PDO::PARAM_LOB告诉PDO将数据映射为流,所以可以使⽤PHP中的⽂件处理函数来操纵这样的数据。
⽰例:
数据写⼊代码:
<?php
/**
* 数据库:testdb
*
* 数据表创建的结构
*
* CREATE TABLE `images`(
* `id` mediumint(8) unsigned NOT NULL AOTU_INCREMENT,
* `contenttype` varchar(50) NOT NULL,
* `imagedata` blob NOT NULL,
* PRIMARY KEY(`id`)
* )
*/
if(filter_has_var(INPUT_POST, "isFile")){
try {
$dbh = new PDO('mysql:dbname=testdb;host=localhost', 'mysqlUser', 'mysqlPass');
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
exit('数据库连接失败' . $e->getMessage());
}
$stmt = $dbh->prepare("INSERT INTO images(contenttype, imagedata) VALUE (?, ?)");
$fp = fopen($_FILES['file']['tmp_name'], 'rb');
$stmt->bindParam(1, $_FILES['file']['type']);
$stmt->bindParam(2, $fp, PDO::PARAM_LOB);
$stmt->execute();
}
>
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="hidden" name="isFile">
<input type="submit" value="提交">
</form>
数据读取代码:
<?php
try {
$dbh = new PDO('mysql:dbname=testdb;host=localhost', 'mysqlUser', 'mysqlPass'); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
exit('数据库连接失败' . $e->getMessage());
}
$stmt = $dbh->prepare("SELECT contenttype, imagedata FROM images WHERE id=?"); $stmt->execute(array($_GET["id"]));
list($type, $lob) = $stmt->fetch(PDO::FETCH_NUM);
if(!empty($type)){
header("Content-Type: $type");
echo$lob;
}else{
echo 'No pictures';
}
>
9.PDO 对 MySQL 数据库的事务处理:
<?php
/**
* use testdb
*
* CREATE TABLE account(
* id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
* name VARCHAR(50) NOT NULL,
* cash DECIMAL(9,2) NOT NULL,
* PRIMARY KEY(id)
* ) engine=InnoDB;
*
* INSERT INTO account (name, cash) VALUES ("userA", 1000);
* INSERT INTO account (name, cash) VALUES ("userB", 9000);
*
* 以下是 userA ⽤户转 80 元给 userB ⽤户
*/
try {
$dbh = new PDO('mysql:dbname=testdb;host=localhost', 'mysqlUser', 'mysqlPassword'); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
exit('数据库连接失败' . $e->getMessage());
}
$dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, 0); //关闭数据库⾃动提交
try{
$price = 80; //转账⾦额 userA -> userB
$dbh->beginTransaction(); //开启数据库事务
$affected_rows = $dbh->exec("UPDATE `account` SET `cash` = `cash` - {$price} WHERE `name` = 'userA'"); //转出if($affected_rows > 0){
echo "userA⽤户成功转出{$price}元⼈民币<br>";
}else{
throw new PDOException("userA⽤户转出失败");
}
$affected_rows = $dbh->exec("UPDATE `account` SET `cash` = `cash` + {$price} WHERE `name` = 'userB'"); //转⼊if($affected_rows > 0){
echo "userB⽤户成功转⼊{$price}元⼈民币<br>";
}else{
throw new PDOException("userB⽤户转⼊失败");
}
$dbh->commit(); //事务处理
echo '交易成功!';
} catch (PDOExcaption $e){
echo '交易失败' . $e->getMessage();
$dbh->roolback(); //SQL 提交失败,数据库回滚
}
$dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, 1); //重启数据库⾃动提交功能
>。