Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
VCuovPmSxTJ
This tutorial will cover creating a login system with registration upon registering an activation link
will be emailed containing a link to activate the account. Once active the user can login, a reset
option is available to reset the password.
Demo
Download
Please click either the Facebook Like button, the Google +1 button or send a tweet to Download
the file.
The database will require a table to store the members, create a table called members:
Config.php
Config.php will be included into all pages enable sessions and turn on output buffering this way
headers can be used anywhere in the project.
Set the timezone and define the credentials for the database, next attempt to make a new PDO
connection if the connection fails display the error and kill the page.
Next include the user class and make an instance of it, pass in the database object to the class
to make use of the database.
1.
2.
3.
4.
5.
6.
<?php
ob_start();
session_start();
//set timezone
date_default_timezone_set('Europe/London');
7.
8. //database credentials
9. define('DBHOST','localhost');
10.
define('DBUSER','database username');
11.
define('DBPASS','password');
12.
define('DBNAME','database name');
13.
14.
//application address
15.
define('DIR','http://domain.com/');
16.
define('SITEEMAIL','noreply@domain.com');
17.
18.
try {
19.
20.
//create PDO connection
21.
$db = new
PDO("mysql:host=".DBHOST.";port=8889;dbname=".DBNAME, DBUSER,
DBPASS);
22.
$db->setAttribute(PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION);
23.
24.
} catch(PDOException $e) {
25.
26.
27.
28.
29.
30.
31.
32.
33.
//show error
echo '<p class="bg-danger">'.$e->getMessage().'</p>';
exit;
}
//include the user class, pass in the database connection
include('classes/user.php');
$user = new User($db);
?>
Next I have a folder called layout in there is a header.php and footer.php these will contain any
layout code that will be used on every page, this saves having to include the stylesheet each
time. header.php is a typical header file, notice the title expects a $title variable, this will be
created in the pages and made available to this file, also making use of Bootstrap this is optional
and is not required.
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4.
<meta charset="utf-8">
5.
<title><?php if(isset($title)){ echo $title; }?></title>
6.
<link
href="//netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.
css" rel="stylesheet">
7.
<link rel="stylesheet" href="style/main.css">
8. </head>
9. <body>
Next footer.php this simply closed the body and html, that would be a good place for placing
tracking code or any javascript includes.
1. </body>
2. </html>
index.php
This is the root page the system loads by default, on this page their is a form for users to register
to the site, along with links to the login page, if they are already a member. Also if the user is
already logged in they will be redirect to the members page.
How these pages start is by including the config file then checking if the user should be
redirected or not.
a call is made to the user object $user->is_logged_in() this will return true or false if the user is
logged in.
1.
2.
3.
4.
5.
6.
<?php
//include config
require_once('includes/config.php');
//check if already logged in move to home page
//if logged in redirect to members page
1.
2.
3.
4.
5.
For new registrations display a form consisting of username, email, password and confirm
password
</div>
<div class="form-group">
<input type="email" name="email"
id="email" class="form-control input-lg" placeholder="Email
Address" value="<?php if(isset($error)){ echo $_POST['email']; }
?>" tabindex="2">
8.
</div>
9.
<div class="row">
10.
<div class="col-xs-6 colsm-6 col-md-6">
11.
<div class="formgroup">
12.
<input
type="password" name="password" id="password" class="form-control
input-lg" placeholder="Password" tabindex="3">
13.
</div>
14.
</div>
15.
<div class="col-xs-6 colsm-6 col-md-6">
16.
<div class="formgroup">
17.
<input
type="password" name="passwordConfirm" id="passwordConfirm"
class="form-control input-lg" placeholder="Confirm Password"
tabindex="4">
18.
</div>
19.
</div>
20.
</div>
21.
22.
<div class="row">
23.
<div class="col-xs-6 colmd-6"><input type="submit" name="submit" value="Register"
class="btn btn-primary btn-block btn-lg" tabindex="5"></div>
24.
</div>
25.
</form>
This is a standard form, one thing to note I make use of sticky forms which means if their has
been a validation error the fields that have been filled out will be populated again with the
supplied data, except for passwords. Username and email would be restored.
This is done by doing an if statement, if the array $error is set meaning it exists then retrain the
$_POST
6. }
Once the new registration has been saved the form will post back to the same page appending a
$_GET key on the end of the URL the key will be called action it will have a value of joined
(this technique is used through the project)
Validation
The validation used is fairly basic and can be improved upon
This example checks the length of the username if it's less then 3 characters an error is created,
if the first check passes the username is looked up to see if it already exists by passing the
username to the database if a record is found an error is created.
7.
8.
9.
10.
11.
12.
if(!empty($row['username'])){
$error[] = 'Username provided is already in use.';
}
}
These check the password to make sure the email has not been used, it's important the email
address is only used once, in the event the user wants to reset their password a link will be
emailed to that user.
}
if(strlen($_POST['passwordConfirm']) < 3){
$error[] = 'Confirm password is too short.';
}
if($_POST['password'] != $_POST['passwordConfirm']){
10.
$error[] = 'Passwords do not match.';
11.
}
12.
13.
//email validation
14.
if(!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)){
15.
$error[] = 'Please enter a valid email address';
16.
} else {
17.
$stmt = $db->prepare('SELECT email FROM members WHERE
email = :email');
18.
$stmt->execute(array(':email' => $_POST['email']));
19.
$row = $stmt->fetch(PDO::FETCH_ASSOC);
20.
21.
22.
23.
24.
if(!empty($row['email'])){
$error[] = 'Email provided is already in use.';
}
25.
Next the user's details are saved to the database using a prepared statement, the first page of
the query tells MySQL what action to perform in this case to add a new row and the
table,columns to insert into.
where their are columns starting with : like :username these are place holders that will be used to
bind the username value to $stmt->execute call. This is done to avoid passing user provided
data to the query directly and avoid chances of MySQL Injection.
calling lastInsertId followed by the primary key will return the id of the record just saved, this is
needed for the next step.
1. $to = $_POST['email'];
2. $subject = "Registration Confirmation";
3. $body = "Thank you for registering at demo site.nn To activate
your account, please click on this link:nn
".DIR."activate.php?x=$id&y=$activasionnn Regards Site Admin nn";
4. $additionalheaders = "From: <".SITEEMAIL.">rn";
5. $additionalheaders .= "Reply-To: $".SITEEMAIL."";
6. mail($to, $subject, $body, $additionalheaders);
The last step is to redirect the page back to itself and adding an action with the value of joined so
the page know if to show a success message.
1. header('Location: index.php?action=joined');
2. exit;
activate.php
This page checks for the id and activation code being passed from the url (this happens when
the user clicks the link from their email)
once the data has been verified the users record is updated, the column active is changed from
the token to hold 'Yes' to say they are active, this will only happen if the id and token passed
match what's stored against that user.
1. <?php
2. require('includes/config.php');
3.
4. //collect values from the url
5. $memberID = trim($_GET['x']);
6. $active = trim($_GET['y']);
7.
8. //if id is number and the active token is not empty carry on
9. if(is_numeric($memberID) && !empty($active)){
10.
11.
//update users record set the active column to Yes
where the memberID and active value match the ones provided in the
array
12.
$stmt = $db->prepare("UPDATE members SET active =
'Yes' WHERE memberID = :memberID AND active = :active");
13.
$stmt->execute(array(
14.
':memberID' => $memberID,
15.
':active' => $active
16.
));
17.
18.
//if the row was updated redirect the user
19.
if($stmt->rowCount() == 1){
20.
21.
//redirect to login page
22.
23.
24.
25.
26.
27.
28.
header('Location: login.php?action=active');
exit;
} else {
echo "Your account could not be activated.";
}
29.
30.
}
?>
Login.php
Now users can register they need a way to login, start off with a form that expects their username
and password
<div class="form-group">
<input type="text" name="username" id="username"
class="form-control input-lg" placeholder="User Name" value="<?php
if(isset($error)){ echo $_POST['username']; } ?>" tabindex="1">
5.
</div>
6.
7.
<div class="form-group">
8.
<input type="password" name="password" id="password"
class="form-control input-lg" placeholder="Password" tabindex="3">
9.
</div>
10.
11.
<div class="row">
12.
<div class="col-xs-9 col-sm-9 col-md-9">
13.
<a href='reset.php'>Forgot your
Password?</a>
14.
</div>
15.
</div>
16.
17.
<hr>
18.
<div class="row">
19.
<div class="col-xs-6 col-md-6"><input
type="submit" name="submit" value="Login" class="btn btn-primary
btn-block btn-lg" tabindex="5"></div>
20.
</div>
21.
</form>
The login page will be used to show messages if the users account has been activated or
password has been changed, the page will know which message to show based on the value
contained inside $_GET['action']
1. if(isset($_GET['action'])){
2.
3.
//check the action
4.
switch ($_GET['action']) {
5.
case 'active':
6.
echo "<h2 class='bg-success'>Your account is
now active you may now log in.</h2>";
7.
break;
8.
case 'reset':
9.
echo "<h2 class='bg-success'>Please check your
inbox for a reset link.</h2>";
10.
break;
11.
case 'resetAccount':
12.
echo "<h2 class='bg-success'>Password
changed, you may now login.</h2>";
13.
break;
14.
}
15.
16.
Next attempt to log the user in. Collect the username and password from the form pass them to
the users object in the login method this internally will fetch the users hash by looking for the
username in the database once the hash is returned it's then passed to password_verify if the
hash and user's hash match it returns true which in turns sets a session $_SESSION['loggedin']
to true otherwise false is returned.
$hashed = $this->get_user_hash($username);
if($this->password_verify($password,$hashed) == 1){
$_SESSION['loggedin'] = true;
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
return true;
}
}
<pre lang="php">
//process login form if submitted
if(isset($_POST['submit'])){
$username = $_POST['username'];
$password = $_POST['password'];
18.
19.
if($user->login($username,$password)){
20.
21.
header('Location: memberpage.php');
22.
exit;
23.
24.
} else {
25.
$error[] = 'Wrong username or password or your
account has not been activated.';
26.
}
27.
28.
}//end if submit
Logout.php
To log a user out its very easy:
1. //logout
2. $user->logout();
Once the user is logged out redirect them.
memberpage.php
Once the user is logged in redirect them to the members only page (optional). To ensure a user
can only access the page if logged in do a check:
reset.php
every system need the ability to reset a password in case it's forgotten, how this will work is a
user enters their email address, a check is made to make sure its belongs to a user.
Next a token is created and saved to the users record, an email is sent to them containing a link
to when clicked the token from the link is verified, if it passed the user is provided with a form to
enter their new password, its then saved to the database.
This may seem like a long winded approach but it does prevent the password being sent by
email which is not recommended.
To start with the form:
<div class="row">
<div class="col-xs-6 col-md-6"><input type="submit"
name="submit" value="Sent Reset Link" class="btn btn-primary btnblock btn-lg" tabindex="2"></div>
9.
</div>
10.
</form>
1. <?php
2. if(isset($_GET['action'])){
3.
4.
//check the action
5.
switch ($_GET['action']) {
6.
case 'active':
7.
echo "<h2 class='bg-success'>Your account is
now active you may now log in.</h2>";
8.
break;
9.
case 'reset':
10.
echo "<h2 class='bg-success'>Please
check your inbox for a reset link.</h2>";
11.
break;
12.
}
13.
}
14.
?>
Next process the form ensure the email matches a user:
1. //email validation
2. if(!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)){
3.
$error[] = 'Please enter a valid email address';
4. } else {
5.
$stmt = $db->prepare('SELECT email FROM members WHERE email =
:email');
6.
$stmt->execute(array(':email' => $_POST['email']));
7.
$row = $stmt->fetch(PDO::FETCH_ASSOC);
8.
9.
if(empty($row['email'])){
10.
recognised.';
11.
}
12.
13.
}
resetPassword.php
First check the token been passed to the page matches a user
2.
3.
4.
5.
6.
7.
1. if(isset($stop)){
2.
echo "<p class='bg-danger'>$stop</p>";
3. }
If no errors have been created show a form to change the password
6.
7.
8.
9.
<div class="row">
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
<input type="password" name="password"
id="password" class="form-control input-lg" placeholder="Password"
tabindex="1">
</div>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group">
10.
<input type="password"
name="passwordConfirm" id="passwordConfirm" class="form-control
input-lg" placeholder="Confirm Password" tabindex="1">
11.
</div>
12.
</div>
13.
</div>
14.
15.
<hr>
16.
<div class="row">
17.
<div class="col-xs-6 col-md-6"><input
type="submit" name="submit" value="Change Password" class="btn
btn-primary btn-block btn-lg" tabindex="3"></div>
18.
</div>
19.
</form>
Once the form has been submitted validate the data then hash the password update the users
row and set resetComplete to Yes to indicate the process is finished if the reset link is clicked
again from email the process will be halted.
1.
2. //if form has been submitted process it
3. if(isset($_POST['submit'])){
4.
5.
//basic validation
6.
if(strlen($_POST['password']) < 3){
7.
$error[] = 'Password is too short.';
8.
}
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
//hash the password
22.
$hashedpassword = $user>password_hash($_POST['password'], PASSWORD_BCRYPT);
23.
24.
try {
25.
26.
$stmt = $db->prepare("UPDATE members SET
password = :hashedpassword, resetComplete = 'Yes' WHERE
resetToken = :token");
27.
$stmt->execute(array(
28.
':hashedpassword' =>
$hashedpassword,
29.
':token' => $row['resetToken']
30.
));
31.
32.
//redirect to index page
33.
header('Location:
login.php?action=resetAccount');
34.
exit;
35.
36.
//else catch the exception and show the error.
37.
} catch(PDOException $e) {
38.
$error[] = $e->getMessage();
39.
}
40.
41.
}
42.
43.
Conclusion
That covers the foundations, this can be used as a starting point to build members based sites or
even a start to an admin panel
Please also note: This version is not maintained anymore. The php-login
project will focus on developing the Professional MVC Version and highly
recommends you to also use that version.
Follow the project on Twitter, Facebook or Google+ and have a look on the official
support blog Dev Metal. Ask questions in the Official Support Forum.
This script is part of the php-login project, a collection of 4 different login
scripts. See php-login.net for more info.
1. One-file version: Full login script in one file. Uses a one-file SQLite database
(no MySQL needed) and PDO. Features: Register, login,
logout. https://github.com/panique/php-login-one-file
2. Minimal version All the basic functions in a clean file structure, uses MySQL
and mysqli. Register, login, logout. https://github.com/panique/php-loginminimal
3. Advanced version Similar to the minimal version, but full of features. Uses
PDO, Captchas, mail sending via SMTP and much
more. https://github.com/panique/php-login-advanced
4. Professional version Everything comes with a professional MVC framework
structure, perfect for building real applications. Additional features like: URL
rewriting, professional usage of controllers and actions, PDO, MySQL, mail
sending via PHPMailer (SMTP or PHP's mail() function/linux sendmail), user
profile pages, public user profiles, gravatars and local avatars, account
upgrade/downgrade etc., login via Facebook, Composer integration,
etc.https://github.com/panique/php-login
Live-demo
Live demo here, live demo's phpinfo(). here
Requirements
PHP 5.3.7+
MySQL 5 database (please use a modern version of MySQL (5.5, 5.6, 5.7) as
very old versions have a exotic bug that makes PDO injections possible.
this version uses mail sending, so you need to have an SMTP mail sending
account somewhere OR you know how to get linux's sendmail etc. to run.
As it's nearly impossible to send real mails with PHP's mail() function (due to
anti-spam blocking of nearly every major mail provider in the world) you
should really use SMTP mail sending.
1. create database login and table users via the SQL statements in
the _installation folder.
i.
To enable PHP's GD graphic functions, do sudo apt-get install php5gd (and restart the apache via sudo service apache2 restart)
fake email addresses. The only way to get around this is renting professional
SMTP mail sending, prices are okay, 10.000 mails for $5.
Security notice
This script comes with a handy .htaccess in the views folder that denies direct
access to the files within the folder (so that people cannot render the views directly).
However, these .htaccess files only work if you have set AllowOverride to All in your
apache vhost configs. There are lots of tutorials on the web on how to do this.
Useful links
A little guideline on how to use the PHP 5.5 password hashing functions and
its "library plugin" based PHP 5.3 & 5.4 implementation
How to setup latest version of PHP 5.5 on Ubuntu 12.04 LTS. Same for
Debian 7.0 / 7.1:
How to setup latest version of PHP 5.5 on Debian Wheezy 7.0/7.1 (and how
to fix the GPG key error)
Notes on password & hashing salting in upcoming PHP versions (PHP 5.5.x &
5.6 etc.)
License
Licensed under MIT. You can use this script for free for any private or commercial
projects.
Contribute
This script is not developed any further, so only commit bugfixes, not new features. If
you want to add new features etc, please contribute into
the https://github.com/panique/php-login repo. Please commit only
in develop branch. The master branch will always contain the stable version.
Support / Donate
If you think this script is useful and saves you a lot of work, then think about
supporting the project:
1. Rent your next server at A2 Hosting or DigitalOcean.
2. Donate via PayPal or GitTip
3. Contribute to this project.