php pdo bereiten sich wiederholende Variablen vor

Lesezeit: 5 Minuten

php pdo bereiten sich wiederholende Variablen vor
Shaokan

Ist es beim Schreiben einer pdo-Anweisung möglich, den Wert einer Variablen zu wiederholen? Ich meine:

$query = "UPDATE users SET firstname = :name WHERE firstname = :name";
$stmt = $dbh -> prepare($query);
$stmt -> execute(array(":name" => "Jackie"));

Bitte beachten Sie, dass ich den Nameholder “:name” wiederhole, während ich den Wert nur einmal angebe. Wie kann ich das zum Laufen bringen?

1645705445 954 php pdo bereiten sich wiederholende Variablen vor
Johannes

Die einfache Antwort ist: Sie können nicht. PDO verwendet eine Abstraktion für vorbereitete Anweisungen, die einige Einschränkungen aufweist. Leider ist dies eine, die Sie mit so etwas umgehen müssen

$query = "UPDATE users SET firstname = :name1 WHERE firstname = :name2";
$stmt = $dbh -> prepare($query);
$stmt -> execute(array(":name1" => "Jackie", ":name2" => "Jackie"));

In bestimmten Fällen, wie z. B. emulierte vorbereitete Anweisungen mit einigen Versionen des PDO/MySQL-Treibers, werden wiederholt benannte Parameter unterstützt; Sie sollten sich jedoch nicht darauf verlassen, da es spröde ist (es kann beispielsweise dazu führen, dass Upgrades mehr Arbeit erfordern).

Wenn Sie das mehrfache Auftreten eines benannten Parameters unterstützen möchten, können Sie PDO und PDOStatement jederzeit erweitern (durch klassische Vererbung oder durch Zusammensetzung) oder einfach PDOStatement und Ihre Klasse als Anweisungsklasse festlegen, indem Sie die PDO::ATTR_STATEMENT_CLASS Attribut. Das erweiterte PDOStatement (bzw PDO::prepare) könnte die benannten Parameter extrahieren, nach Wiederholungen suchen und automatisch Ersetzungen generieren. Es würde auch diese Duplikate aufzeichnen. Die Binde- und Ausführungsmethoden würden, wenn sie einen benannten Parameter übergeben, testen, ob der Parameter wiederholt wird, und den Wert an jeden Ersetzungsparameter binden.

Hinweis: Das folgende Beispiel ist ungetestet und enthält wahrscheinlich Fehler (einige im Zusammenhang mit der Anweisungsanalyse sind in Codekommentaren vermerkt).

class PDO_multiNamed extends PDO {
    function prepare($stmt) {
        $params = array_count_values($this->_extractNamedParams());
        # get just named parameters that are repeated
        $repeated = array_filter($params, function ($count) { return $count > 1; });
        # start suffixes at 0
        $suffixes = array_map(function ($x) {return 0;}, $repeated);
        /* Replace repeated named parameters. Doesn't properly parse statement,
         * so may replacement portions of the string that it shouldn't. Proper
         * implementation left as an exercise for the reader.
         *
         * $param only contains identifier characters, so no need to escape it
         */
        $stmt = preg_replace_callback(
            '/(?:' . implode('|', array_keys($repeated)) . ')(?=\W)/', 
            function ($matches) use (&$suffixes) {
                return $matches[0] . '_' . $suffixes[$matches[0]]++;
            }, $stmt);
        $this->prepare($stmt, 
                       array(
                           PDO::ATTR_STATEMENT_CLASS => array('PDOStatement_multiNamed', array($repeated)))
            );
    }

    protected function _extractNamedParams() {
        /* Not actually sufficient to parse named parameters, but it's a start.
         * Proper implementation left as an exercise.
         */
        preg_match_all('/:\w+/', $stmt, $params);
        return $params[0];
    }
}

class PDOStatement_multiNamed extends PDOStatement {
    protected $_namedRepeats;

    function __construct($repeated) {
        # PDOStatement::__construct doesn't like to be called.
        //parent::__construct();
        $this->_namedRepeats = $repeated;
    }

    /* 0 may not be an appropriate default for $length, but an examination of
     * ext/pdo/pdo_stmt.c suggests it should work. Alternatively, leave off the
     * last two arguments and rely on PHP's implicit variadic function feature.
     */
    function bindParam($param, &$var, $data_type=PDO::PARAM_STR, $length=0, $driver_options=array()) {
        return $this->_bind(__FUNCTION__, $param, func_get_args());
    }

