Trying to convert to OOP mysqli and trouble with fetch

I got this login code from Youtube and trying to convert it from Procedural ( I think ) to OOP and running into a problem fetching information. There may be other errors, but I seem to have cleared up everything up to line 27, which I have highlighted. It gives me this, Fatal error: Call to undefined method mysqli_stmt::fetch_assoc() in C:\Apache24\htdocs\WS\includes\login.inc.php on line 27. Any help will be appreciated. Thank you.

<?php

session_start();

if (isset($_POST['submit'])) {

    include 'dbh.inc.php';

    $uid = $_POST['uid'];
    $pwd = $_POST['pwd'];

    if (empty($uid) || empty($pwd)) {
        header("Location: ../index.php?login=empty");
        exit();
    } else {
        $sql = "SELECT * FROM users WHERE user_uid=? OR user_email=?";
        $stmt = $conn->prepare($sql);
        $stmt->bind_param("ss", $uid, $uid);
        $stmt->execute();
        $resultCheck = $stmt->get_result();
        // Not sure if this is correct way to see if there were no results. It was a IF <1 snippet
        if (!$resultCheck) {
            header("Location: ../index.php?login=error");
            $stmt->close();
            $conn->close();
            exit();
        } else {
        // Line 27 Below 
           if ($row = $stmt->fetch_assoc()) {
            // if ($row = mysqli_fetch_assoc($result)) {
                // De-hashing the password
                $hashedPwdCheck = password_verify($pwd, $row['user_pwd']);
                if ($hashedPwdCheck == false) {
                    header("Location: ../index.php?login=error");
                    exit();
                } elseif ($hashedPwdCheck == true) {
                    // Log in the user here
                    $_SESSION['u_id'] = $row['user_id'];
                    $_SESSION['u_first'] = $row['user_first'];
                    $_SESSION['u_last'] = $row['user_last'];
                    $_SESSION['u_email'] = $row['user_email'];
                    $_SESSION['u_uid'] = $row['user_uid'];
                    header("Location: ../index.php?login=success");
                    exit();
                }
            }
          $stmt->close();
          $conn->close();
        }
    }
} else {
    header("Location: ../index.php?login=error");
    exit();
}

fetch_assoc() isn’t part of stmt. You have to use fetch().

One more reason to use PDO. it doesn’t use different classes for queries and prepared statements.

1 Like

Your code would be easier to read if you use early returns. For example:

if (isset($_POST['submit'])) {
  // lots and lots of code here
} else {
  header("Location: ../index.php?login=error");
  exit();
}

can also be written as

if (!isset($_POST['submit'])) {
  header("Location: ../index.php?login=error");
  exit();
}

// lots and lots of code here

so the indentation level is one less, and you don’t need to scoll all the way down for the else.

I would also suggest to switch from mysqli to PDO, as that a much nicer API (in my opinion).

1 Like

That looks to me like it’s OOP already. Is that after you converted it?

Agreed.

The code also seems a bit clunky in places.

