PHP的数据对象PDO

一、什么是PDO

手册说:PHP 数据对象 (PHP Data Object) 扩展为PHP访问数据库定义了一个轻量级的一致接口。实现 PDO 接口的每个数据库驱动可以公开具体数据库的特性作为标准扩展功能。

PDO可以访问多种的数据库,它把操作类合并在一起,做成一个数据访问抽象层。

统一各种数据库的访问接口,这意味着,不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据

二、检查PDO扩展

1、开启相应扩展

extension = php_pdo.dll
# unix或Linux上
extension = pdo.so


2、开启对相应数据库的扩展

extension = php_pdo_mysql.dll


3、通过phpinfo()可以看到PDO扩展

三、PDO连接数据库

创建PDO对象

创建对象时要是用​​try...catch​​语句,因为在声明PDO实例发生错误时,会自动抛出一个异常

1、 通过参数形式连接(推荐)

$dbms='mysql';     //数据库类型
$host='localhost'; //数据库主机名
$dbName='test'; //使用的数据库
$user='root'; //数据库连接用户名
$pass=''; //对应的密码
$dsn="$dbms:host=$host;dbname=$dbName";
try {
$dbh = new PDO($dsn,$user,$pass);//初始化一个PDO对象
$result = $dbh->query('select * from user');
var_dump($result);
}catch(PDOException $e){
echo $e -> getMessage ();
}


2、 使用url数据源的方式

$dsn  =  'uri:file:///'. __dir__ .'/dsn.txt' ;
$user = 'root' ;
$password = 'root' ;
try {
$dbh = new PDO ( $dsn , $user , $password );
var_dump($dbh);
} catch ( PDOException $e ) {
echo 'Connection failed: ' . $e -> getMessage ();
}


3、通过配置文件连接

在​php.ini​中任意一个位置添加数据源的配置。

 pdo.dsn.test=”mysql:host=localhost;dbname=xxxx”;


test是自定义的数据源名称。为方便,可以在php.ini的首行中添加,然后重启服务器,在程序中引入该数据源

$dsn = "test";    //数据源名称
$username = "xxxxx";
$pwd = "xxxx";
$pdo = new PDO($dsn,$username,$pwd);


四、持久化连接

创建的数据连接为PDO持久化连接时,该连接在使用完毕或者PHP脚本结束后并不会被关闭,而是被PHP缓存起来。当另一个使用相同凭证(主机、端口、数据库名、用户名、密码等信息完全一致)的PHP脚本请求建立连接时,PHP将直接返回之前被缓存起来的连接,从而达到连接重用。持久连接缓存可以避免每次访问数据库都要建立一个新连接的开销

如果是在对象初始化之后用 PDO::setAttribute() 设置此属性,则驱动程序将不会使用持久连接。

$type = 'mysql'; //数据库类型
$db_name = 'demo'; //数据库名
$host = '127.0.0.1';
$username = 'root';
$password = 'root';

$dsn = "$type:host=$host;dbname=$db_name";
try {
//建立持久化的PDO连接
$pdo = new PDO($dsn, $username, $password, array(PDO::ATTR_PERSISTENT => true));
} catch (Exception $e) {
die('连接数据库失败!');
}
$stmt = $pdo->query('SELECT id, body FROM jk_category');
while ($row = $stmt->fetch()) {
echo "id=$row[0],body=$row[1]<br/>";
}
$pdo = null;


五、PDO的行为属性

在PDO对象中有很多属性用来调整PDO的行为或获取底层驱动程序状态。如果在创建PDO对象时,没有在构造方法中最后一个参数设置过属性,也可以在对象创建完成之后,通过PDO对象中​​setAttribute()​​和​​getAttribute​​方法设置和获取这些属性的值

try {
$pdo = new PDO ( $dsn , $user , $password );
//$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_WARNING);

echo $pdo->getAttribute(PDO::ATTR_ERRMODE);
echo '<hr/>';

$sql = 'update user set status = 33 where uid > 3';
$rst = $pdo->query($sql);
if( $rst === false ){
//print_r($pdo->errorInfo());
//var_dump($pdo->errorCode());
}
} catch ( PDOException $e ) {
echo 'Connection failed: ' . $e -> getMessage ();
}


错误处理模式

  • 默认模式 在错误发生时不进行任何操作,PDO将只设置错误代码。我们可以使用PDO对象中的​​errorCode()​​和​​errorInfo()​​方法获取信息
  • 警告warning消息 除了设置错误代码外,PDO还将发出一条PHP的E_WARNING消息。调试或测试这种设置很有用
  • PDO异常处理 除了设置错误代码外,PDO还将抛出一个PDOException,并设置其属性,以反映错误代码和错误信息。(推荐)

六、PDO对象的方法


方法名

说明

exec()

执行一条SQL语句,并返回其受影响的行数

query()

执行一条SQL语句,返回一个PDOStatement对象

prepare()

准备要执行的sql语句,返回PDOStatement对象

quote()

返回一个添加引号的字符串

lastInsertId

返回最后插入行的ID

