I'm using PDO to develop an application with PostgreSQL.
The problem is that the binding functions as PDOStatement::bindValue and PDOStatement::bindParam aren't working at all.
I have the following code:
<?php
try{
$db = new PDO("pgsql:dbname=test;host=localhost", "user", "password");
$all = '*';
$sql = $db->prepare("SELECT :all FROM schema.table");
$sql->bindValue(':all', $all);
var_dump($sql->queryString);
var_dump($sql->execute());
}
catch(PDOException $e){
print $e->getMessage();
}
?>
I just cannot understand the reason $sql->queryString's value still is SELECT :all FROM schema.table, as it var_dump()'d here.
PDOStatement::bindParam does exactly the same thing.
Any tips?
EDIT: This query is for debugging purposes only! Please, don't care about the query itself but in the method that isn't binding.
Prepared statements don't work like that. You can only bind values, not entities.
As far as your statement is concerned, you're executing the query SELECT '*' FROM schema.table, Not, SELECT * FROM schema.table
If you want variable entities (which implies a design flaw about 80% of the time), you will have to resort to string interpolation (or concatenation -- you get the point).
Related
I do not have a problem with this code it surprisingly works fine but I really don't understand how it works or even is it right, so:
My queries with mysqli for ajax post or get calls were like this:
$con = mysqli_connect('localhost','root','','db') or die(header('Location: ./404.php'));
$add = "INSERT INTO table (id, id2, id3) VALUES('','$fid','')";
if(mysqli_query($con, $add)){
echo "added";
}
$remove = "DELETE FROM table WHERE id2='$fid'";
if(mysqli_query($con, $remove)){
echo "removed";
}
$getInfo = "SELECT * FROM table";
$result = $con->query($getInfo);
if($result->num_rows > 0) {
while($row = $result->fetch_assoc()){
//do something
}
}
And for $_POST or $_GET values I used mysqli_real_escape_string
Here is converted to PDO:
try{
$con = new PDO('mysql:host=localhost;dbname=db;charset=utf8mb4', 'root', '', array(PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
$get = $con->query("SELECT * FROM table");
foreach($get->fetchAll(PDO::FETCH_ASSOC) as $row){
$data['0'] = $row['name'];
$data['1'] = $row['email'];
return $data;
}
$add = $con->prepare("INSERT INTO table (id, id2, id3) VALUES(:f1,:f2,:f3)");
$add->execute(array(':f1' => '', ':f2' => $fid, ':f3' => ''));
echo "added";
$remove = $con->prepare("DELETE FROM table WHERE id2=:f1");
$remove->bindValue(':f1', $fid, PDO::PARAM_STR);
$remove->execute();
echo "removed";
}catch(PDOException $ex){
echo "error";
}
Now this works but I don't really know is it properly written with pdo where I don't need to use something like mysqli_real_escape_string with mysqli or something like that.
And all I could find on web is that how it is written now what part of code is doing what for example when I use in mysqli insert, update or delete I use
if(mysqli_query($con, $sql)){echo "success";}else echo 'fail';
How can I do that with pdo?
And also for using try and catch I don't know do I need to use it for every query or as I added above?
Just to say again I am new to pdo, I don't understand it very well and this code from above works but I do not know is it written the right way?
First off, let me congratulate you for going with PDO. Out of all the experienced PHP developers I know, it's near unanimous that they prefer PDO to mysqli.
I highly recommend you read through this guide to using PDO. It should answer all your questions and even answer a few you will likely have in the future.
To your specific questions:
No you do not need to escape anything anymore, so long as you are using prepared statements with placeholders. Escaping existed exactly because people were interpolating variables into SQL statements and that could confuse the quoting you needed to enclose strings.
With prepared statements that issue no longer exists, which also means that there is no longer the danger of SQL injection. SQL injection takes advantage of string concatenation to transform the original SQL statement into an entirely different one, again using quotes, which is why a non-escaped string accepted from user input was the attack vector for SQL injection. Both problems are solved using parameters and prepared statements.
As for error handling with PDO, you want to utilize PDO::ERRMODE_EXCEPTION which is discussed in the manual here.
Unfortunately, the default for PDO is PDO::ERRMODE_SILENT which essentially ignores database errors and just sets PDO object variables you would have to check yourself.
With that said, you can fix this by adding the error mode when you create the PDO connection object or just afterwards. Examples are on the PDO error mode page I linked.
As for Try-Catch blocks, in general an exception is not something you want to catch specifically unless you have some functional code to work around the error. Wrapping every sql call just so you can report an error message is bad, both from the point of view of DRY as well as being an anti-pattern. With the proper error mode, SQL errors will throw exceptions that you can handle in your error handler, and in general are things you shouldn't be eating up and continuing on from.
Your error handler should be (in production) logging the error to disk/emailing a sysadmin or site owner, and displaying a professional looking non-specific error message informing the user of the problem and that should be happening for all exceptions.
I've been playing around with switching over to the PDO way of doing database work in PHP. In my java life, I am able to place named queries into an associative array and call the prepared statement using the index. It's a bit more complex than that, but...
Anyways I thought it would be cool to do the same type of thing in PHP.
$NamedQueries['SelectBlackBoxById'] = "select name, category, rating from blackbox where id = :blackbox_id";
So I can prepare my statements this way:
$sth = $dbh->prepare($NamedQueries['SelectBlackBoxById']);
$sth->execute(array('blackbox_id' => '1'));
$sth->setFetchMode(PDO::FETCH_OBJ);
return $sth->fetch();
Instead of this way:
$sth = $dbh->prepare("select name, category, rating from blackbox where id = :blackbox_id");
$sth->execute(array('blackbox_id' => '1'));
$sth->setFetchMode(PDO::FETCH_OBJ);
return $sth->fetch();
I'm sure I am overlooking something, because my preferred way returns false. Any ideas would be greatly appreciated.
$sth->execute(array('blackbox_id' => '1'));
Should be
$sth->execute(array(':blackbox_id' => '1'));
You have to include the :
Did you try to dump out your Array value before you use it to prepare the query?
Overall, I don't see anything wrong with how the PDO would use it, you could also var_dump() your $sth variable after you prepare it to see what might be in there.
In general, as others pointed out the ':' is something you should include when you bind variables, although in this case I don't necessarily think it is the root problem as you said your second example worked, which uses the same syntax, the only difference is using the query from the array instead of a raw string. Thinking that since it is only 1 variable to bind, not having the ':' is not causing problems (though you should add it)
So, your real problem is lack of error handling. And it persists.
error_reporting(E_ALL);
to be notified of variables that out of scope.
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
after connect to be notified of empty queries and whatever else PDO errors
I am modifying my code from using mysql_* to PDO. In my code I had mysql_real_escape_string(). What is the equivalent of this in PDO?
Well No, there is none!
Technically there is PDO::quote() but it is rarely ever used and is not the equivalent of mysql_real_escape_string()
That's right! If you are already using PDO the proper way as documented using prepared statements, then it will protect you from MySQL injection.
# Example:
Below is an example of a safe database query using prepared statements (pdo)
try {
// first connect to database with the PDO object.
$db = new \PDO("mysql:host=localhost;dbname=xxx;charset=utf8", "xxx", "xxx", [
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
} catch(\PDOException $e){
// if connection fails, show PDO error.
echo "Error connecting to mysql: " . $e->getMessage();
}
And, now assuming the connection is established, you can execute your query like this.
if($_POST && isset($_POST['color'])){
// preparing a statement
$stmt = $db->prepare("SELECT id, name, color FROM Cars WHERE color = ?");
// execute/run the statement.
$stmt->execute(array($_POST['color']));
// fetch the result.
$cars = $stmt->fetchAll(\PDO::FETCH_ASSOC);
var_dump($cars);
}
Now, as you can probably tell, I haven't used anything to escape/sanitize the value of $_POST["color"]. And this code is secure from myql-injection thanks to PDO and the power of prepared statements.
It is worth noting that you should pass a charset=utf8 as attribute, in your DSN as seen above, for security reasons, and always enable
PDO to show errors in the form of exceptions.
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
so errors from you database queries won't reveal sensitive data like your directory structure, database username etc.
Last but not least, there are moments when you should not trust PDO 100%, and will be bound to take some extra measures to prevent sql injection, one of those cases is, if you are using an outdated versions of mysql [ mysql =< 5.3.6 ] as described in this answer
But, using prepared statements as shown above will always be safer, than using any of the functions that start with mysql_
Good reads
PDO Tutorial for MySQL Developers
There is none*! The object of PDO is that you don’t have to escape anything; you just send it as data. For example:
$query = $link->prepare('SELECT * FROM users WHERE username = :name LIMIT 1;');
$query->execute([':name' => $username]); # No need to escape it!
As opposed to:
$safe_username = mysql_real_escape_string($username);
mysql_query("SELECT * FROM users WHERE username = '$safe_username' LIMIT 1;");
* Well, there is one, as Michael Berkowski said! But there are better ways.
$v = '"'.mysql_real_escape_string($v).'"';
is the equivalent of $v = $this->db->quote($v);
be sure you have a PDO instance in $this->db so you can call the pdo method quote()
There is no need of mysql_real_escape_string in PDO.
PDO itself adjust special character in mysql query ,you only need to pass anonymous parameter and bind it run time.like this
Suppose you have user table with attribute name,email and password and you have to insert into this use prepare statement like this
you can pass name as => $name="Rajes'h ";
it should execute there is no need of equivalent of mysql_real_escape_string
$stmt="INSERT into user(name,email,password) VALUES(:name,:email,:password)";
try{
$pstmt=$dbh->prepare($stmt);//$dbh database handler for executing mysql query
$pstmt->bindParam(':name',$name,PDO::PARAM_STR);
$pstmt->bindParam(':email',$email,PDO::PARAM_STR);
$pstmt->bindParam(':password',$password,PDO::PARAM_STR);
$status=$pstmt->execute();
if($status){
//next line of code
}
}catch(PDOException $pdo){
echo $pdo->getMessage();
}
The simplest solution I've found for porting to PDO is the replacement for mysql_real_escape_string() given at https://www.php.net/manual/en/mysqli.real-escape-string.php#121402. This is by no means perfect, but it gets legacy code running with PDO quickly.
#samayo pointed out that PDO::quote() is similar but not equivalent to mysql_real_escape_string(), and I thought it might be preferred to a self-maintained escape function, but because quote() adds quotes around the string it is not a drop in replacement for mysql_real_escape_string(); using it would require more extensive changes.
In response to a lot of people's comments on here, but I can't comment directly yet (not reached 50 points), there ARE ACTUALLY needs to use the $dbh->quote($value) EVEN when using PDO and they are perfectly justifiable reasons...
If you are looping through many records building a "BULK INSERT" command, (I usually restart on 1000 records) due to exploiting InnoDb tables in MySQL/Maria Db. Creating individual insert commands using prepared statements is neat, but highly inefficient when doing bulk tasks!
PDO can't yet deal with dynamic IN(...) structures, so when you are building a list of IN strings from a list of user variables, YOU WILL NEED TO $dbh->quote($value) each value in the list!
So yes, there is a need for $dbh->quote($value) when using PDO and is probably WHY the command is available in the first place.
PS, you still don't need to put quotes around the command, the $dbh->quote($value) command also does that for you.
Out.
If to answer the original question, then this is the PDO equivalent for mysql_real_escape_string:
function my_real_escape_string($value, $connection) {
/*
// this fails on: value="hello'";
return trim ($connection->quote($value), "'");
*/
return substr($connection->quote($value), 1, -1);
}
btw, the mysqli equivalent is:
function my_real_escape_string($value, $connection) {
return mysqli_real_escape_string($connection, $value);
}
I'm converting all my sites code from using mysql_* functions to PDO. The PHP documentation on PDO is not clear for my needs. It gives you the functions to use, but does not go into detail to explain them in different scenarios.
Basically, I have a mysql fulltext search:
$sql = "SELECT ... FROM search_table WHERE MATCH(some_field) AGAINST ('{$searchFor}*' IN BOOLEAN MODE)";
The actual statements much longer, but this is what it basically does.
My question is, how would I incorporate this into PDO?
I know you're not meant to use quotes around the place-marker, so do you leave them out in the AGAINST() function? Do I include them? If I leave them out, what happens to the wildcard symbol etc?
$sql = $this->db->prepare("SELECT ... FROM search_table WHERE MATCH(some_field) AGAINST(:searchText IN BOOLEAN MODE");
$sql->bindValue(':searchText', $searchFor . '*');
This is unfortunately a weird exception to the use of query parameters (edit: but apparently not in the most recent point-release of each MySQL branch, see below).
The pattern in AGAINST() must be a constant string, not a query parameter. Unlike other constant strings in SQL queries, you cannot use a query parameter here, simply because of a limitation in MySQL.
To interpolate search patterns into queries safely, use the PDO::quote() function. Note that PDO's quote() function already adds the quote delimiters (unlike mysql_real_escape_string()).
$quoted_search_text = $this->db->quote('+word +word');
$sql = $this->db->prepare("SELECT ... FROM search_table
WHERE MATCH(some_field) AGAINST($quoted_search_text IN BOOLEAN MODE");
Re comment from #YourCommonSense:
You're right, I just tested this on MySQL 5.5.31, 5.1.68, and 5.0.96 (MySQL Sandbox is a wonderful tool), and it seems that these versions do accept query parameters in the AGAINST() clause of a dynamic SQL query.
I still have a recollection of a conflict existing in the past. Maybe it has been corrected in the most recent point-release of each branch. For example, I find these related bugs:
Using a stored procedure parameter in the AGAINST() clause always returns the same result: http://bugs.mysql.com/bug.php?id=3734
Crash or strange results with prepared statement, MATCH and FULLTEXT: http://bugs.mysql.com/bug.php?id=14496
$sql = "SELECT * FROM tablename WHERE MATCH (fieldname) AGAINST (:searchstr IN BOOLEAN MODE) LIMIT {$per_page} OFFSET {$pg_offset}";
try {
$database->prepare($sql);
$database->bindParam(':searchstr', $search);
$database->execute();
$result_array = $database->fetch_array($sql);
} catch (Exception $e) {
echo $e->getMessage();
}
I've seen this code that's been floating around, and also the fixed? version. Basically I've gotten this to work:
mysql_connect("host","client_name","client_pw");
mysql_select_db("database");
$q=mysql_query("SELECT * FROM table");
while($e=mysql_fetch_assoc($q))
$output[]=$e;
print(json_encode($output));
mysql_close();
but for some reason I feel it should be in mysqli. I'm new, and tried to write an equivalent mysqli OO code:
$mysqli = new mysqli("host", "client_name", "client_pw");
$mysqli->select_db("database");
$q = "SELECT * FROM table";
while($e=$mysqli->fetch_assoc($q))
$output[]=$e;
print(json_encode($output));
mysql_close();
It fails. I've tried other combinations, such as preparing a query and executing it, and setting that as $e, but all fail.
Do I have to manually build the array for the json_encode or something?
Maybe a better question is why I want to reinvent the wheel, but this has been bothering me.
Ah, I see you are not one with the database. Let us perform an exercise.
Close your eyes, breathe in, breathe out.
Relax.
You are one with the database.
You are one with the code.
Repeat after me.
Prepare.
Bind.
Execute.
Repeat it.
Again.
This is your new mantra, my friend.
You've accidentally skipped a step in your existing code. Let's throw it out and start over.
I am going to show you how to use PDO, one of the better ways PHP has to communicate with a database. It's less convoluted than the mysqli extension.
// Make sure these variables contain the correct data.
$pdo = new PDO("mysql:host=$hostname;dbname=$database", $username, $password);
// Ask PDO to throw exceptions instead of warnings.
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Here's our SQL. We're getting back a PDOStatement object here.
$sh = $pdo->prepare('SELECT * FROM Foo WHERE bar = ?');
// That question mark is a placeholder. bindValue lets us replace the question mark
// with the specified data. This is called a prepared statement. The end result is
// *complete and total immunity* from SQL Injection, if performed correctly.
$sh->bindValue(1, "I'm looking for a bar that is equal to this.");
// Okay, we've bound everything, let's run the query.
$sh->execute();
// And assuming there are no errors (note my severe lack of error handling),
// we should now have our complete list of data from the database.
print_r($sh->fetchAll(PDO::FETCH_ASSOC));
// Alternatively, we could pass bound variables as an array to execute:
$sh = $pdo->prepare('SELECT * FROM Foo WHERE bar = ?');
$sh->execute(array( "I'm a bar!" ));
// And of course, we can use variables in the binding...
$bar = 746;
$sh = $pdo->prepare('SELECT * FROM Foo WHERE bar = ?');
$sh->bindValue(1, $bar);
$sh->execute();
PDO's support for prepared statements and placeholders makes it one of the best choices for database access in modern PHP.
(mysqli also has access to prepared statements, but it forces you to also bind result variables, and that can be damned awkward under a lot of circumstances.)
fetchAll(PDO::FETCH_ASSOC) returns a multidimensional array. The outer array is numerically indexed, each value being a row. Each row is a string-keyed array, where the keys are column names and the values are the data from the database. There are a few other things that fetchAll can do, though I haven't found many of them to be useful. You can also fetch one row at a time
You can probably pass the results directly to json_encode, if you'd like, and not suffer too many problems.
Understand that you will want to add appropriate error detection to this code. I have omitted it here for brevity.
try
{
$db = new mysqli("your_host_ip", "your_username", "your_pass", "your_db", 3306);
if ($db->connect_errno) throw new exception(sprintf("Could not connect: %s", $db->connect_error));
$sqlCmd = "select * from users order by username";
$result = $db->query($sqlCmd);
if(!$result) throw new exception(sprintf("Invalid query : %s", $sqlCmd));
...
$q=mysql_query("SELECT * FROM table");
Here is how to do it with mysqli OOP
After the line $q= etc. -add the following code..
<?php
$result=$mysqli->query($q);
while($e=$result->fetch_assoc()){
$output[]=$e;
}
print(json_encode($output));
?>