Creating Your First PHP Application: Part 2

This post is the second part in a three part series covering how to build a basic web application using OOP PHP. In part 1 I discussed the folder structure, the goals of the project and we created our first class called DB.class.php (a class to handle our common database operations).

In part 2, we’ll be writing two more classes and we’ll be finishing up the “back-end” portion of this basic web application.

The Plan

The ultimate goal of this project is to create a basic application that allows a user to register, log in, log out, and alter their settings. The class we will use to define what a user is and retain all of their user information will be called User and will be defined in User.class.php. The class that we will use to handle user operations such as logging in and out will be called UserTools and will be contained in UserTools.class.php.

A note about naming conventions

Although it does not matter syntactically, it is a common practice to name the file containing the class with the same name as the class itself. This makes it easy to identify the purpose of each file in your classes folder.

Another common practice (that has nothing to do with code execution but can be helpful) is the addition of .class or .inc in the file name. This better defines what the file is and also allows you to more easily restrict public access to certain files in your .htaccess file if you’re using Apache. It is still best practice to have every file end in .php despite the ability to set your webserver to parse PHP in files with .class and .inc extensions.

The User Class (User.class.php)

This class will be defining exactly what a user is. You’ll find that as you expand your web application, your definition of a User may also broaden. Luckily the OOP approach to web development allows for more user attributes to be easily added.

The Constructor

We’ll be using a constructor in this class, which is a function that is called automatically every time a new instance of the class is created. This essentially allows us to automatically populate certain attributes upon creation of the object. In this class, the constructor will take a single argument: an associative array containing data from a single row from the users table in the database. An alternative way to do this could be to use the user’s unique id as the argument and do the database operations from within the constructor. It’s merely a matter of preference as your design your web application.

The Code (with explanation following)

<?php
//User.class.php

require_once 'DB.class.php';

class User {

	public $id;
	public $username;
	public $hashedPassword;
	public $email;
	public $joinDate;

	//Constructor is called whenever a new object is created.
	//Takes an associative array with the DB row as an argument.
	function __construct($data) {
		$this->id = (isset($data['id'])) ? $data['id'] : "";
		$this->username = (isset($data['username'])) ? $data['username'] : "";
		$this->hashedPassword = (isset($data['password'])) ? $data['password'] : "";
		$this->email = (isset($data['email'])) ? $data['email'] : "";
		$this->joinDate = (isset($data['join_date'])) ? $data['join_date'] : "";
	}

	public function save($isNewUser = false) {
		//create a new database object.
		$db = new DB();

		//if the user is already registered and we're
		//just updating their info.
		if(!$isNewUser) {
			//set the data array
			$data = array(
				"username" => "'$this->username'",
				"password" => "'$this->hashedPassword'",
				"email" => "'$this->email'"
			);

			//update the row in the database
			$db->update($data, 'users', 'id = '.$this->id);
		}else {
		//if the user is being registered for the first time.
			$data = array(
				"username" => "'$this->username'",
				"password" => "'$this->hashedPassword'",
				"email" => "'$this->email'",
				"join_date" => "'".date("Y-m-d H:i:s",time())."'"
			);

			$this->id = $db->insert($data, 'users');
			$this->joinDate = time();
		}
		return true;
	}

}

?>

Explanation

The first bit of code in the file is outside the class ensures that the DB class has been included, since the User class has a function that requires it.

As for the class definition itself, you should see some similarities to the DB class that we created in part 1. Instead of the class variables being defined as “protected”, we’re defining them here as “public”. This means that any code outside of the class has access to these variables when dealing with a User object.

The constructor is takes an associative array, where keys are columns in the users table. We set a class variable by using $this->variablename. In the case of this class, we first check to see if a value for a particular key is set. If so, we’ll set the class variable equal that that value. Otherwise we’ll set it to an empty string. The code uses the short form for an if statement as detailed below:

$value = (3 == 4) ? "A" : "B";

It tests to see if 3 is equal to four. If it is, $value = “A”. If not, $value = “B”. In this case, $value will be set to “B”.

Saving the User’s Data to the Database

The save function is used to update the users table in the database with the current values in the User object. This function utilizes the DB class we made in part 1. Using the class variables, an associative array called $data is set. If the user is being saved for the first time, $isNewUser should be passed in as $true (default is false). If $isNewUser is equal to true, then the insert() function of the DB class is called. Otherwise, the update function is called. In both cases, the data from the user object will be saved to the database.

