I am trying to de-bug my prepared statement code but having some problems. When using the below script, I see 'test one' and 'test two' echoed in my browser but no 'test three'; my ->fetch() statement doesn't appear to be working. No errors.
if (empty($login_errors)) { // OK to proceed!
echo 'test one';
$pas = get_password_hash($p);
$loginQuery = $dbc->prepare("SELECT id, username, type FROM user WHERE (email=? AND pass=?)");
$loginQuery->bind_param('ss',$e,$pas);
$loginQuery->execute();
$loginQuery->bind_result($id,$username,$type);
echo 'test two';
while($loginQuery->fetch()){
echo 'test three';
$idRow = $id;
$usernameRow = $username;
$_SESSION['user_id'] = $idRow[0];
$_SESSION['username'] = $usernameRow[0];
}
echo 'test four';
}
My first thought was that ->fetch()
is used when there is only one field being selected (one bind result). I tried using while($loginQuery->fetch_all()){
because i have three ( $loginQuery->bind_result($id,$username,$type);
) but this brings back a HTTP 500 error.Is this thinking correct? fetch() for one, fetch_all() for many?
Why do neither fetches work? Can you see any issues with my code?
'test four' is displayed. This would suggest that the query is returning no data. This is confusing because before this prepared statement, I used concatenation and the below worked without any issues:
$q = "SELECT id, username, type, IF(date_expires >= NOW(), true, false) FROM user WHERE (email='$e' AND pass='" . get_password_hash($p) . "')";
$r = mysqli_query ($dbc, $q);
Is this thinking correct? fetch() for one, fetch_all() for many?
That's actually two different methods of two different objects.
- fetch() belongs to mysqli statement object and uses ugly method of assigning query result to global variables. Can be run in a loop. Loop is a thing where you can do fetch one single row... many times. So, nothing contradicting in using fetch() to get many records
- fetch_all() belongs to mysqli result object and being just a syntax sugar, running fetch_assoc() for you in a loop.
Something like this should work:
$loginQuery = $dbc->stmt_init();
$loginQuery = $loginQuery->prepare("SELECT id, username, type FROM user WHERE LOWER(email) = LOWER(?) AND pass = ?");
$loginQuery->bind_param('ss',$e,$pas);
$loginQuery->execute();
$results = $loginQuery->get_result();
if($results->num_rows > 0)
{
$row = $results->fetch_assoc();
$user_name = $row['username'];
$user_id = $row['id'];
$_SESSION['user_id'] = $user_id;
$_SESSION['username'] = $user_name;
}
else
{
echo "no rows returned, bad username and password.";
}
Assuming your $dbc is a mysqli object, you also need to call stmt_init()
before prepare:
$loginQuery = $dbc->stmt_init();
Also made the query case insensitive for the email by using LOWER(), you'd wanna do this on login and registration. That way someone doesn't register with email@email.com, then someone else registers with EMAIL@email.com. You should check that the email they are registering with does not match a email in your database already, and when checking use the mysql LOWER() function in the comparison.
The above code would get you the first row in the result set. If you need to get all rows you were kind of on the right track. but you would have to do:
$usersQuery = $dbc->prepare("SELECT id, username, type FROM user");
$usersQuery->execute();
$results = $usersQuery->get_result();
if($results->num_rows > 0)
{
while($row = $results->fetch_assoc())
{
$user_id = $row['id'];
$user_name = $row['username'];
$user_type = $row['type'];
}
}