False positive PDO rowcount - php

I'm trying to test if a user has activated their account using rowcount. Currently it's returning a positive even when I deliberately edit the database data to fail.
I tested my SQL to see if that was the problem, it seemed to return the expected values. I'm assuming it is something to do with my PHP code
try
{
$db = new Database;
$query = "SELECT COUNT(*),accountStatus, email FROM users WHERE email = :email AND accountStatus ='Active'";
$stmt = $db->prepare($query);
$stmt->bindValue(':email', $email);
$stmt->execute();
$count = $stmt->rowCount();
}
catch(Exception $e)
{
$errors[] = ["name" => "email", "error" => "Something went wrong contact the administrator or try again later"];
}
if(count($count > 0))
{
return true;
}
else
{
return false;
}

From the docs:
PDOStatement::rowCount() returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement executed by the corresponding PDOStatement object.
If the last SQL statement executed by the associated PDOStatement was a SELECT statement, some databases may return the number of rows returned by that statement. However, this behaviour is not guaranteed for all databases and should not be relied on for portable applications.

Related

PHP: How do I get my IF statement to work with PDO select?

I want my below PDO select to work with the bottom two IF statements?
The first IF I just want to make sure there is no error.
The second IF I want to check how many rows it returns. I know that this number of rows == 0 will not work.
Is there a way to do that?
try {
$conn = new PDO('mysql:host=localhost;dbname=zs', 'zs', 'rlkj08sfSsdf');
$conn ->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
echo $e->getMessage();
die();
}
$stmt = $conn->prepare("SELECT * FROM zip WHERE zip_code =:zip1");
$stmt->bindValue(':zip1', $_POST[zipcode], PDO::PARAM_INT);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
if($rows = "") {
echo "<p><strong>There was a database error attempting to retrieve your ZIP Code.</strong></p>\n";
}
if(number of rows == 0) {
echo "<p><strong>No database match for provided ZIP Code.</strong> Please enter a new ZIP Code.</p>\n";
}
You're interested only in whether there are records containing a particular value. It makes no sense to select everything and count the records in PHP. It's a waste of resources. Imagine what happens if there's a million records.
Solution you're after is to simply ask your database about the COUNT of rows containing a particular value. Your code should be quite simple:
$stmt = $conn->prepare("SELECT COUNT(*) AS num_rows FROM zip WHERE zip_code = :zip");
$stmt->bindValue(':zip', $_POST['zipcode'], PDO::PARAM_INT);
$stmt->execute();
$count = (int)$stmt->fetchColumn();
if($count)
{
echo "Success";
}
else
{
echo "Bummer";
}
Notes:
if successful, the above query will always return 1 row with 1 column, named num_rows which will be 0 for no matching records or an integer larger than 0 if there are records. If you use MySQL native driver with PHP, PHP will correctly represent this value as integer internally. I deliberately put typecasting in, you can remove it (the (int) part) if you have MySQL ND.
if something goes wrong during query execution, an exception will be thrown. The snippet doesn't cover that. You correctly set PDO in exception mode, and along with using bindValue instead of bindParam, this implies you did your research right and you're using PDO correctly which means that error handling should be implemented easily by you in this particular case.

Dot (Full Stop) in Get Value Breaks the SQL Query (PDO)