setAttribute()

设置数据库连接属性

getAttribute()

得到数据库连接的属性

errorCode

获取上一次数据库操作的SQLSTATE

errorInfo

获取数据库上一次操作的错误信息


1、exec()执行

执行一条sql语句并返回其受影响的记录的条数,如果没有受影响返回0对select没有作用

try {
//创建对象
$dbh = new PDO("mysql:host=localhost;dbname=jkfw", "root", "root");
}catch(PDOException $e) {
exit("数据库连接失败:".$e->getMessage());
}

$query = "UPDATE user SET passwod='12345678900' WHERE name='jack'";
$affected = $dbh->exec($query);
if($affected){
//数据表user中受影响的行数为:1
echo '数据表user中受影响的行数为:' .$affected;
}else{
print_r($dbh->errorInfo());
}

$query = "UPDATE user SET passwod='123456789' WHERE (id%2 = 0)";
$affected = $dbh->exec($query);
if($affected){
echo '数据表user中受影响的行数为:' .$affected;
}else{
print_r($dbh->errorInfo());
}

$query = "insert into user(username,password,email,status)values ('jack1','xx1','1231',11);";

$affected = $dbh->exec($query);
echo '最后插入的ID号为'.$dbh->lastInsertId();


2、query执行sql(查询)

$stmt = $pdo->query($sql);
var_dump($stmt);//object(PDOStatement)
foreach($stmt as $row){
//print_r($row);
echo '编号:'.$row['id'],'<br/>';
echo '用户名:'.$row['password'],'<br/>';
echo '邮箱:'.$row['email'],'<br/>';
echo '<hr/>';
}


3. 错误代码errcode错误信息errorInfo

$sql = 'update user set status = 33 where uid > 3';
$rst = $pdo->query($sql);
if( $rst === false ){
print_r($pdo->errorInfo());//sqlstatus sql编码 错误信息
var_dump($pdo->errorCode());
}


4、quote

返回带引号的字符串,过滤字符中的特殊字符

防止sql注入:

//http://localhost/test/1.php?id=1 or 1=1;update user set money=2000
$id=$_GET["id"];
echo $id;//1 or 1=1;update user set money=2000
$sql="select * from user where id=$id";
//会查询出所有表格数据并更改Money
$sql="select * from user where id=quote($id)";


七、PDOStatement对象的方法

PDO对象的​query()​和​prepare()​方法均会返回一个PDOStatement对象,该对象可以用于PDO的预处理执行。其常见方法如下:


方法名

说明

execute()

执行一条预处理SQL语句

rowCount

返回上一个SQL语句影响的行数

fetch()

从结果集中获取一行

fetchAll()

返回一个包含结果集中所有行的数组

setFetchModel()

为语句设置默认的获取模式

fetchColumn()

从结果集中的下一行返回单独的一列

fetchObject()

获取下一行并作为一个对象返回

bingParam()

绑定一个参数到指定的变量名

bindValue()

把一个值绑定到一个参数


PDO提供了一种名为预处理语句(prepared statement)的机制。它可以将整个SQL命令向数据库服务器发送一次,以后只有参数发生变化,数据库服务器只需对命令的结构做一次分析就够了,即编译一次,可以多次执行

其会在服务器上缓存查询的语句和执行过程,而只在服务器和客户端之间传输有变化的列值,以此来消除这些额外的开销。这不仅大大减少了需要传输的数据量,还提高了命令的处理效率。可以有效防止SQL注入,在执行单个查询时快于直接使用query()或exec()的方法,速度快且安全,推荐使用

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
//* _PDO::ATTR_EMULATE_PREPARES_ 启用或禁用预处理语句的模拟


7.1、预处理和查询

prepare()/execute/fetch/fetchAll

try{
$pdo=new PDO('mysql:host=localhost;dbname=demo','root','root');
$sql='select * from user where username="jack"';
//prepare($sql):准备SQL语句
$stmt=$pdo->prepare($sql);

//execute():执行预处理语句
$res=$stmt->execute();
//var_dump($res);

//fetch():得到结果集中的一条记录
$row=$stmt->fetch();
print_r($row);

}catch(PDOException $e){
echo $e->getMessage();
}


7.2、预处理和占位符

重复插入数据

try {
$pdo = new PDO ( $dsn , $user , $password );
$sql = "insert into user(username,password,email,status)values (:user,:pass,:email,:status)";

$stmt = $pdo->prepare($sql);
$stmt->bindParam(":user",$username,PDO::PARAM_STR);
$stmt->bindParam(":pass",$password,PDO::PARAM_STR);
$stmt->bindParam(":email",$eamil,PDO::PARAM_STR);
$stmt->bindParam(":status",$status,PDO::PARAM_INT);

$username = 'jack001';
$password = '123456';
$eamil = 'njz817@163.com';
$status = 1;

$stmt->execute();//插入一行

$username = 'jack002';
$password = '222222';
$eamil = 'njz222@163.com';
$status = 0;

$stmt->execute();//插入另一行

} catch ( PDOException $e ) {
echo 'Connection failed: ' . $e -> getMessage ();
}


