mysqli代码和call_user_func_array()中出现的错误

时间:2013-01-27 05:15:43

标签: php html mysql mysqli

尝试使用mysqli创建动态where子句时,我遇到了很多错误:

  

警告:参数2到mysqli_stmt :: bind_param()应该是一个   参考,在第319行的......中给出的值

     

警告:mysqli_stmt :: execute():( HY000 / 2031):没有提供数据   第328行......中准备好的声明中的参数

     

警告:mysqli_stmt :: bind_result():( HY000 / 2031):未提供数据   对于第331行......中准备好的陈述中的参数

     

警告:mysqli_stmt :: store_result():( HY000 / 2014):以外的命令   同步;你现在不能在......第332行运行这个命令

我猜测解决问题需要做一些改变但是如果两个下拉菜单中的一个不等于All或者两个都不等于All会发生什么然后它出现了错误。

下面是代码显示下拉菜单和查询(带有动态where子句),根据所选的n个选项:

HTML:

学生下拉菜单:

<select name="student" id="studentsDrop">
<option value="All">All</option>
<option value="11">John May</option>
<option value="23">Chris Park</option>
</select>

问题编号下拉菜单

<select name="question" id="questionsDrop">
<option value="All">All</option>
<option value="123">1</option>
<option value="124">2</option>
<option value="125">3</option>
</select>

PHP / mysqli的:

      function StudentAnswers()
        {


    /*BELOW IS THE QUERY WHERE I AM TRYING TO RETRIEVE DATA DEPENDING ON THE ASSESSMENT CHOSEN AND
    THEN DEPENDING ON OPTIONS CHOSEN IN STUDENT AND QUESTION NUMBER DROP DOWN MENU */

        $selectedstudentanswerqry = "
        SELECT
        StudentAlias, StudentForename, StudentSurname, q.SessionId, QuestionNo, QuestionContent, o.OptionType, q.NoofAnswers, GROUP_CONCAT( DISTINCT Answer
        ORDER BY Answer SEPARATOR ',' ) AS Answer, r.ReplyType, QuestionMarks, 
        GROUP_CONCAT(DISTINCT StudentAnswer ORDER BY StudentAnswer SEPARATOR ',') AS StudentAnswer, ResponseTime, MouseClick, StudentMark
        FROM Student s
        INNER JOIN Student_Answer sa ON (s.StudentId = sa.StudentId)
        INNER JOIN Student_Response sr ON (sa.StudentId = sr.StudentId)
        INNER JOIN Question q ON (sa.QuestionId = q.QuestionId)
        INNER JOIN Answer an ON q.QuestionId = an.QuestionId
        LEFT JOIN Reply r ON q.ReplyId = r.ReplyId
        LEFT JOIN Option_Table o ON q.OptionId = o.OptionId
        ";

        // Initially empty
        $where = array('q.SessionId = ?');
        $parameters = array($_POST["session"]);
        $parameterTypes = 'i';

        // Check whether a specific student was selected
        if($_POST["student"] !== 'All') {
            $where[] = 'sa.StudentId = ?';
            $parameters[] =& $_POST["student"];
            $parameterTypes .= 'i';
        }

        // Check whether a specific question was selected
        // NB: This is not an else if!
        if($_POST["question"] !== 'All') {
            $where[] = 'q.QuestionId = ?';
            $parameters[] =& $_POST["question"];
            $parameterTypes .= 'i';
        }

        // If we added to $where in any of the conditionals, we need a WHERE clause in
        // our query
        if(!empty($where)) {
            $selectedstudentanswerqry .= ' WHERE ' . implode(' AND ', $where);
            global $mysqli;
            $selectedstudentanswerstmt=$mysqli->prepare($selectedstudentanswerqry);
            // You only need to call bind_param once
                call_user_func_array(array($selectedstudentanswerstmt, 'bind_param'),
                array_merge(array($parameterTypes), $parameters)); //LINE 319 ERROR 1
        }

    //Add group by and order by clause to query
        $selectedstudentanswerqry .= "
          GROUP BY sa.StudentId, q.QuestionId
          ORDER BY StudentAlias, q.SessionId, QuestionNo
        ";

        // get result and assign variables (prefix with db)
        $selectedstudentanswerstmt->execute(); //LINE 328 ERROR 2

    //bind database fields 
$selectedstudentanswerstmt->bind_result($detailsStudentAlias,$detailsStudentForename,$detailsStudentSurname,$detailsSessionId,$detailsQuestionNo, 
        $detailsQuestonContent,$detailsOptionType,$detailsNoofAnswers,$detailsAnswer,$detailsReplyType,$detailsQuestionMarks,$detailsStudentAnswer,$detailsResponseTime,
        $detailsMouseClick,$detailsStudentMark); //LINE 331 ERROR 3

    //store results retrieved
        $selectedstudentanswerstmt->store_result(); //LINE 332 ERROR 4

    //count number of rows retrieved
        $selectedstudentanswernum = $selectedstudentanswerstmt->num_rows();     

    //output query
        echo "$selectedstudentanswerqry";

        }

        ?>

