- Creating Our First Object
- Class Variables and Visibility
- Constructor Method
- Using Arrays to Speed Up Coding
- Method Interaction
- Integrating OO PHP with MySQL
- Introducing Inheritance
- Parting Words
- Now let’s get started!
Creating Our First Object
not surprisingly, object oriented programming is focused around objects. While the idea of objects may be foreign to you in coding terms, understanding what objects are and why we use them shouldn’t take long. In short, our non-programming world is comprised of objects. Computers, fish, clouds, people, and cars are all objects. Objects have properties like color, size, name, and speed. Objects can also be comprised of other objects: cars have doors; doors have handles; handles have plastic levers; so on and so forth. Objects are everywhere around us. For this reason, many programmers find object oriented programming relatively easy to understand.
So how do objects translate into code? If we were building a website about users, we would create one or many user objects. Objects are created using classes. Classes are groups of related variables and functions. Variables hold our object’s properties like color, size, and speed. Functions perform actions like set variable values or open files. In this example, we could create user objects with a User class. You can think of the User class as a template for any user objects.
Creating an object is called instantiation (creating an instance). Let’s instantiate a user objects and codes the corresponding User class:
/* create new object */
$ram = new User;
/* class to create objects with */
class User {
}
Note: As these tutorial progresses, code will be removed so that you don’t get lost and can focus on the task at hand. Feel free to keep or add back code from previous examples.
Right now this user can’t do anything and doesn’t have any attributes. Within classes, we use methods (aka functions) and variables to give our objects functionality and properties. Let’s redo our first example so that our users can have a name:
/* create a new object */
$ram = new User;
/* call object methods */
$ram->setName( ‘ram’ );
echo $ram->getName().’<br />’;
class User {
private $name;
function setName( $val ) {
$this->name = $val;
return;
}
function getName() {
return $this->name;
}}
As you can see, methods are declared using function [methodName] format, as is standard in PHP. This simple example shows us how we can apply a name to the object, then access the name. Continue on to learn about how we handle variables and methods within classes.
Class Variables and Visibility
Within classes, we first define our variables. In this case, our first variable is $name. A handy feature of PHP5 (not PHP4) is increased visibility control. Our $name variable currently has a visibility of private. Private visibility means that the variable can only be accessed via the methods inside the class (like setName and getName). If we set the variable visibility to public, our variable could be accessed externally (without the methods). The final visibility setting for a variable is protected, which means that variable access is limited to parent and inherited classes, which we will discuss more later.
In PHP4, public is the only visibility option and public variables are declared using var instead of public. Let’s take a look at how variable visibility works by adding a $location variable and setting it to public visibility:
/* instantiate object */
$ram = new User;
/* access private variables via methods, then try without methods */
$ram->setName( ram’ );
echo $ram->getName().’<br />’; // ram
echo $ram->name.’<br />’; // breaks our code
/* access public variables without methods will work */
$ram->location = ‘MIT’;
echo $ram->location.’<br />’; // MIT
class User {
private $name; // must be accessed with methods.
// not available in PHP4
public $location; // can be accessed without methods
// var $location; in PHP4
function setName( $val ) {
$this->name = $val;
return;
}
function getName() {
return $this->name;
}}
So which variable visibility should you choose? The answer is that it depends. In general, you should be fine using private. Private is especially useful because it supports encapsulation – the ability to hide data and only make it accessible through a given interface. In OOP, an interface represents the functionality given to a particular object.
Just as we set a variable’s visibility, we can also set visibility for methods. By default, if we don’t set the visibility of our methods, they will be set to public. For the sake of time, we will not dig into examples with different method visibility. Continue on to learn about an important and useful method, the constructor.
Note about $this: Within methods, we refer to class variables and other methods using $this->. This can be seen within the setName and getName methods. $this is a default variable created upon instantiation and enables an object to reference itself.
Note about naming conventions: While set[PropertyName] and
get[PropertyName] are popular method names because they are easily understood, they are not required. You can name these methods whatever you want.
Constructor Method
Now is a good time to look at the constructor method. The constructor is a method that will automatically be run when an object is instantiated. In PHP5, we have methods called “Magic Methods” (i.e. __construct, __destruct, and a few others) which begin with two underscores. In PHP4, the constructor method would be given the same name as the class itself. So in PHP4, if we have a class named User, our constructor method would also be named User (not __construct). Remember, you do not need to call the constructor method explicitly because it will be automatically run when an instance of that class is created. Here’s a constructor in action:
$ram = new User;
echo $ram->getJob().’<br />’; // Engineer
class User {
private $job;
function __construct() { // function User() in PHP4
$this->job = ‘Engineer’;
}
/* job methods */
function getJob() {
return $this->job;
}}
Now you might be starting to see one of the benefits of OOP – much of the code lies in the background. We could include files containing our classes to hide the heavy duty code even more. Because the naming conventions of classes and methods are fairly self explanatory, we won’t need to constantly look back at our included files. Continue reading to find out how we can use arrays to speed up our process.
Using Arrays to Speed Up Coding
Right now, it’s tedious to set each property using a set method – there must be an easier way. That’s where associative arrays combined with our constructor come in handy. Let’s create our user object and give it several properties upon instantiation:
$attribs = array(
‘name’ => ‘ram,
‘job’ => ‘Engineer’,
’school’ => ‘MIT’,
‘homeTown’ => ‘Spanish Fort’,
‘homeState’ => ‘Alabama’
);
$ram = new User( $attribs );
echo ‘Name: ‘.$ram->getName().’<br />’; // ram
echo ‘Job: ‘.$ram->getJob().’<br />’; // Engineer
echo ‘School: ‘.$ram->getSchool().’<br />’; // MIT
echo ‘Home Town: ‘.$ram->getHomeTown().’<br />’; // Spanish Fort
echo ‘Home State: ‘.$ram->getHomeState().’<br />’; // Alabama
class User {
private $name;
private $job;
private $school;
private $homeTown;
private $homeState;
function __construct( $attribs ) {
$this->name = $attribs['name'];
$this->job = $attribs['job'];
$this->school = $attribs['school'];
$this->homeTown = $attribs['homeTown'];
$this->homeState = $attribs['homeState'];
}
/* name methods */
function setName( $val ) {
$this->name = $val;
return;
}
function getName() {
return $this->name;
}
/* job methods */
function setJob( $val ) {
$this->job = $val;
return;
}
function getJob() {
return $this->job;
}
/* school methods */
function setSchool( $val ) {
$this->school = $val;
return;
}
function getSchool() {
return $this->school;
}
/* home town methods */
function setHomeTown( $val ) {
$this->homeTown = $val;
}
function getHomeTown() {
return $this->homeTown;
}
/* home state methods */
function setHomeState( $val ) {
$this->homeState = $val;
}
function getHomeState() {
return $this->homeState;
}}
I have kept the set methods in case we want to change the properties, but now the properties are established up front via __construct. That is, we passed our $attribs array to the object on instantiation, which in turn passed $attribs to the constructor automatically. The advantage to using associative arrays for this purpose is that we don’t need to explicitly set all of our variables with the methods. This is extremely handy when integrating PHP with MySQL data, which we will cover soon. But first, we need to learn a little bit about method interaction and object grouping.
Method Interaction
At this point, our methods do not interact. Let’s add a little functionality to our example by converting full state names to the appropriate abbreviations.
$attribs = array(
‘name’ => ‘ram’,
‘homeState’ => ‘Alabama’
);
$ram = new User( $attribs );
echo ‘Name: ‘.$ram->getName().’<br />’; // ram
echo ‘Home State: ‘.$ram->getHomeState().’<br />’; // Alabama
echo ‘State Abbr.: ‘.$ram->getHomeStateAbbr().’<br />’; // AL
class User {
private $name;
private $homeState;
function __construct( $attribs ) {
$this->name = $attribs['name'];
$this->homeState = $attribs['homeState'];
}
/* name methods */
function setName( $val ) {
$this->name = $val;
return;
}
function getName() {
return $this->name;
}
/* home state methods */
function setHomeState( $val ) {
$this->homeState = $val;
}
function getHomeState() {
return $this->homeState;
}
function getHomeStateAbbr() {
$abbr = States::$ABBRS[$this->getHomeState()];
if( $abbr ) {
return $abbr;
}
else {
return ‘Unknown’;
}
}}
class States {
/* ideally this would be held in a database */
static $ABBRS = array (
‘Alabama’ => ‘AL’,
‘Michigan’ => ‘MI’,
‘New York’ => ‘NY’
);}
In this example, we used a full state name to get its corresponding abbreviation. We did this using a class with a static variable. Static variables generally do not change and can be accessed without methods. These variables act more as reference and can be useful for things like your database connection settings and other site wide variables. Static variables are usually named with all capital letters.
Next, we will learn to integrate our object oriented PHP with MySQL.
Integrating OO PHP with MySQL
In this section, we will kill two birds with one stone. Not only will we learn how to integrate OO PHP and MySQL, but we will learn how to group objects. In this case, you will create a UsersGroup class which will contain an array of User objects. Each of those User objects will be created using a row from a MySQL query. To try this practical example, you will first need to create a Users table in a MySQL database. I am using a MySQL database named ‘ram_oop’. Use the queries below to create the table and insert the sample data.
/* Create Users table */
CREATE TABLE ‘users’ (
‘user_id’ INT NOT NULL AUTO_INCREMENT,
‘user_name’ TEXT NOT NULL,
PRIMARY KEY (‘user_id’)
);
/* Insert sample data into Users table */
INSERT INTO ‘users’
( ‘user_id’, ‘user_name’ )
VALUES
( ”, ‘ram’ ),
( ”, ‘bwh2′ );
This very simple table just has two columns: ‘user_id’ and ‘user_name’. You may wish to add your own name to this table to spice things up.
As promised, we will create a UsersGroup class. This group will contain an array of User objects, each of which is created using data from our MySQL table. All of my tables are on a database named ‘ram_oop’. If your database is not named ‘ram_oop’, you must change the database name in the Settings class shown below. Here is our UsersGroup class in action:
/* instantiate group */
$group = new UsersGroup;
/* loop through our group, echo user names */
foreach( $group->getUsers() as $user ) {
echo $user->getName().’<br />’;
}
/* User class, same as before */
class User {
private $name;
function __construct( $attribs ) {
$this->name = $attribs['name'];
}
/* name methods */
function setName( $val ) {
$this->name = $val;
return;
}
function getName() {
return $this->name;
}}
/* Contains a group of User objects */
class UsersGroup {
private $name; // name of group
private $group = array(); // group of User objects
function __construct() {
/* Connect to DB using Settings */
$link = mysql_connect(
Settings::$DATABASE['host'],
Settings::$DATABASE['username'],
Settings::$DATABASE['password']
);
mysql_select_db ( Settings::$DATABASE['database'], $link );
/* Get table names from Settings class */
$tbl_users = Settings::$TABLES['tbl_users'];
/* Query */
$sql = “SELECT user_id AS ID,
user_name AS name
FROM $tbl_users”;
$result = mysql_query( $sql ) or die(mysql_error());
/* Adds user to group with each row of data */
while( $row = mysql_fetch_array($result) ) {
$this->addUser( $row );
}
}
/*Add a user to Group
Does simple check to see if we pass an array (like $attribs)
or if we pass an object (like a User object)
*/
function addUser( $user ) {
if( is_object( $user ) ) {
array_push( $this->group, $user );
}
if( is_array( $user ) ) {
$noob = new User( $user );
array_push( $this->group, $noob );
}
return;
}
/* Returns the group (which is an array) */
function getUsers() {
return $this->group;
}}
/* Holds our site settings */
class Settings {
static $DATABASE = array(
// change these as needed ‘database’ => ‘ram_oop’, // May need to be changed ‘username’ => ‘root’, ‘password’ => ”, ‘host’ => ‘localhost’ ); static $TABLES = array( // reference to table name => actual MySQL table name ‘tbl_users’ => ‘users’ ); }
The first thing to note is that we are holding our database connection and table name information inside our Settings class, within static variables. In the event that we need to change our database name, table names, database usernames, etc. we will only do it in our Settings class (which is preferably in a separate, included file). Our User class is nothing new – it only holds the name property and still receives an array of data via __construct.
Let’s look at the code for the UsersGroup class. If you have connected to a MySQL database with PHP before, the connection should be nothing new. The only difference is that we use static variables for our connection settings. We do the same for our table names because within double quotes, our Settings class static variables cannot be parsed. That is, just using Settings::$TABLES['tbl_users'] directly in our double-quoted SQL query will cause an error.
Our SQL query produces an array of rows, which we assign to $result. Using a while loop, we go through our $result array. Each element in $result is an array of values (like id and name). Again, this should look very familiar to anyone who has used PHP to access a MySQL database. Now comes the OOP part: we then pass each row ($row) from our MySQL query to our addUser method. First, the addUser method checks if it’s being passed an array of data (like $row) or a whole User object. In this case, we are passing an array of data. Using that array of data, addUser creates a User object ($noob), then adds that user object to the group array ($group).
Note about column names: When our User object is created with array data, the constructor is looking for an array element called name. To fulfill this request, we use a SQL alias, which can be seen in $sql. If we did not alias user_name AS name the $row variable would not hold an array element called name but rather user_name. Thus, it would not correctly populate the name variable in our User object. I prefer to keep customization in my SQL rather than my PHP class methods because I am more likely to reuse PHP class methods than SQL queries. For instance, if I create a Dogs class, I would also want a getName method and constructor. Rather than modify several PHP methods to support dogs instead of users, I would rather modify one SQL query. Alternatively, you could also rename your user_name column to just name. This would maximize reusability, but may hurt your ability to understand more complex SQL queries and database designs.
You might be thinking this class is overly complex. After all, you can probably code all of this in 15 lines within a regular PHP page. So where’s the advantage? Well, look at how we use our objects:
/* instantiate group */
$group = new UsersGroup;
/* loop through our group, echo user names */
foreach( $group->getUsers as $user ) {
echo $user->getName().’<br />’;
}
Really, that’s 4 lines of code to echo out all of the users in our table. All of the database connection, querying, and real code is sitting behind the scenes. Remember, we are going to include our classes as separate files. On top of that, our classes are very reusable (aka modular). Our code is beautifully generalized and hidden from plain sight. It should also coded semantically, so it’s easy to understand. Debugging and maintenance time should be decreased significantly. In the end, we’re going to reuse this code for many sites and save tons of time and energy.
So that’s a basic user table accessed with a UsersGroup class. But what if we want some users to be Administrators and others just plain Users – how can we do that? For the answer, let’s continue on with the topic of inheritance.
Introducing Inheritance
To make this system more robust, we don’t want all of the users in our group to have the same status. Some users need to be administrators, others moderators, and others just regular users. How can we account for this in our objects? The answer is inheritance – the ability for one class to inherit methods and properties from another class. First, let’s take a look at inheritance in code:
/* ram has admin powers */
$ram = new Admin( array( ‘name’ => ‘ram’ ) );
echo $ram->getName().’<br />’; // ram
echo $ram->getAdmin(); // Administrator
/* Regular does not have admin powers */
$regular = new User( array( ‘name’ => ‘Regular’ ) );
$regular->getAdmin(); // breaks our code
/* Normal User class */
class User {
private $name;
function __construct( $attribs ) {
$this->name = $attribs['name'];
}
function getName() {
return $this->name;
}}
/* Admin is a User, but with additional functionality */
class Admin extends User {
function getAdmin() {
return ‘Administrator’;
}}
As you can see, our Admin object has inherited the functionality of the User class using the keyword extends. On top of that, our Admin is given some additional functionality. In this case, our User class is known as the superclass. In PHP, a class may only inherit one superclass. There are more nuances to inheritance, but this tutorial will only cover the basics. In a more realistic scenario, we would code more functionality for just administrators. Perhaps something like:
/* Additional functionality for just Administrators */
class Admin extends User {
function banUser( $id ) {
/* code to ban user */
}
function unbanUser( $id ) {
/* code to unban or activate user account */
}
//…
}
For the sake of time and focus, these methods haven’t actually been coded. But this should give you some insight into how inheritance works and why it can be useful.
Continue on for a few parting words and direction.
Parting Words
By now, you should understand what objects, how to create them, and how to access their properties and methods. This tutorial is by no means all-encompassing. In fact, this is just the tip of the iceberg and was designed as such. For further reading, I suggest PHP5: Objects, Patterns, and Practice by Matt Zandstra. In addition, you could read through the OOP documentation provided at php.net.
As you begin to understand OOP more and your PHP code becomes more object oriented, you will find more code reusable and ultimately save time and energy. Pre-existing code resources such as PEAR and PHPClasses.org will become increasingly useful