web-dev-qa-db-fra.com

Obtention d'une chaîne de requête PDO avec des paramètres liés sans l'exécuter

Est-il possible d'obtenir une chaîne de requête à partir d'un objet PDO avec des paramètres liés sans l'exécuter au préalable? J'ai un code similaire au suivant (où $ dbc est l'objet PDO):

$query = 'SELECT * FROM users WHERE username = ?';
$result = $dbc->prepare($query);
$username = 'bob';
$result->bindParam(1, $username);
echo $result->queryString;

Actuellement, cela fera écho à une instruction SQL comme: "SELECT * FROM utilisateurs WHERE username =?". Cependant, j'aimerais avoir le paramètre lié inclus pour qu'il ressemble à: 'SELECT * FROM utilisateurs WHERE username =' bob '". Y a-t-il un moyen de le faire sans l'exécuter ou remplacer les points d'interrogation par les paramètres par quelque chose comme preg_replace?

33
VirtuosiMedia

En bref: non. Voir Obtention d'une chaîne de requête SQL brute à partir d'instructions préparées par PDO

Si vous souhaitez simplement l'imiter, essayez:

echo preg_replace('?', $username, $result->queryString);
16
Crescent Fresh

Il s'agit d'une variation générique de la technique d'expression rationnelle , pour un tableau numéroté de paramètres.

C'était un peu plus paranoïaque que la réponse acceptée parce que citer tout, chiffres compris, m'a mordu dans le dos plus d'une fois; dans MySQL ainsi qu'ailleurs1, '123' est moins que '13'. Il en va de même pour 'NULL', Qui est pas NULL, et 'false', Qui est évidemment vrai .

On m'a maintenant fait remarquer que je n'étais pas assez paranoïaque :-), et ma technique de remplacement ? ("#\\?#") Était naïve, car la requête source peut contenir des points d'interrogation sous forme de texte dans le corps pour une raison quelconque:

$query = "SELECT CONCAT('Is ', @value, ' ', ?, '? ', 
       IF(@check != ? AND 123 > '13', 'Yes!', 'Uh, no?')) 
       ;

$values = array('correct', false, 123);

// Expecting valid SQL, selecting 'correct' if check is not false for 123
// and answering Yes if @check is true.

Production:

SELECT CONCAT('Is ', @value, ' ', 'correct', '? ',
   IF(check != false AND 123 > '13', 'Yes!', 'Uh, no?')) 
   ;

Is THIS_TEST correct? Yes!

Ma mise en œuvre plus simple aurait levé une exception en voyant trop de points d'interrogation. Une implémentation encore plus simple aurait renvoyé quelque chose comme

Is THIS_TEST correcttrue Uh, no

Voici donc la fonction modifiée. [~ # ~] note [~ # ~] : Je sais qu'il y a les choses que les regex ne devraient pas faire . Je ne prétends pas que cette fonction fonctionne dans tous les cas et pour tous les cas frontaliers. Je prétends que c'est une tentative raisonnable. N'hésitez pas à commenter ou envoyer un e-mail avec des cas de test non fonctionnels.

function boundQuery($db, $query, $values) {
    $ret = preg_replace_callback(
        "#(\\?)(?=(?:[^']|['][^']*')*$)#ms",
        // Notice the &$values - here, we want to modify it.
        function($match) use ($db, &$values) {
            if (empty($values)) {
                throw new PDOException('not enough values for query');
            }
            $value  = array_shift($values);

            // Handle special cases: do not quote numbers, booleans, or NULL.
            if (is_null($value)) return 'NULL';
            if (true === $value) return 'true';
            if (false === $value) return 'false';
            if (is_numeric($value)) return $value;

            // Handle default case with $db charset
            return $db->quote($value);
        },
        $query
    );
    if (!empty($values)) {
        throw new PDOException('not enough placeholders for values');
    }
    return $ret;
}

On pourrait aussi étendre PDOStatement afin de fournir une méthode $stmt->boundString($values).


(1) puisque c'est PHP, avez-vous déjà essayé $a = 1...1; print $a;?

7
LSerni