Here the thing, other PDO works well, but this one doesn't. I have tried with
execute(array(':t'=>$table));
with no success. Ideas?.
public function __construct($table){
try{
$pdocnx = new PDO("mysql:host=localhost;dbname=sigcat",'root','');
$stmt = $pdocnx->prepare('select * from sigcat.:t');
$stmt->bindParam(':t', urldecode($table), PDO::PARAM_STR,45);
$stmt->execute();
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
var_dump($row);
}catch(Exception $e){
echo $e->getMessage();
}
}
I got many records in 'supplies' but it returns array(0) { }. I'm getting the 'table' parameter with $_GET['table']. No exceptions though.
You can't bind table names, only values.
Maintain a list of valid names and ensure the string is present in the valid list.
If you can't build a list of valid names, you are probably doing something wrong.
You can't bind tables, so you can do a sneaky trick like this:
public function myFunction($table){
$st = "SELECT FROM `" . $table ."` ..some sql";
$statement->prepare($st);
$statement->execute();
}
Hope this helps.
Related
I'm making a song database system, and I'm trying to implement a way for users to search songs based on a category to search (song's name, artist's name, album name, genre etc) and a given search term. To accommodate user input and protect against SQL injection, I'm using a prepared statement made with bind variables, however I'm having trouble with what I currently made:
search("genre", "electropop", $db);
function search($column, $term, $db) {
try {
$sql = "SELECT * FROM Songs WHERE :column=:term;";
$stmt = $db->prepare($sql);
$params = array("column" => $column,
"term" => $term);
$stmt->execute($params);
$arr = $stmt->fetchALL(PDO::FETCH_ASSOC);
print (json_encode($arr));
}
catch(PDOException $ex) {
catch_error("The search failed.", $ex);
}
}
Whenever I test this, I get an empty array back: [ ]
I tested my query ("SELECT * FROM Songs WHERE genre='electropop'") in phpmyadmin and it checks out (gave me back entries).
The correct syntax for WHERE clauses in SQL is that the term needs to be surrounded by quotations (https://www.w3schools.com/sql/sql_where.asp) so I tried escaping quotation marks around the term:
...
$sql = "SELECT * FROM Songs WHERE :column=\':term;\'";
...
But then it fails to even see :term as a token to bind variables to later.
Not sure how to go about solving this. I assumed the empty array is due to a valid search but just no results, but perhaps I'm not correctly using a prepared statement. Any help would be much appreciated! Thanks!
You missed the : before term param. Don't need to bind column name. Just use the $column var instead of :column.
search("genre", "electropop", $db);
function search($column, $term, $db) {
try {
$sql = "SELECT * FROM Songs WHERE $column=:term;";
$stmt = $db->prepare($sql);
$params = array(":term" => $term);
$stmt->execute($params);
$arr = $stmt->fetchALL(PDO::FETCH_ASSOC);
print (json_encode($arr));
}
catch(PDOException $ex) {
catch_error("The search failed.", $ex);
}
}
I have rewritten the PDO fuction following advise from my previous question at PHP function/procedure to bind question marks dynamically
The problem I have is the result set returned is empty. The SQL query is correct int he sense that, when I run it manually, it does return data.
My suspicion is that the binding in the for loop is incorrect.
Could I please request guidance on
1) How to bind data in a for loop with question marks?
2) How to bind LIKE cases if the way I'm doing now is incorrect.
sample_sql_1="select f_name, age, address from table1 where l_name=? and dob >= ? and cty =?"
sample_sql_2="select * from table2 where cty LIKE ?"
$locn= "'" . $location . "%'";
pdo_db_query($sql_run,array(':empname'), array($locn));
function pdo_db_query($query, $bindnames = array(), $bindvals = array()) {
try {
# MySQL with PDO_MYSQL
$DBH = new DbConn();
$DBH->query($query);
foreach ($bindnames as $key => &$bindname) {
$DBH->bind( $bindname,$bindvals[$key]); // bind the value to the statement
}
$result=$DBH->resultset();
if($result){
var_dump($result);
}
# Close the connection
$DBH->CloseConnection();
} catch (PDOException $e) {
echo $e->getMessage();
var_dump($e->getMessage());
}
}
Here's the resultset function
public function resultset() {
$this->execute();
return $this->stmt->fetchAll(PDO::FETCH_ASSOC);
}
I got to the end of this post before I realized $DBH was actually an instance of a custom class. I'm leaving everything here for now in case I'm wrong and any of it helps.
BUT
I think you only problem is in the for loop
foreach ($bindnames as $key => &$bindname) should be foreach ($bindnames as $key => $bindname).
Original Answer
Have you tried a simpler example. It looks like you are doing many things incorrectly.
$DBH->query($query);
If you want to bind params to this later you should be preparing: $sth = $DBH->prepare($query);
foreach ($bindnames as $key => &$bindname) {
Are you sure that's doing what you think. I see no reason to us &$bindname it should be $bindname.
$DBH->bind( $bindname,$bindvals[$key]);
The function is actually PDOStatement::bindParam(), so you should be calling $sth->bindParam($bindname,$bindvals[$key]);. where $sth is the return value from $DBH->prepare($query);
$result=$DBH->resultset();
I need to understand the context of the resultset method better. Are you extending PDO or something?
Had to change
$locn= "'" . $location . "%'";
TO
$locn= $location . "%";
I realized that with binding, there is no need for single quote.
I'm using a mySQL statement in a PHP script to return the email address for a given user. You'll see the function below:
public static function getEmailAddress($username) {
$conn = parent::connect();
$sql = "SELECT emailAddress FROM " . TBL_USERS . " WHERE username = '". $username ."';";
try {
$st = $conn->prepare($sql);
$st->bindValue("username", $username, PDO::PARAM_STR);
$st->execute();
$emailAddress = $st->fetch();
return $emailAddress;
parent::disconnect($conn);
} catch(PDOException $e) {
die("emailAddress lookup query failed: " . $e->getMessage());
}
}
This returned an array with two elements, both being the correct email address. I changed the return statement to
return implode(array_unique($emailAddress));
which solved the problem, but there has to be a better way to do this. This seems like pretty elementary stuff, and I'd like to do it right. What's wrong?
The problem is that the default response type of fetch is
PDO::FETCH_BOTHÂ (default): returns an array indexed by both column name and 0-indexed column number as returned in your result set
I also find this quite confusing.
Specify the parameter fetch_style to either PDO::FETCH_ASSOC or PDO::FETCH_OBJ to get what you would expect.
http://php.net/manual/en/pdostatement.fetch.php
Hope this helps
$q = $db->query(" SELECT username FROM users WHERE userident = '1' ");
echo $q; //error
print_r $q; //prints the query information (SELECT ... etc)
How do I go about getting the specific value of the element I am querying? Say the element under column username and where userident equals '1' contains the value "Patrick"; how do I initialize this string into a variable?
//same query as above
$user = $q;
echo $user; //prints "Patrick"
Sorry if this is something so rudimentary and mundane, but I've never done this outside of a foreach() loop. I'd normally iterate through rows to print specific details. The below works, but the foreach() is unnecessary as far as I understand.
foreach($q as $p) {
$user = $p["username"];
}
echo $print; //this correctly prints "Patrick"
Surely there's a method I missed somewhere?
Using the query method pretty much defeats the purpose of using prepared statements. Plus, I believe for what you're looking for, it isn't quite right.
<?php
if (!isset($_POST['id'])) {
exit;
}
$userId = $_POST['id'];
$db = new PDO(/* Stuff */);
$sql = '
SELECT username
FROM users
WHERE id = :id';
// Get a prepared statement object.
$statement = $db->prepare($sql);
// Bind a parameter to the SQL code.
$statement->bindParam(':id', $userId, PDO::PARAM_INT);
// Actually get the result.
$result = $statement->fetch(PDO::FETCH_ASSOC);
// Close the connection.
$statement->closeCursor();
// Print the result.
print_r($result);
Alternately you can use $statement->fetchAll() to gather more than one result.
Edit: I didn't actually run this code, so you might have to tinker with it to get it working right.
Here's the problematic PHP function:
//Get data associated with $criteria from db
function getUserData($criteria, $value) {
//obtain user data from db based on $criteria=$value
global $pdo;
//echo $criteria . " " . $value;
try {
$sql = 'SELECT id, first, last, email, userid FROM users WHERE :criteria= :value';
//var_dump($sql);
$st = $pdo->prepare($sql);
$st->bindValue(':criteria', $criteria);
$st->bindValue(':value', $value);
$st->execute();
}
catch (PDOException $ex) {
$error = "Failed to obtain user data.";
$errorDetails = $ex->getMessage();
include 'error.html.php';
exit();
}
$row = $st->fetch();
//var_dump($row);
if ($row)
{
$userdata = array();
$userdata['id'] = $row['id'];
$userdata['first'] = $row['first'];
$userdata['last'] = $row['last'];
$userdata['email'] = $row['email'];
$userdata['userid'] = $row['userid'];
return $userdata;
}
return FALSE;
}
I use this function to return a whole row of data associated with specific column in it.
When used at it's current state, with a call like that getUserData("email", "John_Stewart_2013"), it returns false, meaning an empty result, while the same query runs fine in MySQL CLI.
If I, on the other hand, substitute the query string $sql with :
$sql = "SELECT id, first, last, email, userid FROM users WHERE $criteria='$value'";
And comment out the bindValue calls, Every thing runs fine in PHP, and the query returns as desired.
But the problem is, those function arguments are user-submitted form data, meaning the solution is vulnerable to SQL Injection.
What's wrong here in the first query form?
You can't use bindValue with column names I'm afraid.
If you think about what a prepared statement is, this should become more obvious. Basically, when you prepare a statement with the database server, it creates an execution plan for the query beforehand, rather than generating it at the time of running the query. This makes it not only faster but more secure, as it knows where it's going, and the datatypes that it will be using and which are going to be input.
If the column/table names were bindable in any way, it would not be able to generate this execution plan, making the whole prepared statement idea somewhat redundant.
The best way would be to use a hybrid query like so:
$sql = "SELECT id, first, last, email, userid FROM users WHERE $criteria = :value";
I'm going to hope that the $criteria column isn't entirely free form from the client anyway. If it is, you'd be best limiting it to a specific set of allowed options. A simplistic way to do would be to build an array of allowed columns, and check if it's valid with in_array, like so:
$allowed_columns = array('email', 'telephone', 'somethingelse');
if (!in_array($criteria, $allowed_columns))
{
$error = "The column name passed was not allowed.";
$errorDetails = $ex->getMessage();
include 'error.html.php';
exit;
}