Fatal error: Uncaught Error: mysqli object is already closed in C:\xampp\htdocs

Hi I am new to coding and a little stuck on where I have gone wrong with my code, as I keep receiving the following error when logging into my webpage:
Fatal error : Uncaught Error: mysqli object is already closed in C:\xampp\htdocs\flower\login.php:10 Stack trace: #0 C:\xampp\htdocs\flower\login.php(10): mysqli_real_escape_string(Object(mysqli), ‘[email protected]…’) #1 {main} thrown in C:\xampp\htdocs\flower\login.php on line 10
Any help would be very much appreciated.

Login Page

<?php

@include 'config.php';

session_start();

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

   $filter_email = filter_var($_POST['email'], FILTER_SANITIZE_STRING);
   $email = mysqli_real_escape_string($conn, $filter_email);
   $filter_pass = filter_var($_POST['pass'], FILTER_SANITIZE_STRING);
   $pass = mysqli_real_escape_string($conn, md5($filter_pass));

   $select_users = mysqli_query($conn, "SELECT * FROM `users` WHERE email = '$email' AND password = '$pass'") or die('query failed');


   if(mysqli_num_rows($select_users) > 0){
      
      $row = mysqli_fetch_assoc($select_users);

      if($row['user_type'] == 'admin'){

         $_SESSION['admin_name'] = $row['name'];
         $_SESSION['admin_email'] = $row['email'];
         $_SESSION['admin_id'] = $row['id'];
         header('location:admin_page.php');

      }elseif($row['user_type'] == 'user'){

         $_SESSION['user_name'] = $row['name'];
         $_SESSION['user_email'] = $row['email'];
         $_SESSION['user_id'] = $row['id'];
         header('location:home.php');

      }else{
         $message[] = 'no user found!';
      }

   }else{
      $message[] = 'incorrect email or password!';
   }

}

?>

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>login</title>

   <!-- font awesome cdn link  -->
   <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">

   <!-- custom css file link  -->
   <link rel="stylesheet" href="css/style.css">

</head>
<body>

<?php
if(isset($message)){
   foreach($message as $message){
      echo '
      <div class="message">
         <span>'.$message.'</span>
         <i class="fas fa-times" onclick="this.parentElement.remove();"></i>
      </div>
      ';
   }
}
?>
   
<section class="form-container">

   <form action="" method="post">
      <h3>login now</h3>
      <input type="email" name="email" class="box" placeholder="enter your email" required>
      <input type="password" name="pass" class="box" placeholder="enter your password" required>
      <input type="submit" class="btn" name="submit" value="login now">
      <p>don't have an account? <a href="register.php">register now</a></p>
   </form>

</section>

</body>
</html>

Db connect

<?php 

	/* NOTE:  this is a sample file and should be amended as required. */
$servername = "localhost";
$username = "kelly416";
$password = "Cocojackwillphoebetank5";
$dbname = "kelly416_shop_db";

// create the connection
$conn = new mysqli($servername, $username, $password, $dbname);
// if the connection fails send a message
if (!$conn) {
    die("Connection failed. Notify Administrator.");
}


?>

The database connection is failing, but you don’t know it since the connection error handing doesn’t work that way. When you use OOP notation, $conn will always be an object, a true value, even if the connection failed.

The mysqli procedural and OOP notation produces different types of errors for the same problem, which is yet another reason to not use the mysqli extension, and instead use the much simpler and more modern PDO extension.

You should also not output the raw database errors onto a live web page, since this gives hackers useful information when they intentionally do things that cause errors.

Instead, use exceptions for errors and only catch and handle them in your code for user recoverable errors, such as when inserting/updating duplicate or out of range user submitted data. In all other case, simply let php catch and handle the database exception, where php will use its error related settings to control what happens with the actual error information (database statement errors will ‘automatically’ get displayed/logged the same as php errors.) This will let you remove any existing database error handling you may have, since it won’t get executed upon an error, simplifying the code.

Here’s a laundry list of points for this code -

  1. Don’t use the @ error suppressor, ever.
  2. Use ‘require’ for things you code must have for it to work.
  3. Don’t attempt to detect if the submit button is set. There are cases where it won’t be. Instead, detect if a post method form has been submitted.
  4. Keep the form data as a set, in an array variable, then operate on elements in this array variable throughout the rest of the code.
  5. Trim all the input data before validating it.
  6. Validate each input separately, storing user/validation errors in an array using the field name as the array index.
  7. Don’t use the FILTER_SANITIZE_xxx flags, as this modifies the data values. Instead, use the FILTER_VALIDATE_xxx flags.
  8. You must test the result of the filter_var() call to make sure that the input value passed validation/sanitization, before using the value.
  9. Since you haven’t set the character set that php is using when you made the mysqli connection, at all, to match the character set being used with your database tables, the mysqli_real_escape_string() call may be ineffective. The modern method of protecting against sql special characters in a value from being able to break the sql query syntax, which is how sql injection is accomplished, is to use prepared queries. This actually simplifies the code and the sql query syntax, provided you use the much simpler and more modern PDO extension.
  10. Don’t use md5() for hashing passwords. It was never intended for this purpose. Instead, use php’s password_hash() and password_verify().
  11. You can just fetch/test the fetched row in a single operation. You don’t need to use any _num_rows() call.
  12. The only value you store in a session variable upon successful login should be the user’s id. You should query on each page request to get any other user information, such as the user’s name, permissions, … This is so that any changed made to those values will take effect on the very next page request after they have been changed, without requiring the user to log out and back in again.
  13. The only redirect you should have in post method form processing is to the exact same url of the current page, to cause a get request for that page. If you want the user to be able to goto another page, provide navigation links, or even better put the login operation on any page that needs it.
  14. Every redirect needs an exit/die statement to stop php code execution.
  15. When conditional logic ‘failure’ code is much shorter than the ‘success’ code, invert the condition being tested and put the ‘failure’ code first. This makes you code clear and cleaner.
  16. An empty form action="" attribute is actually invalid in html5. To cause the form to submit to the same page it is on, simply leave out the entire action attribute.
  17. You should validate your resulting web pages at validator.w3.org
  18. You should repopulate form field values/options/checkboxs/radiobuttons upon an error so that the user doesn’t need to keep reentering/selecting/checking values over and over.
  19. Any dynamic value that you output in a html context should have htmlentities() applied to it to help prevent cross site scripting.

Thank you so much for taking the time to reply and explain to me in depth it is very much appreciated. I will work through all your points and hopefully have no more error.
Thank you again so much

Sponsor our Newsletter | Privacy Policy | Terms of Service