Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PDO: Passing extra parameters to a prepared statment than needed

Can you send more parameters than needed to a prepared statement using PDO with no undesired side effects?

That mights seem like a strange question but I ask because I have 4 queries in a row which all use similar and different parameters. The relevant parts of the queries:

1st (select, different table to others):
WHERE threadID = :tid

2nd (select):
WHERE user_ID = :u_ID AND thread_ID = :tid

3rd (update if 2nd was successful):
SET time = :current_time WHERE user_ID = :u_ID AND thread_ID = :tid

4th (insert if 2nd was unsuccessful):
VALUES (:u_ID, :tid, :current_time)

Can I declare one array with the three parameters at the beginning and use it for all 4 queries?

To sort out any confusion, the queries would be executed seperately. It is the parameters variable being reused and so that would mean some queries would receive parameters they don't need. So something like:

$parameters = array(':tid' => $tid, ':u_ID' => $u_ID, ':current_time' => $time);

$1st = $db->prepare($query1);
$1st->execute($parameters);

$2nd = $db->prepare($query2);
$2nd->execute($parameters);

$3rd = $db->prepare($query3);
$3rd->execute($parameters);

$4th = $db->prepare($query4);
$4th->execute($parameters);

If I can, should I? Will this slow down or cause security flaws to my database or scripts?

If I can make this question a bit clearer, please ask.

Thank you!

like image 795
joe92 Avatar asked Nov 21 '12 11:11

joe92


3 Answers

Perhaps the documentation has been updated since this question was first asked, but now it is quite clearly stated "No"

You cannot bind more values than specified; if more keys exist in input_parameters than in the SQL specified in the PDO::prepare(), then the statement will fail and an error is emitted.

These answers should be useful in filtering out the extra parameters.

like image 191
Jeff Puckett Avatar answered Nov 03 '22 02:11

Jeff Puckett


I know this is already answered and it's only asking about whether you can send extra params, but I thought people might arrive at this question, and want to know how to get around this limitation. Here's the solution I use:

$parameters = array('tid' => $tid, 'u_ID' => $u_ID, 'current_time' => $time);

$1st = $db->prepare($query1);
$1st->execute(array_intersect_key($parameters, array_flip(array('tid'))));

$2nd = $db->prepare($query2);
$2nd->execute(array_intersect_key($parameters, array_flip(array('u_ID', 'tid'))));

$3rd = $db->prepare($query3);
$3rd->execute(array_intersect_key($parameters, array_flip(array('u_ID', 'tid', 'current_time'))));

$4th = $db->prepare($query4);
$4th->execute(array_intersect_key($parameters, array_flip(array('u_ID', 'tid', 'current_time'))));

That array_interset_key and array_flip maneuver could be extracted to its own function, like:

function filter_fields($params,$field_names) {
    return array_intersect_key($params, array_flip($field_names))
}

I just haven't got around to it yet.

The function flips your array of key names, so you have an array with no values, but the right keys. Then intersect filters the first array so you only have the keys that are in both arrays (in this case, only the ones in your array_flipped array). But you get the values for the original array (not the empties). So you make one array of parameters, but specify which params are actually sent to PDO.

So, with the function, you'd do:

$parameters = array('tid' => $tid, 'u_ID' => $u_ID, 'current_time' => $time);

$1st = $db->prepare($query1);
$1st->execute(filter_fields($parameters, array('tid')));

$2nd = $db->prepare($query2);
$2nd->execute(filter_fields($parameters, array('u_ID', 'tid')));

$3rd = $db->prepare($query3);
$3rd->execute(filter_fields($parameters, array('u_ID', 'tid', 'current_time')));

$4th = $db->prepare($query4);
$4th->execute(filter_fields($parameters, array('u_ID', 'tid', 'current_time')));

If you have PHP 5.4, you can use the square bracket array syntax, to make it even cooler:

$parameters = array('tid' => $tid, 'u_ID' => $u_ID, 'current_time' => $time);

$1st = $db->prepare($query1);
$1st->execute(filter_fields($parameters, ['tid']));

$2nd = $db->prepare($query2);
$2nd->execute(filter_fields($parameters, ['u_ID', 'tid']));

$3rd = $db->prepare($query3);
$3rd->execute(filter_fields($parameters, ['u_ID', 'tid', 'current_time']));

$4th = $db->prepare($query4);
$4th->execute(filter_fields($parameters, ['u_ID', 'tid', 'current_time']));
like image 42
eimajenthat Avatar answered Nov 03 '22 02:11

eimajenthat


I got a chance to test my question, and the answer is you cannot send more parameters than the query uses. You get the following error:

PDOException Object
(
    [message:protected] => SQLSTATE[HY093]: Invalid parameter number: parameter was not defined
    [string:Exception:private] => 
    [code:protected] => HY093
    [file:protected] => C:\Destination\to\file.php
    [line:protected] => line number
    [trace:Exception:private] => Array
        (
            [0] => Array
                (
                    [file] => C:\Destination\to\file.php
                    [line] => line number
                    [function] => execute
                    [class] => PDOStatement
                    [type] => ->
                    [args] => Array
                        (
                            [0] => Array
                                (
                                    [:u_ID] => 1
                                    [:tid] => 1
                                    [:current_time] => 1353524522
                                )

                        )

                )

            [1] => Array
                (
                    [file] => C:\Destination\to\file.php
                    [line] => line number
                    [function] => function name
                    [class] => class name
                    [type] => ->
                    [args] => Array
                        (
                            [0] => SELECT
                                                column
                                            FROM
                                                table
                                            WHERE
                                                user_ID  = :u_ID AND
                                                thread_ID = :tid
                            [1] => Array
                                (
                                    [:u_ID] => 1
                                    [:tid] => 1
                                    [:current_time] => 1353524522
                                )

                        )

                )

        )

    [previous:Exception:private] => 
    [errorInfo] => Array
        (
            [0] => HY093
            [1] => 0
        )

)

I don't know a huge amount about PDO, hence my question, but I think that because :current_time is sent but not used and the error message is "Invalid parameter number: parameter was not defined" you cannot send extra parameters which are not used.

Additionally the error code HY093 is generated. Now I can't seem to find any documentation explaining PDO codes anywhere, however I came across the following two links specifically about HY093:
What is PDO Error HY093
SQLSTATE[HY093]

It seems HY093 is generated when you incorrectly bind parameters. This must be happening here because I am binding too many parameters.

like image 41
joe92 Avatar answered Nov 03 '22 02:11

joe92