【问题标题】:Mysqli Prepared statement usage with AJAX POST to PHP fileMysqli Prepared 语句使用 AJAX POST 到 PHP 文件
【发布时间】:2021-02-11 13:35:50
【问题描述】:

我的问题是,PHP Mysqli 准备语句的效率如何?根据我从基本阅读中了解到的情况,准备好的语句 1)使用绑定输入有助于提高安全性 2)通过某种程度的“预打包”或“准备”sql查询来加速和“减少”发送到服务器的数据,一旦数据可用,它只是将数据附加到准备好的语句并执行它。这也有助于在重复插入相同数据(不同值)时“重复”使用同一语句,因为该语句只准备一次。

现在,我正在构建一个具有多种功能的网站,所有(或大多数)网站都使用 JQuery 和 AJAX 获取用户输入,进行一些检查(在 JS/JQ 或 PHP 中),发送数据到 AJAX URL 中指定的 PHP 文件 PHP_AJAX_Handler.php。 PHP 文件准备 SQL statemtns 以将数据插入数据库,然后返回 JSON 成功/失败消息。例如,我的大部分特性/功能编程如下;下面是一个文件,我用它来 1) 检查现有的大陆-国家对,以及 2) 插入新的大陆-国家对。

HTML:

<input type='text' id='continent'>
<input type='text' id='country'>
<button id='btn1'></button>
<p id='p1'></p>
<p id='p2'></p>
<p id='p3'></p>

JQuery:

$("#btn1")click(function(){
    var Cntt = $("#continent").val();
    var Ctry = $("#country").val();
    $.post("PHP_AJAX_Handler.php",{CN:cntt,CT:ctry},function(DAT)
        { var RET_j = json.PARSE(dat);
                  if(RET_j.PASS=='FAIL')
            { $('#p1').html(RET_j.PASS);
              $('#p2').html(RET_j.MSG1);
            }
                  if(RET_j.PASS=='OKAY')
            { $('#p1').html(RET_j.PASS);
              $('#p3').html(RET_j.MSG2);
        }   }
    );
});

PHP_AJAX_Handler.php

<?PHP       
session_start();
if( (isset($_POST['CT'])) && (isset($_POST['CN'])))
{ require_once ("golin_2.php");             
  $CN = $_POST['CN'];
  $CT = $_POST['CT'];
  $ER = "";
  $CONN = mysqli_connect($SERVER, $USER, $PASS, $DBNAME);
  If($CONN == FALSE) 
    { $ER = $ER . "Err: Conn Could not connect to Databse ".mysqli_connect_errno().' '.mysqli_connect_error();
    }
    else
    {   $SQL_1 = "SELECT * FROM sailors.continental_regions WHERE CONTINENT = ? AND COUNTRY = ?";   
    if(!($STMT_1 = mysqli_stmt_init($CONN)))
    {   $ER = $ER . "Err: Stmt Prepare Failed";
    }
    else
    {  if(!mysqli_stmt_prepare($STMT_1, $SQL_1))    ///FIRST SET of prepared statement lines
       {   $ER = $ER . "Err: Stmt Prepare Failed";
       }
       else
       {   if(!mysqli_stmt_bind_param($STMT_1,"ss",$CN, $CT))
           {   $ER = $ER . "Err: Stmt Prepare Failed";
        }
        else
        {   if(!(mysqli_stmt_execute($STMT_1)))
            {   $ER = $ER . "Err: Stmt_1 Execute Failed";
            }
            else
            {   $RES_1 = mysqli_stmt_get_result($STMT_1);
            $NUMROWS_1 = mysqli_num_rows($RES_1);
            if($NUMROWS_1>0)
            {  $ER = $ER . "Err: duplicate '$CN' '$CT' pair";
            }
            if($NUMROWS_1==0)
            {  $SQL_2 = "INSERT INTO DB.continental_regions (CONTINENT,COUNTRY) values (?, ?)";                     
               if(!($STMT_2=(mysqli_stmt_init($CONN))))
               {    $ER = $ER . "Err: Init2 failed";    
               }
               else
                   {    if(!mysqli_stmt_prepare($STMT_2, $SQL_2))     ///SECOND SET of prepared statement lines
                {   $ER = $ER . "Err: Prep2 failed".mysqli_error($CONN);    
                }
                else
                {   if(!mysqli_stmt_bind_param($STMT_2,"ss",$CN, $CT))
                    {   $ER = $ER . "Err: Bind2 failed";    
                    }
                    else
                    {   
                    if(!(mysqli_stmt_execute($STMT_2)))
                    {   $ER = $ER . "Err: Exec failed"; 
                    }
                    else
                    {   $arr['PASS'] = 'OK';    
                    }
                   }
                }
                }                                                   
            }
            }
        }
       }
    }
    mysqli_free_result($RES_1);
    mysqli_stmt_close($STMT_1);
    mysqli_stmt_close($STMT_2);
    mysqli_close($CONN);                            
   }        
   if($ER!=="")
   {   $arr['MSG'] = $ER;
       $arr['PASS'] = 'FAIL';
   }
   if($arr['PASS']=="OK")       
   {    $arr['MSG2'] = "Insert Success";            
   }
   echo json_encode($arr);  
}   
else
{  header("location: ../Error_Fail.php");       
}
?>

