Запрос mysqli динамического выбора с динамическими параметрами возвращает ошибку, не совпадающую с числом переменных связывания - PullRequest
0 голосов
/ 13 сентября 2018

Я пытаюсь создать запрос выбора с динамическим предложением 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 голосов
/ 14 сентября 2018

Потому что:

  1. Вы используете предоставленные пользователем данные, и вы должны исходить из того, что ваш запрос уязвим для атаки злонамеренного внедрения
  2. Количество данных, которые должны быть встроены в запрос, является переменным / неопределенным
  3. Вы пишете условные проверки только для одного столбца таблицы

Вы должны использовать подготовленный оператор и объединить всю логику предложения WHERE в один оператор IN.

Построение этого динамического подготовленного оператора является более запутанным (с точки зрения синтаксиса), чем использование pdo, но это не означает, что вам нужно отказаться от mysqli просто из-за этой задачи.

Я запекал каждую возможную контрольную точку ошибки, чтобы вы могли легко отлаживать ее в случае возникновения непредвиденных ситуаций. Я должен подчеркнуть, что вы никогда не должны показывать подробности об ошибках широкой публике в качестве наилучшей практики безопасности.

Я протестировал это решение с моими собственными данными в базе данных на моем локальном хосте, чтобы оно работало.

<code>$_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 "
"; } $ Stmt-> близко (); } }

Вывод должен быть таким:

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
)
...
0 голосов
/ 13 сентября 2018

В вашем запросе:

$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]);

Также обратите внимание, что в вашем примере кода есть несколько пропущенных точек с запятой;вам нужно исправить это, чтобы ваш код работал правильно.

Это можно увидеть работающим здесь .

Наконец, обратите вниманиечто даже если вы правильно разделите три строки, выбор ... AND socialmedianame=Facebook AND socialmedianame=Twitter AND socialmedianame=Twitch никогда не будет соответствовать никаким результатам;socialmedianame может содержать только одно значение.Возможно, вы хотите заменить свои AND заявления на OR заявления.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...