The UserTools Class (UserTools.class.php)

The UserTools class will contain functions relating to users. Although you may find yourself adding to this class as you customize and expand this project further, we’ll just be dealing with four functions: login(), logout(), checkUsernameExists() and get().

The Code (with explanation following)

<?php
//UserTools.class.php

require_once 'User.class.php';
require_once 'DB.class.php';

class UserTools {

	//Log the user in. First checks to see if the
	//username and password match a row in the database.
	//If it is successful, set the session variables
	//and store the user object within.
	public function login($username, $password)
	{

		$hashedPassword = md5($password);
		$result = mysql_query("SELECT * FROM users WHERE username = '$username' AND password = '$hashedPassword'");

		if(mysql_num_rows($result) == 1)
		{
			$_SESSION["user"] = serialize(new User(mysql_fetch_assoc($result)));
			$_SESSION["login_time"] = time();
			$_SESSION["logged_in"] = 1;
			return true;
		}else{
			return false;
		}
	}

	//Log the user out. Destroy the session variables.
	public function logout() {
		unset($_SESSION['user']);
		unset($_SESSION['login_time']);
		unset($_SESSION['logged_in']);
		session_destroy();
	}

	//Check to see if a username exists.
	//This is called during registration to make sure all user names are unique.
	public function checkUsernameExists($username) {
		$result = mysql_query("select id from users where username='$username'");
    	if(mysql_num_rows($result) == 0)
    	{
			return false;
	   	}else{
	   		return true;
		}
	}

	//get a user
	//returns a User object. Takes the users id as an input
	public function get($id)
	{
		$db = new DB();
		$result = $db->select('users', "id = $id");

		return new User($result);
	}

}

?>

The login() function

The login function serves an obvious purpose. Taking the $username and $password of the user as arguments, this function verifies their credentials. If the username and password match, we’ll create a User object with all their information and store the object in a session. Notice we’re first using the PHP serialize() function. This generates a storeable version of the object and can be simply undone by calling unserialize(). For good measure it will also store the login time. This can be used later on to tell the user how long they’ve been logged in for or to expire the session manually after a certain period of time.

You’ll notice that we’re also setting $_SESSION['logged_in'] to 1. This provides us with an easy way on every page to verify if the user is logged in or not. Simply check to see if that variable is set.

The logout() function

This function is fairly self-explanatory. The PHP unset() function clears the variables in memory while session_destroy() will delete the session altogether from storage. Why not skip using unset() and just use session_destroy()? It’s mostly for completeness sake and a matter of personal preference. You could also use $_SESSION = array() instead of unset() for each separate value. I prefer listing them all individually so that the code is readable and we can see exactly what session variables are being used and cleared.

The checkUsernameExists() function

Side note: Notice that in all cases, the function name clearly describes exactly what the function will be doing. This is probably the best example of that and in general you should try to continue clearly naming both functions, classes, and variables in your code.

This function simply queries the database to check whether a particular user name already exists. If it does, it will return true, otherwise false.

The get() function

This function takes a unique user id and queries the database using the DB class’s select() function. It will take the associative array returned by the select() function containing the user row and pass create and new User object, passing that array into the constructor. This User object is then returned.

