一尘不染

如何用PDO代替MySQL函数?

php

在php.net上阅读有关 MySQL函数的信息时 。我遇到了这个消息

警告此扩展自PHP5.5.0起不推荐使用,以后将被删除。相反,应使用MySQLi或PDO_MySQL扩展。另请参见MySQL:选择API指南和相关的FAQ,以获取更多信息。此功能的替代方法包括:

  • mysqli_connect()

  • PDO :: __ construct()

我已经了解了PDO。如何使用MySQL或MSSQL将代码更新为PDO?


阅读 347

收藏
2020-05-26

共1个答案

一尘不染

我看到在实现 my_sql函数的 SO上张贴了很多代码。以及其他人(包括我自己)的评论迫使提问者放弃 MySQL功能
并开始使用PDO或MySQLI。这篇文章对您有所帮助。您可以参考它,因为它提供了为什么不推荐使用它们以及什么是 PDO的
解释,以及实现PDO的最少代码示例。

首先:

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

为什么不推荐使用 mysql函数

mysql扩展很古老,自15年前发布(PHP)以来一直存在(!!);与现代PHP试图摆脱过去的不良做法相比,它绝对是另一种野兽。mysql扩展是MySQL的非常原始的低级连接器,它缺少许多便利功能,因此很难以安全的方式正确应用。因此对菜鸟不利。许多开发人员不了解SQL注入,并且mysql
API十分脆弱,即使您知道它,也很难阻止它。它充满了全局状态(例如,通过了隐式连接),这使得编写难以维护的代码变得容易。由于它已经很旧了,因此在PHP核心级别上可能难以合理地维护。

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

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

如何实施PDO

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

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

这非常简单,不需要任何PHP的预设置。现代PHP安装是标准随附的模块,该模块允许PDO连接到MySQL服务器。

该模块是 php_pdo_mysql.dll

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

这是一个更高级的设置。您需要php_pdo_sqlsrv_##_ts.dllphp_pdo_sqlsrv_##_nts.dll drivers。它们是特定于版本的,因此##。在撰写本文时,Microsoft已经发布了PHP
5.5.x的官方驱动程序。5.6驱动程序尚未正式通过微软发布的,但可作为非官方建立由他人。

该模块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脚本期间执行一次此操作。

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

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

要抛出SQL服务器返回的错误,请使用以下选项将此选项添加到PDO实例setAttribute
$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(),使用变量use
prepare()query()会在调用后立即执行。请注意调用(->)的面向对象方式。

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

准备方法

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

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

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

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是一种最多可以包含三个可选参数的方法。它从结果集中获取一行。默认情况下,它返回一个包含列名称作为键和索引结果的 数组
。我们的示例查询可能返回类似

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'];
}
2020-05-26