问题:为什么我不应该在PHP中使用mysql_ *函数?

为什么不应该使用mysql_*函数的技术原因是什么? (例如mysql_query()mysql_connect()mysql_real_escape_string())?

为什么即使我的网站上可以使用我也应该使用其他东西?

如果它们在我的网站上不起作用,为什么会出现

之类的错误?

警告:mysql_connect():没有这样的文件或目录

标签:php,mysql,database

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

What are the technical reasons for why one shouldn't use mysql_* functions? (e.g. mysql_query(), mysql_connect() or mysql_real_escape_string())?

Why should I use something else even if they work on my site?

If they don't work on my site, why do I get errors like

Warning: mysql_connect(): No such file or directory

回答1:

MySQL扩展名:

  • 未处于积极发展状态
  • 从PHP 5.5(自2013年6月发布)开始正式已弃用 )。
  • 从PHP 7.0起,已完全删除 完全 2015年12月发布)
    • 这意味着自 2018年12月31日起,它在任何受支持的PHP版本中都不存在。如果使用的PHP版本支持该版本,则使用的版本不会解决安全问题。
  • 缺少OO接口
  • 不支持:
    • 非阻塞异步查询
    • 准备好的语句或参数化查询
    • 存储过程
    • 多条语句
    • 交易
    • "新"密码验证方法(MySQL 5.6中默认为启用; 5.7中为必需)
    • MySQL 5.1或更高版本中的任何新功能

由于不建议使用它,因此使用它会使您的代码不再受将来的考验。

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

请参见 SQL扩展的比较

A1:

The MySQL extension:

  • Is not under active development
  • Is officially deprecated as of PHP 5.5 (released June 2013).
  • Has been removed entirely as of PHP 7.0 (released December 2015)
    • This means that as of 31 Dec 2018 it does not exist in any supported version of PHP. If you are using a version of PHP which supports it, you are using a version which doesn't get security problems fixed.
  • Lacks an OO interface
  • Doesn't support:
    • Non-blocking, asynchronous queries
    • Prepared statements or parameterized queries
    • Stored procedures
    • Multiple Statements
    • Transactions
    • The "new" password authentication method (on by default in MySQL 5.6; required in 5.7)
    • Any of the new functionality in MySQL 5.1 or later

Since it is deprecated, using it makes your code less future proof.

Lack of support for prepared statements is particularly important as they provide a clearer, less error-prone method of escaping and quoting external data than manually escaping it with a separate function call.

See the comparison of SQL extensions.

回答2:

PHP提供了三种不同的API连接到MySQL。这些是 mysql (自PHP 7), mysqli 和<一个href=" http: php.net manual zh book.pdo.php" rel="noreferrer"> PDO 扩展。

mysql_*函数曾经非常流行,但是不再鼓励使用它们。文档团队正在讨论数据库安全状况,并且教育用户远离常用的ext / mysql扩展是其中一部分(请检查 php.internals:不推荐使用ext / mysql )。

后来的PHP开发人员团队已决定生成 用户通过mysql_connect()mysql_pconnect()或内置的隐式连接功能连接到MySQL时,发生E_DEPRECATED 错误ext/mysql

ext/mysql 正式自PHP 5.5起已弃用 ,并已 自PHP 7起已删除< / strong>

看到红框了吗?

在任何mysql_*函数手册页上,您会看到一个红色框,说明不再使用它。

为什么


离开ext/mysql不仅与安全性有关,而且与获得MySQL数据库的所有功能有关。

ext/mysql是为 MySQL 3.23 构建的,此后仅增加了很少的内容,同时主要保持了与该旧版本的兼容性,这使得代码难以维护。 。 ext/mysql不支持的缺少功能包括:( 来自PHP手册 )。

导致不使用mysql_*函数的原因:

  • 未处于积极发展状态
  • 从PHP 7开始删除
  • 缺少OO接口
  • 不支持非阻塞异步查询
  • 不支持预备语句或参数化查询
  • 不支持存储过程
  • 不支持多条语句
  • 不支持交易
  • 不支持MySQL 5.1中的所有功能

在Quentin的答案中引用以上

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

请参见 SQL扩展比较


禁止弃用警告

将代码转换为MySQLi / PDO时,可以通过在error_reporting中设置error_reporting来抑制E_DEPRECATED错误 php.ini 排除E_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

请注意,这还会隐藏其他弃用警告,但是,这可能是针对MySQL以外的其他内容的。 ( 来自PHP手册 )

文章 PDO与。MySQLi:您应该使用哪个? ,方法是 Dejan Marjanovic 将帮助您选择。

更好的方法是PDO,我现在正在编写一个简单的PDO教程。


一个简单而简短的PDO教程


Q。我想到的第一个问题是:什么是PDO?

A。 " PDO – PHP数据对象 –是数据库访问层,提供了访问多个数据库的统一方法。"

替代文字


连接到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密码

在这里,我认为您熟悉除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

好读连接和连接管理

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

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

稍后我将解释为什么应关闭准备仿真。要找到原因,请检查这篇文章

仅当您使用旧版本的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);

错误处理


mysql_*相比,PDO中的错误处理要容易得多。

使用mysql_*的常见做法是:

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

