【问题标题】:How to use mysqli prepared statements?如何使用mysqli准备好的语句?
【发布时间】:2012-03-26 14:59:41
【问题描述】:

我正在尝试准备好的语句,但下面的代码不起作用。我收到错误消息:

致命错误:在非对象上调用成员函数 execute() /var/www/prepared.php 第 12 行

<?php

    $mysqli = new mysqli("localhost", "root", "root", "test");
    if ($mysqli->connect_errno) {
        echo "Failed to connect to MySQL: " . $mysqli->connect_error;
    }

    $stmt = $mysqli->prepare("INSERT INTO users (name, age) VALUES (?,?)");

    // insert one row
    $stmt->execute(array('one',1));

    // insert another row with different values
    $stmt->execute(array('two',1));
?>

另外,我需要为准备好的语句使用 mysqli 吗?谁能指出一个完整的示例,说明从连接到插入到选择以及错误处理的准备语句?

【问题讨论】:

  • “谁能给我一个完整的例子,说明从连接到插入到选择错误处理的准备语句” 是的。 php.net/manual/en/mysqli.prepare.php。注意 "mysqli_prepare() 行返回一个语句对象或 FALSE 如果发生错误。".

标签: php mysql mysqli prepared-statement


【解决方案1】:

来自mysqli::prepare docs:

在执行语句或获取行之前,必须使用 mysqli_stmt_bind_param() 和/或 mysqli_stmt_bind_result() 将参数标记绑定到应用程序变量。

bind_param docs.

即:

$name = 'one';
$age  = 1;

$stmt = $mysqli->prepare("INSERT INTO users (name, age) VALUES (?,?)");

// bind parameters. I'm guessing 'string' & 'integer', but read documentation.
$stmt->bind_param('si', $name, $age);

// *now* we can execute
$stmt->execute();

【讨论】:

  • 哦,还有@Tomalak 建议首先检查$stmt 是否为FALSE(即发生错误)。
【解决方案2】:

我还需要为准备好的语句使用 mysqli。任何人都可以指出一个完整的示例,说明从连接到插入到选择以及错误处理的准备语句

您也可以使用我更喜欢的 PDO。事实上,您在代码示例中似乎混淆了 PDO 和 Mysqli。

$db = new PDO($dsn, $user, $pass);
$stmt = $db->prepare("INSERT INTO users (name, age) VALUES (?,?)");
$stmt->execute(array($name1, $age1));
$stmt->execute(array($name2, $age2));

与 mysqli 不同,您不必调用单独的绑定函数,但如果您喜欢/想要/需要使用该功能,则可以使用它。

关于 PDO 的另一个有趣的事情是命名占位符,它可以在复杂的查询中减少混乱:

$db = new PDO($dsn, $user, $pass);
$stmt = $db->prepare("INSERT INTO users (name, age) VALUES (:name,:age)");
$stmt->execute(array(':name' => $name1, ':age' => $age1));
$stmt->execute(array(':name' => $name2, ':age' => $age2));

