Kann ich ein Array an eine IN () - Bedingung binden?

562

Ich bin gespannt, ob es möglich ist, ein Array von Werten mithilfe von PDO an einen Platzhalter zu binden. Der Anwendungsfall hier ist der Versuch, ein Array von Werten zur Verwendung mit einer IN()Bedingung zu übergeben.

Ich möchte in der Lage sein, so etwas zu tun:

<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>

Lassen Sie PDO alle Werte im Array binden und in Anführungszeichen setzen.

Im Moment mache ich:

<?php
$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
foreach($ids as &$val)
    $val=$db->quote($val); //iterate through array and quote
$in = implode(',',$ids); //create comma separated list
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN('.$in.')'
);
$stmt->execute();
?>

Was macht den Job sicher, aber ich frage mich nur, ob es eine eingebaute Lösung gibt, die mir fehlt?

Andru
quelle
3
Eine vollständige Anleitung zum Binden eines Arrays an eine IN () - Bedingung , einschließlich des Falls, wenn Sie andere Platzhalter in der Abfrage haben
Ihr gesunder Menschenverstand
Die Frage wurde als Duplikat dieser Frage geschlossen . Ich habe das Duplikat-Flag umgekehrt, weil diese Frage 4 Jahre älter ist, die 4-fache Anzahl der Aufrufe, die 3-fache Anzahl der Antworten und die 12-fache Punktzahl hat. Es ist eindeutig das überlegene Ziel.
miken32
Wer sich das 2020 ansieht: Sie könnten es mit github.com/morris/dop versuchen .
Morris4

Antworten:

262

Ich denke, Soulmerge ist richtig. Sie müssen die Abfragezeichenfolge erstellen.

<?php
$ids     = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids), '?'));

$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(' . $inQuery . ')'
);

// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();
?>

fix: dan, du hattest recht. Der Code wurde behoben (aber nicht getestet)

edit: sowohl chris (kommentare) als auch jemandesintrouble schlugen vor, dass die foreach-loop ...

(...)
// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();

... könnte redundant sein, so dass die foreachSchleife und die $stmt->executedurch nur ...

<?php 
  (...)
  $stmt->execute($ids);
?>

(wieder habe ich es nicht getestet)

Stefs
quelle
7
Das ist eine interessante Lösung, und obwohl ich es vorziehe, über die IDs zu iterieren und PDO :: quote () aufzurufen, denke ich, dass der Index des '?' Platzhalter werden durcheinander gebracht, wenn andere Platzhalter zuerst an anderer Stelle in der Abfrage vorkommen, oder?
Andru
5
Ja, das wäre ein Problem. In diesem Fall können Sie jedoch benannte Parameter anstelle von? 's erstellen.
Stefs
9
Alte Frage, aber ich glaube, es ist erwähnenswert, dass das $foreachund bindValue()nicht erforderlich ist - einfach mit dem Array ausführen. ZB:$stmt->execute($ids);
Chris
2
Das Generieren der Platzhalter sollte folgendermaßen erfolgenstr_repeat('?,', count($array) - 1). '?';
Xeoncross
8
Nur ein Tipp für diejenigen, die es nicht wissen: Sie können keine benannten und unbenannten Parameter mischen. Wenn Sie in Ihrer Abfrage benannte Parameter verwenden, schalten Sie diese auf? 'S aus und erweitern Sie dann Ihren bindValue-Index-Offset, um die Position der IN? Mit der Position anzupassen, an der sie relativ zu Ihren anderen sind. params.
Justin
169

Für etwas schnelles:

//$db = new PDO(...);
//$ids = array(...);