Where might you use this? Lots of places. For example if you create a page that displays users public profiles, you’ll need to dynamically grab their information and display it. Here’s how you might do that: (let’s say the URL is http://www.website.com/profile.php?userID=3)

//note: you will have to open up a database connection first.
//see Part 1 for further information on doing so.
//You'll also have to make sure that you've included the class files.

$tools = new UserTools();
$user = $tools->get($_REQUEST['userID']);

echo "Username: ".$user->username."";
echo "Joined On: ".$user->joinDate."";

Isn’t that simple?

The final piece of the backend: global.inc.php

The file global.inc.php will be required by every page on the website. Why? This way we’ll have a place to put common operations that we’ll need on every page. For example, it will call session_start(). It will also open a database connection.

The Code

<?php
require_once 'classes/User.class.php';
require_once 'classes/UserTools.class.php';
require_once 'classes/DB.class.php';

//connect to the database
$db = new DB();
$db->connect();

//initialize UserTools object
$userTools = new UserTools();

//start the session
session_start();

//refresh session variables if logged in
if(isset($_SESSION['logged_in'])) {
	$user = unserialize($_SESSION['user']);
	$_SESSION['user'] = serialize($userTools->get($user->id));
}
?>

What’s it doing?

There’s a couple different things going on here. Firstly, we’re opening up a database connection. This was outlined in part 1, and here’s where we’ll actually be using it.

Once we’ve connected to the database, we call the PHP function session_start(). This function creates a session or resumes the current one if the user is logged in. Because our web application has users logging in an out, this is absolutely mandatory on all pages. By putting it in global.inc.php we can assure that it will always be called.

The third thing we’re doing is checking to see if they’re logged in. If they are, we’re going to refresh $_SESSION['user'] so that it reflects the most current data we have for that user. For example, if a user changes a setting like their email address, the session will still have the old email address. This ensure that the session will always be up-to-date.

Extra Credit

Try expanding the User class and your database table to include more attributes. For example you might try adding birthday, address or personal website as attributes.

One thing that I’ve omitted in this tutorial that you’ll want to add in later is error handling. The ultimate goal should be to never have something unexpected returned by a function. One place you can attempt this is in the get() function in UserTools.class.php for when a user id is passed to the function that doesn’t exist in the database.

Try making your own function in UserTools.class.php. An example might be a function to get all users that registered after a certain date and return them in an array of User objects.

What’s up next?

This concludes part 2. We’ve managed to build the rest of our back-end for this basic web application. But in order to bring everything together we still need to build all of the front ends. Here’s what you can expect in part 3:

  • Front end PHP/html pages that utilize our back-end
  • Forms and form handling (registration page, login page, update settings page, etc)
  • More on displaying session data and building a dynamic page
  • Stumble It!
  • Bookmark It!
  • Tweet it!

About Brian Muse

Brian is a computer engineer and web developer from Massachusetts. He's the lead developer of yourather.com and also blogs about home computer setups at desktopped.com. Visit his homepage and follow him on twitter @briancmuse.

 

Discussion

  1. Mr.MoOx

    December 10th, 2009 at 3:41 AM

    It ’s a nice tutorial for beginner.
    Do you know the Zend Framework for PHP ? It’s a full oriented object framework wich provide a lot of tools.
    Supported by the PHP Company (Zend!), you can use it for make strong php application. Here is a quick start : http://framework.zend.com/docs/quickstart
    I’m not from the Zend Company, I just really like this framework !

  2. Brian Muse

    December 10th, 2009 at 9:30 AM

    @Mr.MoOx
    These tutorials are designed for those who are just getting into PHP and would like to know more about basic OOP.

    Both the Zend and Codeigniter frameworks are excellent tools for people to become familiar with once they’ve mastered the basics. (In fact, our project at http://yourather.com is built using the Zend Framework)

    If there’s a demand for it, I could do some Zend Framework and Codeigniter tutorials in the future.

  3. Michal Majer

    December 10th, 2009 at 12:59 PM

    Great tut, canĹĄ wait for the next one .. just one question .. How am I supposed to remember all the classes and what they do at all time, when I’m developing new project? :) Even in this simple tut, the third class uses object of the second class and the second uses object of the first class .. I had some troubles scrolling the page just to find out what the class is actually doing =P

  4. Carlos

    December 10th, 2009 at 8:12 PM

    Codeigniter tutorials would be awesome…

  5. Sérgio Soares

    December 11th, 2009 at 1:47 PM

    Nice tutorials!
    I’ll follow this series for sure.

    Thanks

  6. jyoseph

    December 13th, 2009 at 2:09 AM

    As a Coldfusion developer who has only dabbled in php, these tutorials really help everything come together.

    Surely this will help people to move from “just hacking at php” to being able to put an actual web app together.

    Many thanks, please keep up the good work!

  7. Prospect Solution

    December 14th, 2009 at 6:46 AM

    Awesome..I must study now php and build my own website.

    Is there a part 3 until a complete website? lol

  8. Austin

    December 16th, 2009 at 9:29 PM

    Great tutorial, but it seems kind of redundant to initialize the database class every time that the save function is called. I’m not an OOP expert and I barely use OOP, so I don’t know exactly how I would approach it. I would probably use a global variable and then use global $db; to access it from each individual methods. Or something like that.

Join the Conversation!

Remember: Life's not all doom and gloom, so please keep it constructive. If we've made an error or missed something big, please let us know! Learning is revisions, after all.

CommentLuv is Enabled

 

Sponsors

Advertise on Build Internet!