具有动态参数的动态选择mysqli查询返回错误与绑定变量数不匹配

时间:2018-09-12 22:54:13

标签: php mysqli

我正在尝试使用动态where子句和动态参数创建一个选择查询,但是我总是遇到错误:

  

警告:mysqli_stmt :: bind_param():类型中的元素数   定义字符串与绑定变量的数量不匹配

我真诚地不理解,因为似乎计数还可以。因此,这就是代码的粗鲁形式。我看不到我在做什么错。

//get variables
$mediaArray ='Facebook,Twitter,Twitch,';
$otherMedia = 'House';

//convert string to array
$socialArray = explode(',', $mediaArray)

//declare some variables to be used later
$andwhere = '';
$bp = '';
$socialmarray = ''

 //get every value from array of social media
foreach($socialArray as $socialmedia){

    $socialmarray .=$socialmedia.',';
    $andwhere .= " AND socialmedianame=?";
    $bp .='s';
}

//test strings
echo $wheres = $andwhere;//AND socialmedianame=? AND socialmedianame=? AND socialmedianame=?
echo $bip = $bp.'s';//ssss
echo $validarayy = rtrim($socialmarray,',');//Facebook,Twitter,Twitch

//select query
$selectquery = $conn->prepare("select * from mediaservices where socialmedianame=? $wheres");
$selectquery->bind_param("$bip",$otherMedia,$validarayy);
$selectquery->execute();
$resultquery = $selectquery->get_result();

2 个答案:

答案 0 :(得分:2)

在您的查询中:

$selectquery = $conn->prepare("select * from mediaservices where socialmedianame=? $wheres");

?代表一个要传入的参数,而$wheres的求值又添加了三个,从而为您提供了四个参数。

bind_param() 应该将表示要插入的变量类型的字符串作为第一个参数,并将变量本身作为后续参数。

在您的绑定中:

$selectquery->bind_param("$bip",$otherMedia,$validarayy);

$bip的值为ssss,并且$otherMedia是单个字符串("House")。您可能希望$validarayy是三个字符串,但是 rtrim() 返回一个字符串。因此,它只是一个字符串("Facebook,Twitter,Twitch")。当查询需要四个变量时,您将传递两个变量:

$conn->prepare("select * from mediaservices where socialmedianame=House AND socialmedianame=Facebook,Twitter,Twitch AND socialmedianame=? AND socialmedianame=? AND socialmedianame=?"

要解决此问题,您需要将$validarayy转换回数组,并将索引用于各种输入:

$socialmarray2 = explode(',', $validarayy);
$selectquery->bind_param("$bip", $otherMedia, $socialmarray2[0], $socialmarray2[1], $socialmarray2[2]);

还请注意,您的示例代码缺少一些分号。您需要修复这些问题才能使代码正常工作。

可以在 here 上看到它。

最后,请注意,即使您正确地分割了三个字符串,选择... AND socialmedianame=Facebook AND socialmedianame=Twitter AND socialmedianame=Twitch也不会匹配任何结果; socialmedianame只能包含一个值。您可能正在寻找将AND语句替换为OR语句。

答案 1 :(得分:2)

因为:

  1. 您正在使用用户提供的数据,必须假定您的查询容易受到恶意注入攻击
  2. 要构建到查询中的数据量是可变/不确定的
  3. 您仅在单个表列上写条件检查

您应该使用准备好的语句,并将所有WHERE子句逻辑合并到单个IN语句中。

(在语法上)构建动态准备语句比使用pdo更为复杂,但这并不意味着您仅由于此任务就需要放弃mysqli。

我已经遍历所有可能的错误检查点,以便您可以轻松调试是否出现意外情况。我必须强调,作为最佳安全实践,您绝不能向公众展示错误详细信息。

我已经在本地主机上使用自己的db数据测试了该解决方案,这是一个可行的解决方案。

$_POST['userMedia'] ='Facebook,Twitter,Twitch,';
$media = explode(',', rtrim($_POST['userMedia'], ','));  // build array from csv string
$presetMedia = 'House';
$media[] = $presetMedia;                                 // push known media string into array
$media = array_unique($media);                           // make sure there are no duplicates
var_export($media);                                       // see what this generates


if (!$count = count($media)) {
    echo "There are no values in media, so a prepared statement is overkill and IN won't work when empty";
    // handle this case however you wish (if it is even possible within your project)
} elseif (!$conn = new mysqli("localhost", "root", "", "myDB")) {  // use your own credentials
    echo "Database Connection Error: " , $conn->connect_error;
} else {
    $csph = implode(',', array_fill(0, $count, '?'));        // comma-separated placeholders e.g: ?,?,?,?
    echo "<div>Placeholders: $csph</div>";
    $query = "SELECT * FROM `mediaservices` WHERE `socialmedianame` IN ($csph)";
    echo "<div>Query: $query</div>";
    if (!$stmt = $conn->prepare($query)) {
        echo "<div>Syntax Error @ prepare: {$conn->error}</div>";      // NEVER show error details to the public
    }else{
        array_unshift($media, str_repeat('s', $count));      // prepend the type values string e.g: ssss
        var_export($media);                                   // see what this generates
        foreach ($media as &$v) {
            $ref[] = &$v;                                    // call_user_func_array requires array that is passed-by-reference
        }
        call_user_func_array([$stmt, 'bind_param'], $ref);   // call bind_param() upon stmt object, using each media value  

        if (!$stmt->execute() || !$result = $stmt->get_result()) { // execute statement and get the resultset while checking for falsey returned feedback
            echo "<div>Error @ execute/get_result: {$stmt->error}</div>";  // NEVER show error details to the public
        } elseif (!$result->num_rows) {                      // check for empty rows, if you wish
            echo "<div>No Rows Found</div>";
        } else {
            echo "<pre>";
            while ($row = $result->fetch_assoc()) {
                var_export($row);                            // do what you like with the associative-keyed elements
                echo "<br>";
            }
            echo "</pre>";
        }
        $stmt->close();
    }
}

输出应类似于:

array ( 0 => 'Facebook', 1 => 'Twitter', 2 => 'Twitch', 3 => 'House' )
Placeholders: ?,?,?,?
Query: SELECT * FROM `mediaservices` WHERE `socialmedianame` IN (?,?,?,?);
array ( 0 => 'ssss', 1 => 'Facebook', 2 => 'Twitter', 3 => 'Twitch', 4 => 'House', )
array (
    // whatever column names and values you have in the row
)
array (
    // whatever column names and values you have in the row
)
array (
    // whatever column names and values you have in the row
)
...