$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks)");
$sth->execute($ids);
DonVaughn
quelle
7
Ausgezeichnet, ich hatte nicht daran gedacht, das Argument input_parameters auf diese Weise zu verwenden. Für diejenigen, deren Abfragen mehr Parameter als die IN-Liste haben, können Sie array_unshift und array_push verwenden, um die erforderlichen Argumente am Anfang und Ende des Arrays hinzuzufügen. Auch ich bevorzuge $input_list = substr(str_repeat(',?', count($ids)), 1);
Orca
7
Sie könnten es auch versuchen str_repeat('?,', count($ids) - 1) . '?'. Ein Funktionsaufruf weniger.
Orca
5
@erfling, dies ist eine vorbereitete Aussage, woher kommt die Injektion? Ich werde mehr als glücklich sein, Korrekturen vorzunehmen, wenn Sie dies mit einem tatsächlichen Beweis dafür belegen können.
DonVaughn
5
@erfling, ja, das ist richtig, und das Binden der Parameter ist genau das, was wir in diesem Beispiel tun, indem wir executeein Array von IDs
senden
5
Oh, in der Tat. Irgendwie hat die Tatsache übersehen, dass Sie das Array übergeben haben. Dies scheint in der Tat sicher und eine gute Antwort zu sein. Entschuldigen Sie.
Erfling
46

Ist es so wichtig, eine INAnweisung zu verwenden ? Versuchen Sie, FIND_IN_SETop zu verwenden .

Zum Beispiel gibt es in PDO eine solche Abfrage

SELECT * FROM table WHERE FIND_IN_SET(id, :array)

Dann müssen Sie nur noch ein Array von Werten binden, die wie dieses mit Komma implodiert sind

$ids_string = implode(',', $array_of_smth); // WITHOUT WHITESPACES BEFORE AND AFTER THE COMMA
$stmt->bindParam('array', $ids_string);

und es ist geschafft.

UPD: Wie einige Leute in Kommentaren zu dieser Antwort betonten, gibt es einige Probleme, die explizit angegeben werden sollten.

  1. FIND_IN_SETverwendet keinen Index in einer Tabelle und ist noch nicht implementiert - siehe diesen Datensatz im MYSQL-Bug-Tracker . Vielen Dank an @BillKarwin für den Hinweis.
  2. Sie können keine Zeichenfolge mit Komma als Wert des Arrays für die Suche verwenden. Es ist unmöglich, eine solche Zeichenfolge danach richtig zu analysieren, implodeda Sie das Kommasymbol als Trennzeichen verwenden. Vielen Dank an @VaL für den Hinweis.

Wenn Sie nicht stark von Indizes abhängig sind und keine Zeichenfolgen mit Komma für die Suche verwenden, ist meine Lösung viel einfacher, einfacher und schneller als die oben aufgeführten Lösungen.

Tim Tonkonogov
quelle
25
IN () kann einen Index verwenden und zählt als Bereichsscan. FIND_IN_SET () kann keinen Index verwenden.
Bill Karwin
1
Das ist ein Argument. Das wusste ich nicht. Auf jeden Fall gibt es in der Frage keine Leistungsanforderungen. Für nicht so große Tabellen ist es viel besser und sauberer als eine separate Klasse, um Abfragen mit unterschiedlicher Anzahl von Platzhaltern zu generieren.
Tim Tonkonogov
11
Ja, aber wer hat heutzutage einen nicht so großen Tisch? ;-)
Bill Karwin
3
Ein weiteres Problem bei diesem Ansatz ist, dass was passiert, wenn eine Zeichenfolge mit Komma enthalten ist? Zum Beispiel ... FIND_IN_SET(description,'simple,search')wird funktionieren, aber FIND_IN_SET(description,'first value,text, with coma inside')wird fehlschlagen. Also wird die Funktion "first value", "text", "with coma inside" statt gewünscht suchen "first value", "text, with coma inside"
VaL
33

Da ich viele dynamische Abfragen mache, ist dies eine super einfache Hilfsfunktion, die ich gemacht habe.

public static function bindParamArray($prefix, $values, &$bindArray)
{
    $str = "";
    foreach($values as $index => $value){
        $str .= ":".$prefix.$index.",";
        $bindArray[$prefix.$index] = $value;
    }
    return rtrim($str,",");     
}

Verwenden Sie es so:

$bindString = helper::bindParamArray("id", $_GET['ids'], $bindArray);
$userConditions .= " AND users.id IN($bindString)";

Gibt eine Zeichenfolge zurück :id1,:id2,:id3und aktualisiert auch Ihre $bindArrayBindungen, die Sie zum Ausführen Ihrer Abfrage benötigen. Einfach!

Programmierer
quelle
6
Dies ist eine viel bessere Lösung, da dadurch die Regel zur Bindung von Parametern nicht verletzt wird. Dies ist viel sicherer als Inline-SQL, wie es von einigen anderen hier vorgeschlagen wurde.
Dimitar Darazhanski
1
Großartigkeit. Elegant. Perfekt.
Ricalsin
17

