ITT #10: Understanding OOP
A Crash-Course in PHP Classes and Objects
NOTE: This comment by Cal Evans pointed out an oversight on my part regarding naming conventions for classes. To avoid conflicting with other classes, names should be distinctive, and as such, I changed the name of the example class from "date" to "Ennui_Date".
Recently, I've fielded a handful of questions concerning object-oriented PHP. What is it? How does it work? Why should we care?
I had similar questions when I first started looking at OOP. I mean, why the hell would I want to switch from the easy-to-understand procedural method?
Classes and objects might look confusing and difficult to manage at first, but they actually significantly improve the organization and readability of your code if used properly.
A Practical Example
Let's say, for instance, that we need to be able to get a formatted date string for various purposes throughout a site we're building. However, the format of the date may vary throughout the site, and we may need to create a formatted date from any given timestamp.
Additionally, we need to be able to add days to a date, in increments of one week. Let's look at the difference between implementing a solution in both the procedural and object-oriented approaches.
Do What We Know: The Procedural Approach
Our first step is to get a formatted date string. To do this, let's create a function that will accept a formatting string and a timestamp, both of which are optional. Then, inside that function, we check to see if a timestamp was supplied. If so, format for that date. Otherwise, return a formatted string for the current timestamp.
We'll also define the addOneWeek() function, but we'll get to how that works in a minute.
function formatDate($format="F j, Y", $timestamp=NULL)
{
if(isset($timestamp)) {
return date($format, $timestamp);
} else {
return date($format);
}
}
function addOneWeek($timestamp=NULL)
{
if(isset($timestamp)) {
return $timestamp + 7*24*60*60;
} else {
return time() + 7*24*60*60;
}
}
That's nice and easy. I know that when I call formatDate(), I'm going to get a formatted date string. Nothing fancy, nothing confusing, right?
To call this function, we simply do the following:
echo formatDate(); // Outputs today (e.g. 'April 7, 2009')
echo formatDate('m-d-y', '1211756595'); // Outputs '05-25-08'
Trying Out the Object-Oriented Approach
Now, just for fun, let's see how the exact same functionality is achieved with a class in PHP.
class Ennui_Date {
private $format;
private $date;
function __construct($format=NULL, $date=NULL)
{
$this->format = (isset($format)) ? $format : "F j, Y";
$this->date = (isset($date)) ? $date : time();
}
public function formatDate()
{
return date($this->format, $this->date);
}
public function setFormat($format)
{
if(isset($format)) {
$this->format = $format;
}
}
public function setDate($date)
{
if(isset($date)) {
$this->date = $date;
}
}
public function addOneWeek()
{
$this->date += 7*24*60*60;
}
public function __toString()
{
return $this->formatDate();
}
}
But what does all of that mean?
Understanding Classes and Objects in PHP
First and foremost, it's important to note that objects and classes, while closely related, are not the same thing. An object is any collection of stored information. In web applications, this could be data related to entries on a page, users of the site, etc. By itself, this information isn't particularly useful, which is why we need a class.
A class acts a a blueprint for the object. It names the pieces of information and provides methods for interacting with it.
Re-Examining Our Date Handler
Let's look again at the class we've defined. We start by defining the class, instantiating its variables, and declaring our constructor method:
class Ennui_Date {
private $format;
private $date;
function __construct($format=NULL, $date=NULL)
{
$this->format = (isset($format)) ? $format : "F j, Y";
$this->date = (isset($date)) ? $date : time();
}
}
In this snippet, we've essentially said, "Create a class called 'Ennui_Date' that holds two pieces of information, a formatting string and a date timestamp. When when the class gets defined, check if a formatting string was passed, and set it as the object's formatting string. Otherwise, use a default string. Then check if a date was passed, then store it as the object's date timestamp or use the current time."
The __construct() function is a special function that PHP runs whenever a new object is created. Well use it in this instance to allow us to pass our optional parameters to the object. The __construct() function isn't required; it's just convenient.
To format our date, we'll add a public method called formatDate():
public function formatDate()
{
return date($this->format, $this->date);
}
Because we already checked our values in the __construct() function, we can simply return a formatted string based on the values stored in the object.
In order to simplify the display of our formatted date, we'll use another special method, the __toString() method. This method determines what happens if the object is used as a string (using the echo construct, for instance). Because we already have a function to return the date, we simply need to reference it in the __toString() method, like so:
public function __toString()
{
return $this->formatDate();
}
Now, to instantiate the object and output a formatted date, we use the following code:
$date = new Ennui_Date();
echo $date; // Outputs today (e.g. 'April 7, 2009')
Not exactly simple to set up, but how easy is it to ouput the date? But that seems like an awful lot of foundation just to output a formatted string.
As we expand on our class, however, the convenience of OOP starts to become a little more obvious.
Adding More Functionality
Let's say, for instance, we need to be able to change the date somewhere on the page we're building, and we don't want to use the default formatting string.
Procedural Approach
If we're thinking procedurally, we'd do it something like this:
echo formatDate(); // Outputs today (e.g. 'April 7, 2009')
$format2 = "l, F jS at g:iA";
$date2 = 1224981409;
echo formatDate($format, $date2); // Outputs 'Saturday, October 25th at 6:36PM'
While there's nothing wrong with the approach above, it's feeling a little messy.
Object-Oriented Approach
By declaring two new methods in our Ennui_Date class, we can clean up the code a little bit.
public function setFormat($format)
{
if(isset($format)) {
$this->format = $format;
}
}
public function setDate($date)
{
if(isset($date)) {
$this->date = $date;
}
}
To implement the new methods, we simply use the following code:
$date = new Ennui_Date();
echo $date; // Outputs today (e.g. 'April 7, 2009')
$date->setFormat('l, F jS at g:iA');
$date->setDate(1224981409);
echo $date; // Outputs 'Saturday, October 25th at 6:36PM'
Because the variables are stored in the object, we don't have variables floating around our code, which keeps the code more legible and compartmentalized.
Add a Week to the Date
By now, the benefits of OOP are starting to show, but it's still not too painful to use the procedural style. When we get into preprocessing our data, however, it starts becoming very obvious that OOP adds convenience to our code.
addOneWeek(), Procedurally
Let's add a week to our procedural implementation's date. We'll start by defining our addOneWeek() function:
function addOneWeek($timestamp=NULL)
{
if(isset($timestamp)) {
return $timestamp + 7*24*60*60;
} else {
return time() + 7*24*60*60;
}
}
The function accepts an optional paramter, a timestamp, and adds seven days' worth of time (7 days X 24 hours X 60 minutes X 60 seconds) and returns a new value: either seven days from the provided timestamp or seven days from now.
Running the function requires extra variables, like so:
$format = 'm-d-y';
$time = 1211756595;
echo formatDate($format, $time); // Outputs '05-25-08'
$newtime = addOneWeek($time);
echo formatDate($format, $newtime); // Outputs '06-01-08'
It's starting to get a little tough to read. We haven't tied ourselves in a knot by any means, but we now have two functions and three variables to keep organized in our minds.
Organizing with $this->addOneWeek()
To help us stay organized, let's try to add a week with our Ennui_Date class.
public function addOneWeek()
{
$this->date += 7*24*60*60;
}
Right off the bat, the method looks a little easier to swallow than its procedural counterpart. Its implementation is equally straightforward:
$date = new Ennui_Date();
echo $date; // Outputs today (e.g. 'April 7, 2009')
$date->addOneWeek();
echo $date; // Outputs a week from today (e.g. 'April 14, 2009')
Here, we can clearly see how much cleaner OOP can be than procedural code. If we start to imagine these implementations among other code that affects the web app, it's easy to see how much easier it would be to track our Ennui_Date object than our several variables and functions associated with our procedural attempt.
In Conclusion
OOP is an incredibly powerful tool to have in your skill set, and it shouldn't be overlooked as "too complicated" because, as we just discovered, classes and objects actually simplify programming after we take the time to get familiar with them.
It's also important to recognize that not everything needs to be object-oriented, and sometimes creating an object is overkill. However, if used tastefully, adding classes to your applications can greatly increase the simplicity and portability of your code.
Do you use classes and objects? How do you decide what should be a class and what should remain procedural? Let me know in the comments!
Comments for This Entry
Great tip, bad example.
http://uk2.php.net/manual/en/userlandnaming.tips.php
This is a great tip and normally I wouldn't say anything but I know as this tip gets more and more popular, people will start copying and pasting code and that could be a bad thing.
Currently Date is not a reserved word but in the future it could be. A better example would be
class Ennui_Date {
. . .
}
or, after 5.3 released use the Ennui namespace but that's probably beyond the scope of an Intro to OOP post. :)
Other than that, great post, good job of giving a clear example.
=C=
hey! :) Thanks so much for this! really appreciate the intro. I have a good starting point now! :)
Thanks for sharing this series, really looking forward to some more advanced techniques, there is always more to learn! Also, it's really great to see people like Cal Evans giving some input and feedback to the php community :)
I should really take the time to properly understand and learn to think in terms of object oriented programming. I used to do a lot of PHP, but now I'm just getting started with Python - and it feels kind of stupid to relearn the procedural approach instead of taking this opportunity to properly learn to use OOP.
This is very easy to understand and straight to the point tutorial. I've just started learning oop and this serves as a good starting tutorial.
I will definitely keep up with all your future posts about php.
Thanks,
Benga
I am not a programmer, I can program though and have had a very difficult time understanding why some of the programmers I've hired over the years use classes when a procedural approach seems to logically make more sense. An example of this is a simple sign up script that will, in all likely-hood, never need to be expanded or changed much. Building 3 classes as one of my now fired programmers did seems to me like he took this opportunity to practice his newly found OOP skill on something that most definitely didn't need it.
Your comment that creating a class when it really is overkill hit the nail on the head for me. Kudos to your train of thought. For any would programmers out there, overkill is overkill and believe me, it can seriously hurt your job opportunities. Write simple software. By all means use classes if they will make the software better, but please don't overkill development. Nothing like simple, clean and easy to understand code.
Thanks so much for this good explanation of object orientated programming. I've been looking to get more info on the subject and the series you guys created on the subject of designing a web application is really cool and I can't wait to really dig in.
Greg
Good basic tutorial, though maybe a definition of Method/Attribute vs function/variable might make it a little more comprehensive.
Also, the blue on black type for code is a little hard to read...
thanks for the tutorial though
Thanks for the convincing argument and nice intro!
I followed up a reading of your site with a visit to http://www.killerphp.com/tutorials/object-oriented-php/ which, as Mike requested, explains the terminology of php objects.
H Jason,
I am extremely new to PHP and for the last couple of days I have been going crazy for someone to give me a 'laments terms' explanation of this subject. I just wanted to say 'Thank You'. It is still bit confusing but you have made it so much easier to look at. I will be keeping this example close so I can use it to do my own scripting from the PHP course I am doing at the moment.
Thanks again and keep up the great work...
James
fridge2305@yahoo.ca
Thanks for the explanation of OOP. Well said. I agree with Mike though but on a deeper level. The blue on black isn't just hard to read, It's darn near impossible.
Really good article, thanks. I agree with Alex, keep the code SIMPLE. OOP has it's place, but I've had to work on code from other programmers who went crazy with classes and made simple tasks a pain. Classes should be used to make complex code simple - not simple code complex!
Great Intro! That helped me bridge the long gap between me and my first OOP project!
Thanks Jason..!
=)
And, to add to it a bit,
It would be much appreciable if you could put out some real examples,
to what are the possible advantages for OOP, in such a noob-friendly language such as PHP.
I mean, it was so comfortable with procedural PHP, that i was very much reluctant to turn to OOP. And im sure many other dev's would be facing a similar trouble to kick-off with it.
A bit of a case study from experienced proff's like yourself, that can enlighten us new-comer's, about the advantages of the concept would be great!
Also, isn't that code template a bit too dark?
Just suggesting that an off-white bg wouldn't loose the point either.
=)
Thanks again.
-V
@Vaishakh Pushpoth:
Real-world examples are usually pretty involved, which is why it's rare to see them in articles. They'd be too long and have to cover too much ground.
Pro PHP and jQuery is my book on combining OOP with an AJAX front-end to build a real-life web app. It covers all the bases and presents real-life examples, and I think it would be more of what you're looking for: http://bit.ly/b7YC6P
If you don't want to get a book, I've written another article on OOP over at Nettuts (still no real-world examples, but more explanation) that may help: http://edsgn.us/aYnZAh
Good luck!
Post a Comment
Want to show your face? Get a gravatar!