    function bindValue($param, $var, $data_type=PDO::PARAM_STR) {
        return $this->_bind(__FUNCTION__, $param, func_get_args());
    }

    function execute($input_parameters=NULL) {
        if ($input_parameters) {
            $params = array();
            # could be replaced by array_map_concat, if it existed
            foreach ($input_parameters as $name => $val) {
                if (isset($this->_namedRepeats[$param])) {
                    for ($i=0; $i < $this->_namedRepeats[$param], ++$i) {
                        $params["{$name}_{$i}"] = $val;
                    }
                } else {
                    $params[$name] = $val;
                }
            }
            return parent::execute($params);
        } else {
            return parent::execute();
        }
    }

    protected function _bind($method, $param, $args) {
        if (isset($this->_namedRepeats[$param])) {
            $result = TRUE;
            for ($i=0; $i < $this->_namedRepeats[$param], ++$i) {
                $args[0] = "{$param}_{$i}";
                # should this return early if the call fails?
                $result &= call_user_func_array("parent::$method", $args);
            }
            return $result;
        } else {
            return call_user_func_array("parent::$method", $args);
        }
    }
}

  • Ich hatte noch nie Probleme, die gleiche Liste in einer zu wiederholen ON DUPLICATE KEY UPDATE Aussage…

    – jeroen

    29. September 2011 um 22:30 Uhr

  • Tatsächlich hängt es ein wenig vom PDO-Treiber ab, Sie sollten sich nicht darauf verlassen, dass er funktioniert.

    – Johannes

    29. September 2011 um 22:31 Uhr

  • Interessant, bei mir hat es immer funktioniert. Kennen Sie Unterlagen zu diesem Thema?

    – jeroen

    29. September 2011 um 22:35 Uhr


  • Ich müsste es noch einmal testen … es gibt zwei Hauptfälle: a) Datenbanken, die benannte Parameter mögen (Oracle DB und andere) b) Datenbanken, die nur ordinale Parameter unterstützen (mit ? als Platzhalter, wie MySQL) … und dann mit MySQL verwendet PDO standardmäßig eine vollständige Emulation vorbereiteter Anweisungen … Ich erinnere mich nicht, wie diese Dinge in diesem Fall zusammenspielen, und kenne keine gute Dokumentation. Aber ich weiß, dass es solche Probleme in einigen Kombinationen gibt.

    – Johannes

    29. September 2011 um 22:38 Uhr

  • Wenn Sie einstellen PDO::ATTR_EMULATE_PREPARES auf true, dann unterstützt MySQL mehrere Parameter mit demselben Namen (weil die Vorbereitung auf der PHP-Site emuliert/ersetzt wird)

    – Petah

    1. September 2014 um 22:54 Uhr

In meinem Fall trat dieser Fehler auf, als ich von dblib freedts zu sqlsrv PDO-Treiber wechselte. Der Dblib-Treiber hat doppelte Parameternamen ohne Fehler verarbeitet. Ich habe ziemlich komplizierte dynamische Abfragen mit vielen Unions und vielen duplizierten Parametern, also habe ich den folgenden Helfer als Problemumgehung verwendet:

function prepareMsSqlQueryParams($query, $params): array
{
    $paramsCount = [];
    $newParams = [];
    $pattern = '/(:' . implode('|:', array_keys($params)) . ')/';

    $query = preg_replace_callback($pattern, function ($matches) use ($params, &$newParams, &$paramsCount) {
        $key = ltrim($matches[0], ':');
        if (isset($paramsCount[$key])) {
            $paramsCount[$key]++;
            $newParams[$key . $paramsCount[$key]] = $params[$key];
            return $matches[0] . $paramsCount[$key];
        } else {
            $newParams[$key] = $params[$key];
            $paramsCount[$key] = 0;
            return $matches[0];
        }
    }, $query);

    return [$query, $newParams];
}

Dann kannst du es so verwenden:

$query = "UPDATE users SET firstname = :name WHERE firstname = :name";
$params = [":name" => "Jackie"];
// It will return "UPDATE users SET firstname = :name WHERE firstname = :name1"; with appropriate parameters array
list($query, $params) = prepareMsSqlQueryParams($query, $params);
$stmt = $dbh->prepare($query);
$stmt->execute(params);

843070cookie-checkphp pdo bereiten sich wiederholende Variablen vor

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy