关于mysql:为什么不在php中使用mysql_*函数?

Why shouldn't I use mysql_* functions in PHP?

为什么不应该使用mysql_*功能有哪些技术原因?(例如:mysql_query()mysql_connect()mysql_real_escape_string()

即使他们在我的网站上工作,我为什么还要用别的东西?

如果他们不在我的网站上工作,为什么我会遇到这样的错误

Warning: mysql_connect(): No such file or directory


MySQL扩展:

  • 不在积极发展中
  • 从php 5.5(2013年6月发布)起正式被否决。
  • 已从php 7.0完全删除(2015年12月发布)
    • 这意味着截至2018年12月31日,它将不存在于任何支持的PHP版本中。目前,它只获取安全更新。
  • 缺少OO接口
  • 不支持:
    • 非阻塞、异步查询
    • 准备好的语句或参数化查询
    • 存储过程
    • 多个语句
    • 交易
    • "新"密码验证方法(在MySQL5.6中默认为打开;在5.7中是必需的)
    • MySQL5.1中的所有功能

因为它被否决了,所以使用它可以减少代码的未来证明。

缺少对准备好的语句的支持尤其重要,因为它们提供了一种更清晰、更不容易出错的方法来转义和引用外部数据,而不是用单独的函数调用手动转义外部数据。

请参阅SQL扩展的比较。


PHP提供了三种不同的API来连接到MySQL。这些是mysql(从php 7中删除)、mysqliPDO扩展。好的。

mysql_*函数过去非常流行,但现在不再鼓励使用它们了。文档团队正在讨论数据库的安全情况,教育用户远离常用的ext/mysql扩展是其中的一部分(检查php.internals:deprecating ext/mysql)。好的。

后来的PHP开发团队决定在用户连接到MySQL时生成E_DEPRECATED错误,无论是通过mysql_connect()mysql_pconnect()还是嵌入ext/mysql中的隐式连接功能。好的。

从php 5.5起,ext/mysql被正式否决,从php 7起被删除。好的。

看到红盒子了吗?好的。

当您进入任何mysql_*功能手册页面时,会看到一个红色的框,说明不应再使用它。好的。为什么?

离开ext/mysql不仅是为了安全,而且是为了访问MySQL数据库的所有特性。好的。

ext/mysql是为mysql 3.23而构建的,从那时起只增加了很少的内容,但是大部分内容都与旧版本保持兼容,这使得代码维护起来有点困难。缺少ext/mysql不支持的功能包括:(来自php手册)。好的。

  • 存储过程(无法处理多个结果集)
  • 准备的报表
  • 加密(SSL)
  • 压缩
  • 完全字符集支持

不使用mysql_*功能的原因:好的。

  • 不在积极发展中
  • 从php 7起删除
  • 缺少OO接口
  • 不支持非阻塞异步查询
  • 不支持准备好的语句或参数化查询
  • 不支持存储过程
  • 不支持多个语句
  • 不支持事务处理
  • 不支持MySQL5.1中的所有功能

以上点引自昆廷的回答好的。

缺少对准备好的语句的支持尤其重要,因为它们提供了一种更清晰、更不容易出错的方法来转义和引用外部数据,而不是通过单独的函数调用手动转义外部数据。好的。

请参阅SQL扩展的比较。好的。

抑制折旧警告好的。

当代码转换为mysqliPDO时,可以通过在php.ini中设置error_reporting以排除E_DEPRECATED:来抑制E_DEPRECATED错误。好的。

1
error_reporting = E_ALL ^ E_DEPRECATED

请注意,这也会隐藏其他的拒绝警告,不过,这些警告可能是针对MySQL以外的东西的。(来自PHP手册)好的。

文章PDO与mysqli:你应该使用哪一个?由dejan marjanovic将帮助您选择。好的。

还有一个更好的方法是PDO,我现在正在编写一个简单的PDO教程。好的。简单而简短的PDO教程我想的第一个问题是:"pdo"是什么?

a."pdo–php数据对象–是一个数据库访问层,提供对多个数据库的统一访问方法。"好的。

alt text

Ok.

连接到MySQL

使用mysql_*函数,或者我们可以用老方法(在php5.5及更高版本中已弃用)好的。

1
2
3
$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);

使用PDO:您只需要创建一个新的PDO对象。构造器接受指定数据库源PDO构造器的参数,主要采用四个参数:DSN(数据源名称)和可选的usernamepassword。好的。

在这里,我想你对除DSN以外的所有事物都很熟悉;这是PDO中的新事物。DSN基本上是一系列选项,告诉PDO使用哪个驱动程序和连接细节。如需进一步参考,请查看pdo mysql dsn。好的。

1
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

注:您也可以使用charset=UTF-8,但有时会导致错误,所以最好使用utf8。好的。

如果有连接错误,它将抛出一个可以捕获的PDOException对象,以便进一步处理Exception。好的。

阅读良好:连接和连接管理?好的。

还可以将多个驱动程序选项作为数组传递给第四个参数。我建议传递将PDO置于异常模式的参数。因为一些PDO驱动程序不支持本机准备的语句,所以PDO执行准备的仿真。它还允许您手动启用此仿真。要使用本机服务器端准备好的语句,应该显式地将其设置为false。好的。

另一种是关闭默认情况下在MySQL驱动程序中启用的准备仿真,但应关闭准备仿真以安全使用PDO。好的。

稍后我将解释为什么应关闭准备仿真。如需了解原因,请查看本帖。好的。

只有当您使用的是我不推荐的旧版本的MySQL时,它才可用。好的。

下面是一个如何做到这一点的示例:好的。

1
2
3
4
5
$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方法设置一些属性:好的。

1
2
3
4
5
$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_*时的一个常见做法是:好的。

1
2
//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));

OR die()不是处理错误的好方法,因为我们无法处理die中的问题。它只会突然结束脚本,然后将错误回送到通常不想向最终用户显示的屏幕上,并让血腥的黑客发现您的模式。或者,mysql_*函数的返回值通常可以与mysql_error()一起使用来处理错误。好的。

PDO提供了更好的解决方案:例外。我们对PDO所做的任何事情都应该用trycatch块包装。通过设置错误模式属性,我们可以将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可以被捕获并被优雅地处理。

良好阅读:好的。

  • 错误和错误处理?
  • pdoException类?
  • 例外情况?

像:好的。

1
2
3
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

你可以用trycatch包起来,如下:好的。

1
2
3
4
5
6
7
8
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());
}

您现在不必处理trycatch的问题。你可以在任何合适的时候抓住它,但我强烈建议你使用try--catch。另外,在调用PDO东西的函数外部捕捉它可能更有意义:好的。

1
2
3
4
5
6
7
8
9
10
11
12
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并只读取错误日志来隐藏生产中的危险错误消息。好的。

