【问题标题】:How to replace MySQL functions with PDO?如何用 PDO 替换 MySQL 函数?
【发布时间】:2015-03-21 16:11:23
【问题描述】:

当阅读php.net 关于MySQL 函数时。我遇到了这条消息

警告 此扩展自 PHP 5.5.0 起已弃用,并将在未来删除。相反,应该使用 MySQLi 或 PDO_MySQL 扩展。另请参阅 MySQL:选择 API 指南和相关的常见问题解答以获取更多信息。此功能的替代方案包括:

我读过关于 PDO 的文章。如何使用 MySQL 或 MSSQL 将我的代码更新为 PDO?

【问题讨论】:

  • 好东西。我以为这将是一个懒惰的问题,结果证明这是一个很好的自我回答!

标签: php sql pdo


【解决方案1】:

我看到很多关于 SO 实现 my_sql 函数 的代码。其他人(包括我自己)的 cmets 要求提问者放弃 MySQL 函数,开始使用 PDO 或 MySQLI。这篇文章是为了帮助。您可以参考它,因为它解释了为什么不推荐使用它们以及 PDO 是什么,以及实现 PDO 的最小代码示例。

首先:

mysql 函数PDO 的转换不是一个简单的搜索和替换案例。 PDO 是 PHP 语言的面向对象编程插件。 这意味着使用 mysql 函数 编写代码的另一种方法。首先为什么要转换?

为什么不推荐使用 mysql 函数

mysql 扩展是古老的,自 15 年前发布的 PHP 2.0 以来一直存在(!!);这与现代 PHP 截然不同,后者试图摆脱过去的不良做法。 mysql 扩展是一个非常原始的、低级的 MySQL 连接器,它缺乏许多便利功能,因此很难以安全的方式正确应用;因此,这对新手不利。许多开发人员不了解 SQL 注入,而且 mysql API 非常脆弱,即使您知道它也很难阻止它。它充满了全局状态(例如隐式连接传递),这使得编写难以维护的代码变得容易。由于它很旧,在 PHP 核心级别上维护可能会非常困难。

mysqli 扩展更新很多,可以解决上述所有问题。 PDO 也是相当新的,它也解决了所有这些问题,而且还解决了更多问题。

由于这些原因* mysql 扩展将在未来某个时候被删除。

来源Deceze

如何实现 PDO

PDO 提供了一种连接多个数据库的解决方案。此答案仅涵盖 MySQLMSSQL 服务器。

连接到 MySQL 数据库,先决条件

这相当简单,不需要任何 PHP 预先设置。现代 PHP 安装标准附带一个允许 PDO 连接到 MySQL 服务器的模块。

模块是php_pdo_mysql.dll

连接到 MSSQL 数据库,先决条件

这是一个更高级的设置。您需要php_pdo_sqlsrv_##_ts.dllphp_pdo_sqlsrv_##_nts.dll drivers。它们是特定于版本的,因此是##。在撰写本文时,微软已发布 PHP 5.5.x 的官方驱动程序。 Microsoft 尚未正式发布 5.6 驱动程序,但others 提供非官方版本。

模块是 php_pdo_sqlsrv_##_ts.dll 用于线程安全变体 该模块是php_pdo_sqlsrv_##_nts.dll,用于非线程安全变体

使用 PDO 连接到数据库 要连接到数据库,您需要从 PDO 构造创建一个新的 PDO 实例

$connection = new PDO(arguments);

PDO 构造函数接受 1 个必需参数和 3 个可选参数。

  1. DSN 或数据源名称,主要是包含驱动程序、主机和数据库名称信息的字符串。从 PHP 7.4 开始,它还可以包含用户名和密码。
  2. 用户名
  3. 密码
  4. 选项

连接到 MySQL

$dsn = 'mysql:dbname=databasename;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';

$dbh = new PDO($dsn, $user, $password);

让我们看一下$dsn:首先它定义了驱动程序(mysql)。然后是数据库名称,最后是主机。

连接到 MSSQL

$dsn = 'sqlsrv:Server=127.0.0.1;Database=databasename';
$user = 'dbuser';
$password = 'dbpass';

$dbh = new PDO($dsn, $user, $password);

让我们看一下$dsn:首先它定义了驱动程序(sqlsrv)。然后是主机名,最后是数据库名。

当您创建实例时,会与数据库建立连接。您只需在 PHP 脚本执行期间执行一次。

您需要将 PDO 实例的创建包装在 try-catch 子句中。如果创建失败,则会显示回溯,揭示有关您的应用程序的关键信息,例如用户名和密码。为了避免这种情况,请捕获错误。

try 
{
    $connection = new PDO($dsn, $user, $password);
}
catch( PDOException $Exception ) 
{   
     echo "Unable to connect to database.";
     exit;
}

要抛出 SQL 服务器返回的错误,请使用 setAttribute 将此选项添加到 PDO 实例:$connection->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

执行查询

PDO 使用准备好的语句。这是PDO 的 方法和mysql 函数 之间的真正区别。后者很容易受到SQL-INJECTION 的影响。可以构建这样的查询:

$SQL = 'SELECT ID FROM users WHERE user = '.$username ;

当恶意网站或个人发布用户名injector; DROP TABLE users 时。结果将是毁灭性的。您需要通过转义和用引号封装字符串和变量来证明您的代码。必须这样做 对于每个查询。在较大的网站或维护不善的代码上,拥有允许 SQL 注入的表单的风险可能会变得非常高。准备好的语句消除了第一层 SQL 注入的机会,就像上面的例子一样。

