原文链接:http://www.orlion.ga/1153/
PDO是一种数据库抽象层,不止可以访问mysql还可以访问其他数据库。
一、__construct()
PDO::__construct ( string $dsn [, string $username [, string $password [, array$driver_options ]]] )
$dsn:通常,一个 DSN 由 PDO 驱动名、紧随其后的冒号、以及可选的驱动程序的数据库连接变量信息,如主机名、端口和数据库名。例:oci:dbname=//localhost:1521/my_db(这种是连接Oracle数据库的数据源名)mysql:host=localhost;dbname=testdb(这种是连接mysql服务器的数据源名)。php.ini中可以设置DSN信息:pdo.dsn.oraclepdo="OCI:dbname=//localhost:1521/my_db;charset=UTF-8";在创建PDO对象时就可以:new PDO("oraclepdo", …);
$driver_options数组用来指定具体驱动的连接选项,形式是“键=>值”对,一些常用的选项:
例:
try{ $db = newpdo('mysql:host=127.0.0.1;port=3306;dbname=mysql;charset=utf8','user','password',array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, )); }catch(PDOException $pe){ echo $pe->getMessage(); }
如果无法加载驱动或者连接失败会抛出一个PDOException。
二、setAttribute()、getAttribute()方法
可以调用setAttribute()方法为PDO对象设置属性,可以调整PDO的行为和底层驱动状态,而getAttribute()可以获取属性,例:
$db->setArrtibute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Oracle数据库在将空字符串返回给PDO时会将空字符串转为PHP的NULL,但是其他的数据库不这么做,这就带来了可移植性问题,不过PDO提供了一个驱动程序级属性PDO::ATTR_ORACLE_NULLS,该属性可以为其他数据驱动模拟此行为,此属性设置为TRUE,在获取时可以将空字符串转为NULL。
三、exec()、query()
当执行增删改操作时没有返回值可以调用PDO对象的exec方法执行SQL,该方法返回受影响的行,所以有可能会返回0,但是执行是成功的,所以判断返回值时使用$affected === TRUE。例:
$affected = $db->exec("insert into or_users(user_login, user_pass)values('u1', 'p1')"); if (FALSE === $affected) { echo 'Error'; }
当执行select操作时使用query(),该方法返回PDOStatement对象,可以调用PDOStatement对象的rowCount()方法查看返回的行数,例:
$pdostatement = $db->query("select user_login, user_pass from or_users"); foreach ($pdostatement as $row) { print_r($row); }
函数原型:
public PDOStatement PDO::query ( string $statement ) public PDOStatement PDO::query ( string $statement , int $PDO::FETCH_COLUMN , int $colno ) public PDOStatement PDO::query ( string $statement , int $PDO::FETCH_CLASS , string$classname , array $ctorargs ) public PDOStatement PDO::query ( string $statement , int $PDO::FETCH_INTO , object $object)
四、预处理
PDO支持以占位符(?)语法将变量绑定到SQL中的预处理语句,预处理语句的作用是编译一次,多次运行,会在服务器上缓存查询的语法和执行过程,而在服务器和客户端之间传递有变化的列值。可以防止SQL注入,而且执行速度比query()、exec()快。
1、PDOStatement对象
query()方法返回的PDOStatement对象是结果集对象,而prepare()方法返回的PDOStatement对象是查询对象,PDOStatement中的全部成员方法如下:
2、准备语句
重复执行一个sql语句只不过每次执行的参数有可能不同,这时使用预处理语句效率最高。PDO有两种占位符语法"命名参数":
$db->prepare("insert into or_users(user_login, user_pass)values(:user_login, :user_pass)");
"问号参数":
$db->prepare("insert into or_users(user_login, user_pass)values(?, ?)");
prepare()方法返回PDOStatement类对象,原型:
public PDOStatement PDO::prepare ( string $statement [, array $driver_options = array() ] )
3、绑定参数与执行
当SQL准备好之后下一步就是给SQL绑定参数了,可以通过PDOStatement对象的bindParam()方法把参数绑定到占位符上:
bool PDOStatement::bindParam ( mixed $parameter , mixed &$variable [, int $data_type = PDO::PARAM_STR [, int $length [, mixed $driver_options ]]] )
$parameter:参数标识符。对于使用命名占位符的预处理语句,应是类似 :name 形式的参数名。对于使用问号占位符的预处理语句,应是以1开始索引的参数位置。
&$variable:该参数提供给第一个参数所指定占位符的值。可以看到一个"&"符,它需要按引用传递,在结合准备使用存储过程使用此方法时可以根据存储过程的某个动作修改这个值。因为该参数是按引用传递,所以只能提供变量作为参数,不能直接提供值。
$data_type:为当前绑定的参数设置数据类型。可选值:
PDO::PARAM_BOOL: 代表boolean数据类型 PDO::PARAM_NULL: 代表SQL中NULL类型 PDO::PARAM_INT: 代表SQL中INTEGER数据类型 PDO::PARAM_STR: 代表SQL中CHAR、VARCHAR和其他字符串数据类型 PDO::PARAM_LOB: 代表SQL中大对象数据类型。 PDO::PARAM_STMT: 代表PDOStatement对象类型 PDO::PARAM_INPUT_OUTPUT: 专为存储过程使用的数据类型,可以在过程执行后修改
$length: 可选,数据类型的长度。为表明参数是一个存储过程的 OUT 参数,必须明确地设置此长度。
$driver_options: 可选。
例:
$sql = 'insert into or_users(user_login, user_pass)values(:user_login, :user_pass)'; $stmt = $this->conn->prepare($sql); $user_login = 'u2'; $user_pass = 'p2'; // $stmt->bindParam(':user_login', 'u2');这种方式是错的,参数不能是具体的值只能是变量 $stmt->bindParam(':user_pass', $user_login);// 这种是正确的 $stmt->bindParam(':user_pass', $user_pass); $stmt->execute(); // 下面可以改变变量的值继续插入 $user_login = 'u3'; $user_pass = 'p3'; $stmt->execute();
另外还有一种更快捷的方式:
$sql = 'insert into or_users(user_login, user_pass)values(:user_login, :user_pass)'; $stmt = $this->conn->prepare($sql); $stmt->execute(array(':user_login' => 'u3', ':user_pass' => 'p3'));
execute()方法返回布尔值。
4、获取结果
query()与prepare()+execute()返回的结果是一样的,都需要通过PDOStatement对象的方法将结果遍历出来。
(1)fetch()方法
原型:
mixed PDOStatement::fetch ([ int $fetch_style [, int $cursor_orientation = PDO::FETCH_ORI_NEXT [, int $cursor_offset = 0 ]]] )
该方法返回结果集的当前行记录并将结果集指针指向下一行,当到达结果集末尾的时候返回FALSE.
$fetch_style:控制下一行如何返回给调用者,此值必须是 PDO::FETCH_* 系列常量中的一个,缺省为PDO::ATTR_DEFAULT_FETCH_MODE 的值 (默认为 PDO::FETCH_BOTH )
-
PDO::FETCH_ASSOC:返回一个索引为结果集列名的数组
-
PDO::FETCH_BOTH(默认):返回一个索引为结果集列名和以0开始的列号的数组
-
PDO::FETCH_BOUND:返回
TRUE
,并分配结果集中的列值给PDOStatement::bindColumn() 方法绑定的 PHP 变量。 -
PDO::FETCH_CLASS:返回一个请求类的新实例,映射结果集中的列名到类中对应的属性名。如果
fetch_style
包含 PDO::FETCH_CLASSTYPE(例如:PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE),则类名由第一列的值决定 -
PDO::FETCH_INTO:更新一个被请求类已存在的实例,映射结果集中的列到类中命名的属性
-
PDO::FETCH_LAZY:结合使用 PDO::FETCH_BOTH 和 PDO::FETCH_OBJ,创建供用来访问的对象变量名
-
PDO::FETCH_NUM:返回一个索引为以0开始的结果集列号的数组
-
PDO::FETCH_OBJ:返回一个属性名对应结果集列名的匿名对象
$cursor_orientation:可选,用来确定对象是一个可滚动的游标时应当获取哪一行。
$cursor_offset:可选,整数值,表示要获取的行相对于当前游标位置的偏移。
(2)fetchAll()方法
该方法一次获取结果集中所有数据赋给返回的数组:
array PDOStatement::fetchAll ([ int $fetch_style [, mixed $fetch_argument [, array$ctor_args = array() ]]] )
$fetch_style:控制返回数组的内容如同 PDOStatement::fetch() 文档中记载的一样。默认为PDO::ATTR_DEFAULT_FETCH_MODE
的值( 其缺省值为 PDO::FETCH_BOTH
)
想要返回一个包含结果集中单独一列所有值的数组,需要指定 PDO::FETCH_COLUMN
。通过指定 column-index
参数获取想要的列。
想要获取结果集中单独一列的唯一值,需要将 PDO::FETCH_COLUMN
和 PDO::FETCH_UNIQUE
按位或。
想要返回一个根据指定列把值分组后的关联数组,需要将 PDO::FETCH_COLUMN
和PDO::FETCH_GROUP
按位或。
$fetch_argument:可选,是一个整数索引,当在fetchAll()方法的第一个参数中指定PDO::FETCH_COLUMN值时,从结果集中返回通过该参数提供的索引所指定列的所有值。
(3)setFetchMode()
为语句设置默认的获取模式。
(4)bindColumn()
该方法可以将一个列和一个指定的变量名绑定,这样每次使用fetch方法获取各行记录时,会自动将相应的列值付给该变量,但必须是在fetch()方法的第一个参数设置为PDO::FETCH_BOTH值时,原型:
bool PDOStatement::bindColumn ( mixed $column , mixed &$param [, int $type [, int $maxlen [,mixed $driverdata ]]] )
$column:可以使用整数的列偏移位置索引(索引值从1开始),或是列的名称字符串
$param:需要传递一个引用,所以必须提供一个变量名
$type:可选,通过设置变量的类型来限制变量值,该参数支持的值和bindParam()方法相同。
例:
$sql = 'select user_login, user_pass from or_users'; $stmt = $db->prepare($sql); $stmt->execute(); $stmt->bindColumn(1, $user_login); $stmt->bindColumn('user_pass', $user_pass); while($row = $stmt->fetch()) { echo $user_login . $user_pass . '<br/>'; }
5、大数据对象的存取
有时候会在数据库中存储一个提交比较大的数据(图片、音乐、电影)(当然这种设计不太合理)。PDO运行在bindParam()和bindColumn()调用中通过使用PDO::PARAM_LOB类型代码来使用大型数据类型。PDO::PARAM_LOB告诉PDO将数据映射为流,所以可以使用PHP中文件处理函数来操作这种数据。例:
五、事务
非每种数据库都支持事务,因此当第一次打开连接时,PDO 需要在所谓的“自动提交”模式下运行。自动提交模式意味着,如果数据库支持,运行的每个查询都有它自己的隐式事务,如果数据库不支持事务,则没有。如果需要一个事务,则必须用 PDO::beginTransaction() 方法来启动。如果底层驱动不支持事务,则抛出一个 PDOException 异常(不管错误处理设置是怎样的,这都是一个严重的错误状态)。一旦开始了事务,可用PDO::commit() 或 PDO::rollBack()来完成,这取决于事务中的代码是否运行成功。
PDO 仅在驱动层检查是否具有事务处理能力。如果某些运行时条件意味着事务不可用,且数据库服务接受请求去启动一个事务,PDO::beginTransaction() 将仍然返回 TRUE
而且没有错误。试着在 MySQL 数据库的 MyISAM 数据表中使用事务就是一个很好的例子。
当脚本结束或连接即将被关闭时,如果尚有一个未完成的事务,那么 PDO 将自动回滚该事务。
只有通过 PDO::beginTransaction() 启动一个事务后,才可能发生自动回滚。如果手动发出一条查询启动事务, 则 PDO 无法知晓,从而在必要时不能进行回滚。
贴个PHP手册中举的例子:
<?php try { $dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2', array(PDO::ATTR_PERSISTENT => true)); echo "Connected\n"; } catch (Exception $e) { die("Unable to connect: " . $e->getMessage()); } try { $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $dbh->beginTransaction(); $dbh->exec("insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')"); $dbh->exec("insert into salarychange (id, amount, changedate) values (23, 50000, NOW())"); $dbh->commit(); } catch (Exception $e) { $dbh->rollBack(); echo "Failed: " . $e->getMessage(); } ?>
六、贴一个单例类
<?php namespace DB; use PDO; use PDOException; use Exception; class DBPDO { const DB_USER = 'root'; const DB_PWD = ''; const DB_DSN = 'mysql:host=127.0.0.1:3306;dbname=orlion'; private static $DB_OPT = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_ORACLE_NULLS => TRUE); private $conn; private static $_instance = null; private function __construct() { $this->conn = new PDO(self::DB_DSN, self::DB_USER, self::DB_PWD, self::$DB_OPT); } private function __clone() { } public static function getInstance() { if (is_null(self::$_instance) || !self::$_instance instanceof self) { try { self::$_instance = new self(); } catch (PDOException $pe) { // 这里应该抛一个自定义异常以便上层处理 throw new Exception('Could not connect to database:' . self::DB_DSN); } } return self::$_instance; } public function query($sql) { return $this->conn->query($sql); } public function update($sql) { return $this->conn->exec($sql); } public function __destruct() { unset($this->conn); } } ?>