以下是演示: DEMO

在演示中,从下拉菜单中选择评估并提交。您将看到两个下拉菜单。保持它们都设置为All并提交,它将输出查询没有问题。在其中一个下拉菜单中没有,请将All更改为特定的学生或问题,然后提交。现在您将看到错误

VAR DUMP:

当我选择值var_dump(array_merge(array($parameterTypes), $parameters)));的会话(评估),学生编号值31和问题编号值40时,81的结果,以及WHERE CLAUSE { {1}}:

我收到此输出:WHERE q.SessionId = ? AND sa.StudentId = ? AND q.QuestionId = ?

3 个答案:

答案 0 :(得分:1)

  

警告:参数2到mysqli_stmt :: bind_param()应该是一个参考,在第319行中给出的值

这应该是不言自明的:bind_param的参数是通过引用传递的,因此必须是变量。您可能忽略的是array_merge返回一个新数组,该数组不包含对原始变量的引用,只包含值。

以下错误就在此之后,因为参数未绑定。

一种可能的解决方案是在$parameters数组中存储引用,这些引用甚至可以由array_merge保存:

$parameters[] =& $_POST["student"];

$parameters[] =& $_POST["question"];

现在$parameters的数组项是对POST变量的引用,array_merge结果的数组项也是。

编辑:看起来不再可能了,请参阅@Explosion Pills

答案 1 :(得分:1)

这是一个棘手的情况,是由PHP 5.4中的call_user_func_array行为改变引起的(我必须假设):Documentation

虽然这很难看,但这样可以调用bind_param

$selectedstudentanswerqry .= ' WHERE ' . implode(' AND ', $where);
global $mysqli;
$stmt =$mysqli->prepare($selectedstudentanswerqry);

if (count($where) === 1) {
    $stmt->bind_param($parameterTypes, $parameters[0]);
}
else if (count($where) === 2) {
    $stmt->bind_param($parameterTypes, $parameters[0], $parameters[1]);
}
else if (count($where) === 3) {
    $stmt->bind_param($parameterTypes, $parameters[0], $parameters[1],
       $parameters[2]);
}

我讨厌这个,就像你可能做的那样。我建议从mysqli切换到PDO,它以更好的方式处理变量参数(在我看来,一般都有优越的语法):

$pdo = new PDO('mysql:host=localhost', 'username', 'password');
$stmt = $pdo->prepare($selectedstudentanswerqry);
$stmt->execute($parameters);
$selectedstudentanswernum = $stmt->rowCount();

答案 2 :(得分:0)

这仍然不足以让PDO抛弃mysqli,尤其是当我有充分的理由不使用PDO时。另一方面,是的,他们不需要强制mysqli_stmt :: bind_param和bind_result方法来要求变量通过byRef传递,他们可能让它们返回一个数组。但是,真正的问题是调用时间传递不再可能,但是mysqli仍然需要传递引用,所以任何使用动态参数的人(任何拥有直观SQL的人)都会被抓住在这个问题上,我自己包括在内。在花了好几个小时编写一个将数组集转换为动态编写的SQL语句的自定义mysqli包装类之后,我不打算尝试重写它以使用PDO,特别是因为PDO需要将参数命名为绑定的一部分,我必须编写多个函数,以便为每种类型的查询构建发送内容的列表。这是违反直觉的,我不会浪费无数个小时试图模仿我用mysqli所取得的成就。

此时唯一的障碍是我们无法传递call_user_func_array()byRef中的变量,而mysqli_stmt :: bind_ *要求它们通过byRef传递...所以我们唯一的选择是1)写一个新的call_user_func_array()函数,旨在为bind_ *方法显式工作,其中传递的参数基本上被args复制到一个新数组或一组变量中,然后在回调时传递那些byRef;或者2)重写mysqli类以正确的方式执行它,只需查看传递的内容,执行SQL,然后按预期返回结果。没有理由我们传递给执行SQL的方法必须被结果破坏,应该像对待SQL控制台一样对待它。

因为我的编码太累了,直到我的眼睛几乎流血,我将采取非建议的选项,让我的PHP版本保持在5.3.10-1,直到我实际上以正确的方式修复问题。对于那些认为“为了所有这些努力,为什么不把它用于编写PDO包装器呢?”而且我的回答很简单:我在许多项目中使用PHP和MySQL,其中服务器是一个小小的计算机房间和资源非常有限,更不用说CPU功能了。 PDO加载它可以做的一切,而不仅仅是你要使用的东西,所以你的资源中有一些保留用于你甚至不会使用的东西,我的项目不能丢失。

值得庆幸的是,保持我的PHP版本保留不会在这个时候有害。这就是许多企业级服务器场实际上所做的事情,以避免将所有内容都降低到仅仅是“在当前版本上”的东西,每隔几年升级一切也是非常昂贵的。