PDO 驱动程序充当 PHP 服务器和数据库服务器之间的中间人,称为 数据访问抽象层。它不会重写您的 SQL 查询,但确实提供了一种连接到多种数据库类型的通用方法 并为您处理将变量插入到查询中。 Mysql 函数 在 PHP 代码执行时构造查询。使用 PDO,查询实际上是在数据库服务器上构建的。

准备好的 SQL 示例:

$SQL = 'SELECT ID, EMAIL FROM users WHERE user = :username';

注意区别;代替在字符串外使用$ 的PHP 变量,我们在字符串内引入使用: 的变量。另一种方法是:

$SQL = 'SELECT ID, EMAIL FROM users WHERE user = ?';

如何执行实际查询

您的 PDO 实例提供了两种执行查询的方法。当你没有变量时,你可以使用query(),变量使用prepare()query() 在调用时立即执行。请注意调用的面向对象方式(->)。

$result = $connection->query($SQL);

准备方法

prepare 方法有两个参数。第一个是 SQL 字符串,第二个是数组形式的选项。一个基本的例子

$connection->prepare($SQL, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));

在我们的 SQL 字符串示例中,我们使用了一个名为 :username 的命名变量。我们仍然需要将 PHP 变量、整数或字符串绑定到它。我们可以通过两种方式做到这一点。构建一个包含命名变量为key 的数组,或者使用方法bindParambindValue。 为了简单起见,我将解释数组变体和方法bindValue

Array
您可以对命名变量执行类似的操作,将 variable 作为 array 键

$queryArguments = array(':username' => $username);

这对于索引变量(?):

$queryArguments = array($username);

添加完所有需要的变量后,您可以调用方法execute() 来执行查询。从而将数组作为参数传递给函数execute

$result = $connection->execute($queryArguments);

bindValue
bindValue 方法允许您将值绑定到 PDO 实例。该方法采用两个必需参数和一个可选参数。可选参数设置值的数据类型。

对于命名变量:

$connection->bindValue(':username', $username);

对于索引变量:

$connection->bindValue(1, $username);

将值绑定到实例后,您可以在不传递任何参数的情况下调用execute

$result = $connection->execute();

注意:命名变量只能使用一次!使用它们两次将导致无法执行查询。根据您的设置,这将或不会引发错误。

获取结果

再次,我将只介绍从返回的集合中获取结果的基础知识。 PDO 是一个相当高级的插件。

使用fetchfetchAll

如果您执行了选择查询或执行了返回结果集的存储过程

fetch
fetch 是一种最多可以采用三个可选参数的方法。它从结果集中获取一行。默认情况下,它返回一个 array,其中包含作为键的列名和索引结果。 我们的示例查询可能会返回类似

ID      EMAIL
1       someone@example.com

fetch 将返回:

Array
(
    [ID] => 1
    [0] => 1
    [EMAIL] => someone@example.com
    [1] => someone@example.com
)

回显结果集的所有输出:

while($row = $result->fetch())
{
    echo $row['ID'];
    echo $row['EMAIL'];
}

您可以在此处找到其他选项:fetch_style;

fetchAll
获取单个数组中的所有行。使用与fetch 相同的默认选项。

$rows = $result->fetchAll();

如果您使用不返回结果的查询,例如插入或更新查询,您可以使用方法rowCount 来检索受影响的行数。


一个简单的类:

class pdoConnection {
    public $isConnected;

    protected $connection;

    public function __construct($dsn, $username, $password, $options = array()) {
        $this->isConnected = true;
        try {
            $this->connection = new PDO($dsn, $username, $password, $options);
            $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->connection->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); //sets the default to return 'named' properties in array.
        } catch (PDOException $e) {
            $this->isConnected = false;
            throw new Exception($e->getMessage());
        }
    }

    public function disconnect() {
        $this->connection = null;
        $this->isConnected = false;
    }

    public function query($SQL) {
        try {
            $result = $this->connection->query($SQL);
            return $result;
        } catch (PDOException $e) {
            throw new PDOException($e->getMessage());
        }
    }

    public function prepare($SQL, $params = array()) {
        try {
            $result = $this->connection->prepare($SQL);
            $result->execute($params);
            return $result;
        } catch (PDOException $e) {
            throw new PDOException($e->getMessage());
        }
    }
}

使用方法:

$dsn = 'mysql:dbname=databasename;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';

$db = new pdoConnection($dsn, $user, $password);

$SQL = 'SELECT ID, EMAIL FROM users WHERE user = :username';
$result = $db->prepare($SQL, array(":username" => 'someone'));

while($row = $result->fetch())
{
    echo $row['ID'];
    echo $row['EMAIL'];
}   

【讨论】:

  • 将其发布在更规范的问题之一中可能是值得的(更容易找到,更多曝光)。解释异常用法很好,但在从 mysql 函数转换时可能不太合适并且会分散注意力。将 PDOExceptions 下转换为普通的 Exceptions 似乎是不可取的。 pdoConnection 包装器也使您的 PDO 引入复杂化,并且除了组合的准备 + 执行方便方法之外有点多余。
  • 请注意,默认情况下,PDO 的“准备好的语句”并不是真正的准备好的语句,而只是出于性能原因模拟(通过内部转义值)。您需要在 PDO 连接对象上显式设置一些选项以启用真正的准备语句。
  • @AndréDaniel,据我所知,这取决于 SQL 服务器的驱动程序和版本。默认情况下不模拟 MSSQL。在 MySQL 4.1 以上,它还原生准备而不是模拟。
  • @Mario。谢谢你的想法。您能否详细说明您的评论的这一部分:“将 PDOExceptions 下转换为普通异常似乎是不可取的”?
  • “它本身也准备好了”or not.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-25
  • 1970-01-01
  • 2013-08-22
  • 2013-12-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多