使用问号

$query = "INSERT INTO user (username,password,email) VALUES (?,?,?)";
$stmt = $dbh->prepare($query);
$stmt->bindParam("1",$name);//第一个问号
$stmt->bindParam("2",$pass);//第二个问号
$stmt->bindParam("3",$email);//第三个问号
$name = 'jack';
$pass = '123456';
$email = 'jack@qq.com';
$stmt->execute();


批量传参数

try {
//创建对象
$dbh = new PDO("mysql:host=localhost;dbname=jkfw", "root", "root");
$dbh->query('set names utf8');
}catch(PDOException $e) {
echo "数据库连接失败:".$e->getMessage();
exit;
}
# 问号索引数组
$query = "INSERT INTO user (username,password,email) VALUES (?,?,?)";
$stmt = $dbh->prepare($query);
$stmt->execute(array("nienie",'345','666@qq.com'));

# 冒号关联数组
$query = "INSERT INTO user (username,password,email) VALUES (:name,:password,:email)";
$stmt = $dbh->prepare($query);
$ret = $stmt->execute(array(":name"=>"关羽",":password"=>"666",":email"=>"guanyu@qq.com"));
$arr = $stmt ->errorInfo ();
print_r ( $arr );


获取最后的id和影响行数

try {
//创建对象
$dbh = new PDO("mysql:host=localhost;dbname=jkfw", "root", "root");
$dbh->query('set names utf8');
}catch(PDOException $e) {
echo "数据库连接失败:".$e->getMessage();
exit;
}
$query = "INSERT INTO user (username,password,email) VALUES (?,?,?)";
$stmt = $dbh->prepare($query);
$stmt->execute(array("孙权",'aaaa','123456@qq.com'));
echo $dbh->lastInsertId();

$query = "UPDATE user SET username=? WHERE id=?";
$stmt = $dbh->prepare($query);
$stmt->execute(array("聂哥","3"));
echo $stmt->rowCount();

$query = "DELETE FROM user WHERE username= ?";
$stmt = $dbh->prepare($query);
$stmt->execute(["nienie"]);
echo $stmt->rowCount();


八、PDO中的事务


方法名

说明

beginTransaction()

启动一个事务

commit()

提交一个事务

rollback()

回滚一个事务

inTransaction()

检测是否在一个内


1、通过内置方法

try {
//创建对象
$dbh = new PDO("mysql:host=localhost;dbname=test", "root", "root");
//设置错误使用异常的模式
$dbh -> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch(PDOException $e) {
exit("数据库连接失败:".$e->getMessage());
}
try {
//开启一个事务
$dbh -> beginTransaction();
$affected_rows = $dbh->exec("UPDATE user set username = 'Transaction' where id=5");
if($affected_rows > 0) {
echo "用户名更新成功!<br>";
} else {
throw new PDOException("用户名更新失败!<br>");
}
$affected_rows = $dbh-> exec("UPDATE user set status = 2 where id=3");
if($affected_rows) {
echo "用户状态修改成功!<br>";
}else {
throw new PDOException("用户状态修改失败!<br>");
}
echo "事务测试成功!<br>";
//提交以上的操作
$dbh->commit();
}catch(PDOException $e) {
echo "错误:".$e->getMessage();
echo "事务测试失败!<br>";
//撤销所有操作
$dbh -> rollback();
}


2、利用设置自动提交

//关闭自动提交
$dbh-> setAttribute(PDO::ATTR_AUTOCOMMIT, 0);
//运行完成以后, 最后开启自动提交
$dbh-> setAttribute(PDO::ATTR_AUTOCOMMIT, 1);


PDO属性参考
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//$dbh->setAttribute(3,2);
$dbh->setAttribute(PDO::ATTR_AUTOCOMMIT,0);//$dbh->setAttribute(0,0);
$dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
//$dbh->setAttribute(19,2);

echo "\nPDO是否关闭自动提交功能:". $dbh->getAttribute(PDO::ATTR_AUTOCOMMIT);
echo "\n当前PDO的错误处理的模式:". $dbh->getAttribute(PDO::ATTR_ERRMODE);
echo "\n表字段字符的大小写转换: ". $dbh->getAttribute(PDO::ATTR_CASE);
echo "\n与连接状态相关特有信息: ". $dbh->getAttribute(PDO::ATTR_CONNECTION_STATUS);
echo "\n空字符串转换为SQL的null:". $dbh->getAttribute(PDO::ATTR_ORACLE_NULLS);
echo "\n应用程序提前获取数据大小:".$dbh->getAttribute(PDO::ATTR_PERSISTENT);
echo "\n与数据库特有的服务器信息:".$dbh->getAttribute(PDO::ATTR_SERVER_INFO);
echo "\n数据库服务器版本号信息:". $dbh->getAttribute(PDO::ATTR_SERVER_VERSION);
echo "\n数据库客户端版本号信息:". $dbh->getAttribute(PDO::ATTR_CLIENT_VERSION);






 ​