现在,在阅读了以上所有内容之后,你可能会想:当我只想开始学习简单的SELECTINSERTUPDATEDELETE语句时,到底是什么意思?别担心,我们开始吧:好的。选择数据

PDO select image好的。

所以你在mysql_*所做的是:好的。

1
2
3
4
5
6
7
8
<?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中,您可以这样做:好的。

1
2
3
4
5
6
<?php
$stmt = $db->query('SELECT * FROM table');

while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo $row['field1'];
}

或好的。

1
2
3
4
5
<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

//Use $results

注意:如果您使用的方法如下(query()所示),则此方法返回PDOStatement对象。因此,如果您想要获取结果,请像上面那样使用它。好的。

1
2
3
4
<?php
foreach($db->query('SELECT * FROM table') as $row) {
    echo $row['field1'];
}

在PDO数据中,它是通过->fetch()获得的,这是您的语句处理方法。在调用fetch之前,最好的方法是告诉PDO您希望如何获取数据。在下面的部分中,我将解释这一点。好的。提取模式

注意上面的fetch()fetchAll()代码中使用了PDO::FETCH_ASSOC。这就告诉PDO将行作为关联数组返回,字段名作为键。还有许多其他的获取模式,我将逐一解释。好的。

首先,我解释了如何选择获取模式:好的。

1
 $stmt->fetch(PDO::FETCH_ASSOC)

在上面,我一直在使用fetch()。您还可以使用:好的。

  • PDOStatement::fetchAll()—返回一个包含所有结果集行的数组
  • PDOStatement::fetchColumn()—从结果集的下一行返回一列
  • PDOStatement::fetchObject()—获取下一行并将其作为对象返回。
  • PDOStatement::setFetchMode()—设置此语句的默认提取模式

现在我来取模式:好的。

  • PDO::FETCH_ASSOC:返回结果集中返回的按列名索引的数组。
  • PDO::FETCH_BOTH(默认值):返回结果集中返回的按列名和0索引列号索引的数组。

还有更多的选择!在PDOStatement获取文档中阅读所有相关内容。好的。

获取行计数:好的。

不使用mysql_num_rows来获取返回的行数,您可以获取PDOStatementrowCount(),例如:好的。

1
2
3
4
<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';

获取最后插入的ID好的。

1
2
3
<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();

插入、更新或删除语句

Insert and update PDO image好的。

我们在mysql_*函数中所做的是:好的。

1
2
3
<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);

在PDO中,同样的事情可以通过:好的。

1
2
3
<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;

在上面的查询中,PDO::exec执行一条SQL语句,并返回受影响的行数。好的。

insert and delete稍后将介绍。好的。

只有在查询中不使用变量时,上述方法才有用。但是,当您需要在查询中使用变量时,永远不要像上面那样尝试,因为准备好的语句或参数化的语句是存在的。好的。准备的报表

问:准备好的声明是什么?为什么我需要它们?a.准备好的语句是预先编译好的SQL语句,可以通过只向服务器发送数据来多次执行。好的。