如您所见,PHP 文件变得相当长。有一组准备语句来检查 CC 对是否已经存在于表中,然后另一组用于插入 CC 对。 据我所见,对于每个添加一对新值的 AJAX 请求,mysqli 语句都会重新准备。然后再次进行下一个请求,依此类推。我想这会为服务器带来大量开销和数据,只是为了实现安全性。对于其他使用 AJAX-POST-PHP 开发 Web 应用程序的人来说,这是真的吗?对我来说,对于每个准备,值只能插入一次似乎是不可避免的?如何绕过准备此语句一次,并且只在数据可用时重复执行?我似乎无法理解准备好的陈述的“效率”因素..

谢谢.. 不胜感激一些经验丰富的程序员的建议..

【问题讨论】:

    标签: php ajax mysqli prepared-statement


    【解决方案1】:

    你说:

    如您所见,PHP 文件变得相当长。

    确实如此,但这不是准备好的语句的错。您一定是从一个写得不好的教程中学习 PHP 开发的。这段代码不需要那么长。事实上,它可以大大缩短。

    只需修复现有代码即可使其更具可读性。我使用了 OOP 风格的 mysqli 并删除了所有这些 if 语句。您应该改为启用错误报告。

    <?php
    
    session_start();
    if (isset($_POST['CT'],$_POST['CN'])) {
        require_once "golin_2.php";
        $CN = $_POST['CN'];
        $CT = $_POST['CT'];
        $ER = "";
        $arr = [
            'PASS' => "OK",
            'MSG2' => "Insert Success",
        ]; // successful state should be the default outcome
    
        mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
        $CONN = new mysqli($SERVER, $USER, $PASS, $DBNAME);
        $CONN->set_charset('utf8mb4'); // always set the charset
    
        // To check existance of data in database we use COUNT(*)
        $stmt = $CONN->prepare("SELECT COUNT(*) FROM sailors.continental_regions WHERE CONTINENT = ? AND COUNTRY = ?");
        $stmt->bind_param("ss", $CN, $CT);
        $stmt->execute();
        $NUMROWS = $stmt->get_result()->fetch_row()[0];
            
        if ($NUMROWS) {
            $ER .= "Err: duplicate '$CN' '$CT' pair";
        } else {
            $stmt = $CONN->prepare("INSERT INTO DB.continental_regions (CONTINENT,COUNTRY) values (?, ?)");
            $stmt->bind_param("ss", $CN, $CT);
            $stmt->execute();
        }
                
        if ($ER) {
            $arr = [
                'PASS' => "FAIL",
                'MSG' => $ER,
            ];
        }
        echo json_encode($arr);
    } else {
        header("location: ../Error_Fail.php");
    }
    

    如果您在表中的这两列上有一个复合 UNIQUE 键,那么您可以删除 select 语句。此外,您应该清理您的响应准备。成功状态应该是默认状态,并且只有在出现问题时才应替换为错误消息。

    在本例中,我删除了一条 SQL 语句。整个事情现在变得简单多了。

    <?php
    
    define('DUPLICATE_KEY', 1062);
    session_start();
    if (isset($_POST['CT'],$_POST['CN'])) {
        require_once "golin_2.php";
        $CN = $_POST['CN'];
        $CT = $_POST['CT'];
        $arr = [
            'PASS' => "OK",
            'MSG2' => "Insert Success",
        ]; // successful state should be the default outcome
    
        mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
        $CONN = new mysqli($SERVER, $USER, $PASS, $DBNAME);
        $CONN->set_charset('utf8mb4'); // always set the charset
    
        try {
            $stmt = $CONN->prepare("INSERT INTO continental_regions (CONTINENT,COUNTRY) values (?, ?)");
            $stmt->bind_param("ss", $CN, $CT);
            $stmt->execute();
        } catch (mysqli_sql_exception $e) {
            if ($e->getCode() !== DUPLICATE_KEY) {
                // if it failed for any other reason than duplicate key rethrow the exception
                throw $e;
            }
            // if SQL failed due to duplicate entry then set the error message
            $arr = [
                'PASS' => "FAIL",
                'MSG' => "Err: duplicate '$CN' '$CT' pair",
            ];
        }
        
        echo json_encode($arr);
    } else {
        header("location: ../Error_Fail.php");
    }
    

    关于性能。
    此示例中的性能没有问题,准备好的语句不会提高或降低性能。我假设您正在尝试将性能与静态 SQL 查询进行比较,但在您的简单示例中应该完全没有区别。当您需要多次执行相同的 SQL 时,与静态查询相比,准备好的语句可以使您的代码更快。

    如果您发现每次编写 3 行代码太多,那么您可以创建一个包装函数,将其减少为单个函数调用。公平地说,您应该避免单独使用 mysqli。要么切换到 PDO,要么使用 mysqli 周围的某种抽象库。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-07-27
      • 1970-01-01
      • 2014-03-13
      • 1970-01-01
      • 2012-08-22
      • 1970-01-01
      • 1970-01-01
      • 2018-11-17
      相关资源
      最近更新 更多