How do you add a single quote to a variable within a SQL statement? If I put 'jeremy' in place of the '\$user'\ variable it works perfectly. I can't figure out how to escape the quote for the variable in the SQL statement. Thank you for your help.
$resultArticles = mysql_query("SELECT COUNT(id) FROM articleList WHERE user = '\$user'\ ");
$totalArticlesLeaderboard = mysql_result($resultArticles, 0);
echo "<strong>Total Articles: </strong>" . $totalArticlesLeaderboard;
I've tried to find a suitable duplicate of your question, but I only found real dupes which are based on the ancient mysql_* functions. The mysql_* functions (like the ones you are using) are no longer maintained by the PHP commuity (for some time now) and the deprecation process has begun on it. See the red box?
You should really try to pick up the better PDO or MySQLi. Both of these option should be fine. Imho PDO has a better API, but mysqli is more towards mysql (in most cases PDO will do whatever you want to use it for).
With the two "new" API there is also the possibilty to use prepared statements. With prepared statements you should not have to worry about manually escaping values before inserting them into your queries.
An example of this using the PDO API would be:
$db = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $db->prepare('SELECT COUNT(id) FROM articleList WHERE user = :user');
$stmt->execute(array('user' => $user));
As you can see the values are not inserted directly into the query, but instead it uses placeholders. This code will make it impossible for people to inject arbitrary SQL into your query. And also you don't need to do any escaping anymore.
If you need more help in deciding between PDO or mysql check out the docs with more information about it. If you choose PDO you can find a good tutorial on the topic here.
Test this
$resultArticles = sprintf("SELECT COUNT(id) FROM articleList WHERE user='%s",
mysql_real_escape_string($user));
You should be able to just remove the escape characters:
$user = mysql_real_escape_string($user);
$resultArticles = mysql_query("SELECT COUNT(id) FROM articleList WHERE user = '$user'");
If you ever have trouble with variables, you can always just end the string and concatenate. I do this often to avoid confusion:
$user = mysql_real_escape_string($user);
$resultArticles = mysql_query("SELECT COUNT(id) FROM articleList WHERE user = '".$user."'");
As PeeHaa said, make sure you try to use PDO or MySQLi.
Don't forget to escape all user input, or they potentially can destroy your database. If you are using MySQLi, you can use mysqli::real_escape_string. Sanitizing ALL your user data is absolutely essential. DO NOT SKIP THIS!
If the variable $user contains any special characters, it is necessary to escape these, as shown in the first answer. If you don't have the mysql_real_escape_string() function available, use addslashes().
Related
Ok I would like to know how you convert this mysql code into mysqli.
function protect($string) {
return mysql_real_escape_string(strip_tags(addslashes($string)));
}
I know you change mysql to mysqli but it asks for 2 parameters this worked with mysql so I would like to see it in mysqli
also I haven't yet found someone on stackoverflow with a question about the new mysqli version so I wasn't able to find out myself
It is better not to use it at all!
mysql_real_escape_string() was a hack which was used to prevent SQL injection, and it didn't even do that 100%. This function was never meant to protect anything. It is a simple string formatting function.
mysqli_real_escape_string() is yet another hack to make the transition easier. Although, at the time of writing this post mysql_* has been deprecated for so long, that no one should have any excuse to use some kind of shim for transitioning, because everyone should already be using MySQLi with prepared statements or even better PDO.
As for strip_tags() and addslashes() they are useless in this context and only mutilate your data. Don't use them.
To protect against SQL injection, one should use prepared statements and ensure that no variable input is inserted into SQL directly.
For example:
$stmt = $mysqli->prepare('SELECT columnA FROM tableB WHERE columnC=?');
$stmt->bind_param('s', $someVariable);
$stmt->execute();
$result = $stmt->get_result();
This function is a bad idea.
Using strip_tags() and addslashes() indiscriminately on all incoming data needlessly mutilates it, with zero added security.
To feed data into the database, use only the string escaping function, real_escape_string().
To display data from the user on a HTML page, strip the tags then or use htmlspecialchars() to avoid any scripting attacks.
Try like this:
$mysqli = new mysqli("host", "username", "pword", "db");
function protect($string) {
return $mysqli->real_escape_string(strip_tags(addslashes($string)));
}
EDIT
$link = mysqli_connect("localhost", "root", "", "aaa");
$city = "'s Hertogenbosch";
$city = mysqli_real_escape_string($link, $city);
echo($city);
How do you escape ' on doctrine?
I made this code
$query = $em->createQuery(
"SELECT a FROM AcmeTopBundle:ArtData a WHERE
a.name = '". mysql_escape_string($name) ."'");
but when the $name is A'z
it returns error
[Doctrine\ORM\Query\QueryException]
SELECT a FROM AcmeTopBundle:ArtData a WHERE
a.name = 'A\'s'
I think I escaped by mysql_escape_string in case of using raw sql.
How can I avoid this error on doctrine?
The way I usually handle this is using parameters and querybuilder (https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/query-builder.html)...
$qb = $em->createQueryBuilder(
"SELECT a FROM AcmeTopBundle:ArtData a WHERE
a.name = :name")
->setParameter('name',$name);
$result = $qb->getQuery()->execute();
Well, even though there is accepted answer it is not for question as it is in title.
#Sven's answer comes close, but fails to mention:
Doctrine documentation
To escape user input in those scenarios use the Connection#quote() method.
And I have a gripe with "scenarios", or more with people pushing prepared statements like some holy grail. Well they are nice in theory, in practice at least in PHP they are quite shity, as they are unable to do simple stuff like IN (<list>) or multi inserts with VALUES (<bla bla>), (<more stuff>) which is a huge ass deal, as without it one ends up resorting to quite sub-optimal SQL (to put it lightly) quite commonly (well if one religiously insist on prepared statements at least).
This does not answer your question, but explains what's wrong with your code. It didn't fit into a comment.
You cannot and should not use mysql_escape_string()
It is the wrong escape function, the right one used to be mysql_real_escape_string(). Reading the documentation does not sound like it, but to properly escape, you have to know which character encoding is being used. In western encoding schemes like ASCII, ISO-8859-x or even UTF-8 it probably does not make a difference, but there are some exotic chinese encodings around which absolutely need to know whether that " byte belongs to another byte, or comes on it's own.
When using mysql_real_escape_string(), you need to have an already open DB connection created with mysql_connect(). If you don't, PHP tries to open a new connection with default user and password as defined in the php.ini file. This usually results in an error because without password the database won't let you do anything. And additionally, if you have success, then the encoding setting of this connection most likely is not the one used by Doctrine.
Using any of the mysql_* functions is wrong, because these are deprecated. The correct way would be to use mysqli_* functions.
Doctrine may use any of the three database connection methods: mysql, mysqli or PDO. You have to choose the one really being used if you want to manually call the correct escaping function. While the connection is already created. And somehow you need to grab that connection resource to allow the function you are calling to detect the used encoding.
So in the end there are plenty of reasons why it is wrong to just use any escaping that sound like it is doing the job.
The right way is to use the escaping of the database layer you are using. If you use Doctrine, the use it for escaping. Or better, avoid escaping, use prepared statements or the query builder and let Doctrine deal with the rest.
Based on https://stackoverflow.com/a/13377430/829533
you can use prepared statements http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/data-retrieval-and-manipulation.html#using-prepared-statements
From the documentation:
$date = new \DateTime("2011-03-05 14:00:21");
$stmt = $conn->prepare("SELECT * FROM articles WHERE publish_date > ?");
$stmt->bindValue(1, $date, "datetime");
$stmt->execute();
This will show how to insert the data into the database where you would normally have to use real_escape_string.
Doctrine and Symfony 3 using prepared not QueryBuilder:
// get the post value
$value = $request->request->get('value');
$sql = "INSERT INTO `table_name`
(`column_name1`,`column_name2`)
VALUES
('Static Data',?)
";
$em = $this->getDoctrine()->getManager();
$result = $em->getConnection()->prepare($sql);
$result->bindValue(1, $value);
$result->execute();
Now for a bonus to get a success/fail if you are using auto increment records:
$id = $em->getConnection()->lastInsertId();
if $id has a value then it executed the insert. If it does not the insert failed.
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);
}
The code below is written in php:
$user = addslashes($_POST['user']);
$pwd = addslashes($_POST['pwd']);
$query = "SELECT * FROM userdata WHERE UserName='$user' AND Password=PASSWORD('$pwd')";
the query will then be sent to mysql
Is there anything more I need to take care of?
Please point out.
No it's not safe, use mysql_real_escape_string at minimum:
$user = mysql_real_escape_string($_POST['user']);
$pwd = mysql_real_escape_string($_POST['pwd']);
And for better security go for prepared statements.
Best Options:
PDO
mysqli
You may ask which one to choose, check out:
What is difference between mysql,mysqli and pdo?
Nope.
The reason is that while a single quote ' is not the only char that break a sql query, quotes are the only chars escaped by addslashes().
Better: use mysql_real_escape_string
$user = mysql_real_escape_string($_POST['user'], $conn);
$pwd = mysql_real_escape_string($_POST['pwd'], $conn);
$query = "SELECT * FROM userdata WHERE UserName='$user' AND Password=PASSWORD('$pwd')";
Best: use PDO and prepared statements
$stmt = $dbh->prepare("SELECT * FROM userdata WHERE UserName=':user' AND Password=PASSWORD(':pass')");
$stmt->bindParam(':user', $user);
$stmt->bindParam(':pass', $pass);
No. You should not be using addslashes() to escape your data. That's been obsolete for years. You should be either:
using mysql_real_escape_string() as a replacement
using prepared statements with PDO or MySQLi
Plus using MySQL's Password() function is also poor pracdtive. Use hashes with salts. Bcrypt is my recommendation. Also, check out PHPass.
Protecting against SQL injection is easy:
Filter your data.
This cannot be overstressed. With good data filtering in place, most security concerns are mitigated, and some are practically eliminated.
Quote your data.
If your database allows it (MySQL does), put single quotes around all values in your SQL statements, regardless of the data type.
Escape your data.
Sometimes valid data can unintentionally interfere with the format of the SQL statement itself. Use mysql_escape_string() or an escaping function native to your particular database. If there isn't a specific one, addslashes() is a good last resort.
Read more: http://phpsec.org/projects/guide/3.html#3.2
I've always done the simple connection of mysql_connect, mysql_pconnect:
$db = mysql_pconnect('*host*', '*user*', '*pass*');
if (!$db) {
echo("<strong>Error:</strong> Could not connect to the database!");
exit;
}
mysql_select_db('*database*');
While using this I've always used the simple method to escape any data before making a query, whether that be INSERT, SELECT, UPDATE or DELETE by using mysql_real_escape_string
$name = $_POST['name'];
$name = mysql_real_escape_string($name);
$sql = mysql_query("SELECT * FROM `users` WHERE (`name` = '$name')") or die(mysql_error());
Now I understand this is safe, to an extent!
It escapes dangerous characters; however, it is still vulnerable to other attacks which can contain safe characters but may be harmful to either displaying data or in some cases, modifying or deleting data maliciously.
So, I searched a little bit and found out about PDO, MySQLi and prepared statements. Yes, I may be late to the game but I've read many, many tutorials (tizag, W3C, blogs, Google searches) out there and not a single one has mentioned these. It seems very strange as to why, as just escaping user input really isn't secure and not good practice to say the least. Yes, I'm aware you could use Regex to tackle it, but still, I'm pretty sure that's not enough?
It is to my understanding that using PDO/prepared statements is a much safer way to store and retrieve data from a database when the variables are given by user input. The only trouble is, the switch over (especially after being very stuck in my ways/habits of previous coding) is a little difficult.
Right now I understand that to connect to my database using PDO I would use
$hostname = '*host*';
$username = '*user*';
$password = '*pass*';
$database = '*database*'
$dbh = new PDO("mysql:host=$hostname;dbname=$database", $username, $password);
if ($dbh) {
echo 'Connected to database';
} else {
echo 'Could not connect to database';
}
Now, function names are different so no longer will my mysql_query, mysql_fetch_array, mysql_num_rows etc work. So I'm having to read/remember a load of new ones, but this is where I'm getting confused.
If I wanted to insert data from say a sign up/registration form, how would I go about doing this, but mainly how would I go about it securely? I assume this is where prepared statements come in, but by using them does this eliminate the need to use something like mysql_real_escape_string? I know that mysql_real_escape_string requires you to be connected to a database via mysql_connect/mysql_pconnect so now we aren't using either won't this function just produce an error?
I've seen different ways to approach the PDO method too, for example, I've seen :variable and ? as what I think are known as place holders (sorry if that is wrong).
But I think this is roughly the idea of what should be done to fetch a user from a database
$user_id = $_GET['id']; // For example from a URL query string
$stmt = $dbh->prepare("SELECT * FROM `users` WHERE `id` = :user_id");
$stmt->bindParam(':user_id', $user_id, PDO::PARAM_INT);
But then I'm stuck on a couple things, if the variable wasn't a number and was a string of text, you have to given a length after PDO:PARAM_STR if I'm not mistaken. But how can you give a set length if you're not sure on the value given from user in-putted data, it can vary each time? Either way, as far as I know to display the data you then do
$stmt->execute();
$result = $stmt->fetchAll();
// Either
foreach($result as $row) {
echo $row['user_id'].'<br />';
echo $row['user_name'].'<br />';
echo $row['user_email'];
}
// Or
foreach($result as $row) {
$user_id = $row['user_id'];
$user_name = $row['user_name'];
$user_email = $row['user_email'];
}
echo("".$user_id."<br />".$user_name."<br />".$user_email."");
Now, is this all safe?
If I am right, would inserting data be the same for example:
$username = $_POST['username'];
$email = $_POST['email'];
$stmt = $dbh->prepare("INSERT INTO `users` (username, email)
VALUES (:username, :email)");
$stmt->bindParam(':username, $username, PDO::PARAM_STR, ?_LENGTH_?);
$stmt->bindParam(':email, $email, PDO::PARAM_STR, ?_LENGTH_?);
$stmt->execute();
Would that work, and is that safe too? If it is right what value would I put in for the ?_LENGTH_?? Have I got this all completely wrong?
UPDATE
The replies I've had so far have been extremely helpful, can't thank you guys enough! Everyone has got a +1 for opening my eyes up to something a little different. It's difficult to choose the top answer, but I think Col. Shrapnel deserves it as everything is pretty much covered, even going into other arrays with custom libraries which I wasn't aware of!
But thanks to all of you:)
Thanks for the interesting question. Here you go:
It escapes dangerous characters,
Your concept is utterly wrong.
In fact "dangerous characters" is a myth, there are none.
And mysql_real_escape_string escaping but merely a string delimiters. From this definition you can conclude it's limitations - it works only for strings.
however, it is still vulnerable to other attacks which can contain safe characters but may be harmful to either displaying data or in some cases, modifying or deleting data maliciously.
You're mixing here everything.
Speaking of database,
for the strings it is NOT vulnerable. As long as your strings being quoted and escaped, they cannot "modify or delete data maliciously".*
for the other data typedata - yes, it's useless. But not because it is somewhat "unsafe" but just because of improper use.
As for the displaying data, I suppose it is offtopic in the PDO related question, as PDO has nothing to do with displaying data either.
escaping user input
^^^ Another delusion to be noted!
a user input has absolutely nothing to do with escaping. As you can learn from the former definition, you have to escape strings, not whatever "user input". So, again:
you have escape strings, no matter of their source
it is useless to escape other types of data, no matter of the source.
Got the point?
Now, I hope you understand the limitations of escaping as well as the "dangerous characters" misconception.
It is to my understanding that using PDO/prepared statements is a much safer
Not really.
In fact, there are four different query parts which we can add to it dynamically:
a string
a number
an identifier
a syntax keyword.
so, you can see that escaping covers only one issue. (but of course, if you treat numbers as strings (putting them in quotes), when applicable, you can make them safe as well)
while prepared statements cover - ugh - whole 2 isues! A big deal ;-)
For the other 2 issues see my earlier answer, In PHP when submitting strings to the database should I take care of illegal characters using htmlspecialchars() or use a regular expression?
Now, function names are different so no longer will my mysql_query, mysql_fetch_array, mysql_num_rows etc work.
That is another, grave delusion of PHP users, a natural disaster, a catastrophe:
Even when utilizing old mysql driver, one should never use bare API functions in their code! One have to put them in some library function for the everyday usage! (Not as a some magic rite but just to make the code shorter, less repetitive, error-proof, more consistent and readable).
The same goes for the PDO as well!
Now on with your question again.
but by using them does this eliminate the need to use something like mysql_real_escape_string?
YES.
But I think this is roughly the idea of what should be done to fetch a user from a database
Not to fetch, but to add a whatever data to the query!
you have to given a length after PDO:PARAM_STR if I'm not mistaken
You can, but you don't have to.
Now, is this all safe?
In terms of database safety there are just no weak spots in this code. Nothing to secure here.
for the displaying security - just search this site for the XSS keyword.
Hope I shed some light on the matter.
BTW, for the long inserts you can make some use of the function I wrote someday, Insert/update helper function using PDO
However, I am not using prepared statements at the moment, as I prefer my home-brewed placeholders over them, utilizing a library I mentioned above. So, to counter the code posted by the riha below, it would be as short as these 2 lines:
$sql = 'SELECT * FROM `users` WHERE `name`=?s AND `type`=?s AND `active`=?i';
$data = $db->getRow($sql,$_GET['name'],'admin',1);
But of course you can have the same code using prepared statements as well.
* (yes I am aware of the Schiflett's scaring tales)
I never bother with bindParam() or param types or lengths.
I just pass an array of parameter values to execute(), like this:
$stmt = $dbh->prepare("SELECT * FROM `users` WHERE `id` = :user_id");
$stmt->execute( array(':user_id' => $user_id) );
$stmt = $dbh->prepare("INSERT INTO `users` (username, email)
VALUES (:username, :email)");
$stmt->execute( array(':username'=>$username, ':email'=>$email) );
This is just as effective, and easier to code.
You may also be interested in my presentation SQL Injection Myths and Fallacies, or my book SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
Yes, :something is a named placeholder in PDO, ? is an anonymous placeholder. They allow you to either bind values one by one or all at once.
So, basically that makes four options to provide your query with values.
One by one with bindValue()
This binds a concrete value to your placeholder as soon as you call it. You may even bind hard coded strings like bindValue(':something', 'foo') if desired.
Providing a parameter type is optional (but suggested). However, since the default is PDO::PARAM_STR, you only need to specify it when it is not a string. Also, PDO will take care of the length here - there is no length parameter.
$sql = '
SELECT *
FROM `users`
WHERE
`name` LIKE :name
AND `type` = :type
AND `active` = :active
';
$stm = $db->prepare($sql);
$stm->bindValue(':name', $_GET['name']); // PDO::PARAM_STR is the default and can be omitted.
$stm->bindValue(':type', 'admin'); // This is not possible with bindParam().
$stm->bindValue(':active', 1, PDO::PARAM_INT);
$stm->execute();
...
I usually prefer this approach. I find it the cleanest and most flexible.
One by one with bindParam()
A variable is bound to your placeholder that will be read when the query is executed, NOT when bindParam() is called. That may or may not be what you want. It comes in handy when you want to repeatedly execute your query with different values.
$sql = 'SELECT * FROM `users` WHERE `id` = :id';
$stm = $db->prepare($sql);
$id = 0;
$stm->bindParam(':id', $id, PDO::PARAM_INT);
$userids = array(2, 7, 8, 9, 10);
foreach ($userids as $userid) {
$id = $userid;
$stm->execute();
...
}
You only prepare and bind once which safes CPU cycles. :)
All at once with named placeholders
You just drop in an array to execute(). Each key is a named placeholder in your query (see Bill Karwins answer). The order of the array is not important.
On a side note: With this approach you cannot provide PDO with data type hints (PDO::PARAM_INT etc.). AFAIK, PDO tries to guess.
All at once with anonymous placeholders
You also drop in an array to execute(), but it is numerically indexed (has no string keys). The values will replace your anonymous placeholders one by one in the order they appear in your query/array - first array value replaces first placeholder and so forth. See erm410's answer.
As with the array and named placeholders, you cannot provide data type hints.
What they have in common
All of those require you to bind/provide as much values as you have
placeholders. If you bind too many/few, PDO will eat your children.
You don't have to take care about escaping, PDO handles that. Prepared PDO statements are SQL injection safe by design. However, that's not true for exec() and query() - you should generally only use those two for hardcoded queries.
Also be aware that PDO throws exceptions. Those could reveal potentially sensitive information to the user. You should at least put your initial PDO setup in a try/catch block!
If you don't want it to throw Exceptions later on, you can set the error mode to warning.
try {
$db = new PDO(...);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING)
} catch (PDOException $e) {
echo 'Oops, something went wrong with the database connection.';
}
To answer the length question, specifying it is optional unless the param you are binding is an OUT parameter from a stored procedure, so in most cases you can safely omit it.
As far as safety goes, escaping is done behind the scenes when you bind the parameters. This is possible because you had to create a database connection when you created the object. You are also protected from SQL injection attacks since by preparing the statement, you are telling your database the format of the statement before user input can get anywhere near to it. An example:
$id = '1; MALICIOUS second STATEMENT';
mysql_query("SELECT * FROM `users` WHERE `id` = $id"); /* selects user with id 1
and the executes the
malicious second statement */
$stmt = $pdo->prepare("SELECT * FROM `users` WHERE `id` = ?") /* Tells DB to expect a
single statement with
a single parameter */
$stmt->execute(array($id)); /* selects user with id '1; MALICIOUS second
STATEMENT' i.e. returns empty set. */
Thus, in terms of safety, your examples above seem fine.
Finally, I agree that binding parameters individually is tedious and is just as effectively done with an array passed to PDOStatement->execute() (see http://www.php.net/manual/en/pdostatement.execute.php).