如何用 PDO 替换 MySQL 函数?

How to replace MySQL functions with PDO?

php.net 上阅读有关 MySQL 函数的内容时 。我遇到了这条消息

Warning This extension is deprecated as of PHP 5.5.0, and will be removed in the future. Instead, the MySQLi or PDO_MySQL extension should be used. See also MySQL: choosing an API guide and related FAQ for more information. Alternatives to this function include:

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

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

首先:

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

为什么 mysql 函数 已弃用?

The mysql extension is ancient and has been around since PHP 2.0, released 15 years ago (!!); which is a decidedly different beast than the modern PHP which tries to shed the bad practices of its past. The mysql extension is a very raw, low-level connector to MySQL which lacks many convenience features and is thereby hard to apply correctly in a secure fashion; it's therefore bad for noobs. Many developers do not understand SQL injection and the mysql API is fragile enough to make it hard to prevent it, even if you're aware of it. It is full of global state (implicit connection passing for instance), which makes it easy to write code that is hard to maintain. Since it's old, it may be unreasonably hard to maintain at the PHP core level.

The mysqli extension is a lot newer and fixes all the above problems. PDO is also rather new and fixes all those problems too, plus more.

Due to these reasons* the mysql extension will be removed sometime in the future.

来源Deceze

如何实现PDO

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

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

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

The module is php_pdo_mysql.dll

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

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

作为非官方版本获得

The module is php_pdo_sqlsrv_##_ts.dll for the thread safe variant The module is php_pdo_sqlsrv_##_nts.dll for the non-thread safe variant

正在使用 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 脚本期间,您只需执行一次。

You need to wrap the PDO instance creation in a try-catch clause. If the creation fails a back trace is shown revealing critical information about your application, like username and password. To avoid this catch the errors.

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

To throw errors returned by your SQL server add this options to your PDO instance using setAttribute: $connection->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

执行查询

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

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

当一个恶意网站或人 post 使用用户名 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 key:

$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();

NOTE: You can only use a named variable once! Using them twice will result in a failure to execute the query. Depending on your settings this will or will not throw an error.

正在获取结果

同样,我将只介绍从 returned 集合中获取结果的基础知识。 PDO 是一个相当高级的附加组件。

使用fetchfetchAll

如果您执行了 select 查询 或执行了 存储过程 return 结果集:

fetch
fetch 是一种最多可以接受三个可选参数的方法。它从结果集中获取一行。默认情况下,它 return 是一个 数组 包含列名作为键和索引结果。 我们的示例查询可以 return 类似于

ID      EMAIL
1       someone@example.com

fetch 将 return 为:

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();

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


一个简单的class:

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