Forgot password recovery feature is mandatory for login system. It helps the user to update account password which they have forgotten. Using forgot password link user can easily reset their account password. In this tutorial, we’ll show you the forgot password recovery process and create a script to implement forgot password email functionality in PHP Login System.
Our previous PHP Login System tutorial had provided a guide to implementing user registration and login system in PHP. Now we’ll extend that PHP login script with forgot password functionality with PHP and MySQL. Also, we’ll show you how can send reset password link via email to the user. Before you begin with this forgot password recovery tutorial, we suggest go through the previous PHP login system tutorial first.Here the files list that is used in forgot password recovery process.
user.php
– handle database related worksuserAccount.php
– handle forgot password, reset password, and email sending.index.php
– display login form with forgot password link.forgotPassword.php
– display forgot password form.resetPassword.php
– display reset password form.style.css
– styling login, forgot, and reset form.
Database Table Creation
To store the user details you already have ausers
table in the MySQL database. Now add a column named forgot_pass_identity
to user table.ALTER TABLE `users` ADD `forgot_pass_identity` VARCHAR(32) NOT NULL AFTER `phone`;Entire
users
table SQL will like the following.
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`last_name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`email` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`password` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`phone` varchar(15) COLLATE utf8_unicode_ci NOT NULL,
`forgot_pass_identity` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
`status` enum('1','0') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
User Class (user.php)
User Class handles all the database related works, like get, insert, update user data. This class has extended with one method (update()
) for update forgot password identity code in the users
table.
<?php
/*
* User Class
* This class is used for database related (connect fetch, and insert) operations
* @author CodexWorld.com
* @url http://www.codexworld.com
* @license http://www.codexworld.com/license
*/
class User{
private $dbHost = "localhost";
private $dbUsername = "root";
private $dbPassword = "";
private $dbName = "codexworld";
private $userTbl = "users";
public function __construct(){
if(!isset($this->db)){
// Connect to the database
$conn = new mysqli($this->dbHost, $this->dbUsername, $this->dbPassword, $this->dbName);
if($conn->connect_error){
die("Failed to connect with MySQL: " . $conn->connect_error);
}else{
$this->db = $conn;
}
}
}
/*
* Returns rows from the database based on the conditions
* @param string name of the table
* @param array select, where, order_by, limit and return_type conditions
*/
public function getRows($conditions = array()){
$sql = 'SELECT ';
$sql .= array_key_exists("select",$conditions)?$conditions['select']:'*';
$sql .= ' FROM '.$this->userTbl;
if(array_key_exists("where",$conditions)){
$sql .= ' WHERE ';
$i = 0;
foreach($conditions['where'] as $key => $value){
$pre = ($i > 0)?' AND ':'';
$sql .= $pre.$key." = '".$value."'";
$i++;
}
}
if(array_key_exists("order_by",$conditions)){
$sql .= ' ORDER BY '.$conditions['order_by'];
}
if(array_key_exists("start",$conditions) && array_key_exists("limit",$conditions)){
$sql .= ' LIMIT '.$conditions['start'].','.$conditions['limit'];
}elseif(!array_key_exists("start",$conditions) && array_key_exists("limit",$conditions)){
$sql .= ' LIMIT '.$conditions['limit'];
}
$result = $this->db->query($sql);
if(array_key_exists("return_type",$conditions) && $conditions['return_type'] != 'all'){
switch($conditions['return_type']){
case 'count':
$data = $result->num_rows;
break;
case 'single':
$data = $result->fetch_assoc();
break;
default:
$data = '';
}
}else{
if($result->num_rows > 0){
while($row = $result->fetch_assoc()){
$data[] = $row;
}
}
}
return !empty($data)?$data:false;
}
/*
* Insert data into the database
* @param string name of the table
* @param array the data for inserting into the table
*/
public function insert($data){
if(!empty($data) && is_array($data)){
$columns = '';
$values = '';
$i = 0;
if(!array_key_exists('created',$data)){
$data['created'] = date("Y-m-d H:i:s");
}
if(!array_key_exists('modified',$data)){
$data['modified'] = date("Y-m-d H:i:s");
}
foreach($data as $key=>$val){
$pre = ($i > 0)?', ':'';
$columns .= $pre.$key;
$values .= $pre."'".$val."'";
$i++;
}
$query = "INSERT INTO ".$this->userTbl." (".$columns.") VALUES (".$values.")";
$insert = $this->db->query($query);
return $insert?$this->db->insert_id:false;
}else{
return false;
}
}
/*
* Update data into the database
* @param string name of the table
* @param array the data for inserting into the table
*/
public function update($data, $conditions){
if(!empty($data) && is_array($data) && !empty($conditions)){
//prepare columns and values sql
$cols_vals = '';
$i = 0;
if(!array_key_exists('modified',$data)){
$data['modified'] = date("Y-m-d H:i:s");
}
foreach($data as $key=>$val){
$pre = ($i > 0)?', ':'';
$cols_vals .= $pre.$key." = '".$val."'";
$i++;
}
//prepare where conditions
$whereSql = '';
$ci = 0;
foreach($conditions as $key => $value){
$pre = ($ci > 0)?' AND ':'';
$whereSql .= $pre.$key." = '".$value."'";
$ci++;
}
//prepare sql query
$query = "UPDATE ".$this->userTbl." SET ".$cols_vals." WHERE ".$whereSql;
//update data
$update = $this->db->query($query);
return $update?true:false;
}else{
return false;
}
}
}
userAccount.php
This file controls the registration, login, logout, forgot password, and reset password request. This script has extended with two requestsforgotSubmit
and resetSubmit
. Also, the email sending functionality is added in forgotSubmit request to send the email with reset password link to the user.Note that:
<?php
if(isset($_POST['forgotSubmit'])){
//check whether email is empty
if(!empty($_POST['email'])){
//check whether user exists in the database
$prevCon['where'] = array('email'=>$_POST['email']);
$prevCon['return_type'] = 'count';
$prevUser = $user->getRows($prevCon);
if($prevUser > 0){
//generat unique string
$uniqidStr = md5(uniqid(mt_rand()));;
//update data with forgot pass code
$conditions = array(
'email' => $_POST['email']
);
$data = array(
'forgot_pass_identity' => $uniqidStr
);
$update = $user->update($data, $conditions);
if($update){
$resetPassLink = 'http://codexworld.com/resetPassword.php?fp_code='.$uniqidStr;
//get user details
$con['where'] = array('email'=>$_POST['email']);
$con['return_type'] = 'single';
$userDetails = $user->getRows($con);
//send reset password email
$to = $userDetails['email'];
$subject = "Password Update Request";
$mailContent = 'Dear '.$userDetails['first_name'].',
<br/>Recently a request was submitted to reset a password for your account. If this was a mistake, just ignore this email and nothing will happen.
<br/>To reset your password, visit the following link: <a href="'.$resetPassLink.'">'.$resetPassLink.'</a>
<br/><br/>Regards,
<br/>CodexWorld';
//set content-type header for sending HTML email
$headers = "MIME-Version: 1.0" . "\r\n";
$headers .= "Content-type:text/html;charset=UTF-8" . "\r\n";
//additional headers
$headers .= 'From: CodexWorld<sender@example.com>' . "\r\n";
//send email
mail($to,$subject,$mailContent,$headers);
$sessData['status']['type'] = 'success';
$sessData['status']['msg'] = 'Please check your e-mail, we have sent a password reset link to your registered email.';
}else{
$sessData['status']['type'] = 'error';
$sessData['status']['msg'] = 'Some problem occurred, please try again.';
}
}else{
$sessData['status']['type'] = 'error';
$sessData['status']['msg'] = 'Given email is not associated with any account.';
}
}else{
$sessData['status']['type'] = 'error';
$sessData['status']['msg'] = 'Enter email to create a new password for your account.';
}
//store reset password status into the session
$_SESSION['sessData'] = $sessData;
//redirect to the forgot pasword page
header("Location:forgotPassword.php");
}elseif(isset($_POST['resetSubmit'])){
$fp_code = '';
if(!empty($_POST['password']) && !empty($_POST['confirm_password']) && !empty($_POST['fp_code'])){
$fp_code = $_POST['fp_code'];
//password and confirm password comparison
if($_POST['password'] !== $_POST['confirm_password']){
$sessData['status']['type'] = 'error';
$sessData['status']['msg'] = 'Confirm password must match with the password.';
}else{
//check whether identity code exists in the database
$prevCon['where'] = array('forgot_pass_identity' => $fp_code);
$prevCon['return_type'] = 'single';
$prevUser = $user->getRows($prevCon);
if(!empty($prevUser)){
//update data with new password
$conditions = array(
'forgot_pass_identity' => $fp_code
);
$data = array(
'password' => md5($_POST['password'])
);
$update = $user->update($data, $conditions);
if($update){
$sessData['status']['type'] = 'success';
$sessData['status']['msg'] = 'Your account password has been reset successfully. Please login with your new password.';
}else{
$sessData['status']['type'] = 'error';
$sessData['status']['msg'] = 'Some problem occurred, please try again.';
}
}else{
$sessData['status']['type'] = 'error';
$sessData['status']['msg'] = 'You does not authorized to reset new password of this account.';
}
}
}else{
$sessData['status']['type'] = 'error';
$sessData['status']['msg'] = 'All fields are mandatory, please fill all the fields.';
}
//store reset password status into the session
$_SESSION['sessData'] = $sessData;
$redirectURL = ($sessData['status']['type'] == 'success')?'index.php':'resetPassword.php?fp_code='.$fp_code;
//redirect to the login/reset pasword page
header("Location:".$redirectURL);
}
Don’t forget to change the email content, from address, and website base URL in
$resetPassLink
as per your project requirement.Login Form with Forgot Password Link (index.php)
A forgot password link is added in the existing login form for navigating user to forgot password form.
<div class="container">
<form action="userAccount.php" method="post">
<input type="email" name="email" placeholder="EMAIL" required="">
<input type="password" name="password" placeholder="PASSWORD" required="">
<div class="send-button">
<input type="submit" name="loginSubmit" value="LOGIN">
</div>
<a href="forgotPassword.php">Forgot password?</a>
</form>
</div>
Forgot Password Form (forgotPassword.php)
A form displays to collect the account email for identifying user account. The form is submitted to theuserAccount.php
file with a forgotSubmit
request.
<?php
session_start();
$sessData = !empty($_SESSION['sessData'])?$_SESSION['sessData']:'';
if(!empty($sessData['status']['msg'])){
$statusMsg = $sessData['status']['msg'];
$statusMsgType = $sessData['status']['type'];
unset($_SESSION['sessData']['status']);
}
?>
<h2>Enter the Email of Your Account to Reset New Password</h2>
<?php echo !empty($statusMsg)?'<p class="'.$statusMsgType.'">'.$statusMsg.'</p>':''; ?>
<div class="container">
<div class="regisFrm">
<form action="userAccount.php" method="post">
<input type="email" name="email" placeholder="EMAIL" required="">
<div class="send-button">
<input type="submit" name="forgotSubmit" value="CONTINUE">
</div>
</form>
</div>
</div>
Reset Password Form (resetPassword.php)
This script is loaded when the user clicks on the reset password link in the mail. A form will appear to update account password and the form is submitted to theuserAccount.php
file with a resetSubmit
request.
<?php
session_start();
$sessData = !empty($_SESSION['sessData'])?$_SESSION['sessData']:'';
if(!empty($sessData['status']['msg'])){
$statusMsg = $sessData['status']['msg'];
$statusMsgType = $sessData['status']['type'];
unset($_SESSION['sessData']['status']);
}
?>
<h2>Reset Your Account Password</h2>
<?php echo !empty($statusMsg)?'<p class="'.$statusMsgType.'">'.$statusMsg.'</p>':''; ?>
<div class="container">
<div class="regisFrm">
<form action="userAccount.php" method="post">
<input type="password" name="password" placeholder="PASSWORD" required="">
<input type="password" name="confirm_password" placeholder="CONFIRM PASSWORD" required="">
<div class="send-button">
<input type="hidden" name="fp_code" value="<?php echo $_REQUEST['fp_code']; ?>"/>
<input type="submit" name="resetSubmit" value="RESET PASSWORD">
</div>
</form>
</div>
</div>
CSS Code (style.css)
No new CSS code are added to implement forgot password functionality. The same CSS file is included from our login system.
.container {
width: 40%;
margin: 0 auto;
background-color: #f7f7f7;
color: #757575;
font-family: 'Raleway', sans-serif;
text-align: left;
padding: 30px;
}
h2 {
font-size: 30px;
font-weight: 600;
margin-bottom: 10px;
}
.container p {
font-size: 18px;
font-weight: 500;
margin-bottom: 20px;
}
.regisFrm input[type="text"], .regisFrm input[type="email"], .regisFrm input[type="password"] {
width: 94.5%;
padding: 10px;
margin: 10px 0;
outline: none;
color: #000;
font-weight: 500;
font-family: 'Roboto', sans-serif;
}
.send-button {
text-align: center;
margin-top: 20px;
}
.send-button input[type="submit"] {
padding: 10px 0;
width: 60%;
font-family: 'Roboto', sans-serif;
font-size: 18px;
font-weight: 500;
border: none;
outline: none;
color: #FFF;
background-color: #2196F3;
cursor: pointer;
}
.send-button input[type="submit"]:hover {
background-color: #055d54;
}
a.logout{float: right;}
p.success{color:#34A853;}
p.error{color:#EA4335;}
Comments