This tutorial is designed to give those familiar with PHP programming an introduction to Object Oriented Programming. It is not intended as a beginner tutorial, nor is it intended to be an all-encompassing tutorial on the inner-workings of OOP. However, if you're unfamiliar with the ideas behind and the implementation of classes and objects, this may be a good place for you to start.
This tutorial will use as an example a user registration class (more on classes shortly). This class is a real world example of the type of implementation a PHP programmer may choose to use when programming a web-based community or forum system, but could also be expanded to a customer registration database or something similar. While we choose to use this as an example, remember, the concepts here can be used on virtually any programming project if your chosen language supports Object Oriented Programming.
In order to complete this tutorial, you will need a working installation of PHP with MySQL support installed and access to a MySQL database. The database will be used to store the user data. You can download a zip file of all the examples used in this tutorial here: phpClassTutorial.zip (less than 4k).
So, what is Object Oriented Programming (or OOP as it's generally referred to)? OOP is a different way of thinking about programming. Once you understand the concepts behind it, the implementation is the easy part. In OOP, the idea is to abstract what you're trying to accomplish to more resemble real-world things, or objects.
Many introductions to these concepts use a car stereo as an example. From a real world perspective, it's a very valid example, so it is discussed here briefly. A car stereo is a "thing" or "object" that you as a user generally know how to use, but probably don't understand all the technical mumbo-jumbo that happens behind-the-scenes. For example, when you switch to a certain radio, you most likely don't know how the electronic components receive different frequencies of radio waves and how those are turned into the music that you hear.
This is one of the main "features" of OOP. A person can use one of your classes without actually having to understand how it works internally. All the user has to know is how to work the controls. For example, the volume control, the station selection knob, etc.
This describes a "feature," but doesn't really explain why you would want something like this. The idea is that there are specific data elements that your class or object will contain (for example, the frequency and the volume), and there are also specific ways those data elements can be manipulated. Instead of keeping these related data members and functions separate, OOP dictates that since they are so related, they should be packaged together. For example, in our previous example, our data elements would consist of radio frequency and volume level, and the functions we would write to manipulate those data members would be volumeUp(), volumeDown(), frequencyUp(), and frequencyDown().
This is all well and good, but how does this help with PHP programming? Frankly, the car stereo example didn't help me much, either, at first. It's important to work with an example that deals with the specific tasks PHP was designed to deal with. So without further ado, let's dive into our user registration class.
The word "class" has been thrown around a few times, but hasn't been clearly defined yet. This is a very difficult thing to explain with just words, but once the design is hammered out, it's relatively easy to understand. So, if you don't understand the next few sentences, don't worry about it, classes and objects will become clear as this tutorial progresses. Think of a "class" as sort of a template for data. If we have a user registration class, say our template will contain 3 variables: $userID, $userName, and $userPassword. The "class" then, is just a way to define a structured placeholder for these variables - when you create a class, no variables are assigned, and no space is set aside in memory. A "class" is simply a template. Furthering that concept, a class also contains functions (very similar to standard PHP functions) that are specifically designed to manipulate the variables that are part of the class. So the class that contains $userID, $userName, and $userPassword would probably also contain functions such as verifyPassword(), changeUserName(), changePassword(), etc. Some classes are absolutely massive and scope, and some are very small, but most contain 10-20 data members and about 3 times that many functions.
Some notes on OOP and terminology: Functions defined inside your class are generally called "methods." Don't ask me why, it's probably just more techno-babble to make the guys that thought it up feel smarter. Variables inside the classes (or 'objects' - more on this shortly) are sometimes referred to as data members. This tutorial will use these terms interchangeably, so please don't get confused!
As mentioned above, a class is just a template for these data members and methods. So, what good is a template, and how do you use it? The idea is that you can create any number of "objects" based on a single "class." So at the top of your document, you'd have your class definition, and then below, you could instantiate any number of "objects" based on that class. So if your class is called "User" you may create an object based on that class called $myUser or $myOtherUser or both in one script. You can even create an array of fifty or a hundred objects, if that's what you needed to do.
So to concrete this concept, say we had our "User" class defined with the methods mentioned above. When someone accesses a page on our site, they are required to enter their user name and their password. When they hit the "Submit" button, our User class gets to do some work! The first thing we need to do is instantiate an object based on the User class. This is done with the following statement.
$myUser = new User;
Very simple. Now, we need to assign the values the user entered to the data members of the $myUser object. This is done with very straight-forward syntax. C/C++ programmers will be familiar with the pointer-to-member operator.
$myUser->userName = $inputtedUserName;
$myUser->userPassword = $inputtedUserPassword;
This isn't the way data is typically assigned to the data members of the object, but it will do for this example. More on this later. Now that the data is there, we want our script to verify that they entered a correct user name and password combination. This is done simply by calling the verifyPassword() function/method mentioned earlier. Remember, all we need to know at this point is how to use the class - we don't need to know what is actually taking place behind the scenes. In this example, the only data the object needs to verify the password is the user name and password. Then, we simply call the correct method.
$myUser->verifyPassword();
Depending on how the class is designed, the function could return a value (true if the password was good, false if not), directly error out and kill the script (not recommended - more on this later), or take any other action determined by the person who programmed the class.
Now that a basic introduction to OOP, classes, data members, and methods has been completed, we can begin the design phase of our user registration class. If the above sections didn't make much sense, please continue reading - a lot of the time, real code and working examples are the best way to learn a new concept.
When designing a class, the first step is generally brainstorming a basic idea of what the class is supposed to do. In our example, the user registration class should handle everything from registering and deleting users to manipulating all the data members that are part of the class. As mentioned above, we also want to include a method for verifying that a user has access to a specific page.
Here are the data members this example will use. Remember, you can have a virtually unlimited number of data elements, so this example can be easily expanded upon. Once we figure out what data we're going to store, we can add a table to our database to hold it.
$userID
- assigned automatically by the auto_increment field in the MySQL database.
$userName
- limited to 32 characters for this example.
$userPassword
- also limited to 32 characters.
$dbHost
- MySQL server host.
$dbUser
- MySQL user.
$dbName
- MySQL database name.
$dbPass
- MySQL database password.
$dbUserTable
- the table that will hold our data.
That's enough data for this tutorial. Now we need to figure out what methods our class will need to manipulate the above data. Below is not an exhaustive list, we may need more that will be discovered as we go along. Included with each method is the argument list it should be designed to take.
registerUser($userName, $userPassword)
- instead of assigning these values directly, the registerUser() method will be designed to make sure they are the correct length, don't use any special characters, aren't already taken, etc.
verifyPassword()
- assume we've already assigned the values to the variables.
changePassword($newPassword)
- assume old password is already registered to the userPassword variable.
displayUserInfo()
- a quick and dirty method to allow us to view the variables in our object.
Those are enough for this example. Remember, you can add as many methods as you want.
So with the above data members, here is the table schema I used to store the data.
create table usersExample (
userID int not null primary key auto_increment,
userName varchar(32) not null,
userPassword varchar(32) not null);
Once you have your table set up, it's time to start coding!
I find it helpful to code a basic 'Skeleton' of my classes as a roadmap. It helps keeps my thoughts and code organized.
The first thing we need to do is create a UserClass.php file which will contain our class definition. This file will then be included (using the include() function) in each of our pages that need it. Please read through the following code very carefully, paying special attention to the comments. Consider the comments part of this tutorial - if you skip them, you will be lost!
<?
// This is the start of the class definition. Note the key word 'class'
class User {
// Next comes the variable list as defined above
// Note the key word 'var' and then a comma-separated list
var $userID,
$userName,
$userPassword,
$dbHost,
$dbUser,
$dbName,
$dbPass,
$dbUserTable;
// Next come all our methods with their argument lists
// The syntax for these is just like normal PHP functions
// Remember, this is just a skeleton, we will fill in these functions later
function registerUser($userName, $userPassword) {
} // End registerUser()
function verifyPassword() {
} // End verifyPassword()
function changePassword($newPassword) {
} // End changePassword()
function displayUserInfo() {
} // End displayUserInfo()
} // End User class definition
?>
When you create a class, it's a template. In order to use the template, you have to instantiate an object based on that template. Once your object is created, the variables inside that object are accessible to you anywhere in your script. This is a deviation from a full implementation of OOP because generally, these variables are supposed to be hidden or 'private' - meaning, the only functions that can access the variables are those inside the class definition.
So, when you instantiate an object using:
$myUser = new User;
$myUser now contains all the variables listed under the 'var' section of your class definition. You can access these variables using the following syntax. This was already covered above, but we're going to dig a little deeper.
echo $myUser->userName;
Concerning scope... It's perfectly okay to have a variable called $userName
in your main script - it will not conflict with the one in your $myUser object. Also, if you create another instance of the User class called $myOtherUser
, each instance will have it's own userName data member. So $userName
, $myUser->userName
, and $myOtherUser->userName
are all different variables.
Important: There is special syntax required inside methods/class functions to access variables inside a specified class. If that didn't make sense, I'll try different wording. If you have a User class that contains the variables we listed above, many of your methods will need to directly access those variables (in order to manipulate, update, etc.). Inside those functions, there is special syntax required to let the functions know that they are accessing the variables belonging to the specific object that's calling them, rather than a global variable or a data member from another instance of the object.
That still seems very complicated, when in fact, it's not. Let's keep trying, to make sure this is crystal clear. Each function you write only exists once in memory. When an object calls that function, the function needs to know which data it's supposed to be accessing or manipulating. So, if you have a $myUser->userName variable and a $myOtherUser->userName variable, how does the verifyPassword() function know which instance of the User class it's supposed to be manipulating?
Since you call the function with a specific instance of the object, for example, $myUser->verifyPassword()
or $myOtherUser->verifyPassword()
you would assume that the function would know which object actually called it. To a certain extent, this is true. However, since when you are actually coding the class, there are no objects actually instantiated, you need to have notation to tell the function "use the data from the object that is calling you." This is accomplished with the "this pointer." The term "this pointer" is taken from C++, a popular compiled language.
The "this pointer" is an invisible variable passed to the method (function) by the calling object. It is simply a variable that points back to the calling object to let the function know which instance of the class actually called it. This may seem overly complex, but in practice, it's actually very simple. Let's look at some sample code.
Below is the displayUserInfo() method. The following code should be placed in the correct spot in your skeleton. Basically, right now we are making the displayUserInfo() method actually do something. Remember, the beginning and end portions of our User class definition will appear above and below this. Just as before, be sure to read the comments, they are very important.
<?
function displayUserInfo() {
// Notice the use of the $this-> notation - that simply
// tells the function to use the variables from the calling object
echo '<b>User ID: </b>' . $this->userID . '<br>\n';
echo '<b>User Name: </b>' . $this->userName . '<br>\n';
// In a real life program, you would never have a function
// that directly outputted a user's password!
echo '<b>User Password: </b>' . $this->userPassword . '<br>\n';
} // End displayUserInfo()
?>
Now that we know the special notation required inside our methods, we can finish writing the remaining methods.
A "constructor" is simply a method inside a class that gets run automatically each time an instance of the class is created. For instance, when a script says:
$myUser = new User;
If the User class had a constructor, it would get executed at that point. Constructors are very important and have many uses. In our example, we will need a constructor to set all the database values. These values will be the same for each object, so it makes sense to set them in a constructor.
A function is designated a constructor by virtue of sharing its name with the name of the class. So, since our class is called 'User' our constructor will be called 'User' as well. The 'User' function should set all the variables needed for the database connection.
Please pay special attention to the constructor function declaration in the code displayed in the next section.
This section contains the source for the remaining methods of our class. It's important that you have an understanding of how they work and why they work the way they do. The best way to gain this understanding is to read through the source and the comments especially. The code here has been kept very small because it's an academic example. In a real-world scenario, these methods would be required to do a lot more error checking, etc.
<?
// NOTE: Previous comments in this file have been ommitted - so all the comments are new
// Please refer to the skeleton above for more comments on exactly what is happening
class User {
var $userID,
$userName,
$userPassword,
$dbHost,
$dbUser,
$dbName,
$dbPass,
$dbUserTable;
// This is the constructor function definition - it's possible to pass
// it values just like a normal function, but that isn't demonstrated here
// These variables will be set for each object that is created using this class
function User() {
$this->dbHost = 'localhost';
$this->dbUser = 'yourUser';
$this->dbName = 'yourDatabase';
$this->dbPass = 'yourPassword';
$this->dbUserTable = 'usersExample';
}
function registerUser($userName, $userPassword) {
// Connect to database
$dbLink = mysql_connect($this->dbHost, $this->dbUser, $this->dbPass);
if(!$dbLink) die("Could not connect to database. " . mysql_error());
// Select database
mysql_select_db($this->dbName);
// Insert data
// Notice the "$userName" and "$userPassword" - these are the ones
// passed in from your main script, not the class/object variables!
$query = "insert into $this->dbUserTable values (NULL, \"$userName\", \"$userPassword\")";
$result = mysql_query($query);
// Test to make sure query worked
if(!$result) die("Query didn't work. " . mysql_error());
// Get the user ID
$this->userID = mysql_insert_id();
// Close database connection
mysql_close($dbLink);
// Assign the values to the data members
$this->userName = $userName;
$this->userPassword = $userPassword;
} // End registerUser()
function verifyPassword() {
// Remember, by the time this method is called, your main script will have already
// put the userName and userPassword variables equal to the ones the user typed in
// Connect to database
$dbLink = mysql_connect($this->dbHost, $this->dbUser, $this->dbPass);
if(!$dbLink) die("Could not connect to database. " . mysql_error());
// Select database
mysql_select_db($this->dbName);
// Get data
$query = "select userPassword from $this->dbUserTable where userName = \"$this->userName\"";
$result = mysql_query($query);
// Test to make sure query worked
if(!$result) die("Query didn't work. " . mysql_error());
// Get the password from the database
$actualPassword = mysql_result($result, 0);
// Verify that they match
if(!($actualPassword == $this->userPassword)) die("Incorrect Password.");
// Close database connection
mysql_close($dbLink);
} // End verifyPassword()
function changePassword($newPassword) {
// This function assumes you've already verified that the user has
// permission to change the password - it recieves the new password
// as an argument, with the old password already registered in $userPassword
// Connect to database
$dbLink = mysql_connect($this->dbHost, $this->dbUser, $this->dbPass);
if(!$dbLink) die("Could not connect to database. " . mysql_error());
// Select database
mysql_select_db($this->dbName);
// Get data
$query = "update $this->dbUserTable set userPassword = \"$newPassword\" where userName = \"$this->userName\"";
$result = mysql_query($query);
// Test to make sure query worked
if(!$result) die("Query didn't work. " . mysql_error());
// It worked, so update the password stored in the object
$this->userPassword = $newPassword;
// Close database connection
mysql_close($dbLink);
} // End changePassword()
function displayUserInfo() {
echo '<b>User ID: </b>' . $this->userID . '<br>';
echo '<b>User Name: </b>' . $this->userName . '<br>';
echo '<b>User Password: </b>' . $this->userPassword . '<br>';
} // End displayUserInfo()
} // End User class definition
?>
That looks like it for our User class. Does that make you a proficient OOP programmer? Not yet. Now, we'll explore some examples of how to actually use this class to make our programming easier.
The first example we'll use is how to register a user into the database. This is a fairly simple process, and will require two files. The first file, registerForm.html
, is simply an HTML file containing a form. The second file, processRegister.php
, actually contains the logic to register the user.
<!-- registerForm.html -->
<form method="post" action="processRegister.php">
User Name: <input type="text" name="userName" maxlength="32"><br>
Password: <input type="password" name="userPassword" maxlength="32"><br>
<input type="submit">
</form>
<!-- End registerForm.html -->
Now for the processRegister.php
file which processes the data from the previous form.
<?
// processRegister.php
//////////////////////
// First include the class definition
include('UserClass.php');
// Next, create an instance of the class
$newUser = new User;
// Call the registerUser() method, passing in the required variables
$newUser->registerUser($userName, $userPassword);
// If it was an error, the class will kill the script, if not, it will reach this point
$newUser->displayUserInfo();
?>
The next example is a bit more complex (but still not overly so). This time, we will write a set of scripts to allow the user to change his/her password. This requires them to type in their user name, their old password, and a new password. The new password will only be changed if the old password is correct! This example will also contain two files, passwordForm.html
and processPassword.php
.
<!-- passwordForm.html -->
<form method="post" action="processPassword.php">
User Name: <input type="text" name="userName" maxlength="32"><br>
Old Password: <input type="password" name="userPassword" maxlength="32"><br>
New Password: <input type="password" name="newPassword" maxlength="32"><br>
<input type="submit">
</form>
<!-- End passwordForm.html -->
Now for the processpassword.php
file which processes the data from the previous form.
<?
// processPassword.php
//////////////////////
// First include the class definition
include('UserClass.php');
// Next, create an instance of the class
$myUser = new User;
// Register the variables with the object
$myUser->userName = $userName;
$myUser->userPassword = $userPassword;
// Verify the password is correct
$myUser->verifyPassword();
// Change the password
$myUser->changePassword($newPassword);
// If it was an error, the class will kill the script, if not, it will reach this point
$myUser->displayUserInfo();
?>
Hopefully by now you can see that it's very easy to use the classes once they're programmed. We don't have to know how the class operates internally, all we have to know is what data members it has and what methods there are. Notice that in our *.php files, there isn't any complex code such as database connections, queries, etc. Since it's all defined in the class, we don't have to worry about typing database connection code each time we need it - the class connects to the database when it needs to.
Please understand that this is an academic example, one not suited to regular use on a public web site. We haven't done any error checking on the data, the table isn't properly indexed, and we're obviously not storing enough data for a real world application. However, this can be used as a base to build upon.
Encapsulation, polymorphism, and inheritance are three buzz-words that simply must be addressed when dealing with Object Oriented Programming. However, PHP doesn't exactly contain a 'full' implementation of OOP, so these words don't exactly apply like they do to other languages. Here is a quick run-down; not a lot of detail, but enough to give you some basic information.
All of these terms are important and may be covered PHP-style in later tutorials.
This document © Copyright 2001 Brian Lozier (brian@massassi.net)