Short notes first:
- The warning
mysql_fetch_assoc() expects parameter 1 to be resource, boolean given ...
was probably raised because mysqli_query returned FALSE - the value used to signalize a failure - instead of a mysqli_result (see this). Why you have this situation, it's up to you to find out. I didn't saw any inadvertence which might have produced it.
- The notice
Undefined variable: check_username ...
is raised in the case when no record is found in the db table. In that case, the variable definitions and the values assignation inside of while statement don't take place. Therefore, outside of while statement the variables $check_username
and $check_password
are "recognized" as not defined.
- Don't forget to call exit() after the redirectioning code, e.g. after sending the Location response header.
Now, your first big problem is, that you don't use any prepared statements in order to avoid SQL injection. So, instead of mysqli_query, you should definitely use the functions involved in preparing an sql statement - in this case yours - and injecting the values for eventual parameter markers found in it.
The second big problem would be, indeed, if you would decide to save the password in clear text, or in a not secure form - like md5. The password should be strongly encrypted. For this task I would recommend you to use the password_hash function - either with the PASSWORD_BCRYPT option (a constant defining the Blowfish hashing algorithm), or with the PASSWORD_ARGON2I option (constant defining the Argon2 hashing algorithm and introduced as of PHP 7.2.0). You should thoroughly read the documentation of password_hash and of the used constants. You might maybe want to read this post too.
I wrote a code version below, implementing the two points above, in the hope that it will bring more clarity. I used the object-oriented mysqli library (instead of the procedural one) and my naming/coding conventions though (inclusive for the db table) - so, as I would apply them in my project. It also contains a credentials validation part on the server-side. A required step, actually.
In principle, before creating a login.php code, you should create a page responsible with saving user credentials in the db table. In that page (create-user.php for example), for the given password a password hash will be created (using the password_hash function) and saved. The hash will be a string of minimum 60 aleatory characters. The relevant page code would look like this:
//...
$username = $_POST['username'];
$password = $_POST['password'];
// Create a hash from the posted password.
$passwordHash = password_hash($password, PASSWORD_BCRYPT);
$sql = 'INSERT INTO user_credentials (username, password) VALUES (?, ?)';
$statement = $connection->prepare($sql);
$statement->bind_param('ss', $username, $passwordHash);
$statement->execute();
//...
But, in order to shorten the task for now, you can create in PHP a password hash from the value pass
and copy and save it manually, along with the user
value:
// Create a hash from the "pass" value.
$passwordHash = password_hash('pass', PASSWORD_BCRYPT);
echo $passwordHash;
Suggestion: for applying error/exception handling you could take a look at this and this tutorial articles.
At last, here is the login page, the db connection code, the table structure and the file system structure that I used:
login.php
<?php
/* Don't use include_once to include the connection page! */
require 'includes/connection.php';
// Operations upon form submission.
if (isset($_POST['submit'])) {
// Validate username.
if (!isset($_POST['username']) || empty($_POST['username'])) {
$errors[] = 'Please provide the username.';
} /* Other validations here using "else if". For example on username length. */
// Validate password.
if (!isset($_POST['password']) || empty($_POST['password'])) {
$errors[] = 'Please provide the password.';
} /* Other validations here using "else if". For example on password length. */
if (!isset($errors)) { // No errors.
// Get posted credentials.
$username = $_POST['username'];
$password = $_POST['password'];
/*
* The SQL statement to be prepared. Notice the so-called markers,
* e.g. the "?" signs. They will be replaced later with the
* corresponding values when using mysqli_stmt::bind_param.
*
* @link http://php.net/manual/en/mysqli.prepare.php
*/
$sql = 'SELECT username, password
FROM user_credentials
WHERE username = ?
LIMIT 1';
/*
* Prepare the SQL statement for execution - ONLY ONCE.
*
* @link http://php.net/manual/en/mysqli.prepare.php
*/
$statement = $connection->prepare($sql);
/*
* Bind variables for the parameter markers (?) in the
* SQL statement that was passed to prepare(). The first
* argument of bind_param() is a string that contains one
* or more characters which specify the types for the
* corresponding bind variables.
*
* @link http://php.net/manual/en/mysqli-stmt.bind-param.php
*/
$statement->bind_param('s', $username);
/*
* Execute the prepared SQL statement.
* When executed any parameter markers which exist will
* automatically be replaced with the appropriate data.
*
* @link http://php.net/manual/en/mysqli-stmt.execute.php
*/
$statement->execute();
/*
* Get the result set from the prepared statement.
*
* NOTA BENE:
* Available only with mysqlnd ("MySQL Native Driver")! If this
* is not installed, then uncomment "extension=php_mysqli_mysqlnd.dll" in
* PHP config file (php.ini) and restart web server (I assume Apache) and
* mysql service. Or use the following functions instead:
* mysqli_stmt::store_result + mysqli_stmt::bind_result + mysqli_stmt::fetch.
*
* @link http://php.net/manual/en/mysqli-stmt.get-result.php
* @link https://stackoverflow.com/questions/8321096/call-to-undefined-method-mysqli-stmtget-result
*/
$result = $statement->get_result();
/*
* Fetch the credentials into an associative array.
* If no record is found, the operation returns NULL.
*/
$credentials = $result->fetch_array(MYSQLI_ASSOC);
/*
* Free the memory associated with the result. You should
* always free your result when it is not needed anymore.
*
* @link http://php.net/manual/en/mysqli-result.free.php
*/
$result->close();
/*
* Close the prepared statement. It also deallocates the statement handle.
* If the statement has pending or unread results, it cancels them
* so that the next query can be executed.
*
* @link http://php.net/manual/en/mysqli-stmt.close.php
*/
$statement->close();
/*
* Close the previously opened database connection. Not really needed,
* because PHP closes the connection after script processing finishes.
*
* @link http://php.net/manual/en/mysqli.close.php
*/
$connection->close();
if (isset($credentials) && $credentials) { // Record found.
$fetchedUsername = $credentials['username'];
$fetchedPasswordHash = $credentials['password'];
/*
* Compare the posted username with the one saved in db and the posted
* password with the password hash saved in db using password_hash.
*
* @link https://secure.php.net/manual/en/function.password-verify.php
* @link https://secure.php.net/manual/en/function.password-hash.php
*/
if (
$username === $fetchedUsername &&
password_verify($password, $fetchedPasswordHash)
) {
header('Location: welcome.html');
exit();
} else {
$errors[] = 'Invalid credentials. Please try again.';
}
} else {
$errors[] = 'No credentials found for the given user.';
}
}
}
echo '<pre>' . print_r(password_hash('pass', PASSWORD_BCRYPT), TRUE) . '</pre>';
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
<meta charset="UTF-8" />
<!-- The above 3 meta tags must come first in the head -->
<title>Demo - Login</title>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#username').focus();
});
function validateForm() {
return true;
}
</script>
<style type="text/css">
body {
padding: 30px;
}
label {
display: block;
font-weight: 400;
}
input[type="text"],
input[type="password"] {
display: block;
margin-bottom: 20px;
}
button {
display: block;
padding: 7px 10px;
background-color: #8daf15;
color: #fff;
border: none;
}
.messages {
margin-bottom: 20px;
}
.messages .error {
color: #c00;
}
</style>
</head>
<body>
<div class="messages">
<?php
if (isset($errors)) {
foreach ($errors as $error) {
?>
<div class="error">
<?php echo $error; ?>
</div>
<?php
}
}
?>
</div>
<div class="form_login">
<form name="credentials" action="" method="post" onsubmit="return validateForm();">
<label for="username">Username:</label>
<input type="text" id="username" name="username" value="<?php echo isset($username) ? $username : ''; ?>">
<label for="password">Password:</label>
<input type="password" id="password" name="password" value="<?php echo isset($password) ? $password : ''; ?>">
<button type="submit" name="submit" value="submit">
Submit
</button>
</form>
</div>
</body>
</html>
includes/connection.php
<?php
// Db configs.
define('HOST', 'localhost');
define('PORT', 3306);
define('DATABASE', 'users');
define('USERNAME', 'youruser');
define('PASSWORD', 'yourpass');
/*
* Enable internal report functions. This enables the exception handling,
* e.g. mysqli will not throw PHP warnings anymore, but mysqli exceptions
* (mysqli_sql_exception).
*
* MYSQLI_REPORT_ERROR: Report errors from mysqli function calls.
* MYSQLI_REPORT_STRICT: Throw a mysqli_sql_exception for errors instead of warnings.
*
* @link http://php.net/manual/en/class.mysqli-driver.php
* @link http://php.net/manual/en/mysqli-driver.report-mode.php
* @link http://php.net/manual/en/mysqli.constants.php
*/
$mysqliDriver = new mysqli_driver();
$mysqliDriver->report_mode = (MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
/*
* Create a new db connection.
*
* @see http://php.net/manual/en/mysqli.construct.php
*/
$connection = new mysqli(HOST, USERNAME, PASSWORD, DATABASE, PORT);
Create table syntax
CREATE TABLE `user_credentials` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(100) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
File system structure
includes
connection.php
login.php
welcome.html