连接到 MySQL
使用 mysql_* 函数或者我们可以用旧的方式说它(在 PHP 5.5 及更高版本中已弃用)
$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);
使用PDO:您需要做的就是创建一个新的PDO对象。构造函数接受指定数据库源的参数PDO的构造函数主要有四个参数,分别是DSN(数据源名称)和可选的username、password。
这里除了DSN,我想你都熟悉了;这是PDO 中的新功能。 DSN 基本上是一串选项,告诉PDO 使用哪个驱动程序和连接细节。如需进一步参考,请查看PDO MySQL DSN。
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');
笔记:你也可以使用charset=UTF-8,但有时会出错,所以最好使用utf8。
如果有任何连接错误,它会抛出一个PDOException对象,可以被捕获以进一步处理Exception。
好读: Connections and Connection management ¶
您还可以将几个驱动程序选项作为数组传递给第四个参数。我建议传递将 PDO 置于异常模式的参数。因为某些PDO 驱动程序不支持本地准备语句,所以PDO 执行准备模拟。它还允许您手动启用此仿真。要使用本机服务器端准备好的语句,您应该显式设置它false。
另一种是关闭默认在MySQL驱动程序中启用的prepare emulation,但应该关闭prepare emulation才能安全使用PDO。
我稍后会解释为什么要关闭准备仿真。要查找原因,请查看this post。
它仅在您使用我不推荐的旧版本 MySQL 时可用。
以下是您如何做到这一点的示例:
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8',
'username',
'password',
array(PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
PDO构建后可以设置属性吗?
是的,我们也可以在 PDO 构造之后使用 setAttribute 方法设置一些属性:
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8',
'username',
'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
错误处理
PDO 中的错误处理比 mysql_* 中的错误处理要容易得多。
使用mysql_* 时的常见做法是:
//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));
OR die() 不是处理错误的好方法,因为我们无法处理 die 中的内容。它只会突然结束脚本,然后将错误回显到您通常不想向最终用户显示的屏幕上,让该死的黑客发现您的模式。或者,mysql_* 函数的返回值通常可以与 mysql_error() 结合使用来处理错误。
PDO 提供了一个更好的解决方案:异常。我们对 PDO 所做的任何事情都应该包装在 try-catch 块中。我们可以通过设置错误模式属性强制PDO进入三种错误模式之一。下面是三种错误处理模式。
-
PDO::ERRMODE_SILENT。它只是设置错误代码,其行为与 mysql_* 几乎相同,您必须检查每个结果,然后查看 $db->errorInfo(); 以获取错误详细信息。
-
PDO::ERRMODE_WARNING提高E_WARNING。 (运行时警告(非致命错误)。脚本的执行不会停止。)
-
PDO::ERRMODE_EXCEPTION:抛出异常。它表示 PDO 引发的错误。您不应该从自己的代码中抛出 PDOException。看例外情况有关 PHP 异常的更多信息。当它没有被捕获时,它的行为非常像or die(mysql_error());。但与or die() 不同的是,PDOException 可以被捕获并优雅地处理,如果您选择这样做的话。
好读:
喜欢:
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
您可以将其包装在try-catch 中,如下所示:
try {
//Connect as appropriate as above
$db->query('hi'); //Invalid query!
}
catch (PDOException $ex) {
echo "An Error occured!"; //User friendly message/message you want to show to user
some_logging_function($ex->getMessage());
}
您现在不必处理try-catch。你可以在任何合适的时候抓住它,但我强烈建议你使用try-catch。此外,在调用 PDO 东西的函数外部捕获它可能更有意义:
function data_fun($db) {
$stmt = $db->query("SELECT * FROM table");
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
//Then later
try {
data_fun($db);
}
catch(PDOException $ex) {
//Here you can handle error and show message/perform action you want.
}
此外,您可以通过or die() 处理,或者我们可以说像mysql_*,但它确实会有所不同。您可以通过转display_errors off 并阅读您的错误日志来隐藏生产中的危险错误消息。
现在,看完以上所有内容后,您可能在想:到底是什么时候我只想开始学习简单的SELECT、INSERT、UPDATE或DELETE语句?别担心,我们开始:
选择数据
所以你在 mysql_* 中所做的是:
<?php
$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'];
}
现在在PDO,你可以这样做:
<?php
$stmt = $db->query('SELECT * FROM table');
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['field1'];
}
或者
<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
//Use $results
笔记: 如果您使用如下方法 (query()),此方法返回一个 PDOStatement 对象。所以如果你想获取结果,像上面那样使用它。
<?php
foreach($db->query('SELECT * FROM table') as $row) {
echo $row['field1'];
}
在 PDO 数据中,它是通过 ->fetch() 获取的,这是您的语句句柄的一种方法。在调用 fetch 之前,最好的方法是告诉 PDO 您希望如何获取数据。在下面的部分中,我将对此进行解释。
获取模式
请注意在上面的fetch() 和fetchAll() 代码中使用PDO::FETCH_ASSOC。这告诉PDO 将行作为关联数组返回,字段名称作为键。还有很多其他的fetch方式,我会一一解释。
首先,我解释一下如何选择抓取模式:
$stmt->fetch(PDO::FETCH_ASSOC)
在上面,我一直使用fetch()。您还可以使用:
现在我来获取模式:
-
PDO::FETCH_ASSOC:返回一个由结果集中返回的列名索引的数组
-
PDO::FETCH_BOTH(默认):返回一个由结果集中返回的列名和 0 索引列号索引的数组
还有更多的选择!在PDOStatement Fetch documentation. 中阅读所有内容。
获取行数:
您可以获取 PDOStatement 并执行 rowCount(),而不是使用 mysql_num_rows 来获取返回的行数,例如:
<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';
获取最后插入的 ID
<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();
插入和更新或删除语句
我们在mysql_*函数中做的是:
<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);
在 pdo 中,同样的事情可以通过以下方式完成:
<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;
在上面的查询PDO::exec 中执行一条 SQL 语句并返回受影响的行数。
插入和删除将在后面介绍。
上述方法仅在您不在查询中使用变量时才有用。但是当你需要在查询中使用变量时,永远不要像上面那样尝试,prepared statement or parameterized statement 就是这样。
准备好的陈述
问。什么是准备好的陈述,为什么我需要它们?
一种。准备好的语句是预编译的 SQL 语句,可以通过仅将数据发送到服务器来执行多次。
使用准备好的语句的典型工作流程如下(quoted from Wikipedia three 3 point):
-
准备:语句模板由应用程序创建并发送到数据库管理系统(DBMS)。某些值未指定,称为参数、占位符或绑定变量(下面标记为 ?):
INSERT INTO PRODUCT (name, price) VALUES (?, ?)
DBMS对语句模板进行解析、编译、查询优化,并存储结果,但不执行。
-
执行:稍后,应用程序提供(或绑定)参数值,DBMS 执行语句(可能返回结果)。应用程序可以使用不同的值多次执行该语句。在此示例中,它可能为第一个参数提供“面包”,为第二个参数提供
1.00。
您可以通过在 SQL 中包含占位符来使用准备好的语句。基本上有三个没有占位符的(不要用上面的变量尝试这个),一个有未命名的占位符,一个有命名的占位符。
问。那么现在,什么是命名占位符以及如何使用它们?
一种。命名占位符。使用以冒号开头的描述性名称,而不是问号。我们不关心名称占位符中的位置/价值顺序:
$stmt->bindParam(':bla', $bla);
bindParam(parameter,variable,data_type,length,driver_options)
您也可以使用执行数组进行绑定:
<?php
$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);
OOP 朋友的另一个不错的功能是命名占位符能够将对象直接插入数据库,假设属性与命名字段匹配。例如:
class person {
public $name;
public $add;
function __construct($a,$b) {
$this->name = $a;
$this->add = $b;
}
}
$demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);
问。那么现在,什么是未命名占位符,我该如何使用它们?
一种。让我们举个例子:
<?php
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->bindValue(2, $add, PDO::PARAM_STR);
$stmt->execute();
和
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));
在上面,您可以看到那些 ? 而不是名称占位符中的名称。现在在第一个示例中,我们将变量分配给各种占位符 ($stmt->bindValue(1, $name, PDO::PARAM_STR);)。然后,我们为这些占位符赋值并执行语句。在第二个示例中,第一个数组元素转到第一个?,第二个转到第二个?。
笔记: 在未命名的占位符我们必须注意传递给PDOStatement::execute() 方法的数组中元素的正确顺序。
SELECT、INSERT、UPDATE、DELETE准备查询
-
SELECT:
$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:
$stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
$stmt->execute(array(':field1' => $field1, ':field2' => $field2));
$affected_rows = $stmt->rowCount();
-
DELETE:
$stmt = $db->prepare("DELETE FROM table WHERE id=:id");
$stmt->bindValue(':id', $id, PDO::PARAM_STR);
$stmt->execute();
$affected_rows = $stmt->rowCount();
-
UPDATE:
$stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
$stmt->execute(array($name, $id));
$affected_rows = $stmt->rowCount();
笔记:
然而PDO和/或MySQLi并不完全安全。检查答案Are PDO prepared statements sufficient to prevent SQL injection?通过ircmaxell。另外,我从他的回答中引用了一部分:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));