为什么我不应该使用mysql

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

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

如果他们不在我的网站上工作,为什么我会得到类似的错误

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


MySQL扩展:

  • 没有积极发展
  • 自PHP 5.5(2013年6月发布)起正式弃用
  • 已从 PHP 7.0(2015年12月发布) 完全删除
  • 这意味着截至2018年12月31日,它不会存在于任何受支持的PHP版本中。 目前,它只获取安全更新。
  • 缺少OO界面
  • 不支持:
  • 非阻塞异步查询
  • 编写语句或参数化查询
  • 存储过程
  • 多重陈述
  • 交易
  • “新”密码认证方法(在MySQL 5.6中默认开启;在5.7中需要)
  • MySQL 5.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界面
  • 不支持非阻塞异步查询
  • 不支持预准备语句或参数化查询
  • 不支持存储过程
  • 不支持多个语句
  • 不支持交易
  • 不支持MySQL 5.1中的所有功能
  • 昆汀的回答引用了上述观点

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

    查看SQL扩展的比较。


    抑制弃用警告

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

    error_reporting = E_ALL ^ E_DEPRECATED
    

    请注意,这也会隐藏其他的弃用警告 ,但是,这可能是MySQL以外的其他情况。 (来自PHP手册)

    文章PDO与MySQLi:你应该使用哪一个? 由德扬Marjanovic将帮助您选择。

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


    一个简单而简短的PDO教程


    问:我脑海中的第一个问题是:什么是“PDO”?

    :“ 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 (数据源名称)和可选的usernamepassword

    在这里,我认为你除了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会执行准备的模拟。 它也可以让你手动启用这个模拟。 要使用本机服务器端准备好的语句,您应该明确地将其设置为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构建后设置属性吗?

    是的 ,我们也可以在使用setAttribute方法构造PDO后设置一些属性:

    $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_*时的一种常见做法是:

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

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

    PDO提供了一个更好的解决方案:例外。 我们用PDO做的任何事情都应该包含在try - catch块中。 通过设置错误模式属性,我们可以强制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类¶
  • 例外¶
  • 喜欢:

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

    此外,你可以处理or die()或)我们可以说像mysql_* ,但它会变得非常多。 您可以通过关闭display_errors off并只读取错误日志来隐藏生产中的危险错误消息。

    现在,在阅读了上述所有内容之后,您可能会想:当我只想简单地学习简单的SELECTINSERTUPDATEDELETE语句时,究竟是什么感觉? 别担心,我们走吧:


    选择数据

    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()来获得的,这是你的语句句柄的一个方法。 在调用fetch之前,最好的方法是告诉PDO您想如何获取数据。 在下面的部分我正在解释这一点。

    获取模式

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

    首先,我解释如何选择获取模式:

     $stmt->fetch(PDO::FETCH_ASSOC)
    

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

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

  • PDO::FETCH_ASSOC :返回按结果集中返回的列名索引的数组
  • PDO::FETCH_BOTH (默认值):返回由结果集中返回的列名和索引列号索引的数组
  • 有更多的选择! 在PDOStatement Fetch文档中阅读有关它们的全部内容。

    获取行数

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

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

    插入和更新或删除语句

    我们在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语句,可以通过仅将数据发送到服务器多次执行。

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

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

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

  • DBMS解析,编译并在语句模板上执行查询优化,并存储结果而不执行它。

  • 执行 :稍后,应用程序为参数提供(或绑定)值,并且DBMS执行语句(可能返回结果)。 应用程序可以根据需要多次执行该语句并使用不同的值。 在这个例子中,它可能为第一个参数提供'Bread',为第二个参数提供1.00
  • 您可以通过在SQL中包含占位符来使用预准备语句。 基本上有三个没有占位符的地方(不要试着用上面那个变量来做这个),一个带有未命名的占位符,另一个带有指定的占位符。

    问:那么现在,什么是指定的占位符,我如何使用它们?
    A.命名的占位符。 使用以冒号开头的描述性名称,而不是问号。 我们不关心名称所在地的价值的位置/顺序:

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

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

    你也可以使用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);
    

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

    <?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准备好的查询

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

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

    $stmt = $db->prepare("DELETE FROM table WHERE id=:id");
    $stmt->bindValue(':id', $id, PDO::PARAM_STR);
    $stmt->execute();
    $affected_rows = $stmt->rowCount();
    
  • 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 /*"));
    

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

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

    让我们一句接一句地说,并解释:

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

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

    新! - 从PHP 5.5开始,ext / mysql现已被正式弃用!

    较新的! ext / mysql已在PHP 7中删除。

  • 相反,你应该学习准备好的陈述

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

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

  • 看到红盒?

    当你去任何mysql功能手册页面,你会看到一个红色的框,说明它不应该再被使用。

  • 使用PDO或MySQLi

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

  • 链接地址: http://www.djcxy.com/p/687.html

    上一篇: Why shouldn't I use mysql

    下一篇: Encode URL in JavaScript?