PDO详解
一、PDO诞生的意义
PHP对数据库支持的抽象度不够,接口不统一。每一种数据库环境都必须重新定义数据库的操作。在这种背景下,统一操作接口PDO诞生了。
在PHP中,有三种数据库连接方式:
(1)mysql 最常用,过程式风格的一种应用
(2)mysqli,mysql函数的增强版,提供面向对象和过程两种风格的API,增加了预编译和参数绑定等新的特性
(3)PDO统一抽象接口,更类似于mysqli
二、PDO常用函数
PDO中包含三个预定义类:PDO、PDOStatement和PDOException
(1)PDO类中的常用方法有:
beginTransaction():开启事务机制
commit():提交事务
exec():执行一条SQL语言并返回影响的行数
prepare():为执行准备一条SQL语句,返回语句执行后的联合结果集
query():执行一条SQL语句并返回一个结果集
rollBack():回滚一个事务
getAttribute():获取一个数据库连接属性
setAttribute():设置一个数据库连接属性
(2)PDOStatement类中常用方法有:
bindParam():绑定一个PHP变量到一个预处理语句中的参数
execute():执行一条预处理语句
fetch():从结果集中取出一行
fetchAll():从结果集中取出一个包含所有行的数组
fetchColumn():返回结果集中某一列的数据
(3)PDOException是对exception类的简单重写,这里不作介绍
三、PDO的简单使用
1.在windows系统下,开启PDO需要在php.ini中将
;extension=php_pdo_mysql.dll
前面的分号去掉,如果使用的数据库不是mysql,则将对应的数据库扩展配置前面的分号去掉
在linux或者ngnix系统下,开启PDO也需要在php配置文件中将对应的拓展前的注释符号去掉。
2.在数据库中建立test数据库和一张test表,如下图所示
在服务器目录下新建一个php脚本如下:
<?php
try{
//配置数据源,数据库服务器IP和数据库名
$dsn="mysql:host=127.0.0.1;dbname=test";
$db=new PDO($dsn,"root","");
//设置异常可捕获
$db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
$db->exec("set names utf8");
//插入新数据
$sql="insert into test(name) values('HeCheng')";
$db->exec($sql);
}catch(PDOException $err){
echo $err->getMessage();
}
?>
结果如下:
三、PDO预编译和参数绑定
预编译:
<?php
try{
//数据库地址,数据库,数据库账户和密码
$dsn="mysql:host=127.0.0.1;dbname=test";
$db=new PDO($dsn,"root","");
$db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
$insert=$db->prepare("insert into test(name) values(?);");
//插入
$insert->execute(Array("HeChengLei"));
//异常语句
//$insert->execute(Array("ZhangJun","1983-09-21"));
//查找
$select=$db->prepare("select * from test");
$select->execute();
var_dump($select->fetchAll(PDO::FETCH_ASSOC));
}catch(PDOException $err){
echo $err->getMessage();
}
?>
结果:
参数绑定:
<?php
$name='HeCheng';
$dsn="mysql:host=127.0.0.1;dbname=test";
$db=new PDO($dsn,"root","");
//用?代替param
$sth=$db->prepare("select * from test where name=?");
//参数绑定
$sth->bindParam(1,$name,PDO::PARAM_STR,12);
$sth->execute();
var_dump($sth->fetchAll(PDO::FETCH_ASSOC));
//用:param代替param
$sth=$db->prepare("select * from test where name=:name");
$sth->bindParam(':name',$name,PDO::PARAM_STR,12);
$sth->execute();
var_dump($sth->fetchAll(PDO::FETCH_ASSOC));
?>
结果:
PDO最大的特点就是引入了预编译和参数绑定,二者的关系其实就是同一件事情的不同阶段,参数绑定使用bindParam()函数传入参数。
四、SQL注入与参数绑定
1.SQL注入的原理
MySQL注入又称为SQL Injection,通过构造特定的SQL语句获取权限外的数据。
SQL注入的原理非常简单,就是在原有SQL语句上添加一些布尔条件语句。
例,在浏览器中执行下列请求:
http://127.0.0.1/index.php?name=HeCheng
数据库执行对应的SQL语句如下
select * from test where name=’HeCheng’;
但如果一些不良客户构造这样的请求如:
http://127.0.0.1/index.php?name=HeCheng or ‘1=1
数据库即执行下列SQL语句
select * from test where name=’HeCheng’; or 1=1;
将会把数据库表中所有客户的信息查找出来,造成客户信息泄露
2.SQL注入的防范
实际上,SQL注入的技术含量并不高,防范也非常简单。
在处理客户输入的信息时,如果是整型变量,就是用intval()把传入的参数转化为一个数值。对于字符型变量,可以使用addslashes()把所有单引号、双引号、反斜线和空字符转化为含有反斜线的溢出字符。对于可能出现的特殊字符,进行转译和过滤。
3.使用PDO参数绑定防范SQL注入
PDO的参数绑定就是防范SQL注入的一种好办法。
其函数原型为:
<?php
function bindParam(&$sql,$location,$var,$type){
switch ($type) {
default:
case 'STRING':
$var=addslashes($var);
$var="'".$var."'";
break;
case "INTEGER":
case "INT":
$var=(int)$var;
//还可以拓展多种类型
}
for($i=1,$pos=0;$i<=$location;$i++)
$pos=strpos($sql,'?',$pos+1);
$sql=substr($sql,0,$pos).$var.substr($sql,$pos+1);
}
?>
五、PDO的事务机制
PDO中使用beginTransaction()创建事务,使用commit()或者rollback()结束事务。
在使用beginTransaction()后,如果事务中有异常出现或者没有提交事务即关闭数据库连接和结束脚本,事务会自动回滚,即终止前的所有语句都不会生效。这体现了事务的原子性。
如:
$db->beginTransaction();
$db->exec(“insert into test(name) values(‘ZhangDa’)”);
$db->exec(“insert test(name) values(‘WangGang’)”);//这条语句是错误的,无法执行
$db->commit();
在这段代码中,因为使用了事务机制,第二个插入语句错误会导致第一个插入语句也不会生效。
六、PDO的效率问题
PDO比mysql、mysqli的连接更为稳定,但在效率上却不一定比直连更好。而且在实际应用中,数据库迁移的情况不是很多,PDO更无法保证一次编写,到处运行。所以推荐在新应用中考虑使用PDO,在旧的应用中则没有必要进行重构。
- MySQL中批量初始化数据的对比测试(r12笔记第71天)
- Golang语言--包的概念、导入与可见性
- MySQL中的change,modify和自增列的关系(r12笔记第70天)
- Golang 值得注意的地方
- MySQL数值类型在binlog中需要注意的细节(r12笔记第69天)
- WordPress评论滑动/拉链解锁myQaptcha修改为自动提交的方法
- MySQL root用户登录的几个小问题(r12笔记第67天)
- Java实现生产者消费者的两种方式(r12笔记第66天)
- Golang语言的函数调用信息
- mysqldump的一点使用总结(r12笔记第81天)
- 转-Golang语言Interface漫谈
- WordPress导航菜单图标字体插件font awesome 4 menus纯代码版
- Oracle 12c远程克隆PDB的问题及修复(r12笔记第78天)
- Oracle表中含有255列以上时需要注意的(r12笔记第77天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Hyperledger Explorer 环境搭建详解
- [译]在Solidity中创建无限制列表
- java安全编码指南之:声明和初始化
- java安全编码指南之:表达式规则
- java安全编码指南之:Number操作
- 如何提高代码质量
- 小姐姐非要问我:spring编程式事务是啥?
- 阿里3面:Spring声明式事务连环炮,让我措手不及。。
- 旷视科技|商用端侧Raw图像降噪方案
- Python | 时间戳转换
- Stata | 2020年国家社科基金立项名单分析
- 还在用Guava Cache?它才是Java本地缓存之王
- 死信队列的消息处理方案
- 【Ceph】Rook 中使用 External Cluster 的方式管理外部集群
- Redis排行榜的设计与实现