Introduction
OOP or Object Oriented Programming, at first, can be a challenge, but certainly one that is worth overcoming. Procedural programing is much to the disadvantage of some systems as it presents us with numerous problematic scenarios in application design. The ease of creating dynamic and interactive applications through the OOP method is quite refreshing. This is a subject I deeply enjoy exploring quite often.
In the OOP world programmers aim to develop objects that operate on their own data structures. This means each object can be reproduced and manipulated independently without any procedural changes to the code. This is a good aid to have in application design.
Building an application with objects is a little different from trying to write procedures and functions that aim to tackle certain programming tasks. The primary difference is apparent in the methods available to each style. In the OOP approach we can have multiple objects each encapsulating their own data and sub-routines.
For example, say we have an application that requires certain input from a user. If the input changes based on the type of request the user makes or other input provided by the user then a procedural approach may take some tinkering to handle the different data structures in a complex design. With an OOP approach we can simply create an input object and replicate it, allowing the object to carry out its own sub-routines and handle its own data independently.
Creating a member system where users on your site can register and log in is rather common place for the web these days. Most all interactive sites provide user registration and some member system that controls the user input. So, I thought it would be a good idea to explain how you can accomplish this on your own sites by writing an OO (Object Oriented) class in PHP and using a MySQL database. The point of the tutorial is to demonstrate how the use of the OO method allows you to focus more on building your application and less on the framework of how that application will work. You’ll quickly see how writing this simple class can take away the worry of key areas like security, functionality, and usability. This just leaves you with focus on application design. By the end of this tutorial you’ll be able to simply write a page that only requires a couple of lines of code to check if a user is logged in to your site, allow them to log in, allow them to log out, or have them register as a new user.
Creating the Database
Let’s begin by creating a new MySQL database with two tables. The first table will be used to store user data such as the username and password, and the second table will be used to store user sessions. This allows us to keep track of who is logged in to the site.
You can use PHPMyAdmin to create the database using the following SQL code or you can simply enter this code directly into the MySQL CLI, if you have access to a terminal.
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(24) COLLATE utf8_bin NOT NULL,
`password` tinytext COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`),
KEY `username` (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE IF NOT EXISTS `sessions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(24) COLLATE utf8_bin NOT NULL,
`value` tinytext COLLATE utf8_bin NOT NULL,
`active` int(1) NOT NULL,
`time` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `username` (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
You will also need to create a new user that we can use for accessing the database from our PHP scripts. The user must have READ/WRITE/DELETE privileges.
Creating the PHP Class
The PHP class will act as the object model for our member system. It allows us to interact with the different objects that will be used in a user-based application. It is not thoroughly written so do not expect to use this code in a production environment. There are some limitations, but it is apt for demonstration of this tutorial in a development environment.
The most important part of this class is the signin() method. This allows us to easily sign a user into the system with just one line of code. The only data you need to provide for a new user sign-in are the username and password variables, which are specified by the user.
We can also check to see if a user is already signed-in to the system, with an active session, by using the CheckUser() method. As you can see the methods are rather self-served, attempting to use the signin() method will automatically check to see if the user is already signed-in before attempting to make use of the method. The methods also check for an existing database connection before creating one so as not to consume any unnecessary concurrent connections to the mysql server. The built in error handling method errorhandle() allows us to build our own debug information and handle errors internally. Of course the error handling in this class is rather simple right now, but you can see how it is easily extended to fit your particular application.
Of course the system is not complete without a way to create new user accounts for those who want to register. Again the method here is rather simplistic and takes care of all the grunt work for you. You simply provide the username and password (supplied by the user) and the method will automatically check for things such as illegal characters, username/password required lengths, unescaped data that may compromise the database (such as through sql injection), whether the username already exists, and all that jazz. So it’s actually rather safe to just use something as simple as $user->newuser($_POST['username'], $_POST['password']) and let the class handle the rest. If an error is returned the method will return false and the error will show up in $user->ERROR.
So let’s use the code below by storing it in a file called login.class.php.
<?php
class login {
// Define class objects
var $username;
var $password;
var $ERROR;
var $database_link;
var $SUCCESS;
var $userdata;
var $Session;
var $username_min = USERNAME_MINCHARS;
var $password_min = PASSWORD_MINCHARS;
var $dbhost = DATABASE_HOST;
var $dbuser = DATABASE_USER;
var $dbpass = DATABASE_PASSWORD;
var $dbname = DATABASE_NAME;
function newuser($username, $password) {
// Check username/password for illegal chars, validate length, and make sure the username does not already exist in the database
if (strlen($username) < $this->username_min || !preg_match('/^[a-z]{1,}[a-z0-9]{0,}$/i', $username)) { $this->errorhandle(1); return false; }
if (strlen($password) < $this->password_min || !preg_match('/^[a-z]{1,}\d{1,}[a-z0-9]{0,}$/i', $password)) { $this->errorhandle(1); return false; }
if (!$this->database_link) $this->database_link = $this->dbconnect();
$username = mysql_real_escape_string(strtolower($username));
$password = mysql_real_escape_string($password);
$sql = "SELECT COUNT(*) FROM users WHERE username = '" . $username . "'";
$query = mysql_query($sql, $this->database_link);
$result = mysql_fetch_row($query);
if (!$result) { $this->errorhandle(2); return false; }
if ($result[0] > 0) { $this->errorhandle(3); return false; }
$query = "INSERT INTO users (username, password) VALUES ('" . $username . "', '" . md5($password) . "')";
$result = mysql_query($query, $this->database_link);
if (!$result) { $this->errorhandle(2); return false; }
return true;
}
function signin($username, $password) {
session_start();
// Check that the user doesn't already have an active session
if ($this->CheckUser() == true) {
mysql_close($this->database_link);
return;
}
// Check for illegal chars and other indiscretions in the username/password
if (strlen($username) < $this->username_min || !preg_match('/^[a-z]{1,}[a-z0-9]{0,}$/i', $username)) { $this->errorhandle(1); return false; }
if (strlen($password) < $this->password_min || !preg_match('/^[a-z]{1,}\d{1,}[a-z0-9]{0,}$/i', $password)) { $this->errorhandle(1); return false; }
// If database connection does not exist create one
if (!$this->database_link) $this->database_link = $this->dbconnect();
$username = mysql_real_escape_string(strtolower($username));
$password = mysql_real_escape_string($password);
// User exists
$sql = 'SELECT COUNT(*) FROM users WHERE username = "' . $username . '"';
$query = mysql_query($sql, $this->database_link);
$result = mysql_fetch_row($query);
if (!$result[0]) { $this->errorhandle(0); return; }
// Password is correct
$sql = 'SELECT * FROM users WHERE username = "' . $username . '"';
$query = mysql_query($sql, $this->database_link);
$result = mysql_fetch_assoc($query);
if (md5($password) != $result['password']) { $this->errorhandle(0); return; }
// User is logged in!
$this->GetUserData($username);
$_SESSION['usrsession'] = $this->NewSession($this->userdata['username']);
$this->Session = $_SESSION['usrsession'];
setcookie("usrsession", $this->Session, (time() + (60*60*24)));
mysql_close($this->database_link);
}
function signout() {
session_start();
if (!$this->database_link) $this->database_link = $this->dbconnect();
if ($this->CheckUser() == true) {
$query = "UPDATE sessions SET active = '0' WHERE username = '" . $this->userdata['username'] . "'";
$result = mysql_query($query, $this->database_link);
if (!$result) { $this->errorhandle(2); return false; }
mysql_close($this->database_link);
session_destroy();
return true;
}
else return false;
}
function CheckUser() {
session_start();
if (!$this->database_link) $this->database_link = $this->dbconnect();
if (!isset($_SESSION['usrsession']) && !isset($_COOKIE['usrsession'])) return false;
if (isset($_SESSION['usrsession'])) $ses = mysql_real_escape_string($_SESSION['usrsession']);
elseif (isset($_COOKIE['usrsession'])) $ses = mysql_real_escape_string($_COOKIE['usrsession']);
$sql = "SELECT * FROM users,sessions WHERE sessions.value = '" . $ses ."' AND sessions.active = '1' AND users.username = sessions.username LIMIT 0,1";
$query = mysql_query($sql, $this->database_link);
$result = mysql_fetch_assoc($query);
if (!$result) return false;
$this->username = $result['username'];
$this->Session = $ses;
$this->GetUserData($this->username);
return true;
}
function GetUserData($username) {
$sql = 'SELECT * FROM users WHERE username = "' . $username . '"';
$query = mysql_query($sql, $this->database_link);
$result = mysql_fetch_assoc($query);
if (!$result) { $this->errorhandle(2); return; }
$this->SUCCESS = true;
$this->userdata = array();
foreach ($result as $key => $var) {
$this->userdata[$key] = $var;
}
}
function NewSession($ses_id) {
$query = "UPDATE sessions SET active = '0' WHERE username = '" . $ses_id . "'";
$result = mysql_query($query, $this->database_link);
if (!$result) { $this->errorhandle(2); return; }
$salt = sha1(rand(1000000,10000000000) . time());
$newsession = $salt . $this->userdata['id'] . $this->userdata['username'] . $this->userdata['password'] . $salt;
$newsession = $salt . sha1($newsession);
$query = "INSERT INTO sessions (username, value, active, time) VALUES ('" . $ses_id . "', '" . $newsession . "', '1', '" . time() . "')";
$result = mysql_query($query, $this->database_link);
if (!$result) { $this->errorhandle(2); return; }
return $newsession;
}
function dbconnect() {
$this->databse_link = mysql_connect($this->dbhost, $this->dbuser, $this->dbpass);
if (!$this->databse_link) {
$this->errorhandle(2);
return;
}
mysql_select_db($this->dbname, $this->databse_link);
if (mysql_error()) { $this->errorhandle(2); return; }
return $this->databse_link;
}
function errorhandle($err) {
switch ($err) {
case 0:
$this->ERROR = "Please supply a valid username and password.";
break;
case 1:
$this->ERROR = "Username / password contain invalid characters.";
break;
case 2:
$this->ERROR = "Database Error: " . mysql_error();
break;
case 3:
$this->ERROR = "Username is already taken.";
break;
}
}
}
?>
Creating The User Interface
Here is the fun part. This is where we can make use of the class to actually design a web site where users can register and log in! The first script is the home page. It simply displays our content and also checks to see if the user is already logged in. If they are we can display user specific content, which is stored in $this->userdata (in array form), and other useful information we may have in the database. If not we can provide the user with an option to log in or to sign up.
The method to keep the user logged in after they have authenticated to the system using their username and password is two-fold. First, the user session is created by generating a random hash and stored in the database on the server-side. Second, the user session is also stored on the client-side by passing the concatenated hashes to the browser in the form of a session variable (using the $_SESSION identifier) as well as a cookie. If the user has cookies disabled (which is rare) this will not work and passing session ids in the query string is extremely unsafe and frowned upon. The cookie merely allows the user to remain logged in for up to 24 hours even if the user decides to close the browser window and revisit the web site later. Starting a new session automatically closes any previously opened sessions the user may have started on the server-side. This can be remedied by simply editing the NewSession() method. This can be useful if you would like to allow the same user the ability to start multiple sessions (i.e. they would like to remain logged in from different browsers/machines). Furthermore you can easily edit this method to enable/disable the user’s ability to log in from different IPs/machines. This can be done by extending the sessions table in the database to store user agent information and IP information using $_SERVER['HTTP_USER_AGENT'] and $_SERVER['REMOTE_ADDR'].
We can put this code in an index.php file and use it as the home page.
<?php
include('config.php');
include('login.class.php');
$User = new login;
if (isset($_GET['logout'])) if ($User->signout() == true) { echo "You have been successfully logged out! <a href='" . $_SERVER['PHP_SELF'] . "'>Click here</a> to return to the main page."; exit; } else { echo "We were unable to log you out. <a href='" . $_SERVER['PHP_SELF'] . "'>Click here</a> to return to the main page."; exit; }
if ($_POST['Submit']) $User->signin($_POST['username'], $_POST['password']); else $User->CheckUser();
if ($User->SUCCESS == true) {
echo 'User <b>' . $User->userdata['username'] . '</b> was logged in successfully! User ID #' . $User->userdata['id'] . "<br />\n\r" . "You are logged in with session id: " . $User->Session . "<br />\n\r" . '<a href="?logout">Log Out</a>';
}
else {
?>
<form action="" method="POST">
Username:
<input id="username" name="username" type="text" /><br />
Password:
<input id="password" name="password" type="password" /><br />
<input id="Submit" name="Submit" value = "Submit" type="submit" />
</form>
<br />
<a href="register.php">Register</a>
<?php
}
if ($User->ERROR) echo "<p>" . $User->ERROR . "</p>";
?>
The script below is used as our registration form. It allows new users to register. We can store it in a register.php file for easy access later.
<?php
include('config.php');
include('login.class.php');
$User = new login;
if ($_POST['Submit']) {
if ($User->newuser($_POST['username'],$_POST['password'])) {
echo "<p>User registered successfully!</p><a href='index.php'>Click Here</a> to login...";
exit;
}
}
?>
<p>Register a new user.</p>
<form action="" method="POST">
Username:
<input id="username" name="username" type="text" /><br />
Password:
<input id="password" name="password" type="password" /><br />
<input id="Submit" name="Submit" value = "Submit" type="submit" />
</form>
<?php
if ($User->ERROR) echo "<p>" . $User->ERROR . "</p>";
?>
Configurations
Finally, we need to make sure we have an easy way to configure the application in case of any global changes. This configuration file is stored in config.php and used to configure some variables such as the database username/password, the required minimal length for the username/password, etc… We’ll need to include this along with login.class.php at the top of every script that makes use of the member system.
<?php
DEFINE('USERNAME_MINCHARS', 4);
DEFINE('PASSWORD_MINCHARS', 6);
DEFINE('DATABASE_HOST', 'localhost');
DEFINE('DATABASE_USER', 'user');
DEFINE('DATABASE_PASSWORD', 'pass');
DEFINE('DATABASE_NAME', 'db');
?>
Synopsis
Now we have a fully working member system that functions as an OO model! Of course all this can be extended to work on a much more complex level, but for the purpose of this tutorial it can help you get an idea of how to approach these methods in your own applications, which will eventually minimize your workload in the long run and make your UI easier to write. Continue reading →