mysql_*函数已通过时,至关一段时间以来,mysql_*函数在其余SQL数据库编程接口方面已经有所差异;它不支持预处理,存储过程,事务等一些现代数据库设计思想,SQL语句字符串转义函数 mysql_real_escape_string()
和 拼接SQL语句的编程方法 已通过时而且很容易出错。最近一段时间里,它缺少开发者的关注,缺乏维护将可能致使一些安全问题不能被即时修复,或者在适配新版本的MySQL的时候不能 正常工做,这成为mysql_*函数面临的的另外一个问题。PHP社区最近也对mysql_*函数给出不建议使用的建议,也有可能在将来的版本中最终被弃用 (不过不用过于担忧,这可能还须要很长一段时间)。php
PDO拥有更好的编程接口,你可使用它写出更加简洁,高效,安全的代码。PDO还为不一样的SQL数据库提供了不一样的驱动,方便你 使用新的数据库而不用再学习不一样的编程接口。与拼接SQL语句构造查询语句不一样,绑定参数能够简洁方便的构造出更加安全的查询语句,使用绑定参数的方法在 屡次类似语句查询(仅仅某个参数不一样)中也能够提升很多性能。PDO在错误处理方面也提供了多种方法。mysql_*函数缺少一致的处理,与PDO的异常 模式相比,或者说没有处理异常,使用PDO,你能够获得一致的错误处理,这将节省您大量的时间来跟踪问题。mysql
在当前的PHP版本中,PDO模块是默认安装启用的,可是在使用PDO前你还须要安装另外两个软件包,一个是pdo_mysql数据库驱动程序,另一个是相似php-mysql的mysql驱动程序。sql
$link = mysql_connect('localhost', 'user', 'pass');mysql_select_db('testdb', $link);mysql_set_charset('UTF-8', $link);
* 建立一个PDO对象,参数包括 DSN, username, password 和 一个驱动选项的数组(可忽略)。
* DSN其实就是一个告诉PDO该使用哪种数据库驱动 和 一些链接信息的字符串,了解更多 PDO MYSQL DSN .数据库
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');
注意:确保DSN中设置了字符编码信息,不然将可能返回字符编码设置错误的信息,出于安全考虑,DSN最好包括字符编码信息设置。 编程
你也能够在第四个参数数组里填写一些驱动选项,建议将 PDO异常模式
(下文讲解) 和 关闭预处理模拟
(默认打开的,仅对于旧版本MySQL有用)两个参数加入到第四个参数数组中。数组
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password', array(PDO::ATTR_EMULATE_PREPARES => false,PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
你也能够在建立PDO对象后再经过setAttribute方法设置相应选项。安全
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
//connected to mysql$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));
OR die()
是个不错的错误处理方法,可是会所以结束页面,将错误信息呈现到用户面前,这多是咱们不想看到的结果。
PDO有三种错误处理模式:服务器
PDO::ERRMODE_SILENT # 和 mysql_*函数相似,检查代码并查看 $db->errorInfo();
获取详细信息。app
PDO::ERRMODE_WARNING # 抛出PHP警告。数据库设计
PDO::ERRMODE_EXCEPTION #抛出 PDOException
异常,在我认为,这是咱们应该使用的模式, 这和 die(mysql_error());
相似,可是它能够捕获并抛出具体异常信息。
try { //connect as appropriate as above $db->query('hi'); //invalid query!} catch(PDOException $ex) { echo "An Error occured!"; //user friendly message some_logging_function($ex->getMessage());}
注意:你能够不用当即执行并捕获异常,你能够在任何合适的时候随时捕获。
function getData($db) { $stmt = $db->query("SELECT * FROM table"); return $stmt->fetchAll(PDO::FETCH_ASSOC);}//then much latertry { getData($db);} catch(PDOException $ex) { //handle me.}
若是你不想使用try/catch
来处理异常,就像使用OR die()
那样处理,在production模式下关闭display_errors
选项便可。
SELECT
)$result = mysql_query('SELECT * from table') or die(mysql_error());$num_rows = mysql_num_rows($result);while($row = mysql_fetch_assoc($result)) { echo $row['field1'].' '.$row['field2']; //etc...}
foreach($db->query('SELECT * FROM table') as $row) { echo $row['field1'].' '.$row['field2']; //etc...}
query()
方法返回了一个 PDOStatement
对象,你能够经过以下方法获取结果:
$stmt = $db->query('SELECT * FROM table');while($row = $stmt->fetch(PDO::FETCH_ASSOC)) { echo $row['field1'].' '.$row['field2']; //etc...}
或者
$stmt = $db->query('SELECT * FROM table');$results = $stmt->fetchAll(PDO::FETCH_ASSOC);//use $results
注意 fetch()
和 fetchAll()
代码中的PDO::FETCH_ASSOC
,它高速 PDO 以关联数组的形式返回 键,值;其余好比PDO::FETCH_NUM
模式,则返回数值键值的数组,默认模式是 PDO::FETCH_BOTH
则返回前面二者的集合,既有数值键值的数组,又有关联数组。PDO也能够获取数据返回对象PDO::FETCH_OBJ
,PDO::FETCH_CLASS
,PDO::FETCH_BOUND
,bindColumn
方法等更多内容,请阅读: PDOStatement Fetch documentation。
代替 mysql_num_rows
方法,你可使用 PDOStatement
对象的rowCount();
方法。
$stmt = $db->query('SELECT * FROM table');$row_count = $stmt->rowCount();echo $row_count.' rows selected';
注意:官方文档称此函数仅适用于返回 `UPDATE`, `INSERT`, `DELETE`操做的`affected rows`,而 `SELECT`操做,仅对于`PDO_MYSQL` 驱动,此函数一样适用(谨记),在操做其余数据库的时候尤为注意。
mysql_*代码:
$result = mysql_query("INSERT INTO table(firstname, lastname) VALUES('John', 'Doe')") or die("Insert Failed ".mysql_error());$insert_id = mysql_insert_id();
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");$insertId = $db->lastInsertId();
INSERT
, UPDATE
, DELETE
操做$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());$affected_rows = mysql_affected_rows($result);echo $affected_rows.' were affected';
$affected_rows = $db->exec("UPDATE table SET field='value'");echo $affected_rows.' were affected'
DELETE
, INSERT
操做一样适用。
对于 不携带任何参数的查询语句,咱们可使用 query
方法处理SELECT
操做,使用exec
方法处理 INSERT
,UPDATE
,INSERT
操做,而对于携带查询参数的语句,你应该使用绑定参数的方法来安全的处理这些操做。
$results = mysql_query(sprintf("SELECT * FROM table WHERE id='%s' AND name='%s'", mysql_real_escape_string($id), mysql_real_escape_string($name))) or die(mysql_error());$rows = array();while($row = mysql_fetch_assoc($results)){ $rows[] = $row;}
$stmt = $db->prepare("SELECT * FROM table WHERE id=? AND name=?");$stmt->execute(array($id, $name));$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
prepare方法将查询语句发送到服务器,以“?”做为参数占位符进行编译,execute方法将查询参数发送到服务器,运行以前编译好的查询语句。由于 查询语句 和 查询参数 是分开发送的,因此在参数里的SQL语句是不可能被执行的,因此不会发生 SQL注入,这是一种比链接字符串构造SQL语句更加安全的解决方法。
注意: 当你使用**绑定参数**的时候,不要对"?"占位符使用引号(SQL语句原来是对参数使用引号的),由于参数类型是在execute方法的时候肯定的,因此在prepare的时候没必要对占位符使用引号。
还有一些绑定参数的方法,bindValue方法能够分别绑定每一个参数来代替execute方法的数组方式,同时还分别设置每一个参数的类型。
$stmt = $db->prepare("SELECT * FROM table WHERE id=? AND name=?");$stmt->bindValue(1, $id, PDO::PARAM_INT);$stmt->bindValue(2, $name, PDO::PARAM_STR);$stmt->execute();$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
若是你有许多参数须要绑定,不要使用问号占位符
,以防混淆出错,你可使用命名占位符
代替问号占位符
。
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");$stmt->bindValue(':id', $id, PDO::PARAM_INT);$stmt->bindValue(':name', $name, PDO::PARAM_STR);$stmt->execute();$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
你也可使用execute方法,以数组的方式绑定参数:
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");$stmt->execute(array(':name' => $name, ':id' => $id));$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
INSERT
, DELETE
, UPDATE
预处理语句的使用和SELECT
相似,咱们举几个例子:
$stmt = $db->prepare("INSERT INTO table(field1,field2,field3,field4,field5) VALUES(:field1,:field2,:field3,:field4,:field5)");$stmt->execute(array(':field1' => $field1, ':field2' => $field2, ':field3' => $field3, ':field4' => $field4, ':field5' => $field5));$affected_rows = $stmt->rowCount();
$stmt = $db->prepare("DELETE FROM table WHERE id=:id");$stmt->bindValue(':id', $id, PDO::PARAM_STR);$stmt->execute();$affected_rows = $stmt->rowCount();
$stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");$stmt->execute(array($name, $id));$affected_rows = $stmt->rowCount();
=>无效方法:
//THIS WILL NOT WORK!$time = 'NOW()';$name = 'BOB';$stmt = $db->prepare("INSERT INTO table(`time`, `name`) VALUES(?, ?)");$stmt->execute(array($time, $name));
=>正确方法
$name = 'BOB';$stmt = $db->prepare("INSERT INTO table(`time`, `name`) VALUES(NOW(), ?)");$stmt->execute(array($name));
=>你也能够在SQL函数里绑定参数:
$name = 'BOB';$password = 'badpass';$stmt = $db->prepare("INSERT INTO table(`hexvalue`, `password`) VALUES(HEX(?), PASSWORD(?))");$stmt->execute(array($name, $password));
=>可是不能做为LIKE的参数:
//THIS DOES NOT WORK$stmt = $db->prepare("SELECT field FROM table WHERE field LIKE %?%");$stmt->bindParam(1, $search, PDO::PARAM_STR);$stmt->execute();
=>正确使用LIKE并绑定参数的方法:
$stmt = $db->prepare("SELECT field FROM table WHERE field LIKE ?");$stmt->bindValue(1, "%$search%", PDO::PARAM_STR);$stmt->execute();
注意:这里使用的是bindValue而不是bindParam,不然会发生PDOException或致命错误。
预处理语句能够一次设置,屡次调用,由于仅在第一次传入的时候编译,所以在后来的屡次调用中提升了很多效率。
典型的应用就是bindParam
,bindParam
与bindValue
的不一样之处在于,它不是绑定了参数的值,而是绑定参数变量自己,所以,若是参数变量变化了,那么在execute处理的时候,查询也将相应变化。
$values = array('bob', 'alice', 'lisa', 'john');$name = '';$stmt = $db->prepare("INSERT INTO table(`name`) VALUES(:name)");$stmt->bindParam(':name', $name, PDO::PARAM_STR);foreach($values as $name) { $stmt->execute();}
注意:调用`beginTransaction()`方法即自动关闭了`自动提交`。
try { $db->beginTransaction(); $db->exec("SOME QUERY"); $stmt = $db->prepare("SOME OTHER QUERY?"); $stmt->execute(array($value)); $stmt = $db->prepare("YET ANOTHER QUERY??"); $stmt->execute(array($value2, $value3)); $db->commit();} catch(PDOException $ex) { //Something went wrong rollback! $db->rollBack(); echo $ex->getMessage();}