使用准备好的语句的典型工作流程如下(引自维基百科3点):好的。

  • 准备:语句模板由应用程序创建并发送到数据库管理系统(DBMS)。某些值未指定,称为参数、占位符或绑定变量(下面标记为?):好的。

    INSERT INTO PRODUCT (name, price) VALUES (?, ?)好的。

  • DBMS对语句模板进行分析、编译和执行查询优化,并在不执行的情况下存储结果。好的。

  • 执行:稍后,应用程序为参数提供(或绑定)值,DBMS执行语句(可能返回结果)。应用程序可以使用不同的值执行该语句任意多次。在本例中,它可以为第一个参数提供"面包",为第二个参数提供1.00
  • 通过在SQL中包含占位符,可以使用准备好的语句。基本上有三个不带占位符(不要用上面的变量来尝试这个方法),一个带未命名的占位符,一个带命名的占位符。好的。

    那么,现在,什么是命名的占位符,我如何使用它们呢?a.指定的占位符。使用带冒号的描述性名称,而不是问号。我们不关心名下持仓人的地位/价值顺序:好的。

    1
     $stmt->bindParam(':bla', $bla);

    bindParam(parameter,variable,data_type,length,driver_options)好的。

    也可以使用执行数组进行绑定:好的。

    1
    2
    3
    4
    <?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朋友的另一个好特性是,命名占位符可以直接将对象插入数据库,前提是属性与命名字段匹配。例如:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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);

    问:那么现在,什么是未命名的占位符,如何使用它们?A.让我们举个例子:好的。

    1
    2
    3
    4
    5
    <?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();

    和好的。

    1
    2
    $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()方法的元素的正确顺序。好的。SELECTINSERTUPDATEDELETE准备的查询

  • SELECT:好的。

    1
    2
    3
    $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:好的。

    1
    2
    3
    $stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
    $stmt->execute(array(':field1' => $field1, ':field2' => $field2));
    $affected_rows = $stmt->rowCount();
  • DELETE:好的。

    1
    2
    3
    4
    $stmt = $db->prepare("DELETE FROM table WHERE id=:id");
    $stmt->bindValue(':id', $id, PDO::PARAM_STR);
    $stmt->execute();
    $affected_rows = $stmt->rowCount();
  • UPDATE:好的。

    1
    2
    3
    $stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
    $stmt->execute(array($name, $id));
    $affected_rows = $stmt->rowCount();
  • 注:

    然而,PDO和/或MySQLi并不完全安全。检查答案PDO准备的语句是否足以防止SQL注入?由IcMax。另外,我从他的回答中引用了一些部分:好的。

    1
    2
    3
    4
    $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 /*"));

    好啊。


    首先,让我们从我们给每个人的标准评论开始:

    Please, don't use mysql_* functions in new code. They are no longer maintained and are officially deprecated. See the red box? Learn about prepared statements instead, and use PDO or MySQLi - this article will help you decide which. If you choose PDO, here is a good tutorial.

    让我们一句一句地来看看这个,并解释一下:

    • 它们不再被维护,并被正式否决

      这意味着PHP社区正在逐渐放弃对这些非常老的函数的支持。它们很可能不存在于未来(最新)版本的PHP中!继续使用这些函数可能会在将来(而不是现在)破坏代码。

      新!-从php 5.5开始,ext/mysql现在正式被否决了!

      更新!ext/mysql已在php 7中删除。
    • 相反,你应该学习准备好的陈述

      mysql_*扩展不支持准备好的语句,这是(除其他外)对抗SQL注入的非常有效的对策。它修复了MySQL相关应用程序中的一个非常严重的漏洞,允许攻击者访问您的脚本并对您的数据库执行任何可能的查询。

      有关详细信息,请参阅如何在PHP中防止SQL注入?

    • 看到红盒子了吗?

      当您进入任何mysql功能手册页面时,您会看到一个红色的框,说明不应再使用它。

    • 使用PDO或mysqli

      有更好、更健壮和构建良好的替代方案,pdo-php database object,它提供了一个完整的OOP数据库交互方法,mysqli,它是一个mysql特有的改进。


    易用性

    已经提到了分析和综合原因。对于新来者来说,停止使用过时的mysql_u函数有更大的动机。好的。

    现代的数据库API更易于使用。好的。

    主要是绑定参数可以简化代码。有了优秀的教程(如上图所示),向PDO的过渡不会过于困难。好的。

    然而,一次重写一个更大的代码库需要时间。中间方案的理由:好的。等效的pdo_*functions代替mysql_*s>

    使用您可以用最少的工作量从旧的mysql_u函数切换。增加了pdo_功能包装纸,取代了mysql_功能包装纸。好的。

  • 只需在每个必须与数据库交互的调用脚本中使用include_once("pdo_mysql.php");。好的。

  • 删除所有地方的mysql_功能前缀,并用pdo_替换。好的。

    • mysql_变为pdo_connect()
    • mysql_变为pdo_query()
    • mysql_变为pdo_num_rows()
    • mysql_变为pdo_insert_id()
    • mysql_变为pdo_fetch_array()
    • mysql_变为pdo_fetch_assoc()
    • mysql_变为pdo_real_escape_string()
    • 等等…
  • 您的代码将工作得很相似,并且大部分看起来仍然相同:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    include_once("pdo_mysql.php");

    pdo_connect("localhost","usrABC","pw1234567");
    pdo_select_db("test");

    $result = pdo_query("SELECT title, html FROM pages");  

    while ($row = pdo_fetch_assoc($result)) {
        print"$row[title] - $row[html]";
    }
  • ET Voice。您的代码正在使用PDO。现在是时候实际利用它了。好的。绑定参数易于使用

    好的。

    你只需要一个不那么笨拙的API。好的。

    pdo_query()增加了对绑定参数的轻松支持。转换旧代码很简单:好的。

    好的。

    将变量移出SQL字符串。好的。

    • 将它们作为逗号分隔的函数参数添加到pdo_query()中。
    • 将问号?放在变量之前的位置。
    • 去掉以前包含字符串值/变量的单引号。

    对于长度更大的代码来说,优势变得更加明显。好的。

    通常,字符串变量不只是被插入到SQL中,而是与中间的转义调用相连接。好的。

    1
    2
    3
    4
    pdo_query("SELECT id, links, html, title, user, date FROM articles
       WHERE title='"
    . pdo_real_escape_string($title) ."' OR id='".
       pdo_real_escape_string($title) ."' AND user <> '" .
       pdo_real_escape_string($root) ."' ORDER BY date")

    使用?个占位符,您不必为此烦恼:好的。

    1
    2
    pdo_query("SELECT id, links, html, title, user, date FROM articles
       WHERE title=? OR id=? AND user<>? ORDER BY date"
    , $title, $id, $root)

    记住,PDO仍然允许或。不要转义变量并将其绑定到同一查询中。好的。

    • 占位符功能由它后面的真正PDO提供。
    • 因此也允许稍后使用:named占位符列表。

    更重要的是,您可以在任何查询后面安全地传递$u request[]变量。提交时,

    字段与数据库结构完全匹配,甚至更短:好的。

    1
    pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);

    这么简单。但是,让我们回到一些重写建议和技术原因上,来说明为什么您可能希望摆脱mysql_和escaping。好的。修复或删除任何旧学校sanitize()功能

    将绑定参数的所有mysql_调用转换为pdo_query调用后,删除所有多余的pdo_real_escape_string调用。好的。

    特别是,您应该以一种或另一种形式修复过时的教程所宣传的任何sanitizecleanfilterThisclean_data功能:好的。

    1
    2
    3
    function sanitize($str) {
       return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
    }

    这里最突出的缺陷是缺乏文档。更重要的是,过滤的顺序完全是错误的。好的。

    • 正确的顺序应该是:不赞成使用stripslashes作为最内部的调用,然后使用trim,然后使用strip_tagshtmlentities作为输出上下文,最后只使用_escape_string作为其应用程序,直接进行SQL交叉分析。好的。

    • 但是,作为第一步,你只需取消_real_escape_string的电话。好的。

    • 如果您的数据库和应用程序流需要HTML上下文安全字符串,那么现在您可能需要保留其余的sanitize()函数。添加一条注释,说明从今以后它只应用HTML转义。好的。

    • 字符串/值处理委托给PDO及其参数化语句。好的。

    • 如果在你的卫生功能中提到过stripslashes(),这可能意味着更高级别的监督。好的。

      • 这通常是为了消除被否决的magic_quotes的损坏(双重逃逸)。但最好集中固定,而不是逐串固定。好的。

      • 使用其中一个userland反转方法。然后删除sanitize功能中的stripslashes()。好的。

      Historic note on magic_quotes. That feature is rightly deprecated. It's often incorrectly portrayed as failed security feature however. But magic_quotes are as much a failed security feature as tennis balls have failed as nutrition source. That simply wasn't their purpose.

      Ok.

      The original implementation in PHP2/FI introduced it explicitly with just"quotes will be automatically escaped making it easier to pass form data directly to msql queries". Notably it was accidentially safe to use with mSQL, as that supported ASCII only.
      Then PHP3/Zend reintroduced magic_quotes for MySQL and misdocumented it. But originally it was just a convenience feature, not intend for security.

      Ok.

    准备好的陈述有什么不同

    当您将字符串变量扰乱到SQL查询中时,它不仅仅变得更加复杂,您也可以按照它进行操作。对于MySQL来说,再次分离代码和数据也是一项额外的工作。好的。

    好的。

    SQL注入只是在数据流入代码上下文时进行的。数据库服务器稍后无法发现php最初将变量粘在查询子句之间的位置。好的。

    通过绑定参数,可以在PHP代码中分离SQL代码和SQL上下文值。但它不会在幕后再次被洗牌(除了与pdo::仿真_准备)。您的数据库接收不可变的SQL命令和1:1的变量值。好的。

    好的。

    虽然这个答案强调,您应该关心droppingmysql_的可读性优势。由于这种可见的和技术性的数据/代码分离,有时还会有性能优势(重复插入的值不同)。好的。

    注意,对于所有SQL注入,参数绑定仍然不是一个神奇的一站式解决方案。它处理数据/值的最常用用法。但是不能白名单列名/表标识符,帮助动态子句构造,或只是简单的数组值列表。好的。混合PDO使用

    这些pdo_*包装函数构成了一个编码友好的stop-gap API。(如果不是因为特殊的函数签名移位,这几乎就是MYSQLI所能做到的)。他们也经常暴露真实的PDO。重写不必停止使用新的pdo_u函数名。您可以逐个将每个pdo_query()转换为普通的$pdo->prepare()->execute()调用。好的。

    不过,最好还是从简化开始。例如,常见的结果提取:好的。

    1
    2
    $result = pdo_query("SELECT * FROM tbl");
    while ($row = pdo_fetch_assoc($result)) {

    可以用foreach迭代替换:好的。

    1
    foreach ($result as $row) {

    或者更好的是直接和完整的阵列检索:好的。

    1
    $result->fetchAll();

    在大多数情况下,您会得到比PDO或MySQL_u通常在失败查询后提供的更有用的警告。好的。其他选项

    因此,我们希望能看到一些实际的原因,以及一条下降的途径。好的。

    切换到PDO并不能完全切断它。pdo_query()也只是它的前端。好的。

    除非您还引入了参数绑定或者可以使用更好的API中的其他东西,否则它是一个无意义的开关。我希望它的描述足够简单,不至于让新来的人更灰心。(教育通常比禁止更有效。)好的。

    虽然它符合最简单的事情,可能工作类别,但它仍然是非常实验性的代码。我只是在周末写的。然而,还有很多选择。只需谷歌的PHP数据库抽象和浏览一点。一直以来都有很多优秀的图书馆来完成这些任务。好的。

    如果您想进一步简化数据库交互,那么像Paris/Deirm这样的映射器值得一试。就像不再有人在javascript中使用平淡的dom一样,现在您不必临时保管一个原始的数据库接口。好的。好啊。


    mysql_功能:

  • 已经过时了-它们不再被维护了
  • 不允许轻松移动到其他数据库后端
  • 不支持准备好的语句,因此
  • 鼓励程序员使用串联来构建查询,从而导致SQL注入漏洞

  • 说到技术原因,只有少数几个,非常具体,很少使用。很可能你在生活中永远不会使用它们。也许我太无知了,但我从来没有机会使用它们好的。

    • 非阻塞、异步查询
    • 返回多个结果集的存储过程
    • 加密(SSL)
    • 压缩

    如果你需要它们——毫无疑问,这些都是从MySQL扩展转向更时尚、更现代的技术原因。好的。

    不过,也有一些非技术性的问题,这会使您的体验更加困难。好的。

    • 在现代PHP版本中进一步使用这些函数将引起不赞成的级别注意。它们只需关闭即可。
    • 在不久的将来,它们可能会从默认的php构建中删除。也没什么大不了的,因为mydsqlext将被转移到pecl中,并且每个宿主都会很乐意用它编译PHP,因为他们不想失去那些站点已经工作了几十年的客户。
    • 来自StackOverflow社区的强大抵抗力。每次你提到这些诚实的职能时,你都会被告知他们是被严格禁止的。
    • 作为一个普通的PHP用户,您使用这些函数的想法很可能是错误的和错误的。正因为有这么多的教程和手册教你错误的方法。不是函数本身——我必须强调它——而是它们的使用方式。

    后一个问题是个问题。但是,在我看来,提出的解决方案也没有更好。在我看来,所有这些PHP用户都将学习如何同时正确处理SQL查询,这是一个过于理想化的梦想。很可能他们只是机械地将mysql_ux改为mysqli_x,保持相同的方法。尤其是因为mysqli让准备好的语句使用起来非常痛苦和麻烦。更不用说本机准备的语句不足以防止SQL注入,mysqli和pdo都没有提供解决方案。好的。

    所以,我宁愿与错误的做法作斗争,以正确的方式教育人们,而不是与这种诚实的延伸作斗争。好的。

    此外,还有一些错误或不重要的原因,例如好的。

    • 不支持存储过程(我们使用mysql_query("CALL my_proc");已久)
    • 不支持事务(同上)
    • 不支持多个语句(谁需要它们?)
    • 不在积极发展中(那又怎样?它是否以任何实际的方式影响您?)
    • 缺少OO接口(创建一个接口需要几个小时)
    • 不支持准备好的语句或参数化查询

    最后一点很有趣。尽管mysql ext不支持本机准备的语句,但出于安全考虑,它们不是必需的。我们可以使用手动处理的占位符(就像PDO一样)轻松地伪造准备好的语句:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    function paraQuery()
    {
        $args  = func_get_args();
        $query = array_shift($args);
        $query = str_replace("%s","'%s'",$query);

        foreach ($args as $key => $val)
        {
            $args[$key] = mysql_real_escape_string($val);
        }

        $query  = vsprintf($query, $args);
        $result = mysql_query($query);
        if (!$result)
        {
            throw new Exception(mysql_error()." [$query]");
        }
        return $result;
    }

    $query  ="SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
    $result = paraQuery($query, $a,"%$b%", $limit);

    瞧,一切都是参数化的,安全的。好的。

    但好吧,如果你不喜欢手册中的红框,就会出现一个选择问题:mysqli还是pdo?好的。

    答案如下:好的。

    • 如果您了解使用数据库抽象层并寻找API来创建抽象层的必要性,那么mysqli是一个非常好的选择,因为它确实支持许多特定于mysql的特性。
    • 如果像绝大多数PHP人员一样,您在应用程序代码中正确地使用原始API调用(这实际上是错误的实践),那么PDO是唯一的选择,因为这个扩展假装不仅是API,而是一个半DAL,仍然不完整,但提供了许多重要的功能,其中两个功能使PDO与mysqli有着至关重要的区别:好的。

      • 与mysqli不同,pdo可以按值绑定占位符,这使得动态构建的查询在没有多个屏幕的情况下是可行的。
      • 与mysqli不同,pdo总是可以以简单的常规数组返回查询结果,而mysqli只能在mysqlnd安装上执行。

    因此,如果您是一个普通的PHP用户,并且想在使用本机准备的语句时为自己节省大量的麻烦,那么PDO——同样也是——是唯一的选择。然而,PDO也不是一颗银弹,它有它的困难。所以,我在pdo标签wiki中为所有常见的陷阱和复杂的案例编写了解决方案好的。

    然而,每个人都在说扩展的缺失是2重要的事实:mysqli和PDO

  • A预处理语句是不是银弹。有动态标识符使用准备好的语句是不能被绑定。有一个未知的动态查询参数的查询数,使建设困难的任务。

  • "_ mysqli * NOR PDO函数要出现在应用程序代码中。那里应该是一个抽象层和应用程序代码之间的他们,希望你所有的肮脏的工作)的结合,循环、错误处理等。在制作应用,干和清洁的代码。特别是在复杂的情况下样动态查询建设。

  • 因此,就切换到这个PDO或是不够的。一个使用在安切洛蒂的ORM查询生成器,或A,或任何数据库抽象类而不是原始的API函数调用的代码。相反,如果你在与层之间的抽象的应用程序代码和MySQL API是物,它没有引擎是用来。你可以使用MySQL的,直到它已被废弃,然后去Ext的抽象类可以重写到另一个引擎,所有的应用程序代码和变形。

    这里是一些例子的基于知识的搜索显示在safemysql类抽象类应该是:

    1
    2
    $city_ids = array(1,2,3);
    $cities   = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);

    这是一个比较一个单一的线的量的代码你需要一个PDO。然后比较与疯狂的金额与原代码,你将需要这个预处理语句。注意,错误处理,分析,查询日志和运行已经内置。

    1
    2
    $insert = array('name' => 'John', 'surname' =>"O'Hara");
    $db->query("INSERT INTO users SET ?u", $insert);

    比较它与正常的PDO的插入,当一个单场的名字是六到十次的重复在这些众多的placeholders命名查询,绑定,和定义。

    另一个例子:

    1
    $data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);

    你可以找到一个例子包含在PDO实际案例上搜索。它想太罗嗦,不安全的和最有可能的。

    所以,再一次它不只是原始的驱动程序应该是有用的,但抽象类的实例,让初学者帮助和手册不解决任何实际问题。

    好的。


    有很多原因,但也许最重要的一个原因是这些函数鼓励不安全的编程实践,因为它们不支持准备好的语句。准备好的语句有助于防止SQL注入攻击。

    使用mysql_*函数时,必须记住通过mysql_real_escape_string()运行用户提供的参数。如果您只在一个地方忘记了,或者您碰巧只逃避了输入的一部分,那么您的数据库可能会受到攻击。

    PDOmysqli中使用准备好的语句将使这种编程错误更加难以产生。


    因为(除其他原因外)很难确保对输入数据进行清理。如果使用参数化查询,就像使用PDO或MySQLI一样,可以完全避免风险。

    例如,有人可以使用"enhzflep); drop table users"作为用户名。旧函数允许每个查询执行多个语句,因此类似于讨厌的bugger可以删除整个表。

    如果要使用mysqli的pdo,用户名最终将是"enhzflep); drop table users"

    见bobby-tables.com。


    这个答案是为了说明绕过写得不好的PHP用户验证代码是多么的简单,这些攻击是如何工作的(以及使用什么),以及如何用一个安全的准备好的语句替换旧的MySQL函数-而且,基本上,为什么stackOverflow用户(可能有很多rep)对新用户叫苦连天,提出问题来改善他们的性能。代码。好的。

    首先,请随意创建这个测试mysql数据库(我叫mine prep):好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    mysql> create table users(
        -> id int(2) primary key auto_increment,
        -> userid tinytext,
        -> pass tinytext);
    Query OK, 0 rows affected (0.05 sec)

    mysql> insert into users values(null, 'Fluffeh', 'mypass');
    Query OK, 1 row affected (0.04 sec)

    mysql> create user 'prepared'@'localhost' identified by 'example';
    Query OK, 0 rows affected (0.01 sec)

    mysql> grant all privileges on prep.* to 'prepared'@'localhost' with grant option;
    Query OK, 0 rows affected (0.00 sec)

    完成后,我们可以转到我们的PHP代码。好的。

    让我们假设以下脚本是网站上管理员的验证过程(如果复制并用于测试,则简化但有效):好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    <?php

        if(!empty($_POST['user']))
        {
            $user=$_POST['user'];
        }  
        else
        {
            $user='bob';
        }
        if(!empty($_POST['pass']))
        {
            $pass=$_POST['pass'];
        }
        else
        {
            $pass='bob';
        }

        $database='prep';
        $link=mysql_connect('localhost', 'prepared', 'example');
        mysql_select_db($database) or die("Unable to select database");

        $sql="select id, userid, pass from users where userid='$user' and pass='$pass'";
        //echo $sql."";
        $result=mysql_query($sql);
        $isAdmin=false;
        while ($row = mysql_fetch_assoc($result)) {
            echo"My id is".$row['id']." and my username is".$row['userid']." and lastly, my password is".$row['pass']."";
            $isAdmin=true;
            // We have correctly matched the Username and Password
            // Lets give this person full access
        }
        if($isAdmin)
        {
            echo"The check passed. We have a verified admin!";
        }
        else
        {
            echo"You could not be verified. Please try again...";
        }
        mysql_close($link);

    ?>

    <form name="exploited" method='post'>
        User: <input type='text' name='user'>
        Pass: <input type='text' name='pass'>
        <input type='submit'>
    </form>

    乍一看似乎足够合法。好的。

    用户必须输入登录名和密码,对吗?好的。

    才华横溢,不可进入以下:好的。

    1
    2
    user: bob
    pass: somePass

    并提交。好的。

    输出如下:好的。

    1
    You could not be verified. Please try again...

    超级的!按预期工作,现在让我们试试实际的用户名和密码:好的。

    1
    2
    user: Fluffeh
    pass: mypass

    太神了!大家好,代码正确验证了管理员。太棒了!好的。

    嗯,不是真的。假设用户是一个聪明的小人物。假设那个人是我。好的。

    输入以下内容:好的。

    1
    2
    user: bob
    pass: n' or 1=1 or 'm=m

    输出为:好的。

    1
    The check passed. We have a verified admin!

    恭喜你,你只允许我输入你的超级保护管理员,我输入了一个错误的用户名和一个错误的密码。说真的,如果你不相信我,用我提供的代码创建数据库,并运行这个PHP代码——乍一看,这确实很好地验证了用户名和密码。好的。

    所以,作为回应,这就是为什么你会被吼。好的。

    所以,让我们看看哪里出了问题,为什么我刚进入你的超级管理员蝙蝠洞。我做了一个猜测,并假设您对输入不小心,只是将它们直接传递到数据库。我用一种方法构造输入,它将改变您实际运行的查询。那么,它应该是什么,结果是什么?好的。

    1
    select id, userid, pass from users where userid='$user' and pass='$pass'

    这就是查询,但是当我们用实际的输入替换变量时,我们得到了以下信息:好的。

    1
    select id, userid, pass from users where userid='bob' and pass='n' or 1=1 or 'm=m'

    看看我是如何构造我的"密码"的,这样它将首先关闭围绕密码的单引号,然后引入一个全新的比较?为了安全起见,我添加了另一个"字符串",这样单引号就可以像我们最初拥有的代码中预期的那样结束。好的。

    然而,这不是人们现在对你大喊大叫,而是向你展示如何使你的代码更安全。好的。

    好吧,出了什么问题,我们怎么解决?好的。

    这是典型的SQL注入攻击。这是最简单的方法之一。在攻击向量的尺度上,这是一个幼童攻击坦克并获胜。好的。

    那么,我们如何保护你神圣的行政部门,使它变得美好和安全呢?要做的第一件事是停止使用那些非常陈旧和不受欢迎的mysql_*函数。我知道,你遵循了你在网上找到的一个教程,它是有效的,但它是旧的,它是过时的,在几分钟的时间内,我刚刚突破它,没有那么多汗水。好的。

    现在,您可以更好地选择使用mysqli_u或pdo。我个人是PDO的忠实粉丝,所以我将在剩下的答案中使用PDO。有赞成者和反对者,但我个人认为赞成者远远超过反对者。它是跨多个数据库引擎的可移植的-无论您使用的是MySQL或Oracle,或只是血腥的任何东西-只是通过更改连接字符串,它有我们想要使用的所有花哨的功能,它是美好和干净的。我喜欢干净。好的。

    现在,让我们再次看看该代码,这次是使用pdo对象编写的:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    <?php

        if(!empty($_POST['user']))
        {
            $user=$_POST['user'];
        }  
        else
        {
            $user='bob';
        }
        if(!empty($_POST['pass']))
        {
            $pass=$_POST['pass'];
        }
        else
        {
            $pass='bob';
        }
        $isAdmin=false;

        $database='prep';
        $pdo=new PDO ('mysql:host=localhost;dbname=prep', 'prepared', 'example');
        $sql="select id, userid, pass from users where userid=:user and pass=:password";
        $myPDO = $pdo->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
        if($myPDO->execute(array(':user' => $user, ':password' => $pass)))
        {
            while($row=$myPDO->fetch(PDO::FETCH_ASSOC))
            {
                echo"My id is".$row['id']." and my username is".$row['userid']." and lastly, my password is".$row['pass']."";
                $isAdmin=true;
                // We have correctly matched the Username and Password
                // Lets give this person full access
            }
        }

        if($isAdmin)
        {
            echo"The check passed. We have a verified admin!";
        }
        else
        {
            echo"You could not be verified. Please try again...";
        }

    ?>

    <form name="exploited" method='post'>
        User: <input type='text' name='user'>
        Pass: <input type='text' name='pass'>
        <input type='submit'>
    </form>

    主要区别在于没有更多的mysql_*函数。这一切都是通过PDO对象完成的,其次,它使用的是一个准备好的语句。现在,你问的预先准备好的声明是什么?这是一种在运行查询之前告诉数据库我们要运行什么查询的方法。在本例中,我们告诉数据库:"嗨,我将运行一个select语句,想要id、userid和pass,它来自用户表,其中userid是变量,pass也是变量。"。好的。

    然后,在execute语句中,我们向数据库传递一个数组,其中包含它现在期望的所有变量。好的。

    结果非常好。让我们再次尝试以前的用户名和密码组合:好的。

    1
    2
    user: bob
    pass: somePass

    未验证用户。令人惊叹的。好的。

    怎么样:好的。

    1
    2
    user: Fluffeh
    pass: mypass

    哦,我只是有点兴奋,它起作用了:支票通过了。我们有一个经过验证的管理员!好的。

    现在,让我们试试聪明人输入的数据,试图通过我们的小验证系统:好的。

    1
    2
    user: bob
    pass: n' or 1=1 or 'm=m

    这次,我们得到以下信息:好的。

    1
    You could not be verified. Please try again...

    这就是为什么你在发问的时候会被大喊大叫的原因——这是因为人们可以看到你的代码可以不费吹灰之力就能被绕过。请务必使用此问题和答案来改进代码,使其更安全,并使用最新的函数。好的。

    最后,这并不是说这是完美的代码。您可以做很多事情来改进它,例如使用哈希密码,确保当您在数据库中存储敏感信息时,您不会以纯文本的形式存储它,具有多个级别的验证-但实际上,如果您只是将旧的易于注入的代码更改为该代码,那么在编写良好的代码的过程中,您会很好地做到这一点。代码——事实上,你已经走了这么远,而且还在阅读,这让我有一种希望,你不仅可以在编写网站和应用程序时实现这种类型的代码,还可以出去研究我刚才提到的其他事情——等等。写出你能写出的最好的代码,而不是那些几乎不起作用的最基本的代码。好的。好啊。


    mysql扩展是三种扩展中最老的一种,也是开发人员用来与mysql通信的原始方式。由于PHP和MySQL两个新版本都做了改进,现在不推荐使用此扩展来支持其他两个选项。

    • mysqli是用于mysql数据库的"改进"扩展。它利用了MySQLServer较新版本中可用的功能,向开发人员公开了面向函数和面向对象的接口,并做了一些其他漂亮的事情。

    • PDO提供了一个API,它整合了以前分布在主要数据库访问扩展中的大部分功能,如mysql、postgresql、sqlite、mssql等。该接口公开了高级对象,供程序员使用数据库连接、查询和结果集,低级驱动程序执行通信和恢复。使用数据库服务器处理源。PDO正在进行大量的讨论和工作,它被认为是现代专业代码中处理数据库的适当方法。


    我发现上面的答案非常冗长,所以总结一下:

    The mysqli extension has a number of
    benefits, the key enhancements over
    the mysql extension being:

    • Object-oriented interface
    • Support for Prepared Statements
    • Support for Multiple Statements
    • Support for Transactions
    • Enhanced debugging capabilities
    • Embedded server support

    资料来源:mysqli概述

    正如上面的答案所解释的,mysql的替代方案是mysqli和pdo(php数据对象)。

    • API支持服务器端准备的语句:由mysqli和pdo支持
    • API支持客户端准备的语句:仅受PDO支持
    • API支持存储过程:mysqli和pdo
    • API支持多个语句和所有MySQL4.1+功能-由MySQLI和PDO支持

    mysqli和pdo都是在php5.0中引入的,而mysql是在php3.0之前引入的。需要注意的一点是,mysql包含在php5.x中,尽管在以后的版本中已被弃用。


    几乎可以使用mysqli或pdo定义所有mysql_*函数。只需将它们放在旧的PHP应用程序之上,它就可以在PHP7上工作。我的解决方案。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    <?php

    define('MYSQL_LINK', 'dbl');
    $GLOBALS[MYSQL_LINK] = null;

    function mysql_link($link=null) {
        return ($link === null) ? $GLOBALS[MYSQL_LINK] : $link;
    }

    function mysql_connect($host, $user, $pass) {
        $GLOBALS[MYSQL_LINK] = mysqli_connect($host, $user, $pass);
        return $GLOBALS[MYSQL_LINK];
    }

    function mysql_pconnect($host, $user, $pass) {
        return mysql_connect($host, $user, $pass);
    }

    function mysql_select_db($db, $link=null) {
        $link = mysql_link($link);
        return mysqli_select_db($link, $db);
    }

    function mysql_close($link=null) {
        $link = mysql_link($link);
        return mysqli_close($link);
    }

    function mysql_error($link=null) {
        $link = mysql_link($link);
        return mysqli_error($link);
    }

    function mysql_errno($link=null) {
        $link = mysql_link($link);
        return mysqli_errno($link);
    }

    function mysql_ping($link=null) {
        $link = mysql_link($link);
        return mysqli_ping($link);
    }

    function mysql_stat($link=null) {
        $link = mysql_link($link);
        return mysqli_stat($link);
    }

    function mysql_affected_rows($link=null) {
        $link = mysql_link($link);
        return mysqli_affected_rows($link);
    }

    function mysql_client_encoding($link=null) {
        $link = mysql_link($link);
        return mysqli_character_set_name($link);
    }

    function mysql_thread_id($link=null) {
        $link = mysql_link($link);
        return mysqli_thread_id($link);
    }

    function mysql_escape_string($string) {
        return mysql_real_escape_string($string);
    }

    function mysql_real_escape_string($string, $link=null) {
        $link = mysql_link($link);
        return mysqli_real_escape_string($link, $string);
    }

    function mysql_query($sql, $link=null) {
        $link = mysql_link($link);
        return mysqli_query($link, $sql);
    }

    function mysql_unbuffered_query($sql, $link=null) {
        $link = mysql_link($link);
        return mysqli_query($link, $sql, MYSQLI_USE_RESULT);
    }

    function mysql_set_charset($charset, $link=null){
        $link = mysql_link($link);
        return mysqli_set_charset($link, $charset);
    }

    function mysql_get_host_info($link=null) {
        $link = mysql_link($link);
        return mysqli_get_host_info($link);
    }

    function mysql_get_proto_info($link=null) {
        $link = mysql_link($link);
        return mysqli_get_proto_info($link);
    }
    function mysql_get_server_info($link=null) {
        $link = mysql_link($link);
        return mysqli_get_server_info($link);
    }

    function mysql_info($link=null) {
        $link = mysql_link($link);
        return mysqli_info($link);
    }

    function mysql_get_client_info() {
        $link = mysql_link();
        return mysqli_get_client_info($link);
    }

    function mysql_create_db($db, $link=null) {
        $link = mysql_link($link);
        $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
        return mysqli_query($link,"CREATE DATABASE `$db`");
    }

    function mysql_drop_db($db, $link=null) {
        $link = mysql_link($link);
        $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
        return mysqli_query($link,"DROP DATABASE `$db`");
    }

    function mysql_list_dbs($link=null) {
        $link = mysql_link($link);
        return mysqli_query($link,"SHOW DATABASES");
    }

    function mysql_list_fields($db, $table, $link=null) {
        $link = mysql_link($link);
        $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
        $table = str_replace('`', '', mysqli_real_escape_string($link, $table));
        return mysqli_query($link,"SHOW COLUMNS FROM `$db`.`$table`");
    }

    function mysql_list_tables($db, $link=null) {
        $link = mysql_link($link);
        $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
        return mysqli_query($link,"SHOW TABLES FROM `$db`");
    }

    function mysql_db_query($db, $sql, $link=null) {
        $link = mysql_link($link);
        mysqli_select_db($link, $db);
        return mysqli_query($link, $sql);
    }

    function mysql_fetch_row($qlink) {
        return mysqli_fetch_row($qlink);
    }

    function mysql_fetch_assoc($qlink) {
        return mysqli_fetch_assoc($qlink);
    }

    function mysql_fetch_array($qlink, $result=MYSQLI_BOTH) {
        return mysqli_fetch_array($qlink, $result);
    }

    function mysql_fetch_lengths($qlink) {
        return mysqli_fetch_lengths($qlink);
    }

    function mysql_insert_id($qlink) {
        return mysqli_insert_id($qlink);
    }

    function mysql_num_rows($qlink) {
        return mysqli_num_rows($qlink);
    }

    function mysql_num_fields($qlink) {
        return mysqli_num_fields($qlink);
    }

    function mysql_data_seek($qlink, $row) {
        return mysqli_data_seek($qlink, $row);
    }

    function mysql_field_seek($qlink, $offset) {
        return mysqli_field_seek($qlink, $offset);
    }

    function mysql_fetch_object($qlink, $class="stdClass", array $params=null) {
        return ($params === null)
            ? mysqli_fetch_object($qlink, $class)
            : mysqli_fetch_object($qlink, $class, $params);
    }

    function mysql_db_name($qlink, $row, $field='Database') {
        mysqli_data_seek($qlink, $row);
        $db = mysqli_fetch_assoc($qlink);
        return $db[$field];
    }

    function mysql_fetch_field($qlink, $offset=null) {
        if ($offset !== null)
            mysqli_field_seek($qlink, $offset);
        return mysqli_fetch_field($qlink);
    }

    function mysql_result($qlink, $offset, $field=0) {
        if ($offset !== null)
            mysqli_field_seek($qlink, $offset);
        $row = mysqli_fetch_array($qlink);
        return (!is_array($row) || !isset($row[$field]))
            ? false
            : $row[$field];
    }

    function mysql_field_len($qlink, $offset) {
        $field = mysqli_fetch_field_direct($qlink, $offset);
        return is_object($field) ? $field->length : false;
    }

    function mysql_field_name($qlink, $offset) {
        $field = mysqli_fetch_field_direct($qlink, $offset);
        if (!is_object($field))
            return false;
        return empty($field->orgname) ? $field->name : $field->orgname;
    }

    function mysql_field_table($qlink, $offset) {
        $field = mysqli_fetch_field_direct($qlink, $offset);
        if (!is_object($field))
            return false;
        return empty($field->orgtable) ? $field->table : $field->orgtable;
    }

    function mysql_field_type($qlink, $offset) {
        $field = mysqli_fetch_field_direct($qlink, $offset);
        return is_object($field) ? $field->type : false;
    }

    function mysql_free_result($qlink) {
        try {
            mysqli_free_result($qlink);
        } catch (Exception $e) {
            return false;
        }
        return true;
    }


    与此mysql_connect()mysql_query()类型类似的函数是以前版本的php,即(php4)函数,现在没有使用。

    在最新的php5中,这些被mysqli_connect()mysqli_query()所取代。

    这就是错误背后的原因。


    这是今天(2019年1月)的一个老问题,但这可能仍然有用。大约7年前,我创建了mysql/mysql i/pdo功能的表格映射。可能是一个有用的参考。它在这里在线并在下面复制。请随意复制和粘贴HTML。

    作为一个实际问题,我发现将过程性mysql函数转换为oop mysql i是阻力最小的途径。同时打开两个DB连接是非常好的,这在转换过程中给了我们一些灵活性——我们可以逐段转换脚本,一次一个查询。虽然我今天可能不建议这样做,但那是当时的权宜之计。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    Mapping Obsolete MySQL Functions to Current PHP Extensions
    <table>
    <tr><th>MySQL Extension</th><th>MySQLi</th><th>PDO</th></tr>
    <tr><td>mysql_affected_rows</td>
        <td>mysqli::$affected_rows</td>
        <td>PDOStatement::rowCount</td>
        </tr>
    <tr><td>mysql_client_encoding</td>
        <td>mysqli::character_set_name</td>
        <td> </td>
        </tr>
    <tr><td>mysql_close</td>
        <td>mysqli::close</td>
        <td>Assign NULL to PDO Object</td>
        </tr>
    <tr><td>mysql_connect</td>
        <td>mysqli::__construct</td>
        <td>PDO::__construct</td>
        </tr>
    <tr><td>mysql_create_db</td>
        <td>Query: CREATE DATABASE</td>
        <td> </td>
        </tr>
    <tr><td>mysql_data_seek</td>
        <td>mysqli_stmt::data_seek</td>
        <td>PDO::FETCH_ORI_ABS (?)</td>
        </tr>
    <tr><td>mysql_db_name</td>
        <td>Query: SELECT DATABASE()</td>
        <td> </td>
        </tr>
    <tr><td>mysql_db_query</td>
        <td> </td>
        <td> </td>
        </tr>
    <tr><td>mysql_drop_db</td>
        <td>Query: DROP DATABASE</td>
        <td> </td>
        </tr>
    <tr><td>mysql_errno</td>
        <td>mysqli::$errno</td>
        <td>PDO::errorCode</td>
        </tr>
    <tr><td>mysql_error</td>
        <td>mysqli::$error_list</td>
        <td>PDO::errorInfo</td>
        </tr>
    <tr><td>mysql_escape_string</td>
        <td> </td>
        <td> </td>
        </tr>
    <tr><td>mysql_fetch_array</td>
        <td>mysqli_result::fetch_array</td>
        <td>PDOStatement::fetch</td>
        </tr>
    <tr><td>mysql_fetch_assoc</td>
        <td>mysqli_result::fetch_assoc</td>
        <td>PDOStatement::fetch</td>
        </tr>
    <tr><td>mysql_fetch_field</td>
        <td>mysqli_result::fetch_field</td>
        <td>PDOStatement::getColumnMeta</td>
        </tr>
    <tr><td>mysql_fetch_lengths</td>
        <td>mysqli_result::$lengths</td>
        <td>PDOStatement::getColumnMeta</td>
        </tr>
    <tr><td>mysql_fetch_object</td>
        <td>mysqli_result::fetch_object</td>
        <td>PDOStatement::fetch</td>
        </tr>
    <tr><td>mysql_fetch_row</td>
        <td>mysqli_result::fetch_row</td>
        <td>PDOStatement::fetch</td>
        </tr>
    <tr><td>mysql_field_flags</td>
        <td>mysqli_result::fetch_fields</td>
        <td>PDOStatement::getColumnMeta</td>
        </tr>
    <tr><td>mysql_field_len</td>
        <td>mysqli_result::fetch_field_direct</td>
        <td>PDOStatement::getColumnMeta</td>
        </tr>
    <tr><td>mysql_field_name</td>
        <td>mysqli_result::fetch_field_direct</td>
        <td>PDOStatement::getColumnMeta</td>
        </tr>
    <tr><td>mysql_field_seek</td>
        <td>mysqli_result::field_seek</td>
        <td>PDOStatement::fetch</td>
        </tr>
    <tr><td>mysql_field_table</td>
        <td>mysqli_result::fetch_field_direct</td>
        <td>PDOStatement::getColumnMeta</td>
        </tr>
    <tr><td>mysql_field_type</td>
        <td>mysqli_result::fetch_field_direct</td>
        <td>PDOStatement::getColumnMeta</td>
        </tr>
    <tr><td>mysql_free_result</td>
        <td>mysqli_result::free</td>
        <td>PDOStatement::closeCursor</td>
        </tr>
    <tr><td>mysql_get_client_info</td>
        <td>mysqli::get_client_info</td>
        <td>PDO::getAttribute</td>
        </tr>
    <tr><td>mysql_get_host_info</td>
        <td>mysqli::$host_info</td>
        <td>PDO::getAttribute</td>
        </tr>
    <tr><td>mysql_get_proto_info</td>
        <td>mysqli::$protocol_version</td>
        <td> </td>
        </tr>
    <tr><td>mysql_get_server_info</td>
        <td>mysqli::$server_info</td>
        <td>PDO::getAttribute</td>
        </tr>
    <tr><td>mysql_info</td>
        <td>mysqli::$info</td>
        <td> </td>
        </tr>
    <tr><td>mysql_insert_id</td>
        <td>mysqli::$insert_id</td>
        <td>PDO::lastInsertId</td>
        </tr>
    <tr><td>mysql_list_dbs</td>
        <td>Query: SHOW DATABASES</td>
        <td> </td>
        </tr>
    <tr><td>mysql_list_fields</td>
        <td>Query: SHOW COLUMNS</td>
        <td> </td>
        </tr>
    <tr><td>mysql_list_processes</td>
        <td>mysqli::$thread_id</td>
        <td> </td>
        </tr>
    <tr><td>mysql_list_tables</td>
        <td>Query: SHOW TABLES</td>
        <td> </td>
        </tr>
    <tr><td>mysql_num_fields</td>
        <td>mysqli::$field_count</td>
        <td>PDOStatement::columnCount</td>
        </tr>
    <tr><td>mysql_num_rows</td>
        <td>mysqli_stmt::$num_rows</td>
        <td>PDOStatement::rowCount</td>
        </tr>
    <tr><td>mysql_pconnect</td>
        <td>mysqli::__construct</td>
        <td>PDO::__construct</td>
        </tr>
    <tr><td>mysql_ping</td>
        <td>mysqli::ping</td>
        <td> </td>
        </tr>
    <tr><td>mysql_query</td>
        <td>mysqli::query</td>
        <td>PDO::query</td>
        </tr>
    <tr><td>mysql_real_escape_string</td>
        <td>mysqli::real_escape_string</td>
        <td>PDO::quote</td>
        </tr>
    <tr><td>mysql_result</td>
        <td>Combination</td>
        <td>PDOStatement::fetchColumn</td>
        </tr>
    <tr><td>mysql_select_db</td>
        <td>mysqli::send_query</td>
        <td>PDO::__construct</td></td>
        </tr>
    <tr><td>mysql_set_charset</td>
        <td>mysqli::character_set_name</td>
        <td>PDO::__construct</td></td>
        </tr>
    <tr><td>mysql_stat</td>
        <td>mysqli::stat</td>
        <td>PDO::getAttribute</td>
        </tr>
    <tr><td>mysql_tablename</td>
        <td>Query: SHOW TABLES</td>
        <td> </td>
        </tr>
    <tr><td>mysql_thread_id</td>
        <td>mysqli::$thread_id</td>
        <td> </td>
        </tr>
    <tr><td>mysql_unbuffered_query</td>
        <td>See Buffering Concepts</td>
        <td> </td>
        </tr>
    </table>

    <!-- container -->

    由于开发了更好的函数和代码结构,mysql_*函数被弃用(从php5.5开始)。该功能被弃用的事实意味着,在性能和安全性方面,将不再努力改进它,这意味着它不再是未来的证据。

    如果您需要更多理由:

    • mysql_*函数不支持准备好的语句。
    • mysql_*函数不支持参数绑定。
    • mysql_*函数缺乏面向对象编程的功能。
    • 名单还在继续……