Die Lösung von EvilRygy hat bei mir nicht funktioniert. In Postgres können Sie eine andere Problemumgehung durchführen:


$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (string_to_array(:an_array, ','))'
);
$stmt->bindParam(':an_array', implode(',', $ids));
$stmt->execute();
Sergey Galkin
quelle
Das funktioniert nicht : ERROR: operator does not exist: integer = text. Zumindest müssen Sie explizites Casting hinzufügen.
Collimarco
17

Ein sehr sauberer Weg für Postgres ist die Verwendung des Postgres-Arrays ("{}"):

$ids = array(1,4,7,9,45);
$param = "{".implode(', ',$ids)."}";
$cmd = $db->prepare("SELECT * FROM table WHERE id = ANY (?)");
$result = $cmd->execute(array($param));
ESCOBAR
quelle
es blockiert SQL-Injektion?
Fábio Zangirolami
1
@ FábioZangirolami es ist PDO, also ja.
ESCOBAR
JA, gU! nach 4 Jahren. Ihre Antwort war gut für mich, sehr einfach und effektiv. Vielen Dank!!!
Fábio Zangirolami
14

Hier ist meine Lösung:

$total_items = count($array_of_items);
$question_marks = array_fill(0, $total_items, '?');
$sql = 'SELECT * FROM foo WHERE bar IN (' . implode(',', $question_marks ). ')';

$stmt = $dbh->prepare($sql);
$stmt->execute(array_values($array_of_items));

Beachten Sie die Verwendung von array_values. Dies kann wichtige Bestellprobleme beheben.

Ich habe Arrays von IDs zusammengeführt und dann doppelte Elemente entfernt. Ich hatte so etwas wie:

$ids = array(0 => 23, 1 => 47, 3 => 17);

Und das schlug fehl.