if (isset($_POST['submit'])) {

I see this a lot in the forums. But I prefer to see:-

 if($_SERVER['REQUEST_METHOD'] === 'POST'){

Stuff like thins:-

$hashedPwdCheck = password_verify($pwd, $row['user_pwd']);
if ($hashedPwdCheck == false) {

Why not just:-

if(!password_verify($pwd, $row['user_pwd'])){
     header("Location: ../index.php?login=error");
     exit();
}
else{
      // Log in !!
}

Thanks everyone for all the feedback. Yes, it WAS Procedural but what you see is me trying to change it to OOP. I had stopped coding back in 2012 and just now decided to take it back up as a hobby. I guess my issue is I have been bouncing all over Youtube watching different videos and everyone codes differently. You mention PDO. Well, I have seen examples of it, but even that seems like one person codes it a little differently from another, so how do I know which person is doing it the RIGHT way?

I have lots of time to learn, as I mentioned this just being a hobby, so if PDO is the way I should go, could someone point me to some decent materials to learn from, so that I don’t take up someone elses bad habits?

Thanks again.

A good question.
But not always easy to answer,as a lot of it is opinion and personal preference.
But on the other hand a lot of stuff is either good/best practice or plain wrong.
It can sometimes be hard to make those distinctions.

PDO is the same. It has multiple different ways to bind parameters which leads to confusion and inconsistency. Some people will use both bindValue and bindParam in the same prepared call. But you should only use one of them. mysqli_ on the other hand only has one way of binding parameters.

The first thing I would do is, stop closing the database connection. Let PHP do it by itself. It’s bad practice to open and close the connection every time you want to make a database call. You should open the connection and leave it open so other calls can also use the same connection. PHP will close the connection by default after it gets done executing the file.

1 Like

The only time I can think of that I might want to explicitly close a connection is when I was connecting to different databases and I was concerned about resource use. eg.

connect to Postgres, get what I need, done
connect to MySQL, get want I need, done
etc.

In practice, I can count on one finger the number of times I’ve done that.

1 Like

Hello again. I’ve tried the same code above but as PDO, but on line 29, I get the following:
Fatal error: Call to a member function execute() on boolean on:

$stmt->execute(array(‘:uid’ => $uid, ‘:uid’ => $uid));

I am really trying to understand PDO . Any help or advice would be appreciated. I even took some of the advice from above posters and incorporated them into it.

<?php
session_start();

if (!isset($_POST['submit'])) {
    header("Location: ../index.php?login=error");
    exit();
}
    include 'dbh.inc.php';

	$pdo = new PDO($conn, $user, $password);

	$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
	$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

    $uid = $_POST['uid'];
    $pwd = $_POST['pwd'];

    if (empty($uid) || empty($pwd)) {
        header("Location: ../index.php?login=empty");
        exit();
    } else {
		$sql = 'SELECT * FROM users WHERE user_uid = :uid OR user_email = :uid';
		$stmt = $pdo->prepare($sql);
		$stmt->execute(array(':uid' => $uid, ':uid' => $uid));
		$resultCheck = $stmt->rowCount();
        if ($resultCheck <1) {
            header("Location: ../index.php?login=error");
            exit();
        } else {
			if ($row = $stmt->fetchAll()) {
				if(!password_verify($pwd, $row['user_pwd'])){
                    header("Location: ../index.php?login=error");
                    exit();
                } else {
                    $_SESSION['u_id'] = $row['user_id'];
                    $_SESSION['u_first'] = $row['user_first'];
                    $_SESSION['u_last'] = $row['user_last'];
                    $_SESSION['u_email'] = $row['user_email'];
                    $_SESSION['u_uid'] = $row['user_uid'];
                    header("Location: ../index.php?login=success");
                    exit();
                }
            }
        }
    }

http://php.net/manual/en/pdo.prepare.php

Return Values

If the database server successfully prepares the statement, PDO::prepare() returns a PDOStatement object. If the database server cannot successfully prepare the statement, PDO::prepare() returns FALSE or emits PDOException (depending on error handling).

In other words, most likely the query fails (can’t be prepared into a valid query) or the connection fails (no resource handle to attach the query to ).

Is your dbh.inc.php correct? Does the query work if you temporarily hard code in known values that should work?

I think it’s because of this. It’s entirely wrong. You are using the same exact variable for both the user id and the email.

2 Likes

That’s because there’s one variable being passed from form accepting either the username or useremail… making the value $uid. I even removed the email one and just tried username only and got the same error. I am so confused.

Check for typos in your queries and in your tables. Make sure they are spelt exactly the same.

Try temporarily changing the code to this

    exit();
}
//    include 'dbh.inc.php';
    require 'dbh.inc.php'; 

	$pdo = new PDO($conn, $user, $password);
/* debugging only */ 
var_dump($pdo);
exit;
/* end debugging */
	$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
1 Like

Thanks everyone for the help. I tried Mittineague’s change and I get this;

object(PDO)#1 (0) { }

Not sure what that means.

@Mittineague is trying to make sure that you are able to connect to the database.

Yes, failed connections are one of the more common reasons for getting a boolean false. Knowing that is not the cause of the problem narrows things down and you can change that bit of code back.

My guess is that prepare is putting variables into memory and because they are named the same are causing a problem. Though I think it is a good idea to have the named placeholders have names that help in recognition of what value they’ll get, they don’t have to.

Try changing to this

    } else {
		$sql = 'SELECT * FROM users WHERE user_uid = :u_uid OR user_email = :e_uid';
		$stmt = $pdo->prepare($sql);
		$stmt->execute(array(':u_uid' => $uid, ':e_uid' => $uid));
4 Likes

That was my initial thought in my OP above.

2 Likes

Wow! That was amazing! Changing those two made it work. I appreciate you both for helping.
The final code in that area looks like this now. You can see the changes I made as remarks. I had to change one other thing… the $row = $stmt->fetch() from $row = $stmt->fetchAll(). It works, but I may have taken on a tough hobby. -laughs-

		$sql = 'SELECT * FROM users WHERE user_uid = :u_uid OR user_email = :e_uid';
//        $result = mysqli_query($conn, $sql);
		$stmt = $pdo->prepare($sql);
//		$stmt->execute(array(':uid' => $uid, ':uid' => $uid));
		$stmt->execute(array(':u_uid' => $uid, ':e_uid' => $uid));
//        $resultCheck = mysqli_num_rows($result);
		$resultCheck = $stmt->rowCount();
        if ($resultCheck <1) {
            header("Location: ../index.php?login=error");
            exit();
        } else {
//          if ($row = mysqli_fetch_assoc($result)) {
			if ($row = $stmt->fetch()) {
//				if(!password_verify($pwd, $row['user_pwd'])){
				if(!password_verify($pwd, $row->user_pwd)){
                    header("Location: ../index.php?login=err");
                    exit();
                } else {
1 Like