ORdie()不是处理错误的好方法,因为我们无法处理die中的错误。它只会突然结束脚本,然后将错误回显到您通常不希望显示给最终用户的屏幕上,并让流血的黑客发现您的架构。或者,mysql_*函数的返回值通常可以与 mysql_error()错误和错误处理

  • 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.
    }
    

    此外,您可以使用或die()进行处理,或者可以说mysql_*一样,但这确实会有所不同。您可以通过关闭display_errors并仅阅读错误日志来隐藏生产中的危险错误消息。

    现在,在阅读完以上所有内容之后,您可能会想:当我只想开始学习简单的SELECTINSERT UPDATE 还是DELETE语句?不用担心,我们开始:


    选择数据

     PDO选择图片

    所以您在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()(语句句柄的一种方法)获得的。调用提取之前,最好的方法是告诉PDO您希望如何提取数据。在下面的部分中,我将对此进行解释。

    获取模式

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

    首先,我说明如何选择提取模式:

     $stmt->fetch(PDO::FETCH_ASSOC)
    

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

    现在我进入提取模式:

    • PDO::FETCH_ASSOC:返回一个在结果集中返回的按列名索引的数组
    • PDO::FETCH_BOTH(默认值):返回一个数组,该数组由结果集中返回的列名和0索引列号索引

    还有更多选择!在 PDOStatement提取文档中了解所有这些信息。

    获取行数

    您可以使用PDOStatement并执行rowCount(),而不是使用mysql_num_rows来获取返回的行数,例如: / p>

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

    插入和更新或删除语句

    插入并更新PDO图像

    我们在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语句并返回受影响的行数。

    插入和删除将在以后介绍。

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


    准备好的声明

    。什么是预备语句,为什么需要它们?
    A。预备语句是可以执行的预编译SQL语句。通过仅将数据发送到服务器多次。

    使用准备好的语句的典型工作流程如下(引自Wikipedia的三分之三 ):

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

      INSERTINTOPRODUCT(名称,价格)值(?,?)

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

    3. 执行:稍后,应用程序提供(或绑定)参数值,然后DBMS执行该语句(可能返回结果)。应用程序可以使用不同的值多次执行该语句。在此示例中,它可能为第一个参数提供"面包",为第二个参数提供1.00

    您可以通过在SQL中包含占位符来使用准备好的语句。基本上有三种不带占位符的变量(不要在变量上面使用占位符尝试此操作),一个具有未命名的占位符,一个具有命名的占位符。

    。现在,什么是占位符,我该如何使用它们?
    A。。请在描述性名称前加上冒号,而不要使用问号。我们不在乎名称占位符中的位置/值的顺序:

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

    bindParam(参数,变量,数据类型,长度,驱动程序选项)

    您还可以使用execute数组进行绑定:

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

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

    <?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()方法。


    SELECTINSERTUPDATEDelete准备的查询

    1. 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);
      
    2. INSERT

      $stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
      $stmt->execute(array(':field1' => $field1, ':field2' => $field2));
      $affected_rows = $stmt->rowCount();
      
    3. Delete

      $stmt = $db->prepare("DELETE FROM table WHERE id=:id");
      $stmt->bindValue(':id', $id, PDO::PARAM_STR);
      $stmt->execute();
      $affected_rows = $stmt->rowCount();
      
    4. UPDATE

      $stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
      $stmt->execute(array($name, $id));
      $affected_rows = $stmt->rowCount();
      

    注意:

    但是PDO和/或MySQLi并不完全安全。检查答案 PDO准备好的语句是否足以防止SQL注入? 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 /*"));
    

    A2:

    PHP offers three different APIs to connect to MySQL. These are the mysql(removed as of PHP 7), mysqli, and PDO extensions.

    The mysql_* functions used to be very popular, but their use is not encouraged anymore. The documentation team is discussing the database security situation, and educating users to move away from the commonly used ext/mysql extension is part of this (check php.internals: deprecating ext/mysql).

    And the later PHP developer team has taken the decision to generate E_DEPRECATED errors when users connect to MySQL, whether through mysql_connect(), mysql_pconnect() or the implicit connection functionality built into ext/mysql.

    ext/mysql was officially deprecated as of PHP 5.5 and has been removed as of PHP 7.

    See the Red Box?

    When you go on any mysql_* function manual page, you see a red box, explaining it should not be used anymore.

    Why


    Moving away from ext/mysql is not only about security, but also about having access to all the features of the MySQL database.

    ext/mysql was built for MySQL 3.23 and only got very few additions since then while mostly keeping compatibility with this old version which makes the code a bit harder to maintain. Missing features that is not supported by ext/mysql include: (from PHP manual).

    Reason to not use mysql_* function:

    • Not under active development
    • Removed as of PHP 7
    • Lacks an OO interface
    • Doesn't support non-blocking, asynchronous queries
    • Doesn't support prepared statements or parameterized queries
    • Doesn't support stored procedures
    • Doesn't support multiple statements
    • Doesn't support transactions
    • Doesn't support all of the functionality in MySQL 5.1

    Above point quoted from Quentin's answer

    Lack of support for prepared statements is particularly important as they provide a clearer, less error prone method of escaping and quoting external data than manually escaping it with a separate function call.

    See the comparison of SQL extensions.


    Suppressing deprecation warnings

    While code is being converted to MySQLi/PDO, E_DEPRECATED errors can be suppressed by setting error_reporting in php.ini to exclude E_DEPRECATED:

    error_reporting = E_ALL ^ E_DEPRECATED
    

    Note that this will also hide other deprecation warnings, which, however, may be for things other than MySQL. (from PHP manual)

    The article PDO vs. MySQLi: Which Should You Use? by Dejan Marjanovic will help you to choose.

    And a better way is PDO, and I am now writing a simple PDO tutorial.


    A simple and short PDO tutorial


    Q. First question in my mind was: what is `PDO`?

    A. “PDO – PHP Data Objects – is a database access layer providing a uniform method of access to multiple databases.”


    Connecting to MySQL

    With mysql_* function or we can say it the old way (deprecated in PHP 5.5 and above)

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

    With PDO: All you need to do is create a new PDO object. The constructor accepts parameters for specifying the database source PDO's constructor mostly takes four parameters which are DSN (data source name) and optionally username, password.

    Here I think you are familiar with all except DSN; this is new in PDO. A DSN is basically a string of options that tell PDO which driver to use, and connection details. For further reference, check PDO MySQL DSN.

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

    Note: you can also use charset=UTF-8, but sometimes it causes an error, so it's better to use utf8.

    If there is any connection error, it will throw a PDOException object that can be caught to handle Exception further.

    Good read: Connections and Connection management ¶

    You can also pass in several driver options as an array to the fourth parameter. I recommend passing the parameter which puts PDO into exception mode. Because some PDO drivers don't support native prepared statements, so PDO performs emulation of the prepare. It also lets you manually enable this emulation. To use the native server-side prepared statements, you should explicitly set it false.

    The other is to turn off prepare emulation which is enabled in the MySQL driver by default, but prepare emulation should be turned off to use PDO safely.

    I will later explain why prepare emulation should be turned off. To find reason please check this post.

    It is only usable if you are using an old version of MySQL which I do not recommended.

    Below is an example of how you can do it:

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

    Can we set attributes after PDO construction?

    Yes, we can also set some attributes after PDO construction with the setAttribute method:

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

    Error Handling


    Error handling is much easier in PDO than mysql_*.

    A common practice when using mysql_* is:

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

    OR die() is not a good way to handle the error since we can not handle the thing in die. It will just end the script abruptly and then echo the error to the screen which you usually do NOT want to show to your end users, and let bloody hackers discover your schema. Alternately, the return values of mysql_* functions can often be used in conjunction with mysql_error() to handle errors.

    PDO offers a better solution: exceptions. Anything we do with PDO should be wrapped in a try-catch block. We can force PDO into one of three error modes by setting the error mode attribute. Three error handling modes are below.

    • PDO::ERRMODE_SILENT. It's just setting error codes and acts pretty much the same as mysql_* where you must check each result and then look at $db->errorInfo(); to get the error details.
    • PDO::ERRMODE_WARNING Raise E_WARNING. (Run-time warnings (non-fatal errors). Execution of the script is not halted.)
    • PDO::ERRMODE_EXCEPTION: Throw exceptions. It represents an error raised by PDO. You should not throw a PDOException from your own code. See Exceptions for more information about exceptions in PHP. It acts very much like or die(mysql_error());, when it isn't caught. But unlike or die(), the PDOException can be caught and handled gracefully if you choose to do so.

    Good read:

    Like:

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

    And you can wrap it in try-catch, like below:

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

    You do not have to handle with try-catch right now. You can catch it at any time appropriate, but I strongly recommend you to use try-catch. Also it may make more sense to catch it at outside the function that calls the PDO stuff:

    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.
    }
    

    Also, you can handle by or die() or we can say like mysql_*, but it will be really varied. You can hide the dangerous error messages in production by turning display_errors off and just reading your error log.

    Now, after reading all the things above, you are probably thinking: what the heck is that when I just want to start leaning simple SELECT, INSERT, UPDATE, or DELETE statements? Don't worry, here we go:


    Selecting Data

    So what you are doing in mysql_* is:

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

    Now in PDO, you can do this like:

    <?php
    $stmt = $db->query('SELECT * FROM table');
    
    while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        echo $row['field1'];
    }
    

    Or

    <?php
    $stmt = $db->query('SELECT * FROM table');
    $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
    //Use $results
    

    Note: If you are using the method like below (query()), this method returns a PDOStatement object. So if you want to fetch the result, use it like above.

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

    In PDO Data, it is obtained via the ->fetch(), a method of your statement handle. Before calling fetch, the best approach would be telling PDO how you’d like the data to be fetched. In the below section I am explaining this.

    Fetch Modes

    Note the use of PDO::FETCH_ASSOC in the fetch() and fetchAll() code above. This tells PDO to return the rows as an associative array with the field names as keys. There are many other fetch modes too which I will explain one by one.

    First of all, I explain how to select fetch mode:

     $stmt->fetch(PDO::FETCH_ASSOC)
    

    In the above, I have been using fetch(). You can also use:

    Now I come to fetch mode:

    • PDO::FETCH_ASSOC: returns an array indexed by column name as returned in your result set
    • PDO::FETCH_BOTH (default): returns an array indexed by both column name and 0-indexed column number as returned in your result set

    There are even more choices! Read about them all in PDOStatement Fetch documentation..

    Getting the row count:

    Instead of using mysql_num_rows to get the number of returned rows, you can get a PDOStatement and do rowCount(), like:

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

    Getting the Last Inserted ID

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

    Insert and Update or Delete statements

    What we are doing in mysql_* function is:

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

    And in pdo, this same thing can be done by:

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

    In the above query PDO::exec execute an SQL statement and returns the number of affected rows.

    Insert and delete will be covered later.

    The above method is only useful when you are not using variable in query. But when you need to use a variable in a query, do not ever ever try like the above and there for prepared statement or parameterized statement is.


    Prepared Statements

    Q. What is a prepared statement and why do I need them?
    A. A prepared statement is a pre-compiled SQL statement that can be executed multiple times by sending only the data to the server.

    The typical workflow of using a prepared statement is as follows (quoted from Wikipedia three 3 point):

    1. Prepare: The statement template is created by the application and sent to the database management system (DBMS). Certain values are left unspecified, called parameters, placeholders or bind variables (labelled ? below):

      INSERT INTO PRODUCT (name, price) VALUES (?, ?)

    2. The DBMS parses, compiles, and performs query optimization on the statement template, and stores the result without executing it.

    3. Execute: At a later time, the application supplies (or binds) values for the parameters, and the DBMS executes the statement (possibly returning a result). The application may execute the statement as many times as it wants with different values. In this example, it might supply 'Bread' for the first parameter and 1.00 for the second parameter.

    You can use a prepared statement by including placeholders in your SQL. There are basically three ones without placeholders (don't try this with variable its above one), one with unnamed placeholders, and one with named placeholders.

    Q. So now, what are named placeholders and how do I use them?
    A. Named placeholders. Use descriptive names preceded by a colon, instead of question marks. We don't care about position/order of value in name place holder:

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

    bindParam(parameter,variable,data_type,length,driver_options)

    You can also bind using an execute array as well:

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

    Another nice feature for OOP friends is that named placeholders have the ability to insert objects directly into your database, assuming the properties match the named fields. For example:

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

    Q. So now, what are unnamed placeholders and how do I use them?
    A. Let's have an example:

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

    and

    $stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
    $stmt->execute(array('john', '29 bla district'));
    

    In the above, you can see those ? instead of a name like in a name place holder. Now in the first example, we assign variables to the various placeholders ($stmt->bindValue(1, $name, PDO::PARAM_STR);). Then, we assign values to those placeholders and execute the statement. In the second example, the first array element goes to the first ? and the second to the second ?.

    NOTE: In unnamed placeholders we must take care of the proper order of the elements in the array that we are passing to the PDOStatement::execute() method.


    SELECT, INSERT, UPDATE, DELETE prepared queries

    1. 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);
      
    2. INSERT:

      $stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
      $stmt->execute(array(':field1' => $field1, ':field2' => $field2));
      $affected_rows = $stmt->rowCount();
      
    3. DELETE:

      $stmt = $db->prepare("DELETE FROM table WHERE id=:id");
      $stmt->bindValue(':id', $id, PDO::PARAM_STR);
      $stmt->execute();
      $affected_rows = $stmt->rowCount();
      
    4. UPDATE:

      $stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
      $stmt->execute(array($name, $id));
      $affected_rows = $stmt->rowCount();
      

    NOTE:

    However PDO and/or MySQLi are not completely safe. Check the answer Are PDO prepared statements sufficient to prevent SQL injection? by ircmaxell. Also, I am quoting some part from his answer:

    $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 /*"));
    

    回答3:

    首先,让我们从为大家提供的标准注释开始:

    请不要在新代码中使用mysql_*函数 。它们已不再维护并已正式弃用。看到 红色框 ?了解有关 准备好的语句 的信息,并使用 PDO MySQLi -本文将帮助您确定哪一个。如果选择PDO,这是一个很好的教程

    让我们逐句阅读并解释:

    • 它们已不再维护,并已正式弃用

      这意味着PHP社区正在逐渐放弃对这些非常老功能的支持。它们可能在将来的PHP版本中不存在!继续使用这些功能可能会在不远的将来破坏您的代码。

      新功能! -ext / mysql现在 从PHP 5.5开始正式弃用! < / strong>

      更新! ext / mysql 已在PHP 7中删除

    • 相反,您应该了解准备好的语句

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

      有关更多信息,请参见 如何防止PHP中的SQL注入?

    • 看到红色框吗?

      当您转到任何mysql功能手册页时,都会看到一个红色框,对其进行说明不应再使用。

    • 使用PDO或MySQLi

      有更好,更健壮和完善的替代方案, PDO-PHP数据库对象 ,它提供了用于数据库交互的完整OOP方法以及 MySQLi ,这是MySQL的特定改进。

    A3:

    First, let's begin with the standard comment we give everyone:

    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.

    Let's go through this, sentence by sentence, and explain:

    • They are no longer maintained, and are officially deprecated

      This means that the PHP community is gradually dropping support for these very old functions. They are likely to not exist in a future (recent) version of PHP! Continued use of these functions may break your code in the (not so) far future.

      NEW! - ext/mysql is now officially deprecated as of PHP 5.5!

      Newer! ext/mysql has been removed in PHP 7.

    • Instead, you should learn of prepared statements

      mysql_* extension does not support prepared statements, which is (among other things) a very effective countermeasure against SQL Injection. It fixed a very serious vulnerability in MySQL dependent applications which allows attackers to gain access to your script and perform any possible query on your database.

      For more information, see How can I prevent SQL injection in PHP?

    • See the Red Box?

      When you go to any mysql function manual page, you see a red box, explaining it should not be used anymore.

    • Use either PDO or MySQLi

      There are better, more robust and well-built alternatives, PDO - PHP Database Object, which offers a complete OOP approach to database interaction, and MySQLi, which is a MySQL specific improvement.

    回答4:

    易于使用

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

    当代数据库API只是更容易使用。

    主要是绑定参数可以简化代码。借助出色的教程(如上所示)可以过渡到 PDO 不太繁琐。

    一次重写一个更大的代码库需要花费时间。 Raison d'être可以替代此中间选项:

    等效的pdo_ *函数代替 mysql _ *

    使用 << strong> pdo_mysql.php > 您可以通过最小的努力从旧的mysql_函数切换。它添加了pdo_函数包装器,以替换其mysql_对应器。

    1. 只需include_once( "pdo_mysql.php" );中的每个必须与数据库进行交互的调用脚本。

    2. 删除 mysql_ 函数前缀 Everywhere ,并将其替换为 pdo_

      • mysql_ connect()变为 pdo_ connect()
      • mysql_ query()变为 pdo_ query()
      • mysql_ num_rows()变为 pdo_ num_rows()
      • mysql_ insert_id()变为 pdo_ insert_id()
      • mysql_ fetch_array()变为 pdo_ fetch_array()
      • mysql_ fetch_assoc()变为 pdo_ fetch_assoc()
      • mysql_ real_escape_string()变为 pdo_ real_escape_string()
      • 等...

    3. 您的代码将工作类似,并且仍然大致相同:

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

    Etvoilà。
    您的代码正在使用 PDO。
    现在是时候真正地利用了。

    绑定参数易于使用

    您只需要一个不太麻烦的API。

    pdo_query()为绑定参数添加了非常方便的支持。转换旧代码很简单:

    将变量移出SQL字符串。

    • 将它们作为逗号分隔的函数参数添加到pdo_query()
    • 将问号?作为变量之前的占位符。
    • 摆脱以前包含字符串值/变量的'单引号。

    代码越长,这一优势越明显。

    通常,字符串变量不仅被插入到SQL中,而且还与转义之间的转义连接在一起。

    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")
    

    使用?占位符后,您不必为此担心:

    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占位符列表。

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

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

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

    非常简单。但是,让我们回到更多的重写建议和技术原因上,了解为什么您可能想要摆脱 mysql_ 并转义。

    修复或删除所有过时的 sanitize() 函数

    在将所有 mysql_ 调用转换为带有绑定参数的pdo_query后,请删除所有冗余的pdo_real_escape_string调用。

    特别是,您应该修复过时的教程中宣传的所有sanitizecleanfilterThisclean_data函数以一种或另一种形式:

    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的历史记录。该功能已被弃用。但是,通常将其错误地描述为 security 功能失败。但是,magic_quotes是一个失败的安全功能,就像网球作为营养源失败一样。那根本不是他们的目的。

      PHP2 / FI中的原始实现通过" 引号将被自动转义,从而使将表单数据直接传递到msql查询"变得更加明确。值得注意的是,与 mSQL 一起使用是偶然安全的,因为它仅支持ASCII。
      然后PHP3 / Zend为MySQL重新引入了magic_quotes并错误地记录了它。但最初它只是一个便捷功能 ,不打算出于安全考虑。

    准备好的语句有何不同

    当您将字符串变量加扰到SQL查询中时,它不仅会使您更复杂。对于MySQL再次分离代码和数据,这也是多余的工作。

    SQL注入只是在数据渗入代码上下文中时发生。数据库服务器以后无法发现PHP最初将变量粘贴在查询子句之间的位置。

    使用绑定参数,可以在PHP代码中分隔SQL代码和SQL上下文值。但是它不会在后台再次被洗掉(除非使用PDO :: EMULATE_PREPARES)。您的数据库将接收不变的SQL命令和1:1可变值。

    尽管此答案强调您应该关注删除 mysql_ 的可读性优点。由于这种可见的和技术上的数据/代码分离,有时还具有性能优势(重复的INSERT具有不同的值)。

    请注意,对于 all SQL注入,参数绑定仍然不是一个神奇的一站式解决方案。它处理数据/值的最常见用法。但是不能将列名/表标识符列入白名单,不能帮助动态子句构造,也不能仅将简单数组值列表列入白名单。

    混合使用PDO

    这些pdo_*包装函数可创建易于编码的Stop-gap API。 (如果不是用于特有功能签名转换的话,这几乎是MYSQLI的样子)。它们还会在大多数时候公开真实的PDO。
    重写并不仅限于使用新的pdo_函数名称。您可以将每个pdo_query()逐个转换为普通的$ pdo-> prepare()-> execute()调用。

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

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

    可以用一个foreach迭代代替:

    foreach ($result as $row) {
    

    或者更好的是直接完整的数组检索:

    $result->fetchAll();
    

    在大多数情况下,您将获得比查询失败后通常提供的PDO或mysql_更有用的警告。

    其他选项

    因此,这希望可以看到一些实践原因以及丢弃 mysql_ 的一条有价值的途径。

    仅切换到并不完全剪了它。 pdo_query()也是它的前端。

    除非您还引入了参数绑定或可以使用更好的API中的其他功能,否则这是毫无意义的选择。我希望它描绘得足够简单,以免让新来者感到沮丧。 (教育通常比禁止做得更好。)

    尽管它可以满足可能最简单的工作类别的要求,但它仍然是非常试验性的代码。我只是在周末写的。但是,还有很多其他选择。只需GOOGLE PHP数据库抽象,然后浏览一下。一直存在并且将会有很多出色的库来完成这些任务。

    如果您想进一步简化数据库交互,请尝试使用巴黎/成语这样的映射器。 。就像没有人在JavaScript中不再使用平淡的DOM一样,如今您不必再忍受原始数据库接口了。

    A4:

    Ease of use

    The analytic and synthetic reasons were already mentioned. For newcomers there's a more significant incentive to stop using the dated mysql_ functions.

    Contemporary database APIs are just easier to use.

    It's mostly the bound parameters which can simplify code. And with excellent tutorials (as seen above) the transition to PDO isn't overly arduous.

    Rewriting a larger code base at once however takes time. Raison d'être for this intermediate alternative:

    Equivalent pdo_* functions in place of mysql_*

    Using <pdo_mysql.php> you can switch from the old mysql_ functions with minimal effort. It adds pdo_ function wrappers which replace their mysql_ counterparts.

    1. Simply include_once("pdo_mysql.php"); in each invocation script that has to interact with the database.

    2. Remove the mysql_ function prefix everywhere and replace it with pdo_.

      • mysql_connect() becomes pdo_connect()
      • mysql_query() becomes pdo_query()
      • mysql_num_rows() becomes pdo_num_rows()
      • mysql_insert_id() becomes pdo_insert_id()
      • mysql_fetch_array() becomes pdo_fetch_array()
      • mysql_fetch_assoc() becomes pdo_fetch_assoc()
      • mysql_real_escape_string() becomes pdo_real_escape_string()
      • and so on...

    3. Your code will work alike and still mostly look the same:

      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 voilà.
    Your code is using PDO.
    Now it's time to actually utilize it.

    Bound parameters can be easy to use

    You just need a less unwieldy API.

    pdo_query() adds very facile support for bound parameters. Converting old code is straightforward:

    Move your variables out of the SQL string.

    • Add them as comma delimited function parameters to pdo_query().
    • Place question marks ? as placeholders where the variables were before.
    • Get rid of ' single quotes that previously enclosed string values/variables.

    The advantage becomes more obvious for lengthier code.

    Often string variables aren't just interpolated into SQL, but concatenated with escaping calls in between.

    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")
    

    With ? placeholders applied you don't have to bother with that:

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

    Remember that pdo_* still allows either or.
    Just don't escape a variable and bind it in the same query.

    • The placeholder feature is provided by the real PDO behind it.
    • Thus also allowed :named placeholder lists later.

    More importantly you can pass $_REQUEST[] variables safely behind any query. When submitted <form> fields match the database structure exactly it's even shorter:

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

    So much simplicity. But let's get back to some more rewriting advises and technical reasons on why you may want to get rid of mysql_ and escaping.

    Fix or remove any oldschool sanitize() function

    Once you have converted all mysql_ calls to pdo_query with bound params, remove all redundant pdo_real_escape_string calls.

    In particular you should fix any sanitize or clean or filterThis or clean_data functions as advertised by dated tutorials in one form or the other:

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

    Most glaring bug here is the lack of documentation. More significantly the order of filtering was in exactly the wrong order.

    • Correct order would have been: deprecatedly stripslashes as the innermost call, then trim, afterwards strip_tags, htmlentities for output context, and only lastly the _escape_string as its application should directly preceed the SQL intersparsing.

    • But as first step just get rid of the _real_escape_string call.

    • You may have to keep the rest of your sanitize() function for now if your database and application flow expect HTML-context-safe strings. Add a comment that it applies only HTML escaping henceforth.

    • String/value handling is delegated to PDO and its parameterized statements.

    • If there was any mention of stripslashes() in your sanitize function, it may indicate a higher level oversight.

      • That was commonly there to undo damage (double escaping) from the deprecated magic_quotes. Which however is best fixed centrally, not string by string.

      • Use one of the userland reversal approaches. Then remove the stripslashes() in the sanitize function.

      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.

      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.

    How prepared statements differ

    When you scramble string variables into the SQL queries, it doesn't just get more intricate for you to follow. It's also extraneous effort for MySQL to segregate code and data again.

    SQL injections simply are when data bleeds into code context. A database server can't later spot where PHP originally glued variables inbetween query clauses.

    With bound parameters you separate SQL code and SQL-context values in your PHP code. But it doesn't get shuffled up again behind the scenes (except with PDO::EMULATE_PREPARES). Your database receives the unvaried SQL commands and 1:1 variable values.

    While this answer stresses that you should care about the readability advantages of dropping mysql_. There's occasionally also a performance advantage (repeated INSERTs with just differing values) due to this visible and technical data/code separation.

    Beware that parameter binding still isn't a magic one-stop solution against all SQL injections. It handles the most common use for data/values. But can't whitelist column name / table identifiers, help with dynamic clause construction, or just plain array value lists.

    Hybrid PDO use

    These pdo_* wrapper functions make a coding-friendly stop-gap API. (It's pretty much what MYSQLI could have been if it wasn't for the idiosyncratic function signature shift). They also expose the real PDO at most times.
    Rewriting doesn't have to stop at using the new pdo_ function names. You could one by one transition each pdo_query() into a plain $pdo->prepare()->execute() call.

    It's best to start at simplifying again however. For example the common result fetching:

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

    Can be replaced with just an foreach iteration:

    foreach ($result as $row) {
    

    Or better yet a direct and complete array retrieval:

    $result->fetchAll();
    

    You'll get more helpful warnings in most cases than PDO or mysql_ usually provide after failed queries.

    Other options

    So this hopefully visualized some practical reasons and a worthwile pathway to drop mysql_.

    Just switching to doesn't quite cut it. pdo_query() is also just a frontend onto it.

    Unless you also introduce parameter binding or can utilize something else from the nicer API, it's a pointless switch. I hope it's portrayed simple enough to not further the discouragement to newcomers. (Education usually works better than prohibition.)

    While it qualifies for the simplest-thing-that-could-possibly-work category, it's also still very experimental code. I just wrote it over the weekend. There's a plethora of alternatives however. Just google for PHP database abstraction and browse a little. There always have been and will be lots of excellent libraries for such tasks.

    If you want to simplify your database interaction further, mappers like Paris/Idiorm are worth a try. Just like nobody uses the bland DOM in JavaScript anymore, you don't have to babysit a raw database interface nowadays.

    回答5:

    mysql_函数:

    1. 已过时-已不再维护
    2. 不允许您轻松移动到另一个数据库后端
    3. 不支持预备语句,因此
    4. 鼓励程序员使用串联来构建查询,从而导致SQL注入漏洞

    A5:

    The mysql_ functions:

    1. are out of date - they're not maintained any more
    2. don't allow you to move easily to another database backend
    3. don't support prepared statements, hence
    4. encourage programmers to use concatenation to build queries, leading to SQL injection vulnerabilities

    回答6:

    谈到技术的原因,只有少数几个非常具体且很少使用。
    也许我太无知了,但是我从来没有机会使用

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

    如果您需要它们-毫无疑问,这是从mysql扩展转向更时尚和现代外观的技术原因。

    尽管如此,还有一些非技术性的问题,可能会使您的体验更困难

    • 在现代PHP版本中进一步使用这些功能将引起不推荐使用的通知。只需将其关闭即可。
    • 在不久的将来,可以将它们从默认的PHP版本中删除。没什么大不了的,因为mydsql ext将被转移到PECL中,并且每个托管者都乐于用它编译PHP,因为他们不想失去站点已经使用了数十年的客户端。
    • 来自Stackoverflow社区的强烈反对。每次您提到这些诚实的功能时,都会被告知这些功能是严格的禁忌。
    • 作为一名普通的PHP用户,您使用这些功能的想法很容易出错和出错。仅仅因为所有这些无数的教程和手册都教给您错误的方法。不是功能本身-我必须强调它-而是它们的使用方式。

    后一个问题是一个问题。
    但是,在我看来,所提出的解决方案也不是更好。
    在我看来,过于理想化是所有这些PHP用户的梦想。将学习如何立即正确处理SQL查询。他们很可能会机械地将mysql_ *更改为mysqli _ *,方法不变。尤其是因为mysqli使使用预备语句难以置信的痛苦和麻烦。
    更不用说 native 预备语句不足以保护免受SQL注入,而且mysqli和PDO提供了一个解决方案。

    因此,我宁愿与错误的做法作斗争并以正确的方式教育人们,而不是与诚实的扩展作斗争。

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

    • 不支持存储过程(我们使用mysql_query("CALLmy_proc");很久了)
    • 不支持交易(同上)
    • 不支持多条语句(谁需要它们?)
    • 不在积极发展中(那又是什么呢?它会以任何实际的方式影响吗?)
    • 缺少一个OO接口(创建一个接口大约需要几个小时)
    • 不支持预备语句或参数化查询

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

    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 中为所有常见的陷阱和复杂的案例编写了解决方案。 >

    尽管如此,每个谈论扩展的人都始终缺少关于Mysqli和PDO的 2个重要事实

    1. 准备好的声明不是灵丹妙药。有些动态标识符无法使用准备好的语句进行绑定。有一些动态查询带有未知数量的参数,这使查询的构建变得困难。

    2. 应用程序代码中不应出现mysqli_ *或PDO函数。
      在它们与应用程序代码之间应该有一个抽象层,这将完成内部的绑定,循环,错误处理等所有肮脏的工作,从而使应用程序代码变得干燥而干净。特别是对于诸如动态查询构建之类的复杂情况。

    因此,仅切换到PDO或mysqli是不够的。必须使用ORM,查询生成器或任何数据库抽象类,而不是在其代码中调用原始API函数。
    相反-如果您的应用程序代码和mysql API之间有抽象层- 您可以使用mysql ext直到弃用它,然后轻松地将抽象类重写到另一个引擎,使所有应用程序代码保持完整。 < / p>

    以下是一些基于我的 safemysql类的示例展示这种抽象类应该如何:

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

    将此一行与将PDO需要的代码量进行比较
    然后与 大量的代码。请注意,错误处理,性能分析,查询日志记录已内置并正在运行。

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

    将每个字段名重复六到十次-与所有众多命名的占位符,绑定和查询定义相比,将其与常规PDO插入进行比较。

    另一个例子:

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

    您几乎找不到PDO处理这种实际情况的例子。
    这太罗y了,很可能不安全。

    因此,再一次-它不仅应该是原始驱动程序,而且还应该是抽象类,它不仅对初学者手册中的愚蠢示例有用,而且可以解决任何现实问题。

    A6:

    Speaking of technical reasons, there are only a few, extremely specific and rarely used. Most likely you will never ever use them in your life.
    Maybe I am too ignorant, but I never had an opportunity to use them things like

    • non-blocking, asynchronous queries
    • stored procedures returning multiple resultsets
    • Encryption (SSL)
    • Compression

    If you need them - these are no doubt technical reasons to move away from mysql extension toward something more stylish and modern-looking.

    Nevertheless, there are also some non-technical issues, which can make your experience a bit harder

    • further use of these functions with modern PHP versions will raise deprecated-level notices. They simply can be turned off.
    • in a distant future, they can be possibly removed from the default PHP build. Not a big deal too, as mydsql ext will be moved into PECL and every hoster will be happy to compile PHP with it, as they don't want to lose clients whose sites were working for decades.
    • strong resistance from Stackoverflow community. Еverytime you mention these honest functions, you being told that they are under strict taboo.
    • being an average PHP user, most likely your idea of using these functions is error-prone and wrong. Just because of all these numerous tutorials and manuals which teach you the wrong way. Not the functions themselves - I have to emphasize it - but the way they are used.

    This latter issue is a problem.
    But, in my opinion, the proposed solution is no better either.
    It seems to me too idealistic a dream that all those PHP users will learn how to handle SQL queries properly at once. Most likely they would just change mysql_* to mysqli_* mechanically, leaving the approach the same. Especially because mysqli makes prepared statements usage incredible painful and troublesome.
    Not to mention that native prepared statements aren't enough to protect from SQL injections, and neither mysqli nor PDO offers a solution.

    So, instead of fighting this honest extension, I'd prefer to fight wrong practices and educate people in the right ways.

    Also, there are some false or non-significant reasons, like

    • Doesn't support Stored Procedures (we were using mysql_query("CALL my_proc"); for ages)
    • Doesn't support Transactions (same as above)
    • Doesn't support Multiple Statements (who need them?)
    • Not under active development (so what? does it affect you in any practical way?)
    • Lacks an OO interface (to create one is a matter of several hours)
    • Doesn't support Prepared Statements or Parametrized Queries

    The last one is an interesting point. Although mysql ext do not support native prepared statements, they aren't required for the safety. We can easily fake prepared statements using manually handled placeholders (just like PDO does):

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

    voila, everything is parameterized and safe.

    But okay, if you don't like the red box in the manual, a problem of choice arises: mysqli or PDO?

    Well, the answer would be as follows:

    • If you understand the necessity of using a database abstraction layer and looking for an API to create one, mysqli is a very good choice, as it indeed supports many mysql-specific features.
    • If, like vast majority of PHP folks, you are using raw API calls right in the application code (which is essentially wrong practice) - PDO is the only choice, as this extension pretends to be not just API but rather a semi-DAL, still incomplete but offers many important features, with two of them makes PDO critically distinguished from mysqli:

      • unlike mysqli, PDO can bind placeholders by value, which makes dynamically built queries feasible without several screens of quite messy code.
      • unlike mysqli, PDO can always return query result in a simple usual array, while mysqli can do it only on mysqlnd installations.

    So, if you are an average PHP user and want to save yourself a ton of headaches when using native prepared statements, PDO - again - is the only choice.
    However, PDO is not a silver bullet too and has its hardships.
    So, I wrote solutions for all the common pitfalls and complex cases in the PDO tag wiki

    Nevertheless, everyone talking about extensions always missing the 2 important facts about Mysqli and PDO:

    1. Prepared statement isn't a silver bullet. There are dynamical identifiers which cannot be bound using prepared statements. There are dynamical queries with an unknown number of parameters which makes query building a difficult task.

    2. Neither mysqli_* nor PDO functions should have appeared in the application code.
      There ought to be an abstraction layer between them and application code, which will do all the dirty job of binding, looping, error handling, etc. inside, making application code DRY and clean. Especially for the complex cases like dynamical query building.

    So, just switching to PDO or mysqli is not enough. One has to use an ORM, or a query builder, or whatever database abstraction class instead of calling raw API functions in their code.
    And contrary - if you have an abstraction layer between your application code and mysql API - it doesn't actually matter which engine is used. You can use mysql ext until it goes deprecated and then easily rewrite your abstraction class to another engine, having all the application code intact.

    Here are some examples based on my safemysql class to show how such an abstraction class ought to be:

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

    Compare this one single line with amount of code you will need with PDO.
    Then compare with crazy amount of code you will need with raw Mysqli prepared statements. Note that error handling, profiling, query logging already built in and running.

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

    Compare it with usual PDO inserts, when every single field name being repeated six to ten times - in all these numerous named placeholders, bindings, and query definitions.

    Another example:

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

    You can hardly find an example for PDO to handle such practical case.
    And it will be too wordy and most likely unsafe.

    So, once more - it is not just raw driver should be your concern but abstraction class, useful not only for silly examples from beginner's manual but to solve whatever real-life problems.

    回答7:

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

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

    PDOmysqli中使用准备好的语句将使它变得更加困难,从而使这类编程错误更容易产生。

    A7:

    There are many reasons, but perhaps the most important one is that those functions encourage insecure programming practices because they do not support prepared statements. Prepared statements help prevent SQL injection attacks.

    When using mysql_* functions, you have to remember to run user-supplied parameters through mysql_real_escape_string(). If you forget in just one place or if you happen to escape only part of the input, your database may be subject to attack.

    Using prepared statements in PDO or mysqli will make it so that these sorts of programming errors are more difficult to make.

    回答8:

    由于其他原因,要确保对输入数据进行清理要困难得多。如果您使用参数化查询,就像使用PDO或mysqli一样,则可以完全避免这种风险。

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

    如果要使用mysqli的PDO,则用户名最终将是"enhzflep);删除表用户"

    请参见 bobby-tables.com

    A8:

    Because (amongst other reasons) it's much harder to ensure the input data is sanitized. If you use parametrized queries, as one does with PDO or mysqli you can entirely avoid the risk.

    As an example, someone could use "enhzflep); drop table users" as a username. The old functions will allow executing multiple statements per query, so something like that nasty bugger can delete a whole table.

    If one were to use PDO of mysqli, the user-name would end-up being "enhzflep); drop table users".

    See bobby-tables.com.

    回答9:

    此答案旨在说明绕过编写不佳的PHP用户验证代码有多么微不足道,这些攻击如何(以及使用什么)以及如何用安全的准备好的语句替换旧的MySQL函数-基本上,为什么StackOverflow用户(可能有很多销售代表)对新用户new之以鼻,要求他们改善代码。

    首先,请随时创建此测试mysql数据库(我称其为prep):

    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代码。

    让我们假设以下脚本是网站上管理员的验证过程(已简化,但如果您将其复制并用于测试可使用):

    <?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."<br><br>";
        $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']."<br>";
            $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!<br>";
        }
        else
        {
            echo "You could not be verified. Please try again...<br>";
        }
        mysql_close($link);
    
    ?>
    
    <form name="exploited" method='post'>
        User: <input type='text' name='user'><br>
        Pass: <input type='text' name='pass'><br>
        <input type='submit'>
    </form>
    

    乍一看似乎足够合法。

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

    出色,请不要输入以下内容:

    user: bob
    pass: somePass
    

    并提交。

    输出如下:

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

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

    user: Fluffeh
    pass: mypass
    

    很棒!大家好,我的代码正确地验证了管理员。太完美了!

    嗯,不是真的。可以说用户是一个聪明的小人物。可以说这个人是我。

    输入以下内容:

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

    输出为:

    The check passed. We have a verified admin!
    

    恭喜,您只允许我输入一个错误的用户名和一个错误的密码来输入仅受超级保护的管理员部分。严重的是,如果您不相信我,请使用我提供的代码创建数据库,然后运行此PHP代码-乍一看,它似乎确实可以很好地验证用户名和密码。

    因此,在回答中,您为什么要大喊大叫。

    所以,让我们看一下出了什么问题,以及为什么我才进入您的super-admin-only-bat-cave。我猜了一下,并假设您对输入不小心,只是将它们直接传递给数据库。我以一种可以更改您实际运行的查询的方式构造输入。那么,它应该是什么,最终变成什么?

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

    这是查询,但是当我们将变量替换为使用的实际输入时,将得到以下信息:

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

    看看我是如何构造我的"密码"的,以便它首先关闭密码周围的单引号,然后引入一个全新的比较?然后为了安全起见,我添加了另一个"字符串",以使单引号可以按我们原来的代码中的预期关闭。

    但是,这不是关于人们现在大吼大叫,而是关于向您展示如何使代码更安全。

    好的,那出了什么问题,我们该如何解决?

    这是经典的SQL注入攻击。最简单的事情之一。从攻击向量的角度来看,这是一个蹒跚学步的孩子,正在攻击坦克并赢得胜利。

    那么,我们如何保护您神圣的admin部分并使其安全好用?要做的第一件事是停止使用那些真正过时且已过时的mysql_*函数。我知道,您遵循的是在网上找到的教程,并且可以使用,但是它很旧,已经过时了,在几分钟的时间里,我刚刚摆脱了它,却丝毫不费吹灰之力。

    现在,您可以使用 mysqli_ PDO 。我个人是PDO的忠实拥护者,因此在本答案的其余部分中,我将使用PDO。有优点和缺点,但我个人发现,优点远远超过缺点。它可以跨多个数据库引擎移植-无论您使用的是MySQL还是Oracle,或者几乎是血腥的任何事物-只需更改连接字符串,它就具有我们要使用的所有精美功能,而且非常干净。我喜欢干净。

    现在,让我们再次看一下该代码,这次是使用PDO对象编写的:

    <?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']."<br>";
                $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!<br>";
        }
        else
        {
            echo "You could not be verified. Please try again...<br>";
        }
    
    ?>
    
    <form name="exploited" method='post'>
        User: <input type='text' name='user'><br>
        Pass: <input type='text' name='pass'><br>
        <input type='submit'>
    </form>
    

    主要区别在于不再有mysql_*个函数。所有这些都是通过PDO对象完成的,其次,它使用的是准备好的语句。现在,您要问什么预先准备好的陈述?这是在运行查询之前告诉数据库我们将要运行的查询的一种方式。在这种情况下,我们告诉数据库:"嗨,我要运行一个选择ID,用户ID并从表用户传递的选择语句,其中用户ID是变量,传递也是变量。"

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

    结果太棒了。让我们再次尝试使用这些用户名和密码组合:

    user: bob
    pass: somePass
    

    未验证用户。很棒。

    怎么样:

    user: Fluffeh
    pass: mypass
    

    哦,我有点兴奋,它奏效了:支票通过了。我们的管理员已通过验证!

    现在,让我们尝试一个聪明的小伙子会输入的数据,以通过我们的小型验证系统:

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

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

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

    这就是为什么在发布问题时大喊大叫的原因-因为人们可以看到即使尝试也可以绕过您的代码。请使用此问题和答案来改善您的代码,使其更安全并使用最新的功能。

    最后,这并不是说这是完美的代码。您还可以做很多事情来改进它,例如使用哈希密码,确保当您在数据库中存储感性信息时,不要以纯文本形式存储它,而是要进行多级验证-但实际上,如果您只要将旧的易于注入的代码更改为此,就可以很好地编写良好的代码-而且您已经走了很长一段距离并且仍在阅读中,这一事实让我感到,希望您不仅会实现这种类型编写网站和应用程序时的代码,但您可能会出去研究我刚才提到的其他内容-等等。编写可能的最好的代码,而不是勉强起作用的最基本的代码。

    A9:

    This answer is written to show just how trivial it is to bypass poorly written PHP user-validation code, how (and using what) these attacks work and how to replace the old MySQL functions with a secure prepared statement - and basically, why StackOverflow users (probably with a lot of rep) are barking at new users asking questions to improve their code.

    First off, please feel free to create this test mysql database (I have called mine prep):

    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)
    

    With that done, we can move to our PHP code.

    Lets assume the following script is the verification process for an admin on a website (simplified but working if you copy and use it for testing):

    <?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."<br><br>";
        $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']."<br>";
            $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!<br>";
        }
        else
        {
            echo "You could not be verified. Please try again...<br>";
        }
        mysql_close($link);
    
    ?>
    
    <form name="exploited" method='post'>
        User: <input type='text' name='user'><br>
        Pass: <input type='text' name='pass'><br>
        <input type='submit'>
    </form>
    

    Seems legit enough at first glance.

    The user has to enter a login and password, right?

    Brilliant, not enter in the following:

    user: bob
    pass: somePass
    

    and submit it.

    The output is as follows:

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

    Super! Working as expected, now lets try the actual username and password:

    user: Fluffeh
    pass: mypass
    

    Amazing! Hi-fives all round, the code correctly verified an admin. It's perfect!

    Well, not really. Lets say the user is a clever little person. Lets say the person is me.

    Enter in the following:

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

    And the output is:

    The check passed. We have a verified admin!
    

    Congrats, you just allowed me to enter your super-protected admins only section with me entering a false username and a false password. Seriously, if you don't believe me, create the database with the code I provided, and run this PHP code - which at glance REALLY does seem to verify the username and password rather nicely.

    So, in answer, THAT IS WHY YOU ARE BEING YELLED AT.

    So, lets have a look at what went wrong, and why I just got into your super-admin-only-bat-cave. I took a guess and assumed that you weren't being careful with your inputs and simply passed them to the database directly. I constructed the input in a way tht would CHANGE the query that you were actually running. So, what was it supposed to be, and what did it end up being?

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

    That's the query, but when we replace the variables with the actual inputs that we used, we get the following:

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

    See how I constructed my "password" so that it would first close the single quote around the password, then introduce a completely new comparison? Then just for safety, I added another "string" so that the single quote would get closed as expected in the code we originally had.

    However, this isn't about folks yelling at you now, this is about showing you how to make your code more secure.

    Okay, so what went wrong, and how can we fix it?

    This is a classic SQL injection attack. One of the simplest for that matter. On the scale of attack vectors, this is a toddler attacking a tank - and winning.

    So, how do we protect your sacred admin section and make it nice and secure? The first thing to do will be to stop using those really old and deprecated mysql_* functions. I know, you followed a tutorial you found online and it works, but it's old, it's outdated and in the space of a few minutes, I have just broken past it without so much as breaking a sweat.

    Now, you have the better options of using mysqli_ or PDO. I am personally a big fan of PDO, so I will be using PDO in the rest of this answer. There are pro's and con's, but personally I find that the pro's far outweigh the con's. It's portable across multiple database engines - whether you are using MySQL or Oracle or just about bloody anything - just by changing the connection string, it has all the fancy features we want to use and it is nice and clean. I like clean.

    Now, lets have a look at that code again, this time written using a PDO object:

    <?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']."<br>";
                $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!<br>";
        }
        else
        {
            echo "You could not be verified. Please try again...<br>";
        }
    
    ?>
    
    <form name="exploited" method='post'>
        User: <input type='text' name='user'><br>
        Pass: <input type='text' name='pass'><br>
        <input type='submit'>
    </form>
    

    The major differences are that there are no more mysql_* functions. It's all done via a PDO object, secondly, it is using a prepared statement. Now, what's a prepred statement you ask? It's a way to tell the database ahead of running a query, what the query is that we are going to run. In this case, we tell the database: "Hi, I am going to run a select statement wanting id, userid and pass from the table users where the userid is a variable and the pass is also a variable.".

    Then, in the execute statement, we pass the database an array with all the variables that it now expects.

    The results are fantastic. Lets try those username and password combinations from before again:

    user: bob
    pass: somePass
    

    User wasn't verified. Awesome.

    How about:

    user: Fluffeh
    pass: mypass
    

    Oh, I just got a little excited, it worked: The check passed. We have a verified admin!

    Now, lets try the data that a clever chap would enter to try to get past our little verification system:

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

    This time, we get the following:

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

    This is why you are being yelled at when posting questions - it's because people can see that your code can be bypassed wihout even trying. Please, do use this question and answer to improve your code, to make it more secure and to use functions that are current.

    Lastly, this isn't to say that this is PERFECT code. There are many more things that you could do to improve it, use hashed passwords for example, ensure that when you store sensetive information in the database, you don't store it in plain text, have multiple levels of verification - but really, if you just change your old injection prone code to this, you will be WELL along the way to writing good code - and the fact that you have gotten this far and are still reading gives me a sense of hope that you will not only implement this type of code when writing your websites and applications, but that you might go out and research those other things I just mentioned - and more. Write the best code you can, not the most basic code that barely functions.

    回答10:

    MySQL扩展是这三个中最古老的扩展,它是开发人员用来与MySQL通信的原始方式。现在已已弃用,以支持其他两个 替代方案,因为有所改进在PHP和MySQL的较新版本中完成。

    • MySQLi 是使用MySQL数据库的"改进"扩展。它利用了更新版本的MySQL服务器中提供的功能,向开发人员公开了面向函数和面向对象的界面,并且还做了其他一些精美的事情。

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

    A10:

    The MySQL extension is the oldest of the three and was the original way that developers used to communicate with MySQL. This extension is now being deprecated in favor of the other two alternatives because of improvements made in newer releases of both PHP and MySQL.

    • MySQLi is the 'improved' extension for working with MySQL databases. It takes advantage of features that are available in newer versions of the MySQL server, exposes both a function-oriented and an object-oriented interface to the developer and a does few other nifty things.

    • PDO offers an API that consolidates most of the functionality that was previously spread across the major database access extensions, i.e. MySQL, PostgreSQL, SQLite, MSSQL, etc. The interface exposes high-level objects for the programmer to work with database connections, queries and result sets, and low-level drivers perform communication and resource handling with the database server. A lot of discussion and work is going into PDO and it’s considered the appropriate method of working with databases in modern, professional code.

    回答11:

    我发现以上答案确实很冗长,所以总结一下:

    mysqli扩展具有许多优点,相对于mysql扩展,主要的增强之处在于:

    • 面向对象的界面
    • 对准备好的语句的支持
    • 支持多条语句
    • 交易支持
    • 增强的调试功能
    • 嵌入式服务器支持

    来源: MySQLi概述


    如以上答案所述,mysql的替代品是mysqli和PDO(PHP数据对象)。

    • API支持服务器端预处理语句:由MYSQLi和PDO支持
    • API支持客户端的预处理语句:仅由PDO支持
    • API支持存储过程:MySQLi和PDO都
    • API支持多条语句和所有MySQL 4.1+功能-MySQLi以及大多数PDO都支持

    MySQLi和PDO都是在PHP 5.0中引入的,而MySQL是在PHP 3.0之前引入的。需要注意的一点是,PHP5.x中包含MySQL,尽管在更高版本中已弃用。

    A11:

    I find the above answers really lengthy, so to summarize:

    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

    Source: MySQLi overview


    As explained in the above answers, the alternatives to mysql are mysqli and PDO (PHP Data Objects).

    • API supports server-side Prepared Statements: Supported by MYSQLi and PDO
    • API supports client-side Prepared Statements: Supported only by PDO
    • API supports Stored Procedures: Both MySQLi and PDO
    • API supports Multiple Statements and all MySQL 4.1+ functionality - Supported by MySQLi and mostly also by PDO

    Both MySQLi and PDO were introduced in PHP 5.0, whereas MySQL was introduced prior to PHP 3.0. A point to note is that MySQL is included in PHP5.x though deprecated in later versions.

    回答12:

    几乎可以使用mysqli或PDO定义所有mysql_*函数。只要将它们包括在您的旧PHP应用程序之上,它将在PHP7上运行。我的解决方案此处

    <?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;
    }
    

    A12:

    It's possible to define almost all mysql_* functions using mysqli or PDO. Just include them on top of your old PHP application, and it will work on PHP7. My solution here.

    <?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;
    }
    

    回答13:

    类似于此mysql_connect()mysql_query()类型的函数是以前版本的PHP即(PHP 4)函数,现在不再使用。

    在最新的PHP5中,它们被mysqli_connect()mysqli_query()取代。

    这是错误背后的原因。

    A13:

    The functions which are as similar to this mysql_connect(), mysql_query() type are the previous version PHP i.e(PHP 4) functions and now not in use .

    These are replaced by mysqli_connect(), mysqli_query() similarly in the latest PHP5.

    This is the reason behind the error.

    回答14:

    MySQL在PHP 5.5.0中已弃用,在PHP 7.0.0中已删除。对于大型的旧应用程序,很难搜索和替换每个功能。

    我们可以通过为下面每个正在运行的代码创建一个包装函数来使用MySQL函数。 单击此处

    A14:

    MySQL deprecated in PHP 5.5.0, and removed in PHP 7.0.0. For a large and old application, this is difficult to search and replace each function.

    We can use MySQL functions by creating a wrapper function for each below is running code. Click here

    回答15:

    鉴于已开发出更好的功能和代码结构,因此不推荐使用

    mysql _ * 函数(自 PHP 5.5 起)。该功能已被弃用,这意味着在性能和安全性方面将不再付出更多精力来改进它,这意味着它的未来证明会更少

    如果您需要更多原因:

    • mysql _ * 函数不支持预备语句。
    • mysql _ * 函数不支持参数绑定。
    • mysql _ * 函数缺少面向对象编程的功能。
    • 列表继续...

    A15:

    mysql_* functions were deprecated (as of PHP 5.5) given the fact that better functions and code structures were developed. The fact that the function was deprecated means that no more effort will be placed into improving it in terms of performance and security, which means it is less future proof.

    If you need more reasons:

    • mysql_* functions do not support prepared statements.
    • mysql_* functions do not support the binding of parameters.
    • mysql_* functions lack functionality for Object Oriented Programming.
    • the list goes on ...
    回到顶部