I'm fetching results from MySQL database using PDO and I use value from $_GET request method as a condition. Everything works fine but if there is any fullstop (dot) in the $_GET value, MySQL returns 0 rows.
Here is my sample:
<?php
function filter($val) {
$f = htmlentities($val);
$f = filter_input(INPUT_GET, $f);
return strip_tags($f);
}
$dev = filter("dev");
function DevFetch($dev) {
$q = $this->link->prepare("SELECT app FROM table WHERE dev = ?");
$q->bindValue("1", $dev);
$q->execute();
if($q->rowCount() > 0) {
return $q->fetchAll();
} else {
return false;
}
}
?>
Here are some examples.
Case 1:
results.php?developer=Google+Inc // works fine
Case 2:
results.php?developer=Google // works fine
Case 3:
results.php?developer=Google+Inc. // doesn't work with dot at the end
Please help with this. Note that I'm encoding (urlencode()) the $_GET value as well as filtering it using filter_input() function. Without filtering / encoding also doesn't work.
Since you use prepared statements, you don't need that filter function.
Just that simple:
function DevFetch($dev) {
$q = $this->link->prepare("SELECT app FROM table WHERE dev = ?");
$q->bindValue(1, $dev);
$q->execute();
$result = $q->fetchAll();
if(count($result) > 0) {
return $result;
} else {
return false;
}
}
$input = $_GET["dev"];
DevFetch($input);
Taken directly from the docs:
PDOStatement::rowCount() returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement executed by the corresponding PDOStatement object.
If the last SQL statement executed by the associated PDOStatement was a SELECT statement, some databases may return the number of rows returned by that statement. However, this behaviour is not guaranteed for all databases and should not be relied on for portable applications.
This means that this statement (being a SELECT):
$this->link->prepare("SELECT app FROM table WHERE dev = ?");
does not affect the return value of rowCount. To get the row count, you'll have to resort to mysqli or write:
$rows = $stmt->fetchAll();
$rowCount = count($rows);
If what you say is indeed true, and only the value with a dot on the end doesn't return a value for rowCount, then here's a couple of things you really ought to check:
PDO dsn string: specify the charset (add ;charset=utf8 to the end of the DSN string. details here
Set the error mode to have PDO throw exceptions on failure: PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
Check your DB for rows with the value that has the dot on the end, if it isn't there, than your code works as expected, simply because there are no results to work with

MYSQL result not returning correctly

So I have a piece of code that will check if you have followed a user or not. And basically let you follow them if you haven't. So here it is
if($_SESSION['loggedIn'] == true){
$result = $con->prepare("SELECT * FROM followers WHERE follow_from = :username AND follow_to = :post_id");
$result->bindParam(':username', $username);
$result->bindParam(':post_id', $follower);
$result->execute();
$reprint = $result->fetchAll(PDO::FETCH_ASSOC);
}
print_r($reprint);
if($reprint < 1){
$stmt = $con->prepare("INSERT INTO followers (follow_from, follow_to) VALUES (:ff, :ft)");
$stmt->bindValue(':ff', $follower, PDO::PARAM_STR);
$stmt->bindValue(':ft', $username, PDO::PARAM_STR);
$stmt->execute();
}
else{
echo 'Error';
exit();
}
//Display follower
$stmt1 = $con->prepare("SELECT COUNT(*) AS count FROM followers WHERE follow_to = :username");
$stmt1->bindValue(':username', $username, PDO::PARAM_STR);
$stmt1->execute();
$likes = $stmt1->fetchAll(PDO::FETCH_ASSOC);
print_r($likes);
So when I run it once. I get the else statement echoed. My question is why does this happen? In the database I have no record, so I'd expect it to go in once. I get no errors at all. loggedIn is true. And variables are being passed through successfully.
Any ideas?
You're misusing the result you get from fetchAll(). It's an associative array, not a scalar value. It could, as you've probably guessed, be empty.
But, more significantly than that, your code has a potential race condition. What happens if two different sessions are trying to set this same followers row? (Admittedly, in a small system that is unlikely, but in a large system it might happen).
What you actually do is just the INSERT operation. If your followers row has a unique key on the (follow_from,follow_to) columns, then, if that row is already there you'll get a 'Duplicate entry' error on the INSERT. Otherwise it will just happen. You can just ignore the 'Duplicate entry' error, because all you want is for that row to make it into that table.
So your code would go like this:
$stmt = $con->prepare("INSERT
INTO followers (follow_from, follow_to)
VALUES (:ff, :ft)");
$stmt->bindValue(':ff', $follower, PDO::PARAM_STR);
$stmt->bindValue(':ft', $username, PDO::PARAM_STR);
$result = $stmt->execute();
if ($result) {
/* this follow pair was successfully added */
} else {
/* MySQL may return the error 'Duplicate entry' */
if (false == stripos($stmt->errorCode,'Duplicate')){
echo 'Something failed in the insert: ' . '$stmt->errorCode';
}
else {
/* this follow pair was already in your table */
}
}
Pro tip: Don't use SELECT * in software; it can mess up query optimization; it often sends more data than you need from the server to your program, and it makes your program less resilient if your change your table definitions.
Pro tip: If you must count rows matching a particular WHERE statement, use COUNT() rather than fetching the rows and counting them in the client. What if you get a million rows?
You'd want to use count($reprint) other that a direct comparison. $reprint is an array, not a number
if(count($reprint) < 1)
{
$stmt = $con->prepare("INSERT INTO followers (follow_from, follow_to) VALUES (:ff, :ft)");
$stmt->bindValue(':ff', $follower, PDO::PARAM_STR);
$stmt->bindValue(':ft', $username, PDO::PARAM_STR);
$stmt->execute();
}
else
{
echo 'Error';
exit();
}
PDOStatement::fetchAll
PDOStatement::fetchAll — Returns an array containing all of the result set rows
If you check the size of the array then you would actually know if something happened.
Using proper error handling can tell you if something's failing deep down:
try
{
...
}
catch (PDOException $e)
{
echo $e->getMessage();
}
You will need to enable PDO error-displaying:
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
If checking the size of the array doesn't do it and you get no errors then it's simply some logic error.
Most likely the logic error is that
$reprint = $result->fetchAll(PDO::FETCH_ASSOC);
doesn't get executed (wrapping the error handling around that should tell you why), so
$reprint = $result->fetchAll(PDO::FETCH_ASSOC);
isn't given a proper value, meaning you'll always hit the else statement.
Edit
Your original problem was that "So when I run it once. I get the else statement echoed. [...] In the database I have no record" but now you're saying "It adds the record, but doesn't limit it to me one".
Can you be more clear about the actual, current, problem?

Verifying MYSQL executed a result not using get_result()

What's the best way to verify mysql executed successfully and then returned a result when you CANNOT use the following code:
$db = dbConnect();
//begin prepared statements to search db
$stmt = $db->prepare("SELECT email,authentication,email_confirm,externalid,password,id, admin FROM users WHERE email=?");
$stmt->bind_param('s',$email);
$stmt->execute();
$result = $stmt->get_result();
if (!$result){
//error statement
} if (!(mysqli_num_rows($result)==0)){
//action to perform
} else {
// no result returned
}
I was using get_result numerous times in my scripts, and my hosting provider doesn't have mysqlnd driver so I have to rewrite a lot of code. I know I am limited to bind_result and fetch(), but I need a little help rewriting the code since my mindset is stuck in the way I first did it.
I'm also using mysqli and not PDO.
The Mysqli fetch() function will return one of 3 values:
TRUE - Success. Data has been fetched
FALSE - Error occurred
NULL - No more rows/data exists or data truncation occurred
This means you can set your query like this:
$db = dbConnect();
$query = "SELECT email,authentication,email_confirm,externalid,password,id, admin FROM users WHERE email=?";
$stmt = $db->prepare();
$stmt->bind_param('s',$email);
$stmt->execute();
$stmt->bind_result($email,$auth,$email_confirm,$externalid,$password,$id,$admin);
// Will only execute loop if returns true
$record_count = 0;
while($result = $stmt->fetch())
{
// Increment counter
$record_count++;
// Do something with bound result variables
echo("Email is $email");
}
// After the loop we either ran out of results or had an error
if($result === FALSE)
{
echo("An error occurred: " . $db->error());
}
elseif($record_count == 0)
{
echo("No records exist.");
}

record updated? when $stmt->execute() $stmt->affected_rows

considering the following for my question:
$success = false;
$err_msg = '';
$sql = 'UPDATE task SET title = ? WHERE task_id = ?';
$conn = connect('w'); // create database connection: r= read, w= write
$stmt = $conn->stmt_init(); // initialize a prepared statement
$stmt->prepare($sql);
$stmt->bind_param('si', $_POST['title'], $_POST['id']);
$stmt->execute();
If i want to check if an insert or a deletion was succesfull, i could easily check for the affected_rows, like this:
if ($stmt->affected_rows > 0) {
$success = true;
} else {
$err_msg = $stmt->error;
}
If $stmt->affected_rows equals -1, it means that $stmt->execute() executed correctly but did not insert the record or did not delete the record successfully.
But, what about an update ? What is the correct way to deal with an update?
The way i do it is by checking for the return value :
$isRecordUpdated = $stmt->execute();
if (!$isRecordUpdated) {
// execute failed, therefore NO record updated!
} else {
//execute success, record updated!
}
Is that the correct way you guys are doing it?
It seems to me that there are really two equivalent and correct ways of doing this: either by checking the return value of execute as you do, or by checking the affected_rows value. -1 means the query errored out; 0 means that it did not affect (delete or update) any rows because there were none matching the query.
Since it seems there is no "better" way, you should pick what would be most convenient for your code. If e.g. using one approach over the other means that you can then share code among all types of queries, you might want to pick that one.
Why not store the value from mysql-affected-rows into a property of that object when you call execute()?
http://php.net/manual/en/function.mysql-affected-rows.php

Categories