mysqli准备语句中的致命错误

时间:2016-02-10 16:28:36

标签: php mysqli

我正在尝试使用下面的示例代码学习如何编写预备语句。

我有一个初始SQL语句,对于每个返回的记录,请对返回的结果执行另一次SQL检查。

<?php

$sql_b = "SELECT fld_title, fld_tag FROM j_oracle_cat WHERE fld_tag IS NOT NULL ORDER BY fld_title";

if (!$result_b = $conn -> query($sql_b)) {

    die('There was an error running the query [' . $conn -> error . ']');

} else {

    if (!$result_b -> num_rows) {

        echo "<h4>No Categories Defined</h4>";

    } else {

        while($row_b = $result_b -> fetch_assoc()) {

            $cat_ttl = $row_b["fld_title"];
            $cat_tag = $row_b["fld_tag"];

            echo "<h4><a href='category.php?tag=" . $cat_tag . "'>" . $cat_ttl . "</a></h4>";

            $sql_c = "SELECT p.fld_tag FROM j_oracle_cat c, j_oracle_pages p WHERE c.fld_id = p.fld_catid AND c.fld_tag = ?";

            /* Prepare statement */
            $stmt = $conn->prepare($sql_c);
            if($stmt === false) {
                trigger_error('Wrong SQL: ' . $sql_c . '<hr />Error: ' . $conn->error, E_USER_ERROR);
            }

            /* Bind parameters. Types: s = string, i = integer, d = double,  b = blob */
            $stmt->bind_param('s',$cat_tag);

            /* Execute statement */
            $stmt->execute();                       

        }

        $result_b -> free();

    }

}

?>

当我运行代码时,它得到了这么远:

<h4><a href='category.php?tag=accounts-payable'>Accounts Payable</a></h4>
<h4><a href='category.php?tag=accounts-receivable'>Accounts Receivable</a></h4>

然后错误:

  

致命错误:错误的SQL:SELECT p.fld_is_parent FROM j_oracle_cat c,j_oracle_pages p WHERE c.fld_id = p.fld_catid AND c.fld_tag =?   错误:在 C:\ xampp \ htdocs \ php \ a.php 95

第95行就是这个:

trigger_error('Wrong SQL: ' . $sql_c . '<hr />Error: ' . $conn->error, E_USER_ERROR);

表格结构:

CREATE TABLE `j_oracle_cat` (
  `fld_id` int(11) NOT NULL AUTO_INCREMENT,
  `fld_title` varchar(255) DEFAULT NULL,
  `fld_desc` varchar(255) DEFAULT NULL,
  `fld_tag` varchar(255) DEFAULT NULL,
  `fld_label` varchar(255) DEFAULT NULL,
  `fld_parent` int(2) DEFAULT '0',
  PRIMARY KEY (`fld_id`)
) ENGINE=MyISAM AUTO_INCREMENT=16 DEFAULT CHARSET=latin1

CREATE TABLE `j_oracle_pages` (
  `fld_id` int(11) NOT NULL AUTO_INCREMENT,
  `fld_title` varchar(255) DEFAULT NULL,
  `fld_catid` int(11) DEFAULT NULL,
  `fld_cols` int(2) DEFAULT '1',
  `fld_left` text,
  `fld_content` text,
  `fld_tag` varchar(255) DEFAULT NULL,
  `fld_date` date DEFAULT NULL,
  `fld_is_parent` char(1) DEFAULT NULL,
  PRIMARY KEY (`fld_id`)
) ENGINE=MyISAM AUTO_INCREMENT=83 DEFAULT CHARSET=latin1

我已尝试直接运行sql并且它没有错误,对于示例代码并且它返回例如3行:

SELECT p.fld_tag FROM j_oracle_cat c, j_oracle_pages p WHERE c.fld_id = p.fld_catid AND c.fld_tag = 'accounts-receivable';

与db的连接:

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

$servername = "localhost";
$username = "root";
$password = "";
$dbname = "mydb";

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);

if($conn -> connect_errno > 0){
    die('Unable to connect to database [' . $conn->connect_error . ']');
}

?>

据说我做的事情很傻,但我看不清楚是什么?

1 个答案:

答案 0 :(得分:1)

所以这是我的建议;我没有看到您的代码有任何明显错误,但如果不能彻底解决问题,这些步骤应该可以更容易地诊断问题。首先,将prepare()调用移出循环*。接下来,explicitly create the statement object,这样如果准备工作有问题,您可以直接从语句对象中获取错误。最后,在流程的每个阶段检查有效回报,包括parameter bindingstatement execution

$sql_b = "SELECT fld_title, fld_tag FROM j_oracle_cat WHERE fld_tag IS NOT NULL ORDER BY fld_title";
if (!$result_b = $conn->query($sql_b)) {
    trigger_error("Error executing query: $conn->error<br/>SQL query: $sql_b", E_USER_ERROR);
} else {
    if (!$result_b->num_rows) {
        echo "<h4>No Categories Defined</h4>";
    } else {
        /* Prepare statement */
        $sql_c = "SELECT p.fld_tag FROM j_oracle_cat c, j_oracle_pages p WHERE c.fld_id = p.fld_catid AND c.fld_tag = ?";    
        $stmt = $conn->stmt_init();
        if (!$stmt) {
            trigger_error("Error creating statement: $conn->error", E_USER_ERROR);
        }
        $result = $stmt->prepare($sql_c);
        if(!$result) {
            trigger_error("Error preparing statement: $stmt->error<br/>SQL query: $sql_c", E_USER_ERROR);
        }
        /* Bind parameters. Types: s = string, i = integer, d = double,  b = blob */
        // needs a dummy value for the initial bind
        $row_b = array("fld_tag"=>"");
        $result = $stmt->bind_param("s", $row_b["fld_tag"]);
        if(!$result) {
            trigger_error("Error binding parameter: $stmt->error<br/>", E_USER_ERROR);
        }

        while($row_b = $result_b -> fetch_assoc()) {
            $cat_ttl = htmlspecialchars($row_b["fld_title"]);
            $cat_tag = htmlspecialchars($row_b["fld_tag"]);
            echo "<h4><a href='category.php?tag=$cat_tag'>$cat_ttl</a></h4>";
            /* Execute statement */
            $result = $stmt->execute();
            if(!$result) {
                trigger_error("Error executing statement: $stmt->error<br/>Bound parameter value: $row_b[fld_tag]", E_USER_ERROR);
            }
            // I assume you'll be doing something with $result down here?
        }
        $result_b->free();
        $stmt->close();
    }
}

一些附注:在将它们放入HTML之前,您应始终使用htmlspecialchars()转义数据库结果,以避免注入风险。您可以在字符串内部use double quotes to include variables,并避免在任何地方拥有数十个不必要的.运算符。这是个人偏好的问题,但许多人发现它更具可读性。

*预准备语句的一大好处是它们separate the preparation of the query from its execution,避免了设置MySQL运行查询所涉及的开销。通过反复准备声明,您可以完全取消该利益。