I just recently started using PDO, not that much experience with it, but I recently crashed into an annoying problem.
I am trying to use bindValue with a fetch statement to retrieve informatie from a database. The variables I get are from a GET, from a previous page. Everything is going wel except for the fact that it is not assigning, I guess, a right value to one of the bindValue.
PDO:
$stmt = $dbconnect->prepare('SELECT * FROM :table WHERE id=:id');
$stmt->bindValue(':table', $table);
$stmt->bindValue(':id', $id);
$stmt->execute();
$row = $stmt->fetch();
I know the difference between bindValue and bindParam. The code is working fine when I hardcode the table value. I have been banging my head against a wall for a short hour now but I can't seem to figure it out. Could anyone beside giving me the correct syntax please explain what went wrong with my thinking because at this point I cannot think of a reason, besides maybe the misinterpretation of the string value, why this is going wrong.
Also for in the future I would like to know the precise content of the SQL command. I tried doing this:
$SQL = 'SELECT * FROM :table WHERE id=:id';
$stmt = $dbconnect->prepare($SQL);
$stmt->bindValue(':table', $table);
$stmt->bindValue(':id', $id);
$stmt->execute();
$row = $stmt->fetch();
But this won't bind the variable values to the SQL variable. Your help is much appreciated!
EDIT:
I noticed my post is a duplicate from a FAQ post: FAQ. So my question has been answered however my insight in PDO is not enough to undertand it. Could anyone please explain what happens with the next line of code and why this works, opbtained from the posted link!
$field = "`".str_replace("`","``",$field)."`";
$sql = "SELECT * FROM t ORDER BY $field";
Answer
Thanks to silkfire I came up with fix. Before inserting the SQL string just add the string content into the SQL string:
$SQL = 'SELECT * FROM '.$table.' WHERE id=:id';
PDO does not allow table names or column names to be placeholders. Just create the query with concatenation instead, but make sure the user supplies only valid values. This should be safe.
Related
From experience and also having been told constantly the benefits of using prepared statements and binding my parameters, I have constantly used those two techniques in my code, however I would like to understand exactly the purpose of each of those two techiques:
From my understanding of prepared statements:
$sql = "SELECT * FROM myTable WHERE id = ".$id;
$stmt = $conn->prepare($sql);
$stmt->execute();
The previous code should create a sort of a buffer in the database with the query I proposed. Now FROM MY UNDERSTANDING (and I could be very wrong), the previous code is insecure, because the string $sql could be anything depending on what $id actually is, and if $id = 1; DROP TABLE myTable;--, I would be inserting a malicious query even though I have a prepared statement.
FROM MY UNDERSTANDING this is where binding my parameters com in. If I do the following instead:
$sql = "SELECT * FROM myTable WHERE id = :id";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':id', $id);
$stmt->execute();
The database should know exactly all the parts of the sql statement before hand:
SELECT these columns: * FROM myTable and WHERE id = "a variable that was input by the user", and if "a variable that was input by the user" != a variable, the query fails.
I have been told by some my understanding is correct, and by others that it is false, could someone please let me know if I am wrong, correct, or missing something? And elaborate as much as you want, all feedback is greatly appreciated!
You're correct that the first case is insecure. It's important to understand though, that preparing a statement only has value if you are using variable data, and/or executing the same query repeatedly. If you are executing plain statements with no variables, you could simply do this:
$sql = "SELECT * from myTable WHERE this_column IS NOT NULL";
$result = $conn->query($sql);
And end up with a PDOStatement object to work with, just like when you use PDO::exec().
For your second case, again, you're largely correct. What's happening is the variable passed to the database is escaped and quoted (unless you specify otherwise with the third argument to PDOStatement::bindParam(), it's sent as a string which is fine for most cases.) So, the query won't "fail" if bad data is sent. It behaves exactly as if you had passed a valid number that didn't exist as an ID in the database. There are, of course, some edge cases where you are still vulnerable even with a correctly prepared statement.
Also, to make life easier, you can use prepared statements like this, to do implicit binding:
$sql = "SELECT * FROM myTable WHERE id = :id";
$stmt = $conn->prepare($sql);
$stmt->execute([":id"=>$id]);
Or even like this, with un-named parameters:
$sql = "SELECT * FROM myTable WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->execute([$id]);
Naturally, most of this has been explained in the comments while I was typing up the answer!
I've been urging to know what is the difference between using bindValue and execute(array()) thing-y.
Well let's say I have this example of code
$query = $db->prepare("SELECT embedded_page.uid FROM embedded_page WHERE fbp_id = :fbp_id");
$query->bindValue(":fbp_id", $fbp_id, PDO::PARAM_INT);
$query->execute();
What is the difference between this one?
$query = $db->prepare('SELECT `embedded_page`.`uid`, `embedded_page`.`ticket_id`, `embedded_page`.`event_table` FROM `embedded_page` WHERE `fbp_id` = ?');
$query->execute(array($fbp_id));
Regardless of saving a line of code.
A help would be appreciated.
It is already self explained with your example, That bindValue validate input data type in form of specially defined PDO::PARAM_INT or similar. While in second example no such condition in place as prepare using ?. (Some internal validation may be done by engine assuming for string, int and float types.). Also in case of more variables as place holders in query first form is easier to understand.
I've always used PDO statements, but for some reason I can't persuade the server guy to install PDO for php, but I do have MySQLi, I have no clue what I'm doing wrong, I do not get a connection error and I do not get a query error no matter how I try to output one. Here's what I'm doing.
include 'MySQLiConnect.php';
if($stmt = $mysqli->prepare("SELECT * FROM zipCodeTable WHERE zip_code = ?")){
$stmt->bind_param("s", '07110');
$stmt->execute();
$stmt->bind_result($resultsArray);
$stmt->fetch();
foreach($resultsArray as $columnData){
$matchingZipcode = $columnData['zip_code'];
$matchingTimezone = $columnData['time_zone'];
}
$stmt->close();
}
echo $matchingZipcode.', '.$matchingTimezone;
This is basically just to confirm a users zipcode, never used MySQLi prepared statements before, I tryed to do it straight from the manual, not sure what I'm doing wrong. Thank you for taking the time to read this.
You're trying to "bind" a literal string. You can't do this. You must bind a variable.
Change
$stmt->bind_param("s", '07110');
To
$string = '07110';
$stmt->bind_param("s", $string);
Also, when you bind a result you must provide a variable for each field returned.
For example:
$stmt->bind_result($zipCode, $timeZone);
This is slightly problematic when using SELECT *. You might be interested in checking out this comment for how you might want to go about it: http://www.php.net/manual/en/mysqli-stmt.bind-result.php#85470
So, here's the relevant code from my page. It connects to a sqlite3 database through PDO which I update through forms on the page. I have other sqlite statements, like INSERTS and UPDATES (that does use WHERE id=:id) that work no problem. This DELETE one does not, however. I do have all the code in a try catch block on my page (which is how I got the error, if you were wondeing) but I figured I can omit it here.
Thanks for the help!
<?php
$db = new PDO("sqlite:osuat.sqlite3");
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$id = $_POST['id'];
$update = "DELETE FROM pages
WHERE id=:id";
$stmt = $db->prepare($update);
$stmt->bindParam(':id', $id);
$stmt->execute();
?>
Try adding PDO::PARAM_INT to the bind_param method, to make sure that the value being sent is an INT (which I'm assuming your ID field is) i.e.,
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
Echo the actual sql statement and die prior to actually running it. Then inspect and run the statement directly. I suspect $_POST['id'] doesn't contain what you think it does.
I finally figured it out. In my actual page, I have a bunch of if-else constructs in order to construct the correct $update string. I (wrongly) figured that I could just use bindParam() at the end without paying heed to how many bindParam()s each update statement would need. So, for my DELETE FROM pages WHERE id=:id, it was being supplied a whole bunch of other parameters only used in other $update strings, not just :id.
Its my fault for not including the entire source, I'm sure someone here would have caught it right away, but many thanks to duellsy, he/she led me on the right path looking for ways to log the actual SQL statement. In the end, using stmt->debugDumpParams(); helped me figure what I was doing wrong.
Try writing the DELETE command in one line.
I am trying to write a keyword search using PDO prepared statements. Ideally, I'd like to make use of the LIKE operator to search for a substring inside the field value. Here is my code:
$statement = $this->db->prepare("select * from whatever where title like ? or author like ?");
$statement->execute(array("%$titleKeyword%","%$authorKeyword%"));
$rows = $statement->fetchAll(PDO::FETCH_ASSOC);
Unfortunately, $rows is always empty when I try this. However, if I copy the SQL into phpMyAdmin, and substitute '%keyword%' for each of the ? symbols, it works fine (I get results when the keyword used exists).
I have also tried the following code:
$statement = $this->db->prepare("select * from whatever where title like :titleKeyword or author like :authorKeyword");
$statement->bindValue(":titleKeyword", '%'.$titleKeyword.'%', PDO::PARAM_STR);
$statement->bindValue(":authorKeyword", '%'.$authorKeyword.'%', PDO::PARAM_STR);
$statement->execute();
$rows = $statement->fetchAll(PDO::FETCH_ASSOC);
I had read in another question that you are supposed to include the % when binding the parameters, not in the SQL itself (pre-prepared statement), but that doesn't work.
I could resort to just inserting the keyword directly into the SQL (after doing some sanitization), but I want to stick with prepared statements. Any help would be greatly appreciated.
This actually works for me:
$stmt = $pdo->prepare("select * from t where c like ?");
$stmt->execute(array("70%"));
print_r($stmt->fetchAll());
What PHP version do you use?
Thanks Steffen and Terry for the help.
I ended up solving the problem myself by switching to bindParam() instead of bindValue(). I am not sure why I couldn't do it with bindValue(), but right now I am just too tired to care.