I'm learning PDO from NetTuts and have had success so far searching FETCH_ASSOC based on strings.
Question: Which PDO:: is needed in order to search by integers? I thought it was PDO::Fetch_OBJ. The following is returning false.
$busid = $this->sanitize($string);
$database->query('SELECT name, address FROM business_information WHERE id = :id', array(':id' => $busid));
var_dump($database);
if($database->count() >= '1') {
$results->setFetchMode(PDO::FETCH_OBJ);
while($row = $results->fetch()) {
$test = "Name: ".$row['name']." Address: ".$row['address'];
}else{
$test = "no results were found";
}
var_dump returns:
object(database)#1 (7) {["pdo":"database":private]=> object(PDO)#2 (0)
{ } ["port"]=> int(3306) ["statement"]=> object(PDOStatement)#6 (1) {
["queryString"]=> string(61) "SELECT name, address FROM
business_information WHERE id = :id" } }
1) Prepared statements is better than sanitizing.
2) PDO::query have another signature, read the manual please.
/** #var \PDO $PDO */
$query = $PDO->prepare('SELECT name, address FROM business_information WHERE id = :id');
if (!$query) return false;
if (!$query->execute(array(':id' => $busid))) return false;
$results = $query->fetchAll(\PDO::FETCH_ASSOC);
if (empty($results)) return false;
foreach ($results as $row)
{
$test = "Name: ".$row['name']." Address: ".$row['address'];
echo $test, PHP_EOL;
}
You may want to prepare a statement before firing it to the database. Why? The answer is somewhere near PDO::prepare. When you do use prepare, you will find yourself holding a PDOStatement as a return value.
Okey. Lets bind values to a placeholder with: PDOStatement::bindValue
Take a look at the signature:
`bool PDOStatement::bindValue(mixed $parameter,
mixed $value
[,int $data_type=PDO::PARAM_STR])`
data_type: Explicit data type for the parameter using the PDO::PARAM_* constants.
You can set data_type manually, but is not necessary in 99% of time. (As I've found it out.)
Related
So I have look at so many post, web sites and video and now I am so confused! I can't seem to get it right.
How do you stop injection in this PHP/PDO. I have this code that works, but it allows injection.
//*THIS WORKS BUT ALLOWS INJECTION
//*
//The variable $word comes from another php file where the search is created.
public function getAllCards($word) {
$sql = "SELECT * FROM carddbtable WHERE businessNameDB='".$word."'";
foreach ($this->conn->query($sql) as $row) {
echo json_encode($row)."<br>"."<br>";
}
$db = null;
}
With this new code I am trying to remove the variable "$word" from the "SELECT * FROM " statement
to stop the injection and add the "prepare" and the error checking and the "execute" statement, but I can't get it right. How would I do this? FYI this is a GoDaddy shared server.
//Getting the search "word" from the GetCards.php
public function getAllCards($word) {
//Empty var to store all returned info from db
$returnArray = array();
// sql statement to be executed
$sql = "SELECT * FROM carddbtable WHERE businessNameDB=':word";
// prepare to be executed
$statement = $this->conn->prepare($sql);
// error occurred
if (!$statement) {
throw new Exception($statement->error);
}
// execute statement
$statement->execute( :word => '$word' );
//run the query
foreach ($this->conn->query($statement) as $row) {
echo json_encode($row)."<br>"."<br>";
}
// store all appended $rows in $returnArray to be sent to app
$returnArray[] = $row;
}
You've almost got it. PDO, like many database drivers, will be responsible for all of the escaping, so just leave the placeholder as plain as possible:
$sql = "SELECT * FROM carddbtable WHERE businessNameDB=:word";
No ' necessary there.
Now when you execute() a PDO statement you get a result which you need to capture into a variable:
$res = $statement->execute([ 'word' => $word ]);
As Ibu and chris85 point out the '$word' part is also incorrect. Avoid quoting single variables, it's not only pointless, it can cause trouble, like here where you're binding to literally dollar-sign word, not the value in question. This goes doubly for "$word".
Then you fetch from that. Right now you're calling query() on the statement, which is incorrect.
Another thing to note is kicking the habit of making throw-away variables like $sql as these are just junk. Instead pass the argument directly:
$statement = $this->conn->prepare("SELECT * FROM carddbtable WHERE businessNameDB=:word");
This avoids accidentally mixing up $sql3 with $sql8 if you're juggling a bunch of these things.
This is what i have now.
//Getting the search "word" from the GetCards.php
public function getAllCards($word) {
//Empty var to store all returned info from db
$returnArray = array();
// prepare to be executed sql statement to be executed if not entered word
$statement = $this->conn->prepare("SELECT * FROM carddbtable WHERE businessNameDB=:word");
// error occurred
// if (!$statement) {
// throw new Exception($statement->error);
// }
// execute statement
$res = $statement->execute([ 'word' => $word ]);
//run the query
foreach ($this->conn->query($res) as $row) {
echo json_encode($row)."<br>"."<br>";
}
// store all appended $rows in $returnArray to be sent to app
$returnArray[] = $row;
}
I got this working
//*FUNCTION TO GET CARD FROM SEARCH WORD CALLED FROM GetCards.php
public function getAllCards($word) {
//Connect to db using the PDO not PHP
$db = new PDO('mysql:host=localhost;dbname=xxxx', 'xxxx', 'xxxx');
//Here we prepare the SELECT statement from the search word place holder :word
$sql = $db->prepare('SELECT * FROM carddbtable WHERE businessNameDB=:word');
//We execute the $sql with the search word variable"$word"
$sql->execute([':word' => $word]);
//Looping through the results
foreach ($sql as $row)
//Print to screen
echo json_encode($row). "<br>"."<br>";
}
There is only one record in my database, and the "action_taken" column is set to NULL. How the hell do I get PDO to find it???
If I type the query directly into SQL it works as expected.
Note: This is just a test script to illustrate my problem. Most the time the value passed will be a string, but occasionally the value will be NULL.
include ('include/mysql.php');
$sql = 'SELECT * FROM returns WHERE action_taken = :action';
$sth = $dbh->prepare($sql);
$param = null;
$sth->bindValue(':action', $param, PDO::PARAM_STR);
$sth->execute();
if ($sth->rowCount())
{
echo 'FOUND YOU!';
}
else
{
echo 'NOOOO :(';
}
Basically, you can't say "equal to null". It always has to be IS null:
$sql = 'SELECT * FROM returns WHERE action_taken IS :action';
You'd be better off not using NULL at all, if at all possible.
I'm pretty new to using PDO so I'm not sure if I have it down correctly, however with the following test I'm able to do some injection which I would like to bypass.
In my models class I have some shortcut methods. One of them is called return_all($table,$order,$direction) which simply returns all rows from a table:
public function return_all($table,$order = false, $direction = false) {
try {
if($order == false) {
$order = "create_date";
}
if($direction != false && !in_array($direction,array("ASC","DESC"))) {
$direction = "DESC";
}
$sql = "SELECT * FROM ".mysql_real_escape_string($table)." ORDER BY :order ".$direction;
$query = $this->pdo->prepare($sql);
$query->execute(array("order" => $order));
$query->setFetchMode(PDO::FETCH_ASSOC);
$results = $query->fetchAll();
} catch (PDOException $e) {
set_debug($e->getMessage(), true);
return false;
}
return $results;
}
This works fine, except, if I pass the following as $table into the method:
$table = "table_name; INSERT INTO `users` (`id`,`username`) VALUES (UUID(),'asd');";
Now it's unlikely that someone will ever be able to change the $table value as it's hard-coded into my controller functions, but, i'm a little concerned that I'm still able to do some injection even when I use PDO. What's more surprising is that the mysql_real_escape_string() did absolutely nothing, the SQL still ran and created a new user in the users array.
I also tried to make the table name a bound parameter but got a sql error I assume due to the `` PDO adds around the table name.
Is there a better way to accomplish my code below?
You have already solved your problem with direction.
if($direction != false && !in_array($direction,array("ASC","DESC"))) {
$direction = "DESC";
}
Use the same technique for table names
$allowed_tables = array('table1', 'table2');//Array of allowed tables to sanatise query
if (in_array($table, $allowed_tables)) {
$sql = "SELECT * FROM ".$table." ORDER BY :order ".$direction;
}
I've written the following function to construct and execute an SQL statement with key-value bindings. I'm using bindValue() to bind an array of key-value pairs to their corresponding identifiers in the SQL string. (The echo statements are for debugging).
public function executeSelect($sql, $bindings = FALSE)
{
$stmt = $this->dbPDO->prepare($sql);
if ($bindings)
{
foreach($bindings as $key => $value)
{
$success = $stmt->bindValue($key, $value);
echo "success = $success, key = $key, value = $value<br />";
if (!$success)
{
throw new Exception("Binding failed for (key = $key) & (value = $value)");
}
}
}
echo "Beginning execution<br />";
if ($stmt->execute())
{
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
else
{
return FALSE;
}
}
The input to this function is as follows:
$stmt = "SELECT * FROM test WHERE id = :id";
$bindings = array(":id" => "3", ":Foo" => "Bar");
On the second loop through the $bindings array, I'd expect $success to evaluate to false, thus throwing the custom Exception since "Bar" cannot be bound to ":Foo", since ":Foo" doesn't exist in the input SQL.
Instead, $success evaluates to true (1) for both key-value pairs in the $bindings array, and a PDOException is thrown by ->execute() "(HY000)SQLSTATE[HY000]: General error: 25 bind or column index out of range"
Why isn't bindValue returning false?
Because it works this way.
it throws an error not at bind but at execute. That's all.
So, there is no need in loop and you can make your method way shorter.
public function executeSelect($sql, $bindings = FALSE)
{
$stmt = $this->dbPDO->prepare($sql);
$stmt->execute($bindings);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
There is no need in checking execute result either, I believe.
In case of error it will raise an exception already.
By the way, I'd make several helper functions based on this one, returning scalar value and single row. They are mighty helpful. Though I find named placeholders a bit dull. Compare this code:
$name = $db->getOne("SELECT name FROM users WHERE group=?i AND id=?i",$group,$id);
vs.
$sql = "SELECT name FROM users WHERE group=:group AND id=:id";
$name = $db->getOne($sql,array('group' => $group, 'id' => $id));
named require 2 times more code than anonymous.
A perfect example of WET acronym - "Write Everything Twice"
I am trying to access some information from mysql, but am getting the warning: mysql_fetch_assoc(): supplied argument is not a valid MySQL result resource for the second line of code below, any help would be much appreciated.
$musicfiles=getmusicfiles($records['m_id']);
$mus=mysql_fetch_assoc($musicfiles);
for($j=0;$j<2;$j++)
{
if(file_exists($mus['musicpath']))
{
echo ''.$mus['musicname'].'';
}
else
{
echo 'Hello world';
}
}
function getmusicfiles($m_id)
{
$music="select * from music WHERE itemid=".$s_id;
$result=getQuery($music,$l);
return $result;
}
Generally, the mysql_* functions are used as follows:
$id = 1234;
$query = 'SELECT name, genre FROM sometable WHERE id=' . $id;
// $query is a string with the MySQL query
$resource = mysql_query($query);
// $resource is a *MySQL result resource* - a mere link to the result set
while ($row = mysql_fetch_assoc($resource)) {
// $row is an associative array from the result set
print_r($row);
// do something with $row
}
If you pass something to mysql_fetch_assoc that is not a MySQL result resource (whether it's a string, an object, or a boolean), the function will complain that it doesn't know what to do with the parameter; which is exactly what you are seeing.
A common gotcha: you get this warning if you pass something (other than a valid query string) to mysql_query:
$id = null;
$query = 'SELECT name, genre FROM sometable WHERE id=' . $id;
$res = mysql_query($query);
// $res === FALSE because the query was invalid
// ( "SELECT name, genre FROM sometable WHERE id=" is not a valid query )
mysql_fetch_assoc($res);
// Warning: don't know what to do with FALSE, as it's not a MySQL result resource
Without seeing the code of getmusicfiles there's not a lot we can really help you with. You should be returning a valid mysql resource in that function.
As others have noted, you need to return a valid mysql resource into the mysql_fetch_assoc function to retrieve the next row. For example:
$sql = "select * from table";
$resultSet = mysql_query($sql) or die("Couldn't query the database.");
echo "Num Rows: " . mysql_num_rows($resultSet);
while ($resultRowArr = mysql_fetch_assoc($resultSet)) {
...
}
I think you need to specify what the function getQuery()
$result=getQuery($music,$l);
does
It depends on what exactly getmusicfiles() does. It must return a result of mysql_query() function call, then it will be a "valid MySQL result".
And you most probably wanted to put the line $mus=mysql_fetch_assoc($musicfiles) inside of the for cycle to fetch several rows one after another.
function getmusicfiles($m_id) {
$music="select * from music WHERE itemid=".$s_id;
$m_id != $s_id ?