I am new to using prepared statements in mysql with php. I need some help creating a prepared statement to retrieve columns.
I need to get information from different
Security with MySQL in PHP (or any other language for that matter) is a largely discussed issue. Here are a few places for you to pick up some great tips:
The two most major items in my opinion are:
mysql_real_escape_string()
function (or something similar).You can write this instead:
$qry = "SELECT * FROM mytable where userid='";
$qry.= mysql_real_escape_string($_GET['userid'])."' AND category='";
$qry.= mysql_real_escape_string($_GET['category'])."' ORDER BY id DESC";
But to use prepared statements you better use a generic library, like PDO
<?php
/* Execute a prepared statement by passing an array of values */
$sth = $dbh->prepare('SELECT * FROM mytable where userid=? and category=?
order by id DESC');
$sth->execute(array($_GET['userid'],$_GET['category']));
//Consider a while and $sth->fetch() to fetch rows one by one
$allRows = $sth->fetchAll();
?>
Or, using mysqli
<?php
$link = mysqli_connect("localhost", "my_user", "my_password", "world");
/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$category = $_GET['category'];
$userid = $_GET['userid'];
/* create a prepared statement */
if ($stmt = mysqli_prepare($link, 'SELECT col1, col2 FROM mytable where
userid=? and category=? order by id DESC')) {
/* bind parameters for markers */
/* Assumes userid is integer and category is string */
mysqli_stmt_bind_param($stmt, "is", $userid, $category);
/* execute query */
mysqli_stmt_execute($stmt);
/* bind result variables */
mysqli_stmt_bind_result($stmt, $col1, $col2);
/* fetch value */
mysqli_stmt_fetch($stmt);
/* Alternative, use a while:
while (mysqli_stmt_fetch($stmt)) {
// use $col1 and $col2
}
*/
/* use $col1 and $col2 */
echo "COL1: $col1 COL2: $col2\n";
/* close statement */
mysqli_stmt_close($stmt);
}
/* close connection */
mysqli_close($link);
?>
I agree with several other answers:
ext/mysql
has no support for parameterized SQL statements.mysql_real_escape_string()
can also be effective if you use it correctly, but it's more verbose to code.You should also note that you still have to be cautious about SQL injection even if you use query parameters, because parameters only take the place of literal values in SQL queries. If you build SQL queries dynamically and use PHP variables for the table name, column name, or any other part of SQL syntax, neither query parameters nor mysql_real_escape_string()
help in this case. For example:
$query = "SELECT * FROM $the_table ORDER BY $some_column";
Regarding performance:
There are even some cases where a prepared query harms performance. For example in the following case, the optimizer can't assume it can use an index for the search, because it must assume the parameter value might begin with a wildcard:
SELECT * FROM mytable WHERE textfield LIKE ?
If you're going to use mysqli - which seems the best solution to me - I highly recommend downloading a copy of the codesense_mysqli class.
It's a neat little class that wraps up and hides most of the cruft that accumulates when using raw mysqli such that using prepared statements only takes a line or two extra over the old mysql/php interface
Here's an example using mysqli (object-syntax - fairly easy to translate to function syntax if you desire):
$db = new mysqli("host","user","pw","database");
$stmt = $db->prepare("SELECT * FROM mytable where userid=? AND category=? ORDER BY id DESC");
$stmt->bind_param('ii', intval($_GET['userid']), intval($_GET['category']));
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($column1, $column2, $column3);
while($stmt->fetch())
{
echo "col1=$column1, col2=$column2, col3=$column3 \n";
}
$stmt->close();
Also, if you want an easy way to grab associative arrays (for use with SELECT *) instead of having to specify exactly what variables to bind to, here's a handy function:
function stmt_bind_assoc (&$stmt, &$out) {
$data = mysqli_stmt_result_metadata($stmt);
$fields = array();
$out = array();
$fields[0] = $stmt;
$count = 1;
while($field = mysqli_fetch_field($data)) {
$fields[$count] = &$out[$field->name];
$count++;
}
call_user_func_array(mysqli_stmt_bind_result, $fields);
}
To use it, just invoke it instead of calling bind_result:
$stmt->store_result();
$resultrow = array();
stmt_bind_assoc($stmt, $resultrow);
while($stmt->fetch())
{
print_r($resultrow);
}
Quite late, but this might help someone:
/**
* Execute query method. Executes a user defined query
*
* @param string $query the query statement
* @param array(Indexed) $col_vars the column variables to replace parameters. The count value should equal the number of supplied parameters
*
* Note: Use parameters in the query then supply the respective replacement variables in the second method parameter. e.g. 'SELECT COUNT(*) as count FROM foo WHERE bar = ?'
*
* @return array
*/
function read_sql($query, $col_vars=null) {
$conn = new mysqli('hostname', 'username', 'user_pass', 'dbname');
$types = $variables = array();
if (isset($col_vars)) {
for ($x=0; $x<count($col_vars); $x++) {
switch (gettype($col_vars[$x])) {
case 'integer':
array_push($types, 'i');
break;
case 'double':
array_push($types, 'd');
break;
default:
array_push($types, 's');
}
array_push($variables, $col_vars[$x]);
}
$types = implode('', $types);
$sql = $conn->prepare($query);
$sql -> bind_param($types, ...$variables);
$sql -> execute();
$results = $sql -> get_result();
$sql -> close();
}else {
$results = $conn->query($query) or die('Error: '.$conn->error);
}
if ($results -> num_rows > 0) {
while ($row = $results->fetch_assoc()) {
$result[] = $row;
}
return $result;
}else {
return null;
}
}
You can then invoke the function like so:
read_sql('SELECT * FROM mytable where userid = ? AND category = ? ORDER BY id DESC', array($_GET['userid'], $_GET['category']));