In this advanced tutorial i will teach you the steps required to create a custom build login/member system with PHP.
The system itself ofcourse has alot of room for improvements, also, it’s very easily expandable, i’ve chosen to work in a modular way, so that if you change something you only have to change it in 1 file. I’ve done this by using functions, this script is a good example of the real power of PHP.
Features:
- Registration
- Lost password
- Various checks on passwords and usernames
- users can change their password
- Passwords are stored in a database with a seed added to it and they have sha1 encryption
- Easy to adjust & use
Requirements:
- Mysql database
- a php & mysql enabled host
- php mail() enabled host
- ftp access to your website
Overview
Steps:
- Creating the mysql table
- Creating a db_connect.inc.php file
- Creating the header.php file
- Creating the footer.php file
- Creating the index.php file
- Creating the login.php file
- Creating the logout.php file
- Creating a function.inc.php file
- Creating the mail.functions.inc.php file
- Creating the display.functions.inc.php file
- Creating the login.functions.inc.php file
- Creating the user.functions.inc.php file
- Creating the validation.functions.inc.php file
- Creating the lostpassword.php file
- Creating the changepassword.php file
- Creating the register.php file
- Creating the activate.php file
Step 1: Creating the mysql table
For this tutorials i presume you already know how to add tables to your database.
Table login (SQL code):
1 2 3 4 5 6 7 8 9 10 | CREATE TABLE `login` ( `loginid` int(10) unsigned NOT NULL auto_increment, `username` varchar(30) NOT NULL, `password` varchar(50) NOT NULL, `email` varchar(255) NOT NULL, `actcode` varchar(45) NOT NULL, `disabled` tinyint(1) NOT NULL default '0', `activated` tinyint(1) NOT NULL default '0', PRIMARY KEY (`loginid`) ); |
Now let us add the administrator account:
- username: admin
- password: yourpasswordhere
** Change the text yourpassword here with the desired password.
** Change the text youremailhere with your email adress.
SQL query:
1 | insert into login (username,password,email,activated) value ('admin',sha1(concat('yourpasswordhere','0dAfghRqSTgx')),'youremailhere','1'); |
The table is now ready, and the administrator account has been added. let us move on to step 2.
Step 2: Creating a db_connect.inc.php file
This file will be used to manage the connection to the database.
File db_connect.inc.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php // Database settings // database hostname or IP. default:localhost // localhost will be correct for 99% of times define("HOST", "localhost"); // Database user define("DBUSER", "dbuser"); // Database password define("PASS", "dbpass"); // Database name define("DB", "dbname"); ############## Make the mysql connection ########### $conn = mysql_connect(HOST, DBUSER, PASS) or die('Could not connect !<br />Please contact the site\'s administrator.'); $db = mysql_select_db(DB) or die('Could not connect to database !<br />Please contact the site\'s administrator.'); ?> |
Let me explain:
- HOST: this is the location for the database server it can be a hostname or an ip adress. it is usualy localhost.
- DBUSER: this is the database user account used to access the database.
- PASS: this is the password for the database user account.
- DB: this is the name of the database used.
Step 3: Creating the header.php file
File header.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php error_reporting(0); // we don't want to see errors on screen // Start a session session_start(); require_once ('db_connect.inc.php'); // include the database connection require_once ("functions.inc.php"); // include all the functions $seed="0dAfghRqSTgx"; // the seed for the passwords $domain = "ineedtutorials.com"; // the domain name without http://www. ?> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Complete Member Login / System tutorial - <?php echo $domain; ?></title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> </head> <body> |
Step 4: Creating the footer.php file
The footer file is included at the bottom of every page, it looks like this:
1 2 3 4 | <hr> <div id='footer'>Copyright 2007-2008 © <?php echo $domain; ?></div> </body> </html> |
Step 5: Creating the index.php file
In this step we will create the homepage of the website, we’ll keep it very basic, only the login will be displayed.
File index.php:
1 2 3 4 5 6 7 8 9 | <?php require_once "header.php"; //content include "login.php"; // more content require_once "footer.php"; ?> |
Step 6: Creating the login.php file
In this step we will make the actual login page, because we want to keep it readable we create some custom build functions that will handle the actual login. So basicly all this page will do is call the functions checkLogin(), show_loginform() and isLoggedIn()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <?php if (!isLoggedIn()) { // user is not logged in. if (isset($_POST['cmdlogin'])) { // retrieve the username and password sent from login form & check the login. if (checkLogin($_POST['username'], $_POST['password'])) { show_userbox(); } else { echo "Incorrect Login information !"; show_loginform(); } } else { // User is not logged in and has not pressed the login button // so we show him the loginform show_loginform(); } } else { // The user is already loggedin, so we show the userbox. show_userbox(); } ?> |
Step 7: Creating the logout.php file
The logout file will destroy the session and it’s stored information. Afterwards it will redirect the user to the homepage.
File logout.php:
1 2 3 4 5 6 7 8 9 10 11 12 | <?php session_start(); if( session_unregister('loginid') == true && session_unregister('username')==true ) { session_destroy(); header('Location: index.php'); } else { unset($_SESSION['loginid']); unset($_SESSION['username']); session_destroy(); header('Location: index.php'); } ?> |
Step 8: Creating the function.inc.php file
Now we will create a file that will store all our functions, by including this file all our functions will be accessable.
It will help keep track of your functions.
File functions.inc.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <?php require_once("mail.functions.inc.php"); require_once("user.functions.inc.php"); require_once("display.functions.inc.php"); require_once("login.functions.inc.php"); require_once("validation.functions.inc.php"); function generate_code($length = 10) { if ($length <= 0) { return false; } $code = ""; $chars = "abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ123456789"; srand((double)microtime() * 1000000); for ($i = 0; $i < $length; $i++) { $code = $code . substr($chars, rand() % strlen($chars), 1); } return $code; } ?> |
Step 9: Creating the mail.functions.inc.php file
This file will contain all functions that we use to send emails.
File mail.functions.inc.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | <?php ##### Mail functions ##### function sendLostPasswordEmail($username, $email, $newpassword) { global $domain; $message = " You have requested a new password on http://www.$domain/, Your new password information: username: $username password: $newpassword Regards $domain Administration "; if (sendMail($email, "Your password has been reset.", $message, "no-reply@$domain")) { return true; } else { return false; } } function sendMail($to, $subject, $message, $from) { $from_header = "From: $from"; if (mail($to, $subject, $message, $from_header)) { return true; } else { return false; } return false; } function sendActivationEmail($username, $password, $uid, $email, $actcode) { global $domain; $link = "http://www.$domain/activate.php?uid=$uid&actcode=$actcode"; $message = " Thank you for registering on http://www.$domain/, Your account information: username: $username password: $password Please click the link below to activate your account. $link Regards $domain Administration "; if (sendMail($email, "Please activate your account.", $message, "no-reply@$domain")) { return true; } else { return false; } } ?> |
Step 10: Creating the display.functions.inc.php file
This file will contain all functions that display a form or a userbox on the page.
For example: It contains the loginform, the HTML code for the userbox, the lostpassword form, …
file: display.functions.inc.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | <?php #### Display Functions #### function show_userbox() { // retrieve the session information $u = $_SESSION['username']; $uid = $_SESSION['loginid']; // display the user box echo "<div id='userbox'> Welcome $u <ul> <li><a href='./changepassword.php'>Change Password</a></li> <li><a href='./logout.php'>Logout</a></li> </ul> </div>"; } function show_changepassword_form(){ echo '<form action="./changepassword.php" method="post"> <fieldset> <legend>Change Password</legend> <input type="hidden" value="'.$_SESSION['username'].'" name="username"> <dl> <dt> <label for="oldpassword">Current Password:</label> </dt> <dd> <input name="oldpassword" type="password" id="oldpassword" maxlength="15"> </dd> </dl> <dl> <dt> <label for="password">New Password:</label> </dt> <dd> <input name="password" type="password" id="password" maxlength="15"> </dd> </dl> <dl> <dt> <label for="password2">Re-type new password:</label> </dt> <dd> <input name="password2" type="password" id="password2" maxlength="15"> </dd> </dl> <p> <input name="reset" type="reset" value="Reset"> <input name="change" type="submit" value="Reset Password"> </p> </fieldset> </form> '; } function show_loginform($disabled = false) { echo '<form name="login-form" id="login-form" method="post" action="./index.php"> <fieldset> <legend>Please login</legend> <dl> <dt><label title="Username">Username: </label></dt> <dd><input tabindex="1" accesskey="u" name="username" type="text" maxlength="30" id="username" /></dd> </dl> <dl> <dt><label title="Password">Password: </label></dt> <dd><input tabindex="2" accesskey="p" name="password" type="password" maxlength="15" id="password" /></dd> </dl> <ul> <li><a href="./register.php" title="Register">Register</a></li> <li><a href="./lostpassword.php" title="Lost Password">Lost password?</a></li> </ul> <p><input tabindex="3" accesskey="l" type="submit" name="cmdlogin" value="Login" '; if ($disabled == true) { echo 'disabled="disabled"'; } echo ' /></p></fieldset></form>'; } function show_lostpassword_form(){ echo '<form action="./lostpassword.php" method="post"> <fieldset><legend>Reset Password</legend> <dl> <dt><label for="username">Username:</label></dt> <dd><input name="username" type="text" id="username" maxlength="30"> </dd> </dl> <dl> <dt><label for="email">email:</label></dt> <dd><input name="email" type="text" id="email" maxlength="255"> </dd> </dl> <p> <input name="reset" type="reset" value="Reset"> <input name="lostpass" type="submit" value="Reset Password"> </p> </fieldset> </form>'; } function show_registration_form(){ echo '<form action="./register.php" method="post"> <fieldset><legend>Register</legend> <dl> <dt><label for="username">Username:</label></dt> <dd><input name="username" type="text" id="username" maxlength="30"> </dd> </dl> <dl> <dt><label for="password">Password:</label></dt> <dd><input name="password" type="password" id="password" maxlength="15"> </dd> </dl> <dl> <dt><label for="password2">Re-type password:</label></dt> <dd><input name="password2" type="password" id="password2" maxlength="15"> </dd> </dl> <dl> <dt><label for="email">email:</label></dt> <dd><input name="email" type="text" id="email" maxlength="255"> </dd> </dl> <p> <input name="reset" type="reset" value="Reset"> <input name="register" type="submit" value="Register"> </p> </fieldset> </form>'; } ?> |
Step 11: Creating the login.functions.inc.php file
This file will contain the login functions
file: login.functions.inc.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | <?php #### Login Functions ##### function isLoggedIn() { if (session_is_registered('loginid') && session_is_registered('username')) { return true; // the user is loged in } else { return false; // not logged in } return false; } function checkLogin($u, $p) { global $seed; // global because $seed is declared in the header.php file if (!valid_username($u) || !valid_password($p) || !user_exists($u)) { return false; // the name was not valid, or the password, or the username did not exist } //Now let us look for the user in the database. $query = sprintf(" SELECT loginid FROM login WHERE username = '%s' AND password = '%s' AND disabled = 0 AND activated = 1 LIMIT 1;", mysql_real_escape_string($u), mysql_real_escape_string(sha1($p . $seed))); $result = mysql_query($query); // If the database returns a 0 as result we know the login information is incorrect. // If the database returns a 1 as result we know the login was correct and we proceed. // If the database returns a result > 1 there are multple users // with the same username and password, so the login will fail. if (mysql_num_rows($result) != 1) { return false; } else { // Login was successfull $row = mysql_fetch_array($result); // Save the user ID for use later $_SESSION['loginid'] = $row['loginid']; // Save the username for use later $_SESSION['username'] = $u; // Now we show the userbox return true; } return false; } ?> |
Step 12: Creating the user.functions.inc.php file
This file will contain the user functions
file: user.functions.inc.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | <?php ##### User Functions ##### function changePassword($username,$currentpassword,$newpassword,$newpassword2){ global $seed; if (!valid_username($username) || !user_exists($username)) { return false; } if (! valid_password($newpassword) || ($newpassword != $newpassword2)){ return false; } // we get the current password from the database $query = sprintf("SELECT password FROM login WHERE username = '%s' LIMIT 1", mysql_real_escape_string($username)); $result = mysql_query($query); $row= mysql_fetch_row($result); // compare it with the password the user entered, if they don't match, we return false, he needs to enter the correct password. if ($row[0] != sha1($currentpassword.$seed)){ return false; } // now we update the password in the database $query = sprintf("update login set password = '%s' where username = '%s'", mysql_real_escape_string(sha1($newpassword.$seed)), mysql_real_escape_string($username)); if (mysql_query($query)) { return true; }else {return false;} return false; } function user_exists($username) { if (!valid_username($username)) { return false; } $query = sprintf("SELECT loginid FROM login WHERE username = '%s' LIMIT 1", mysql_real_escape_string($username)); $result = mysql_query($query); if (mysql_num_rows($result) > 0) { return true; } else { return false; } return false; } function activateUser($uid, $actcode) { $query = sprintf("select activated from login where loginid = '%s' and actcode = '%s' and activated = 0 limit 1", mysql_real_escape_string($uid), mysql_real_escape_string($actcode)); $result = mysql_query($query); if (mysql_num_rows($result) == 1) { $sql = sprintf("update login set activated = '1' where loginid = '%s' and actcode = '%s'", mysql_real_escape_string($uid), mysql_real_escape_string($actcode)); if (mysql_query($sql)) { return true; } else { return false; } } else { return false; } } function registerNewUser($username, $password, $password2, $email) { global $seed; if (!valid_username($username) || !valid_password($password) || !valid_email($email) || $password != $password2 || user_exists($username)) { return false; } $code = generate_code(20); $sql = sprintf("insert into login (username,password,email,actcode) value ('%s','%s','%s','%s')", mysql_real_escape_string($username), mysql_real_escape_string(sha1($password . $seed)) , mysql_real_escape_string($email), mysql_real_escape_string($code)); if (mysql_query($sql)) { $id = mysql_insert_id(); if (sendActivationEmail($username, $password, $id, $email, $code)) { return true; } else { return false; } } else { return false; } return false; } function lostPassword($username, $email) { global $seed; if (!valid_username($username) || !user_exists($username) || !valid_email($email)) { return false; } $query = sprintf("select loginid from login where username = '%s' and email = '%s' limit 1", $username, $email); $result = mysql_query($query); if (mysql_num_rows($result) != 1) { return false; } $newpass = generate_code(8); $query = sprintf("update login set password = '%s' where username = '%s'", mysql_real_escape_string(sha1($newpass.$seed)), mysql_real_escape_string($username)); if (mysql_query($query)) { if (sendLostPasswordEmail($username, $email, $newpass)) { return true; } else { return false; } } else { return false; } return false; } ?> |
Step 13: Creating the validation.functions.inc.php file
This file will contain the validation functions, these function will validate the user input to see if it’s valid and doesn’t contain any illegal characters.
file: validation.functions.inc.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | <?php #### Validation functions #### function valid_email($email) { // First, we check that there's one @ symbol, and that the lengths are right if (!ereg("^[^@]{1,64}@[^@]{1,255}$", $email)) { // Email invalid because wrong number of characters in one section, or wrong number of @ symbols. return false; } // Split it into sections to make life easier $email_array = explode("@", $email); $local_array = explode(".", $email_array[0]); for ($i = 0; $i < sizeof($local_array); $i++) { if (!ereg("^(([A-Za-z0-9!#$%&'*+/=?^_`{|}~-][A-Za-z0-9!#$%&'*+/=?^_`{|}~\.-]{0,63})|(\"[^(\\|\")]{0,62}\"))$", $local_array[$i])) { return false; } } if (!ereg("^\[?[0-9\.]+\]?$", $email_array[1])) { // Check if domain is IP. If not, it should be valid domain name $domain_array = explode(".", $email_array[1]); if (sizeof($domain_array) < 2) { return false; // Not enough parts to domain } for ($i = 0; $i < sizeof($domain_array); $i++) { if (!ereg("^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]+))$", $domain_array[$i])) { return false; } } } return true; } function valid_username($username, $minlength = 3, $maxlength = 30) { $username = trim($username); if (empty($username)) { return false; // it was empty } if (strlen($username) > $maxlength) { return false; // to long } if (strlen($username) < $minlength) { return false; //toshort } $result = ereg("^[A-Za-z0-9_\-]+$", $username); //only A-Z, a-z and 0-9 are allowed if ($result) { return true; // ok no invalid chars } else { return false; //invalid chars found } return false; } function valid_password($pass, $minlength = 6, $maxlength = 15) { $pass = trim($pass); if (empty($pass)) { return false; } if (strlen($pass) < $minlength) { return false; } if (strlen($pass) > $maxlength) { return false; } $result = ereg("^[A-Za-z0-9_\-]+$", $pass); if ($result) { return true; } else { return false; } return false; } ?> |
Step 14: Creating the lostpassword.php file
When the user lost his password he can request a new temporary password. He has to enter his username and his password, if they are correct his password will be reset to a radom generated password and an email will be sent containing this new password, the user can use this password to login and change its password.
file: lostpassword.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <?php require_once "header.php"; if (isset($_POST['lostpass'])){ if (lostPassword($_POST['username'], $_POST['email'])){ echo "Your password has been reset, an email containing your new password has been sent to your inbox.<br /> <a href='./index.php'>Click here to return to the homepage.</a> "; }else { echo "Username or email was incorrect !"; show_lostpassword_form(); } } else { //user has not pressed the button show_lostpassword_form(); } require_once "footer.php"; ?> |
Step 15: Creating the changepassword.php file
On this page the user can change his password, ofcouse he has to be logged in first. He will also have to enter his old password.
file: changepassword.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <?php require_once "header.php"; if (isLoggedIn() == true) { if (isset($_POST['change'])) { if (changePassword($_POST['username'], $_POST['oldpassword'], $_POST['password'], $_POST['password2'])) { echo "Your password has been changed ! <br /> <a href='./index.php'>Return to homepage</a>"; } else { echo "Password change failed! Please try again."; show_changepassword_form(); } } else { show_changepassword_form(); } } else { // user is not loggedin show_loginform(); } require_once "footer.php"; ?> |
Step 16: Creating the register.php file
On this page users can create an account.
file: register.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <?php require_once "header.php"; if (isset($_POST['register'])){ if (registerNewUser($_POST['username'], $_POST['password'], $_POST['password2'], $_POST['email'])){ echo "Thank you for registering, an email has been sent to your inbox, Please activate your account. <a href='./index.php'>Click here to login.</a> "; }else { echo "Registration failed! Please try again."; show_registration_form(); } } else { // has not pressed the register button show_registration_form(); } require_once "footer.php"; ?> |
Step 17: Creating the activate.php file
On this page users can activate their account.
file: activate.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?php require_once "header.php"; $uid = (int)htmlentities(strip_tags($_GET['uid'])); $actcode = htmlentities(strip_tags($_GET['actcode'])); if (activateUser($uid, $actcode) == true) { echo "Thank you for activating your account, You can now login. <a href='./index.php'>Click here to login.</a>"; } else { echo "Activation failed! Please try again."; echo "If problem presists please contact the webmaster."; } require_once "footer.php"; ?> |