Progrock
quelle
Dies war eine ausgezeichnete Lösung. Anders als beim Erstellen der Abfragezeichenfolge wird die Bindung korrekt verwendet. Ich kann dieses nur empfehlen.
Lindylead
Hinweis: Mit Ihrer Lösung können Sie Elemente an der Vorder- oder Rückseite des Arrays hinzufügen, um andere Bindungen einzuschließen. $original_array Elemente vor dem ursprünglichen Array hinzufügen : array_unshift($original_array, new_unrelated_item); Elemente nach dem ursprünglichen Array array_push($original_array, new_unrelated_item);hinzufügen : Wenn die Werte gebunden sind, werden die Elemente new_unrelated an den richtigen Stellen platziert. Auf diese Weise können Sie Array- und Nicht-Array-Elemente mischen. `
Lindylead
12

Ich habe PDO erweitert, um etwas Ähnliches zu tun, wie es Stefs vorschlägt, und es war auf lange Sicht einfacher für mich:

class Array_Capable_PDO extends PDO {
    /**
     * Both prepare a statement and bind array values to it
     * @param string $statement mysql query with colon-prefixed tokens
     * @param array $arrays associatve array with string tokens as keys and integer-indexed data arrays as values 
     * @param array $driver_options see php documention
     * @return PDOStatement with given array values already bound 
     */
    public function prepare_with_arrays($statement, array $arrays, $driver_options = array()) {

        $replace_strings = array();
        $x = 0;
        foreach($arrays as $token => $data) {
            // just for testing...
            //// tokens should be legit
            //assert('is_string($token)');
            //assert('$token !== ""');
            //// a given token shouldn't appear more than once in the query
            //assert('substr_count($statement, $token) === 1');
            //// there should be an array of values for each token
            //assert('is_array($data)');
            //// empty data arrays aren't okay, they're a SQL syntax error
            //assert('count($data) > 0');

            // replace array tokens with a list of value tokens
            $replace_string_pieces = array();
            foreach($data as $y => $value) {
                //// the data arrays have to be integer-indexed
                //assert('is_int($y)');
                $replace_string_pieces[] = ":{$x}_{$y}";
            }
            $replace_strings[] = '('.implode(', ', $replace_string_pieces).')';
            $x++;
        }
        $statement = str_replace(array_keys($arrays), $replace_strings, $statement);
        $prepared_statement = $this->prepare($statement, $driver_options);

        // bind values to the value tokens
        $x = 0;
        foreach($arrays as $token => $data) {
            foreach($data as $y => $value) {
                $prepared_statement->bindValue(":{$x}_{$y}", $value);
            }
            $x++;
        }

        return $prepared_statement;
    }
}

Sie können es so verwenden:

$db_link = new Array_Capable_PDO($dsn, $username, $password);

$query = '
    SELECT     *
    FROM       test
    WHERE      field1 IN :array1
     OR        field2 IN :array2
     OR        field3 = :value
';

$pdo_query = $db_link->prepare_with_arrays(
    $query,
    array(
        ':array1' => array(1,2,3),
        ':array2' => array(7,8,9)
    )
);

$pdo_query->bindValue(':value', '10');

$pdo_query->execute();
Chris
quelle
1
Ich habe den ersten Teil von Marks Kommentar angesprochen, aber wie er betonte, ist es immer noch nicht sicher, ob sich ein Token wie :arrayin einer Zeichenfolge in der Abfrage befindet.
Chris
4
Ein Hinweis für alle zukünftigen Leser: Diese Lösung sollte niemals verwendet werden. Asserts sind nicht für den Produktionscode bestimmt
Your Common Sense
1
YCS: Vielen Dank für das Feedback, das an Ihrer Meinung zum Ansatz außerhalb der Eignung von Behauptungen interessiert ist.
Chris
1
Die Idee ist ziemlich dieselbe, aber nur ohne Behauptungen und einfacher und expliziter - nicht als Ausnahme für einen einzelnen Fall, sondern als allgemeine Methode zum Erstellen jeder Abfrage. Jeder Platzhalter ist mit seinem Typ gekennzeichnet. Es macht Rätselraten (wie if (is_array($data))eines) unnötig und macht die Datenverarbeitung viel genauer.
Ihr gesunder Menschenverstand
1
An alle, die die Kommentare lesen: Das von @Your Common Sense erwähnte Problem wurde in Revision 4 behoben .
user2428118
11

Betrachtet man PDO: Vordefinierte Konstanten, gibt es kein PDO :: PARAM_ARRAY, das Sie benötigen würden, wie unter PDOStatement-> bindParam aufgeführt

bool PDOStatement :: bindParam (gemischter $ -Parameter, gemischte & $ Variable [, int $ Datentyp [, int $ Länge [, gemischte $ Treiberoptionen]])

Ich denke also nicht, dass es erreichbar ist.


quelle
1
Ich weiß nicht, ob das funktioniert. Ich würde vermuten, dass die implodierte Zeichenfolge in Anführungszeichen gesetzt wird.
Soulmerge
2
Sie haben Recht, die Anführungszeichen werden ausgeblendet, damit sie nicht funktionieren. Ich habe diesen Code entfernt.
11

Wenn Sie einen anderen Parameter haben, können Sie Folgendes tun:

$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$query = 'SELECT *
            FROM table
           WHERE X = :x
             AND id IN(';
$comma = '';
for($i=0; $i<count($ids); $i++){
  $query .= $comma.':p'.$i;       // :p0, :p1, ...
  $comma = ',';
}
$query .= ')';

$stmt = $db->prepare($query);
$stmt->bindValue(':x', 123);  // some value
for($i=0; $i<count($ids); $i++){
  $stmt->bindValue(':p'.$i, $ids[$i]);
}
$stmt->execute();
Daniel Miloca - Brasilien
quelle
Danke für die tolle Antwort. Dies war der einzige von allen, der tatsächlich für mich funktioniert hat. Ich habe jedoch 1 Fehler gesehen. Die Variable $ rs sollte $ stmt sein
Piet
11

Für mich besteht die sexier Lösung darin, ein dynamisches assoziatives Array zu erstellen und es zu verwenden

// A dirty array sent by user
$dirtyArray = ['Cecile', 'Gilles', 'Andre', 'Claude'];

// we construct an associative array like this
// [ ':name_0' => 'Cecile', ... , ':name_3' => 'Claude' ]
$params = array_combine(
    array_map(
        // construct param name according to array index
        function ($v) {return ":name_{$v}";},
        // get values of users
        array_keys($dirtyArray)
    ),
    $dirtyArray
);

// construct the query like `.. WHERE name IN ( :name_1, .. , :name_3 )`
$query = "SELECT * FROM user WHERE name IN( " . implode(",", array_keys($params)) . " )";
// here we go
$stmt  = $db->prepare($query);
$stmt->execute($params);
RousseauAlexandre
quelle
Es ist schwer sicher zu sein, ohne es in einem realen Szenario zu versuchen, aber es scheint in Ordnung zu sein. + 1
Anant Singh --- Lebendig zu sterben
9

Mir ist auch klar, dass dieser Thread alt ist, aber ich hatte ein einzigartiges Problem, bei dem ich beim Konvertieren des bald veralteten MySQL-Treibers in den PDO-Treiber eine Funktion erstellen musste, die dynamisch sowohl normale Parameter als auch INs daraus erstellen konnte param Array. Also habe ich das schnell gebaut:

/**
 * mysql::pdo_query('SELECT * FROM TBL_WHOOP WHERE type_of_whoop IN :param AND siz_of_whoop = :size', array(':param' => array(1,2,3), ':size' => 3))
 *
 * @param $query
 * @param $params
 */
function pdo_query($query, $params = array()){

    if(!$query)
        trigger_error('Could not query nothing');

    // Lets get our IN fields first
    $in_fields = array();
    foreach($params as $field => $value){
        if(is_array($value)){
            for($i=0,$size=sizeof($value);$i<$size;$i++)
                $in_array[] = $field.$i;

            $query = str_replace($field, "(".implode(',', $in_array).")", $query); // Lets replace the position in the query string with the full version
            $in_fields[$field] = $value; // Lets add this field to an array for use later
            unset($params[$field]); // Lets unset so we don't bind the param later down the line
        }
    }

    $query_obj = $this->pdo_link->prepare($query);
    $query_obj->setFetchMode(PDO::FETCH_ASSOC);

    // Now lets bind normal params.
    foreach($params as $field => $value) $query_obj->bindValue($field, $value);

    // Now lets bind the IN params
    foreach($in_fields as $field => $value){
        for($i=0,$size=sizeof($value);$i<$size;$i++)
            $query_obj->bindValue($field.$i, $value[$i]); // Both the named param index and this index are based off the array index which has not changed...hopefully
    }

    $query_obj->execute();

    if($query_obj->rowCount() <= 0)
        return null;

    return $query_obj;
}

Es ist noch nicht getestet, aber die Logik scheint da zu sein.

Hoffe, es hilft jemandem in der gleichen Position,

Edit: Nach einigen Tests fand ich heraus:

  • PDO mag nicht '.' in ihren Namen (was irgendwie dumm ist, wenn du mich fragst)
  • bindParam ist die falsche Funktion, bindValue ist die richtige Funktion.

Code auf Arbeitsversion bearbeitet.

Sammaye
quelle
8

Eine kleine Bearbeitung über den Code von Schnalle

<?php
$ids     = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids)-1, '?'));

$db   = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(' . $inQuery . ')'
);

foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();
?>

//implode(',', array_fill(0, count($ids)-1), '?')); 
//'?' this should be inside the array_fill
//$stmt->bindValue(($k+1), $in); 
// instead of $in, it should be $id
Aaron Angelo Vicuna
quelle
1
Ich musste -1 nach count ($ ids) entfernen, damit es für mich funktioniert, sonst würde immer ein Platzhalter fehlen.
Marcel Burkhard
7

Welche Datenbank verwenden Sie? In PostgreSQL verwende ich gerne ANY (Array). Um Ihr Beispiel wiederzuverwenden:

<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>

Leider ist dies ziemlich nicht tragbar.

In anderen Datenbanken müssen Sie Ihre eigene Magie erfinden, wie andere bereits erwähnt haben. Sie sollten diese Logik in eine Klasse / Funktion einfügen, um sie natürlich in Ihrem gesamten Programm wiederverwendbar zu machen. In den Kommentaren auf der mysql_querySeite zu PHP.NET finden Sie weitere Gedanken zum Thema und Beispiele für dieses Szenario.

Ryan Bair
quelle
4

Nachdem ich das gleiche Problem durchgearbeitet hatte, ging ich zu einer einfacheren Lösung (obwohl immer noch nicht so elegant wie es PDO::PARAM_ARRAYwäre):

gegeben das Array $ids = array(2, 4, 32):

$newparams = array();
foreach ($ids as $n => $val){ $newparams[] = ":id_$n"; }

try {
    $stmt = $conn->prepare("DELETE FROM $table WHERE ($table.id IN (" . implode(", ",$newparams). "))");
    foreach ($ids as $n => $val){
        $stmt->bindParam(":id_$n", intval($val), PDO::PARAM_INT);
    }
    $stmt->execute();

... und so weiter

Wenn Sie also ein Array mit gemischten Werten verwenden, benötigen Sie mehr Code, um Ihre Werte zu testen, bevor Sie den Typ param zuweisen:

// inside second foreach..

$valuevar = (is_float($val) ? floatval($val) : is_int($val) ? intval($val) :  is_string($val) ? strval($val) : $val );
$stmt->bindParam(":id_$n", $valuevar, (is_int($val) ? PDO::PARAM_INT :  is_string($val) ? PDO::PARAM_STR : NULL ));

Aber ich habe diesen nicht getestet.

alan_mm
quelle
4

Wie ich weiß, gibt es keine Möglichkeit, ein Array in eine PDO-Anweisung zu binden.

Es gibt aber 2 gängige Lösungen:

  1. Verwenden Sie Positionsplatzhalter (?,?,?,?) Oder benannte Platzhalter (: id1 ,: id2 ,: id3).

    $ whereIn = implode (',', array_fill (0, count ($ ids), '?'));

  2. Array früher zitieren

    $ whereIn = array_map (array ($ db, 'quote'), $ ids);

Beide Optionen sind gut und sicher. Ich bevorzuge die zweite, weil sie kürzer ist und ich var_dump-Parameter verwenden kann, wenn ich sie brauche. Mit Platzhaltern müssen Sie Werte binden und am Ende ist Ihr SQL-Code derselbe.

$sql = "SELECT * FROM table WHERE id IN ($whereIn)";

Und das Letzte und Wichtige für mich ist die Vermeidung des Fehlers "Anzahl der gebundenen Variablen stimmt nicht mit Anzahl der Token überein"

Doctrine ist ein großartiges Beispiel für die Verwendung von Positionsplatzhaltern, nur weil es die interne Kontrolle über eingehende Parameter hat.

Oleg Matei
quelle
4

Wenn die Spalte nur Ganzzahlen enthalten kann, können Sie dies wahrscheinlich ohne Platzhalter tun und die IDs direkt in die Abfrage einfügen. Sie müssen nur alle Werte des Arrays in Ganzzahlen umwandeln. So was:

$listOfIds = implode(',',array_map('intval', $ids));
$stmt = $db->prepare(
    "SELECT *
     FROM table
     WHERE id IN($listOfIds)"
);
$stmt->execute();

Dies sollte für keine SQL-Injection anfällig sein.

Kodos Johnson
quelle
3

Hier ist meine Lösung. Ich habe auch die PDO-Klasse erweitert:

class Db extends PDO
{

    /**
     * SELECT ... WHERE fieldName IN (:paramName) workaround
     *
     * @param array  $array
     * @param string $prefix
     *
     * @return string
     */
    public function CreateArrayBindParamNames(array $array, $prefix = 'id_')
    {
        $newparams = [];
        foreach ($array as $n => $val)
        {
            $newparams[] = ":".$prefix.$n;
        }
        return implode(", ", $newparams);
    }

    /**
     * Bind every array element to the proper named parameter
     *
     * @param PDOStatement $stmt
     * @param array        $array
     * @param string       $prefix
     */
    public function BindArrayParam(PDOStatement &$stmt, array $array, $prefix = 'id_')
    {
        foreach($array as $n => $val)
        {
            $val = intval($val);
            $stmt -> bindParam(":".$prefix.$n, $val, PDO::PARAM_INT);
        }
    }
}

Hier ist ein Beispiel für die Verwendung des obigen Codes:

$idList = [1, 2, 3, 4];
$stmt = $this -> db -> prepare("
  SELECT
    `Name`
  FROM
    `User`
  WHERE
    (`ID` IN (".$this -> db -> CreateArrayBindParamNames($idList)."))");
$this -> db -> BindArrayParam($stmt, $idList);
$stmt -> execute();
foreach($stmt as $row)
{
    echo $row['Name'];
}

Lass mich wissen was du denkst

Lippai Zoltan
quelle
Ich habe vergessen zu erwähnen, dass dies auf der Antwort von user2188977 unten basiert.
Lippai Zoltan
Ich bin nicht sicher, was getOne () ist, es scheint nicht Teil von PDO zu sein. Ich habe es nur in PEAR gesehen. Was macht es genau?
Lippai Zoltan
@YourCommonSense können Sie Ihre benutzerdefinierte Funktion als Antwort veröffentlichen?
Nichtigkeit
Ich würde vorschlagen, den Datentyp BindArrayParamim assoziativen Array zu übergeben, da Sie dies anscheinend auf ganze Zahlen beschränken.
Ian Brindley
2

Es ist nicht möglich, ein solches Array in PDO zu verwenden.

Sie müssen für jeden Wert eine Zeichenfolge mit einem Parameter erstellen (oder verwenden?), Zum Beispiel:

:an_array_0, :an_array_1, :an_array_2, :an_array_3, :an_array_4, :an_array_5

Hier ist ein Beispiel:

<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = join(
    ', ',
    array_map(
        function($index) {
            return ":an_array_$index";
        },
        array_keys($ids)
    )
);
$db = new PDO(
    'mysql:dbname=mydb;host=localhost',
    'user',
    'passwd'
);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN('.$sqlAnArray.')'
);
foreach ($ids as $index => $id) {
    $stmt->bindValue("an_array_$index", $id);
}

Wenn Sie weiterhin verwenden möchten bindParam, können Sie stattdessen Folgendes tun:

foreach ($ids as $index => $id) {
    $stmt->bindParam("an_array_$index", $ids[$id]);
}

Wenn Sie ?Platzhalter verwenden möchten , können Sie dies folgendermaßen tun:

<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = '?' . str_repeat(', ?', count($ids)-1);
$db = new PDO(
    'mysql:dbname=dbname;host=localhost',
    'user',
    'passwd'
);
$stmt = $db->prepare(
    'SELECT *
     FROM phone_number_lookup
     WHERE country_code IN('.$sqlAnArray.')'
);
$stmt->execute($ids);

Wenn Sie nicht wissen, ob $idses leer ist, sollten Sie es testen und den Fall entsprechend behandeln (ein leeres Array zurückgeben oder ein Nullobjekt zurückgeben oder eine Ausnahme auslösen, ...).

Pedro Amaral Couto
quelle
0

Ich habe es etwas weiter gebracht, um die Antwort näher an die ursprüngliche Frage zu bringen, Platzhalter zum Binden der Parameter zu verwenden.

Diese Antwort muss zwei Schleifen durch das Array führen, um in der Abfrage verwendet zu werden. Es löst jedoch das Problem, andere Spaltenplatzhalter für selektivere Abfragen zu haben.

//builds placeholders to insert in IN()
foreach($array as $key=>$value) {
    $in_query = $in_query . ' :val_' . $key . ', ';
}

//gets rid of trailing comma and space
$in_query = substr($in_query, 0, -2);

$stmt = $db->prepare(
    "SELECT *
     WHERE id IN($in_query)";

//pind params for your placeholders.
foreach ($array as $key=>$value) {
    $stmt->bindParam(":val_" . $key, $array[$key])
}

$stmt->execute();
Joseph_J
quelle
0

Sie haben zuerst die Nummer "?" in Abfrage und dann durch ein "für" Sendeparameter wie folgt:

require 'dbConnect.php';
$db=new dbConnect();
$array=[];
array_push($array,'value1');
array_push($array,'value2');
$query="SELECT * FROM sites WHERE kind IN (";

foreach ($array as $field){
    $query.="?,";
}
$query=substr($query,0,strlen($query)-1);
$query.=")";
$tbl=$db->connection->prepare($query);
for($i=1;$i<=count($array);$i++)
    $tbl->bindParam($i,$array[$i-1],PDO::PARAM_STR);
$tbl->execute();
$row=$tbl->fetchAll(PDO::FETCH_OBJ);
var_dump($row);
Ali Chegini
quelle