Webforumz Newsletter - October 2007
Tutorials
Abstraction - For the rest of us
Millions of web developers, from those just starting out with sparkling ampersands in their eyes to hardened veterans with <?php burned to the back of their retinas, have found a love for PHP and its possibilities but still talk in hushed whispers about the mystery and intrigue of Object Oriented Programming (OOP) in PHP.
This tutorial aims to remove some of the curly-brace-filled haze that often surrounds OOP and by the end of this lesson you will have created a class, instantiated an object, given inheritance to some children and come to understand why OOP is such a powerful tool to have at your disposal. If you have no idea what any of the above means but are shaking with a desire to find out, read on as we look at Abstraction, but for the rest of us.
NB: The code will be using the OOP first found in PHP 4 (will work in PHP 5) - OOP support was greatly increased in PHP 5 but the most common version of PHP is still 4. The basics of OOP can be learned in PHP 4 so you can move to the more complex ideas in PHP5.
What is OOP and why should I use it?
OOP is the process of abstracting (removing and isolating) variables, and the actions required to manipulate them. By grouping the code together in a logical manner you are able to create objects - self contained areas of code that will perform the leg work within themselves, allowing you to deal with results separately to the actions.
Using the Procedural Approach (the name given to a program that runs from top to bottom following a designated procedure), if you wanted to write a script in which you kick a ball and find out where it lands, you would need to create variables (or an array) for the ball, its position, the speed it was kicked etc.. You would then need to write the functions that process these variables and all of this would be included into and/or intermingled with the code that works with outputting or storing results.
In any script that you wanted to kick a ball and see where it lands, the variables would need to be created and the functions to manipulate them would need to be included.
Using the Object Oriented Approach, instead of creating all these variables and functions (which have no explicit ties to each other), you would create a ball object. The ball object would contain variables pertaining only to it (properties) and functions that deal almost exclusively with these properties (methods). You would not have to worry about including the right functions or the scope of your variables, you would only have to tell the ball what you want it to do (be kicked) and ask it what you need to know (where did you land?).
- When dealing with the variables of objects, we call them properties. Although they behave exactly the same way as variables, they relate specifically to that object.
- Similarly, when dealing with objects, functions become methods. Objects have methods to access and alter properties
Each time you wanted to perform these actions, it would simply be a matter of creating the ball object and asking it what you need to know. This is where the power of OOP starts to emerge, the flexibility of Abstraction. By grouping the properties and methods together logically, they are isolated and can be included where needed - functioning almost like self-contained programs that can be slotted together like blocks of Lego.
OOP allows for flexible, easily expandable, graceful programs.
Okay, How do I do this in PHP?
To create an object, you first need to create a blueprint for the object. The blueprint outlines what properties (variables) the object should possess and what methods (functions) the object should be able to perform. This blueprint is called a class and is best looked at like a cookie cutter. A cookie cutter defines the shape of the cookie and can be used to punch out as many cookies as needed. A class defines the shape of an object and can be used to create as many objects as needed.
Defining a class
To create a class in PHP, you use the class keyword and give it a name - similar to way you would declare a function. The class definition is surrounded by curly braces.
class className
{
// Class definition goes here
}
Building an object
Now that you have a blueprint (however useless it is in its present form) you can create an object. This process is called instantiation.
To instantiate an object it's time to use another PHP keyword that you may not have encountered - new.
<?php
class className
{
// Class definition here
}
//Instantiate the object
$anObject = new className;
echo '$anObject is now an object built from the',
get_class($anObject), ' class';
?>
That's it! Obviously this is just the shell but it is all that is required to start building objects.
Give your object some identity
Now it's time to get your hands dirty and learn how to build objects with properties and methods.
A common task performed by PHP is to maintain state for a user as he/she moves around the site, the rest of this tutorial will focus on defining a class that will create 'user' objects, giving access to and allowing modification of a user's preferences.
Start by defining the class:
<?php
class myUser
{
}
?>
A user traveling around your site will have some properties that will change from user to user. Some of these properties might be:
- First & Last Names
- User Name (For use on the site)
- Email address
- Age
- Gender
To add these to your blueprint or class, use the PHP keyword var and then add the name of the property in the same manner you would use to define a variable.
The user class now looks like this:
<?php
class myUser
{
var $firstName;
var $lastName;
var $userName;
var $email;
var $age;
var $gender;
}
// Instantiate the object
$user = new myUser;
?>
The object in $user now has all of the properties defined in the myUser class. Knowing that $user has these properties is fine, but you need to be able to access them.
To access a property of an object you use the following syntax:
$user->firstName; // $Variable->propertyName
Notice that when you want the property name you drop the dollar sign and just write the name. You can assign a value to a property using the same syntax:
$user->firstName = 'Luciano';
An object property can hold almost any data type including other objects.
Get some action - defining methods
Now that you know how to get and set the properties of an object, you would be able to populate all the properties with values and access them, but using this technique would mean your object wouldn't be anything more than a glorified array. It is a highly recommended coding convention that you avoid accessing object properties directly, you should instead access and modify object properties using methods written for the task. There are many reasons for this including but not limited to:
- PHP allows you to create properties on the fly which may lead to bugs in your code. For example $user->firstNam = 'Luciano'; would not throw and error, instead it would create a
'firstNam'property in$user. Later, when$user->firstName;doesn't return the expected result, it may not be easy to find out why. - Methods allow you to perform checks and tasks on data before assigning it to a property. Adding an array to the
firstNameproperty would not be very useful as people aren't usually named in arrays. With methods you can check that the data is what the object expects before changing the property.
The code below shows how to add two methods to your user class. One will set the firstName of the user and the other will return it you can use it (e.g. Echo it to the screen).
<?php
class myUser
{
var $firstName;
var $lastName;
var $userName;
var $email;
var $age;
var $gender;
function setFirstName($name)
{
//Check that the data is valid for use as a firstname
if (ctype_alpha($name))
{
$this->firstName = $name;
return true;
}
// Return false if the data is not valid
return false;
}
function getFirstName()
{
// Return the current value of firstName
return $this->firstName;
}
}
//Instantiate the user
$user = new myUser;
// set the user's first name
if ($user->setFirstName('Luciano')
echo '$user now has the first name ', $user->getFirstName();
else
// If the method returns false, something is up
echo 'That first name isn\'t valid';
?>
There's quite a bit happening in the myUser class now so let's analyze what's going on. Something obvious is that the only difference between declaring a method and declaring a function is that that a method is declared within a class's curly braces.
function setFirstName($name) defines the method setFirstName that will accept the argument $name and perform a simple check to determine if $name contains only letters from the alphabet. If it does, $name will be assigned to the firstName property using the special variable $this. $this refers to the current object instance that is calling the method
$this->firstName = $name;
is exactly the same as
$user->firstName = $name;
Anytime you need to refer to the current object calling your method (in the case above $user), use the $this special variable - this removes all headaches about how to address a certain object.
The next method is named getFirstName and will return the object's current firstName property - again note that we are using the special $this variable which is really saying "Return the value of the firstName property for the $user object".
In the code above you have now instantiated the object $user and called (invoked) the methods defined. So invoking a method is the same as calling a function except you precede it with the variable name and ->
$user->setFirstName('Luciano'); //$variable->methodName($arguments)
Adding a constructor
You can now add methods that allow you to set and get all of the properties in a myUser object.
Although this has abstracted the properties and methods, it would be cumbersome to call a method and set every property each time you created an object. To fix this problem you can define what is known as a constructor method. In PHP 4, this is a method that has the same name as the class. A constructor method will be invoked when an object is instantiated.
For your user class, define a constructor method to set all the properties at the same time the object is created.
<?php
class myUser
{
var $firstName;
var $lastName;
var $userName;
var $email;
var $age;
var $gender;
// myUser - constructor method, accepts all properties
// to be assigned when myUser object is instantiated
function myUser($firstName, $lastName, $userName, $email, $age, $gender)
{
$this->firstName = $firstName;
$this->lastName = $lastName;
$this->userName = $userName;
$this->email = $email;
$this->age = $age;
$this->gender = $gender;
}
//................further methods
}
//Instantiate the object using the constructor
$user = new myUser('Luciano', 'Dinglini', 'Rakuli', _
'rakuli@example.com', 23, 'Male');
echo '$user->firstName was set to ', $user->getFirstName(), _
' by a constructor method';
?>
You will see from the above code that the only change to creating the object is to add the arguments accepted by the myUser constructor method - just like a function call only preceded by the new keyword. It is now possible to access all of the properties immediately after instantiating the object because the constructor method has taken care of assigning the properties their values.
In the example above, the constructor requires all values to be given when invoked. A user visiting a site might not want to give an email address or their age - you can overcome this by making some or all of the arguments for the constructor optional. By doing this, you can still create an object but fill the properties with default values until set later with methods.
When a user first arrives at your site, you might instantiate a user object from a constructor method that looks like
function myUser($userName = 'Guest', $firstName = '', $lastName = '')
{
$this->firstName = $firstName;
$this->lastName = $lastName;
$this->userName = $userName;
}
This would set the userName property as 'Guest' if you didn't include it when creating the object. Later, you could set the user name with a method that might be $user->setUserName('JohnDoe');
Flexing your properties
That's the basics of OOP in PHP. With what you know now you can create all of the methods for setting and getting your user's properties.
The class could now look like this:
<?php
class myUser
{
var $firstName;
var $lastName;
var $userName;
var $email;
var $age;
var $gender;
// myUser - constructor method, accepts all properties
// to be assigned when object is instantiated
function myUser($userName = 'Guest', $firstName = false, _
$lastName = false)
{
// delegate the setting of the properties to the defined methods
$this->setUserName($userName);
$this->setFirstName($firstName);
$this->setLastName($lastName);
}
function setFirstName($name)
{
//Check that the data is valid for use as a firstname
if (ctype_alpha($name))
{
$this->firstName = $name;
return true;
}
// Return false if the data is not valid
return false;
}
function setLastName($name)
{
//Check that the data is valid for use as a lastName
// We'll let them get away with having 1 hyphen and
// 1 apostrophe only
if (substr_count($name, '-') > 1 || _
substr_count($name, '\'') > 1)
{
// Sorry Mr O'reilly-O'brien I know you may be out there but
// I'm keeping it simple for now
return false;
}
if (ctype_alpha(str_replace('\'', '', str_replace('-', _
'', $name))))
{
$this->lastName = $name;
return true;
}
// Return false if the data is not valid
return false;
}
function setUserName($name)
{
//Check that the data is valid for use as a userName
if (ctype_alpha($name))
{
$this->userName = $name;
return true;
}
// Return false if the data is not valid
return false;
}
function setEmail($email)
{
// Very simple regex to check email validity
if (preg_match("/^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+. _
[a-zA-Z0-9-.]+$/", $email))
{
$this->email = $email;
return true;
}
// Bad email address
return false;
}
function setAge($age)
{
// We'll make sure they're not still in diapers or trying to
// set a guiness world age records
if (is_numeric($age) && $age > 5 && $age < 120)
{
$this->age = (int) $age;
return true;
}
// Lying about your age?
return false;
}
function setGender($gender)
{
// Run a quick switch -- allows for a few methods of declaring
// your gender identity
switch(strtoupper($gender))
{
case 'MALE' :
case 'M' :
case 'MAN' :
$this->gender = 'M';
return true;
break;
case 'FEMALE' :
case 'F' :
case 'WOMAN' :
$this->gender = 'F';
return true;
break;
}
// Gender remains ambiguous
return false;
}
function getFirstName()
{
// Return the current value of firstName
return $this->firstName;
}
function getLastName()
{
return $this->lastName;
}
function getUserName()
{
return $this->userName;
}
function getEmail()
{
return $this->email;
}
function getAge()
{
return $this->age;
}
function getGender()
{
return $this->gender;
}
}
?>
I can hear you saying now - but this is still only an array that validates data types it allows me to store and retrieve data but I can't do anything with it. In its present form, yes, but now you can get creative and start defining methods utilizing the properties.
Try creating a method to say hello to your user:
// This method will return a welcome message for the user
function helloUser()
{
$message = '<p>';
$message .= 'Hello' . $this->userName;
// If userName wasn't given, this will show 'Guest'
// If we have first and last names, we'll show those in brackets
if ($this->firstName || $this->lastName)
$message .= ' <span style="font-size: xx-small;">(' . _
($this->firstName ? $this->firstName : '') . _
($this->lastName ? ' ' . $this->lastName : '') . _
')</span>';
$message .= '</p>';
// Make a remark about their age?
if ($this->age)
$message .= '<p>It\'s nice to have a ' . $this->age . ' _
year old on this site!</p>';
// return the message
return $message;
}
Now you can see that this method will deal with the context for you. It will display a nice hello and also the user's real name -- but only if it's set. This is the Abstraction that OOP allows. Once you have created this class all you ever have to worry about is $user->helloUser() and you will be returned a result without having to worry what's going on in the object.
Using the ball analogy from earlier, you don't have to worry about what's affecting it, you just kick it and watch where it lands - how it gets there is for the ball object to sort out.
Now that you have the basics of OOP down you can build objects and work with the properties in any way you like and the beauty is that when you want the result, you never have to do more than $user->askForIt()
Think of the children
Defining a class and filling it with properties and methods has now allowed you to abstract the conditional processing leaving you to work with the fruits of an objects labor. There will however be occasions that call for something a little different; you may want your object to behave a little differently.
OOP in PHP has this covered for you as well, rather than stacking your object with code and conditionals that will only be used occasionally, you can create another class that not only has its very own properties and methods, it has access to all the properties and methods in another class. What this breaks down to is the original (parent) class and the extended (child) class.
Going back to the myUser class you have been building, think of the following scenario. You have a user that is a VIP and you would like to show you care by displaying a special message on each page. You could achieve this by adding some if's and else's to the myUser->helloUser() method which may also require you create additional properties.
It would be far easier to create a child class for myUser that has these properties specific to it but still has access to the properties and methods of myUser. To do this in PHP, use the extends keyword when defining your class.
<?php
class VIPUser extends myUser
{
function VIPUser($userName = 'VIP', $firstName = false, $lastName = false)
{
// delegate the setting of the properties to the defined methods
parent::myUser($userName,$firstName,$lastName);
}
function helloUser()
{
$message = 'You are a VIP, this site welcomes you.';
return $message;
}
}
$vipUser = new VIPUser('Super Awesome Guy');
?>
Looking at the code above, you are now seeing a few new concepts come into play. Starting with the initial class definition class VIPUser extends myUser is saying that any objects created from this class will be children of the myUser class and will inherit all properties and methods from myUser unless overridden in the child class. This is where the power of extending classes comes from. Normally declaring two functions with the same name will throw a fatal PHP error and stop your script but when you are extending a class, you can declare the same function as defined in the parent and change its functionality. This means that $user->helloUser(); and $vipUser->helloUser(); will have different results even though the objects have the same properties.
In a child class you create a constructor just as normal by defining a method with the same name as the class and instantiate the object in exactly the same way. Child classes can also make a direct reference to methods in their parents using the parent keyword followed by :: then method you wish to call. parent allows you to call a method (in the example above, the myUser constructor) from the child meaning that you get the functionality of two constructors - the child and the parent.
By creating child classes and overriding parent methods you can create an "Inheritance Tree". For example, if you wanted to welcome the Queen of England, you may wish to create a superVIPUser class that extends VIPUser, superVIPUser will have access not only to the properties and methods of VIPUser it gets them from myUser as well.
The power of this inheritance behavior means that you can create a base class that deals with a user and include it on every site you own, any changes you need to make specific to a site can be set up in a child class. If you never have to edit the original class, you never have to edit your scripts that rely on it allowing you to easily build on and expand your programs. This is a rare occasion -- kids feeding off parents provides a richer family ;)
Pass the object to your left
Okay, you can define classes, build objects and work with an object's methods - you can even have a child object inherit properties and methods from its parent - but how you would go about maintaining state as promised above? You can do this using two of PHP's most useful functions.
serialize() and unserialize()
After instantiating an object and working with the methods to flesh out its properties, you can pass the object through serialize to convert the object - and all of its properties -- to a string. This is nothing more than a normal string that you can pass it from page to page with $_SESSION or even store in a database. When you want to use the object again, you can revive it using unserialize.
NB: In order to unserialize an object, you must have the class that it was created from included or defined in the current script. This is achieved by simply creating your class in a separate .php file that can be included when needed.
Okay, better recap
So to sum up what we have covered in this basic introduction to OOP.
Why Use It?
OOP allows you to create individual blocks of self maintained code. The objects can take care of the processing and context, leaving you to work with the results.
How Do I Use It?
In PHP you use the class keyword to define the blueprint - or cookie cutter - and fill this class with properties (using the var keyword) and methods (defined just like normal functions) that your object will contain. When you're ready to create an object, you use the new keyword with your class name.
If you want a method to be invoked when instantiating an object, you define a method with the same name as your class (a constructor method), you can then pass arguments that will be attached to variables or used for other actions when creating an object.
If you would like to change the behavior of a method or need additional properties that won't always be useful in your class, you can create a child class using the extends keyword. The child class will inherit all the parent class' properties and methods. To override a method, you can define a method in the child that has the same name as one in the parent allowing you to change the child's behavior. To access a method of the parent (for example you want to access a method that your child class has overridden) you can address is using the parent keyword and the syntax parent::methodName();
Another look at a full class definition:
<?php
class className
{
// Although it's not advisible, I can access and change this property
// Using the following syntax on the variable that holds my object
// $myObject->property = 'Setting the value';
// $value = $myObject->property . ' getting the value';
var $property;
function className()
{
// This will be run when I create an object
// I put the object in a variable using the new keyword
// $myObject = new className();
}
function method()
{
// I can use this method with the syntax
// $variable->method();
// Using $this is a special variable that acts like
// the current object instance
$this->property = 'Some Value';
}
}
class childClass extends className
{
// This is a child class that will inherit all of the properties
// and methods in the parent or base class
// child classes have constructors
function childClass()
{
// If I want to run the contructor (or any other method)
// in the parent class at the same time as this I can
// use the parent keyword.
parent::className();
// Remember though that $this->method() will work becuase
// the child class inherited the parents methods.
}
}
?>
Anything Else I Should Know?
To store your objects as a string that you can move around in the $_SESSION super global or save to a database you can use the serialize function.
$string = serialize($myObject);
$myObject = unserialize($string);
You need to make sure PHP can find the class definition when using unserialize.
Wrapping up
Through this tutorial you have learned the basics of OOP from the definition of classes through to extension and inheritance from parents. The Object Oriented approach to programming is the way of the future, as PHP 5 showed and PHP 6 will show furthermore.
This has been a brief introduction and it is hoped you will want to develop you understanding further. The best place to start - The PHP website of course. Once you are comfortable with the basics you can delve deeper into some of the more complex philosophies and take Abstraction to your next big project.
For now, it is hoped you will join the revolution of Abstraction - For The Rest Of Us.
You can see the sample class - with a few other snippets in action at http://www.openthource.com/abstraction.php and also download the complete class.
Glossary or OOP terms
- Abstraction
- The process of grouping together and isolating variables and functions and tying them together into an object that can be dealt with separately to the results they produce.
- Child
- A child class is a class inheriting objects and methods from another class - the parent.
- Class
- (noun)The blueprint for an object. Comparable to a cookie cutter - a class can be used to create many objects with predefined attribute.
- (php keyword)Used to tell PHP that you are declaring a class.
- Constructor
- A method that is invoked at the same time an object is Instantiated (see Instantiation). A constructor can be used to set properties of objects when created.
- Extends
- (php keyword) Used to define a class that is an extension of another class. The class being extended becomes the parent and the extending class becomes the child.
- Instantiation
- The process of creating an object from a class. Instantiation is comparable to using a cookie cutter to cut a cookie from some dough.
- Invoke
- The functions of an object, know as methods, are run using the syntax
$variable->methodName(). To call an object function is to invoke and object method. - Method
- Functions of an object are known as methods. Methods handle the conditional processing in order to retrieve and modify object properties.
- New
- (php keyword) New is the keyword used to instantiate and object. The syntax
$myObject = new className(); - Overriding
- Overriding is the processing of declaring a method in a child class that has the same name as a method in the parent class. This allows the child to process data differently to the parent when that method is invoked.
- Parent
- (noun) A parent class is the base class. When it is extended by another class it becomes the parent class allowing child classes to inherit its properties and methods.
- (php keyword) a keyword that can be used in a child class to address methods in the parent. Most often used to access a class that the child may have overridden. The syntax for using the keyword is
parent::methodName(); - Properties
- Variables of an object are known as its properties. A property relates solely to that object.
- Var
- (php keyword) Used to define object properties. The syntax
var $property;orvar $property = 'Default Value';