【讨论】:

    【解决方案3】:

    谁能给我一个完整的例子,说明从连接到插入到选择以及错误处理的准备语句?

    这个问题很老,但直到最近我才完成了一些研究,产生了一系列完全涵盖该请求的文章,所以你去吧:

    连接

    mysqli 连接的重要性经常被忽视,被贬低为一行。而正确的连接代码可以解决许多问题,从安全性到可用性。

    鉴于您的代码是通常的 PHP 程序,这里有一个简单的 mysqli 连接代码将包含在您的脚本中:

    $host = '127.0.0.1';
    $db   = 'test';
    $user = 'root';
    $pass = '';
    $charset = 'utf8mb4';
    
    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
    try {
        $mysqli = new mysqli($host, $user, $pass, $db);
        $mysqli->set_charset($charset);
    } catch (\mysqli_sql_exception $e) {
         throw new \mysqli_sql_exception($e->getMessage(), $e->getCode());
    }
    unset($host, $db, $user, $pass, $charset); // we don't need them anymore
    

    完整的解释可以在我的文章How to connect properly using mysqli 中找到(以及许多有用的提示),但只是一个小引用来突出最重要的部分:

    • 为连接设置正确的字符集将消除整类错误,例如奇怪的字符/问号而不是您的数据、空的 json_encode() 输出、存储表情符号的问题等。
    • 设置正确的错误报告模式将消除神秘的错误消息,如 mysqli_fetch_assoc() 需要参数... / 调用成员函数 bind_param()...,而是为您提供来自 MySQL 的实际错误消息。
    • 安全性不是笑话,不应该有机会向外界泄露您的数据库详细信息

    插入

    Insert query比较简单,其他答案已经介绍过了。

    您只需用问号替换查询中的所有变量(以及周围的引号!),然后准备查询,然后将所有变量及其类型推入bind_param(),最后执行查询。

    只是一个快速提示:MySQL 很乐意将所有变量作为字符串接受,所以不要为某个变量寻找正确的类型,只需使用 "s" 即可。

    所以基本上插入是这样的

    $sql = "INSERT INTO users (name, email, password) VALUES (?,?,?)";
    $stmt= $conn->prepare($sql);
    $stmt->bind_param("sss", $name, $email, $password_hash);
    $stmt->execute();
    

    同样的原则也适用于所有其他查询类型,例如 UPDATE 或 DELETE。

    选择

    运行select query 几乎相同,但有一个小技巧。由于某些未知的原因,您不能在准备好的语句中使用熟悉的 fetch 函数。所以你需要先得到mysqli_result,然后才能使用fetch_assoc()、fetch_obj()等:

    $sql = "SELECT * FROM users WHERE id=?"; // SQL with parameters
    $stmt = $conn->prepare($sql);
    $stmt->bind_param("s", $id);
    $stmt->execute();
    $result = $stmt->get_result(); // get the mysqli result
    $user = $result->fetch_assoc(); // fetch data 
    

    提示:绝对不需要熟悉的mysqli_num_rows() 函数。如果你想到了,你总是可以使用数据本身来查看你的查询是否返回了任何行:

    $user = $result->fetch_assoc();
    if ($user) {
        // found!
    }
    

    多行也是如此,感谢

    另一个提示:有一个方便的函数fetch_all() 可以一次性获取所有选定行的数组。例如,如果查询返回多行,您可以通过将最后一行更改为

    来将它们放入数组中
    $users = $result->fetch_all(MYSQLI_ASSOC); // fetch data 
    

    错误处理

    错误处理是最重要但又有点令人惊讶的部分。尽管有许多文章和示例都说了,但作为一项规则,您根本不应该编写任何错误处理代码。这听起来绝对是疯狂的,但这正是事情必须要做的事情。大多数时候,您需要做的只是报告错误。并且 mysqli/PHP 已经可以为您完成,无需帮助。因此,您不应该编写任何验证查询执行结果的代码——如果出现错误,mysqli 会自动报告,这要归功于#Connection 部分中提到的mysqli_report() 函数调用。同样,这个原理的完整解释可以在另一篇文章中找到,献给将军PHP error reporting。

    在极少数情况下,您确实需要处理错误,即在出现错误时执行一些操作,而不是仅仅报告错误,然后包装您的查询(es)try..catch。

    【讨论】:

      【解决方案4】:

      试试这个,清理传入的数据。 并且不要忘记 php 标签。

      function clean($data)
      { 
        $data = trim(strip_tags(htmlspecialchars($data)));
        return $data;
      }
      
      $field1 = isset($_POST['field1']) ? clean($_POST['field1']): NULL;
      $field2 = isset($_POST['field2']) ? clean($_POST['field2']): NULL;
      $field3 = isset($_POST['field3']) ? clean($_POST['field3']): NULL;
      $field4 = isset($_POST['field4']) ? clean($_POST['field4']): NULL;
      $field5 = isset($_POST['field5']) ? clean($_POST['field5']): NULL;
      $field6 = isset($_POST['field6']) ? clean($_POST['field6']): NULL;
      $field7 = isset($_POST['field7']) ? clean($_POST['field7']): NULL;
      $database = new mysqli("localhost", "username", "password", "database");
      if ($database->errno) die("Error opening database: " . $database->error());
      $query = 'INSERT INTO `tablename` (`field1`, `field2`, `field3`, `field4`, `field5`, `field6`, `field7`) VALUES (?, ?, ?, ?, ?, ?, ?)'; 
      $result = $database->prepare($query); 
      $result->bind_param('sssssss', $field1, $field2, $field3, $field4, $field5, $field6, $field7); 
      $result->execute(); 
      $database->close();
      {
          header("Location: http://www.somewebsite.com");
      }
      

      【讨论】:

      • 这个函数不会清理任何东西,与mysqli准备好的语句无关
      • 这是一个经过测试且功能正常的脚本,请您解释一下您声明的原因。
      猜你喜欢
      • 2012-12-01
      • 2016-07-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多