I hated the notion of Object Oriented (OO) programming. I’d been using PHP since early version 3, and I had put together some very usable web sites with it. Every book I read on the topic seemed to be rich with metaphors about dogs, cars, and various other things, but not much on why I would want to abandon my happy procedural methods. Not that these authors didn’t try. The arguments they made just didn’t hit home with me at the time.
I think I may now understand why this was. Most of the efforts I’ve seen to describe object oriented design are coming from the perspective of someone who thinks in an OO way. I’m not saying this was a bad approach, it just really didn’t work for me. Perhaps if I hadn’t been so deeply entrenched in the methodologies I had been using for years I would have been more receptive to this type of presentation.
I don’t have any better arguments here for designing in an OO way, but I believe this article approaches the topic from an angle that might make things a bit clearer for people like me. Programmers who have been putting together working sites in a procedural manner and can’t seem to make the mental hurdle into OO.
Mind you, if we were still talking about PHP4 avoiding OO would have been a good thing. It really was a hack job slapping on only some of the most basic functionality for objects. It was bad enough that many of the ways you would have done things in version 4 are now, thankfully, obsolete. PHP5 has finally started making it real.
The issue with learning how to deal with objects isn’t in the syntax or the mechanics. I can promise you, once you get a handle on the concept of OO design the mechanics darn near write themselves. Much of it is just a matter of rethinking much of what you already know. Perhaps learning a few new terms along the way.
The real key to your understanding of Object Oriented programming is purely in getting the concept of it. This article intends to focus on concept rather than functioning code. I will be leaving a bunch of details out that I believe will hinder your grasping of the concepts. Many of these are better handled by some of the books I’ve referenced at the end of the article or the PHP manual online.
I will begin by taking you through some of the problems you may have already experienced with a purely procedural approach to your design. One by one I hope to address how what appear to be problems in procedural land are actually features of designing in an Object Oriented way.
Development Stages of a Developer
Looking back on it now I went through a set of stages in my thinking and development habits as the projects I worked on got larger. It is not, as many people might lead you to believe, because an OO design strategy is only useful on a large project. It is due to the fact that many of the pitfalls of writing purely procedural code don’t really become readily apparent until you are dealing with a larger set of code. They become screamingly obvious once you start working on the next project needing similar code as the one before.
If you’ve been working with PHP for a while you may recognize some of these stages. I know I went through each and every one of them, and I know others who have done much of the same.
Stage 1
You’ve got your script talking to a database. You’ve got forms collecting data, and clever SQL and loops producing that data into something presentable. Your PHP code and HTML are coexisting in harmony on all of these wonderful dynamic pages. There is much rejoicing.
Stage 2
The harmony that had existed has started to vanish. The data and the pages they are producing are becoming more complex. Worse still, trying to work out what code is doing a few months after you wrote it has become more of a headache than writing new code. It’s time to move as much of the logic as possible into functions.
Stage 3
Moving the logic into functions has proven to be a beautiful thing. You’ve started to create libraries of these functions which you include when you need them. You may have a set of core functions that you use on multiple projects, site specific items, perhaps some database handling routines. New features mean new functions that grow into new libraries of them. It’s all under control.
Stage 4
The functions are working still, but not all is well in paradise. With all of these functions about you have started giving them wonderfully descriptive names like “PullProductInfoFromDatabaseInHTML()”. It’s starting to get a little nutty so you don’t run into naming conflicts with functions written long ago.
With all of these functions in various files you’ve been including it’s also getting harder to track down which file has a specific function. While looking for a lost function you also find that you’re running across long forgotten functions you’re no longer sure are even still being used.
Things have started to get a little painful, but you’ve got it under control. Just a little filing issue really. Just need to start writing a little documentation for all of those functions. Documentation that, even as you write it, you know doesn’t stand a prayer of staying up to date.
Stage 5
You start up a new project that is similar to a previous one, so you want to use those function libraries over again. Problem is, too many of those functions were tied so closely to the previous project that you end up having to edit those functions in place. Not a bad deal initially, except that now you have two sets of the very same functions with minor tweaks between them. Enough minor tweaks, and you can’t use those libraries over again.
Now you not only have the problem of figuring out what function is in which file, but now you also need to identify which group of files has the up to date versions that you’ve got all optimized and working better than the earlier versions.
Stage 6
What had started out so elegant in it’s simplicity has turned into a mess of complexity. Everything is in the global namespace, so you’re spending as much time trying to name new functions and variables as you are actually programming. Your once good friends, “Copy&Paste”, have actually made your life more, not less, complicated.
The Problem with Functions: Part I – Context
If you will, journey back with me a bit to Stage 4 mentioned previously. Consider for a moment just what you are doing when you give your functions really long descriptive names. What broke down from when you could just write something like productInfo(), or even info()?
This gets into the first problem that needs to be addressed with functions, and that is context. You are trying to put some action into the context of what it is acting upon. By their very nature a function exists without a context of it’s own, so a fully procedural program has to work around this by getting clever with how functions are named. This can get real ugly real fast when you have multiple kinds of data that could really use a getName() procedure.
Jumping a bit further into metaphor land, you can think of a function as a verb without a noun. It’s purely an action statement. Like saying “run”, but not saying what is running.
One reasonably simple way to deal with this is to create a “class” that contains a set of your procedural functions. This is not an Object Oriented approach just yet, but it is a valuable building block.
Old function calls
function pullProductInfoFromDatabaseInHTML()
{
// Pretend to run query and return a result
return "Acme 123 Motorized Widget";
}
Doing the exact same thing with a static class.
class Product
{
public static function pullFromDatabaseInHTML()
{
// Pretend to run query and return a result
return "Acme 123 Motorized Widget";
}
}
To run the old function you would just call it’s name and look for a result.
$productInfo = pullProductInfoFromDatabaseInHTML();
With our fancy new class you would instead say…
$productInfo = Product::pullFromDatabaseInHTML();
These two approaches do the exact same thing. The difference here is now we can group all of the functions dealing with a “Product” inside of this class. Now you can have numerous functions called pullFromDatabaseInHTML() for whatever kind of grouping of data that you wish with much less chance of a naming conflict.
With this alone, you could stick with the procedural style you’ve been doing and start to seriously simplify your naming schemes. Better still, you could pass the entire contents of this class to an entirely different project with a drastically reduced chance of a naming conflict. Better still, you can now import little libraries of these functions from other developers right into your code without conflict. Feeling all organized yet?
We haven’t got into anything involving objects or OO design as of yet. All we’ve done is provided a way to group a bunch of functions together. That is what a “static class” is all about. We’ll be doing quite a bit with classes here shortly. At this point just take this little tid bit with you before leaving this section.
- A class that has all of its functions defined as static is a “static class”.
Now who could possibly argue with that?
The Problem with Functions: Part II – State
Let’s just pretend that you’ve put together a wonderful little library of functions into a class. The problem now is that each and every function has to call out to the database to get some of it’s information, every time. For example, if you wanted to have a dedicated function for getting a product’s name and one for the description, each one has to do their own lookup or come up with some means of sharing that information with the other functions.
There are 3 basic ways to get information into a function.
- Pass an argument in the call.
- Define a variable as global.
- Use a defined constant.
To start with, is there anything more obnoxious than a function with a large number of arguments? I know I love trying to track down a function to figure out what order things went in. Worse still, trying to work out what values were supposed to go in there. It’s all I can do to try and remember if the haystack or needle comes first in strpos(). Let’s just say at this point that one of my new goals here is to limit the number of arguments that need to go into a function call.
You could dance around this problem by using lots of “global” statements in the functions, but then we’re back to the same old naming problems we ran into before we put things into a class. Yet another goal here is not just to have something that works, but to keep that global namespace as clean as can be. From this point forward we can just think of the “global” keyword as the work of the devil. It is an easy temptation that initially sounds good, but can only end poorly.
Defined constants can be used at any level of code. They can be really good stuff for setting up preferences and the like. Of course they’re not much good for the problem we’re running into here. They’re constants! You can’t change their value.
Getting information in or out of a function is quite limited. Oh sure you can toss an arrays around, but in doing so the caller has to know what the arrays will contain. More complexities! What you really want to be able to do is keep your functions small, easy to call to, and easy to understand what will come back out. To do that one thing it needs to do really well. Your also trying to hide the complexity of what is going on behind the curtain so you can focus on the larger picture of your project.
What you need is some reasonable way for related functions to share information with each other in some reasonable way. How do you do that? How would you try to solve this problem?
I believe it was this question that lead to the notion of creating an object in the first place. You’ve got this grouping of functions that not only need to remember stuff, but share that stuff amongst each other within that group of functions.
- An object is a library of functions that can remember and share stuff.
Okay, What Was That Again?
My little definition of an object is worth repeating, but let’s instead repeat it using the proper terminology.
- An object is a library of “methods” that can remember stuff.
When you stuff a function into a class it gets called a “method”. It acts in almost every way like a function. You stuff data into it with arguments, and you can get a single return value from it. It is a little different as you will see shortly.
To make matters even more confusing, in PHP you declare a “method” by calling it a “function”! This has got to be worse than clicking on Start to shut your computer down. I believe this has to do with legacy reasons more than anything else, as you certainly don’t see the keyword “function” used in a language like Java.
If calling a “function” a “method” wasn’t bad enough, now variables get a new name too. A variable that is declared within a class, but outside of a method, is called a “member”. It’s still a variable like you’d use outside the notion of a class. Saying something like $x=”Howdy” still means that $x is a string. Do that outside of a class and it’s just a string variable. Inside a class it’s referred to as a “member”, “data member”, or “instance variable”.
So why in the world do we have to rename perfectly good things like functions and variables when they’re put into a class? Remember, we’re trying to put together a group of methods that can use and share information within that group. That concept doesn’t really exist for functions hanging out in the global namespace.
To appreciate this difference let’s step back a bit and review some basics about what you know about a function.
function adder($x, $y)
{
$z = $x + $y;
return $z;
}
In this little adder() function we have 3 variables, all of which only exist while the function is running. In other words, the contents of this function exist within a little world of their own. The function can’t see anything outside the scope of it’s enclosing brackets. Data goes in, data comes out, but anything about what happened in between is lost once the function has completed.
We need to get together a class that not only has some methods, but also some members. You can think of this as the blueprint of creating an actual object. Hang on to that mouse, it’s about to get a little bumpy here.
A Class Is Not an Object
The first class example I showed you had a “static method”, which essentially means you can just call that method directly with what them PHP folks call a “Scope Resolution Operator”, which is a really big fancy term for a pair of colons “::”.
We could just take everything in our Product class and make it static. What if we had to have two or more of those products at the same time? Consider a product catalog that needs to show lots of products on the same page. Wouldn’t it be cool to have a bunch of objects that could be created using the same class?
Turns out, this is exactly how it works! You use a class to define what an object should do and remember, then you create an object from that. I better slap some code here before I metaphor again.
A Product class that’s ready to be turned into an object:
class Product
{
public $model = “”;
public $description = “”;
public function pullFromDatabase($index)
{
// Fancy database query would run here
$this->$model = $dataRow[“model”];
$this->$description = $dataRow[“description”];
}
}
So now what in the heck can we do with that? How about we finally go and make us an object with this thing already. Gather the family around, this part is really exciting.
$p = new Product();
Pretty cool huh? Okay, so you’ve got the kids and your aunt walking away in disgust at how dull that was. I guess they just can’t appreciate just how cool this really is. You have just made a bona fide object! So what can you do with this thing you just did? Well, you could run that method that is now a part of this object. How about let’s go and get a product record from the database with an index value of 12.
$p->pullFromDatabase(12);
Another big fat yawn you say? Well, now we have some values in those members. Here, let’s use them to show a model and description.
print $p->model.” “.$p->description.”\n”;
There’s some might funky looking syntax there with all that “->” stuff going on. This is PHP’s way of stating what context you mean. You created an object from the Product class and called it $p. Now to use that in your code you need to show that you mean the method or member from that object. Without saying “$p->” PHP is going to think you’re referring to the global namespace. Just saying “pullFromDatabase(12);” tells PHP go look for that as a function, not a method for an object.
I promised you a solution for when you needed more than one product didn’t I? You could just run that same database pull method, but then we just lost what that $p object knows. How about we instead just create a new Product object.
$p1 = new Product(); $p1->pullFromDatabase(15); print $p1->model.” “.$p1->description.”\n”;
Now there’s 2 objects, $p and $p1, with different stuff they know. You could even put those objects into an array. Something I will illustrate after I explain a couple of goodies I tossed in that you may be wondering about if you’ve been watching closely.
Which This Is $this
Hopefully you’re wondering what those “$this->” is about. Before I can tell you that you’ll need some terminology thrown at you.
- The keyword “new” creates a new “instance” of an object.
So what does that mean? Well, you created two objects from that Product class. Each one is considered an instance of that object. Each instance can all do the same things, but they all know different things. Lose you yet?
Our first object instance was $p. That instance has the model and description of that product. In other words, it knows something about that specific product we asked for. $p1 was another instance that also knows a model and description, but a different one than $p.
Even though each instance knows something different, they both have the same action, pullFromDatabase(). Two instances, two sets of data that they know, but one set of actions.
So how does “$this->” fit in? Outside the class we need to specify our created object before requesting something, like $p->model. Inside of the class when a method refers to one of the members it has to specifically state that it is a member of, and here it comes, “this” instance of the object.
If you had instead had a method that looked like the following you wouldn’t have gotten the result you expected.
public function pullFromDatabase($index)
{
// Fancy database query would run here
$model = $dataRow[“model”];
$description = $dataRow[“description”];
}
Methods are quite a bit like functions, in that variables declared within a method only exist within the scope of that method. PHP needs some way to have you tell it what you really mean is the member variable model, not a local one.
Just Do It
It seems kind of silly to have to create this new object, then tell it to pull some information out of the database. It would be nice to instead have it just do that when you create your new instance of the Product class. A perfect lead in to what PHP refers to as “Magic Methods”.
The PHP site doesn’t do a very good job at defining a Magic Method, so I’ll do what I can here for you.
- A “Magic Method” is a method that runs due to some action taken on an object.
A Magic Method isn’t meant to be called directly. They happen when some kind of event is detected. These are methods that are already defined by PHP, so you don’t get to make new ones yourself. Our first of these methods happen when an object instance is constructed. Your just going to love the name of this… it’s called the “constructor”. All of these fancy methods start with two underscores, so as to avoid possible naming conflicts and perhaps to look a bit like Python. Anyone’s guess.
Let’s get a constructor slapped into our Product class already.
class Product
{
public $model = “”;
public $description = “”;
public function __construct($index)
{
$this->pullFromDatabase($index)
}
private function pullFromDatabase($index)
{
// Fancy database query would run here
$this->$model = $dataRow[“model”];
$this->$description = $dataRow[“description”];
}
}
You now must pass an index value argument into the class when you construct a new instance object of this class. That’s a lot of syllables for saying you get to pass an argument on in when you create a new object. Here’s what that looks like.
$p = new Product(12);
There are two new little concepts I sneaked in here on you. First, notice how I needed to use that darn “$this->” operator to call the pullFromDatabase() method? Because it isn’t a static method, it belongs to the instance of this object. Trying to call it without “$this->” and you’ll get yourself a lovely little error message that the function can’t be found in the global namespace.
The second thing to notice is that I changed the first keyword on the pullFromDatabase from “public” to “private”. Why in the world did I do that? What does that mean?
Locking Things Down
Up until this point, everything I had in that class was public, meaning that anywhere this object existed a request to run that method could happen. From way out in the global namespace I could ask for that method to run. I’ve decided that I didn’t want just any old code to call my database routine, only my constructor. By marking that method “private” I’m saying only methods within this specific class can even see it, much less run it. An attempt to run it from anywhere else will kick off an error.
So why are we keeping secrets from other parts of our code? Aren’t we all friends here? What, you don’t trust me? Ah geesh, hurt my feelers why don’t ya.
In my mind there are a couple reasonable answers to the question of why you can and should lock down who is allowed to access a method.
- Classes are supposed to exist to simplify your life. A year after you’ve written your perfectly written class you aren’t going to want to dig through every method. You’re only going to want to know about the methods that you need to use that class. The public ones.
- You may have an order in which different methods need to run that may not be entirely obvious from outside code. By marking those as private your public classes can insure things are run properly. This impacts the integrity of your program.
- Some information that you use within a class may not be any other code’s business. For example, only your database connection class ever need know anything about a database password. Only your billing code ever need any kind of access to a credit card number. You can make things a bit more secure by locking down what parts of your program can be seen.
Taking the next logical step here, let’s also lock down those members, or “instance variables”.
class Product
{
private $model = “”;
private $description = “”;
public function __construct($index)
{
$this->pullFromDatabase($index)
}
private function pullFromDatabase($index)
{
// Fancy database query would run here
$this->$model = $dataRow[“model”];
$this->$description = $dataRow[“description”];
}
}
Oh great, now that only methods within this class can even look at those members how do we get them out of there? We slap together a couple of “accessor” methods to provide those to us.
class Product
{
private $model = “”;
private $description = “”;
public function __construct($index)
{
$this->pullFromDatabase($index);
}
public function getModel()
{
return $this->model;
}
public function getDescription()
{
return $this->description;
}
private function pullFromDatabase($index)
{
// Fancy database query would run here
$this->$model = $dataRow[“model”];
$this->$description = $dataRow[“description”];
}
}
Now we’re starting to see a class that looks something like you would see in the real world. Data members that are private, but accessible via public methods. This is a form of “encapsulation”. We’re now in full control of how members are accessed or changed. In this case, only the pullFromDatabase() method is allowed to change the model and description.
So let’s jump back out into the global namespace realm again and put this to use.
$p[0] = new Product(12); $p[1] = new Product(13); $p[2] = new Product(14);
foreach ($p as $productObj) {
print $productObj->getModel().” “.$productObj->getDescription.”\n”;
}
Oh sure that works, but it’s kind of ugly. All those “->” things really muck things up. There is a prettier way of doing this, and it works in my 2nd favorite Magic Method, “__toString()”. Let’s toss that into our rapidly growing class to see what it does.
class Product
{
private $model = “”;
private $description = “”;
public function __construct($index)
{
$this->pullFromDatabase($index);
}
public function __toString()
{
return $this->getModel().” “.$this->getDescription().”\n”;
}
public function getModel()
{
return $this->model;
}
public function getDescription()
{
return $this->description;
}
private function pullFromDatabase($index)
{
// Fancy database query would run here
$this->$model = $dataRow[“model”];
$this->$description = $dataRow[“description”];
}
}
With our new Magic Method in place let’s see what our code would look like back from where we want to call this from.
$p[0] = new Product(12); $p[1] = new Product(13); $p[2] = new Product(14);
foreach ($p as $productObj) {
print $productObj;
}
Ooooo, pretty! Creating our objects and assigning them to an array let us put together a quick list of those Products ready to foreach through them. All that yucky “$objName->methodCall()” stuff I now have tucked away into a Magic Method that is magically called when I print the object. You just gotta love that, if only a little.
Take a Breather
Let’s just stop right here and say that’s all there is to the story. If this were all there was to it and you wanted to put what we’ve discussed up to this point to use. What good is it?
To start with, we’ve got this wonderful mechanism for both grouping our methods within this fancy wrapper called a “class”. That alone pulls a ton of muck out of the global namespace. Not only have we organized functions that would have all been in the global space, but now those functions can actually remember stuff. We’ve got magical methods that get the ball rolling when you create an object, and put it all together when you need some output.
No matter how many different strings, integers, arrays, or functions you need to handle everything to do with products, none of them muck up your global namespace. This should also make it easier to name things, since the context is provided for by the class. Life is good.
And it gets better, because what I’ve shown you up to this point would allow you to make use of most of those fancy libraries in the PEAR repository. Most everything in PEAR is made up of either object oriented classes or static classes. You would either create an object from them or call to them directly with that “::” notation. Even if you never write anything that goes into a class yourself, you can still use code that has been.
If all that wasn’t cool enough, by knowing how to access objects you also open yourself up to the highly dynamic world of SOAP, Simple Object Access Protocol. SOAP is an impossibly cool technology that’s changing the face of development. I don’t want to get into this too much in this article, but here’s a bird’s eye view of it.
Imagine you wrote a really swell class with a stack of very useful methods. You’d like to integrate that with a project running on a different machine, be it across the LAN or WAN. Although it’s a really swell class you’ve got, it’s pretty heavy on CPU usage and requires access to a database that is not available across the network. You can’t just copy the code on over to your other project, since the resources on that machine are already taxed. Wouldn’t it be cool if you could just share that class out to that other project and have it act just like a locally installed class? You know, with methods and members.
It is this very thing that SOAP provides. If that wasn’t cool enough, there’s one more trick in the hat. SOAP is language agnostic. The server could be running Java and your client code could be in PHP, or vice versa. This pretty much works between most any object oriented language that has an implementation of SOAP, which is most of the major ones today.
There are also a healthy stack of companies out there providing a variety of data and computation services to developers across the Internet. Things like address look ups, sales tax rates, or stock data. All kinds of data that an individual developer would normally never have access to is now readily embeddable in your code thanks to SOAP.
My point to you here is that just knowing how to access objects your value as a developer shoots up tremendously. Implementing things like the PEAR repository or SOAP isn’t much different than my little example creating a new Product object.
Moving Along
If at this juncture if I haven’t managed to convince you that there really is some benefits to all this class and object stuff then you might want to stop now while you’re ahead. Take no shame in this, as I have been there myself many times with various books and tutorials on this subject. More likely my approach to the subject may not have convinced you that there’s enough benefit to this for the cost of changing how you develop today.
For those of you still with me here, we’re about to embark into the realm of thinking in terms of object oriented design. Up to this point I’ve purposely kept things mostly procedural, even the Product class isn’t much more than some grouped functions.
Actual Object Oriented design gets us thinking in terms of how to better share methodologies between the classes that we create. In OO terms this is referred to as “inheritance”. The basic notion is that one class can inherit all the methods and members of another class. The inherited class then gets all those goodies from it’s parent. From there you can extend or override methods and members of the parent.
Inheriting A Fortune
Have I lost you yet? I know this all lost me on the road to OO design many times. To attempt to illustrate this let me present this to you in a purely functional way.
In our pretend scenario we’ve got a file with a dozen or so functions that deal with presenting different aspects of a product. Most of these are perfect for use across different projects that need to display a product.
A new project from Acme Widgets comes across your desk that could sure use most of that library. Unfortunately 2 of the functions are, by necessity are very specific to the project they are used on. In this scenario they are:
function showProductSpecs($index) function displayProductPrice($index)
On top of this, Acme’s project also needs a new function that no other project will likely need.
function generateProductColorDropDown($index)
To address your problems here you could take all of these functions and put them in a new library specific to the project. Well that’s not going to work, since older projects expect to find those first two functions in the old library. You can’t just redefine a function on the fly, and you don’t want two almost identical versions of your library. How do you track changes to other functions?
You could just rename all the functions to something that’s specific to the project at hand. That’s not really all that great either since your function names have already gotten huge to avoid namespace conflicts. When you need all 80 columns for a function name, there’s a problem!
Darn, wouldn’t it just be simpler to let us programmers redefine functions on the fly? In short, that would be a really bad thing. Furthermore, I’m not looking to reinvent PHP here. We’ve got the tools we’ve got. And for those of us enterprising folks that are utilizing Object Oriented methodologies none of these problems are a problem at all. In fact, this is the very basis from where we can start into thinking in OO terms.
Let’s get that Product class back in here with the methods we need to implement. I’ve removed the accessor methods for clarity and put in those two functions that existed for another project.
class Product
{
private $model = “”;
private $description = “”;
public function __construct($index)
{
$this->pullFromDatabase($index);
}
public function showProductSpecs($index)
{
// Some clever display code
}
public function displayProductPrice($index)
{
// Clever price calculations returned
}
private function pullFromDatabase($index)
{
// Fancy database query would run here
$this->$model = $dataRow[“model”];
$this->$description = $dataRow[“description”];
}
}
So how does this solve the problem of needing new functions for the Acme project? We can now inherit this Product class into a new class called AcmeProduct. Let’s start slowly here and not implement anything new here.
class AcmeProduct extends Product
{
}
$ap = new AcmeProduct(12);
What the…? There’s nothing in that class! How can you create a new object from an empty class for crying out loud? The answer is, of course, inheritance. Everything about the Product class becomes AcmeProduct when you “extend” it.
So what’s the big deal then? All we’ve got is two classes that do the exact same thing with a different name. Or so it would seem. How about we add in that special method specific to the Acme project.
class AcmeProduct extends Product
{
public function generateProductColorDropDown($index)
{
// return some kind of form list
}
}
$ap = new AcmeProduct(12);
Now we’ve got everything that Product had, plus a new method only available to code that creates a new AcmeProduct. We’ve just added functionality to the Product class without having to alter anything in the Product class.
Now let’s override those functions in the Product class that need something special for this Acme project.
class AcmeProduct extends Product
{
public function generateProductColorDropDown($index)
{
// return some kind of form list
}
public function showProductSpecs($index)
{
// Specs like Acme wants to see
}
public function displayProductPrice($index)
{
// Pricing just for Acme
}
}
$ap = new AcmeProduct(12);
Unlike functions in the global namespace, methods can be redefined by a child class that’s inherited them. We get all the other methods and members from the Product class as though you had copied and pasted them on in. We’ve added to the library of methods, and changed 2 of them. All the while we haven’t had to alter a single line of code in the original Product class. Can you dig it?
Code that had been utilizing that Product class all along will not be impacted in the least by this new class.
Inheritance Implications
Dear reader I need you to stop for just a moment and consider the implications of this. If you think of this in procedural terms, we’ve just refined an otherwise generic library of functions into a more specific library without losing anything of the original.
If you were to go back and optimize some of your other methods in Product, any class that inherits from this gets all those benefits as well. You may well find that you never actually create an object from that Product class directly, but instead extend it to new classes that refine what it does for each new project. Along that line of thinking, you would try to stuff all those methods and members that child classes could all use into that Product class.
PHP restricts your ability to inherit to just one parent. In other words, a class can only extend from one parent. There’s a darn good reason for this, which I won’t get into here. Just remember that only one parent is allowed. Thing is, you can have multiple generations of parents going up an object’s family tree. For example, here are three classes that live in the same inheritance tree:
Product > AcmeProduct > AcmeProductWidgets
In this case, AcmeProductWidgets gets everything that AcmeProduct has to offer, which is also everything Product has as well.
Where a child is restricted to one parent, a parent can have lots of little class kiddies running about.
Product > AcmeProduct
… > CompProduct
… > SurfProduct
The top most parent in the tree would contain the methods and members most likely needed by it’s children. Then each child further refines or adds functionality.
I Miss My Parent
Our AcmeProduct class has thus far just replaced two perfectly good methods from it’s parent class. In doing so we had to use that evil “Copy&Paste” coding method. What if what you really wanted to do with one of these methods was to just refine it just a bit. Let the parent class do all the work, then just tweak things a bit and take all the credit. Here’s the code:
class AcmeProduct extends Product
{
public function generateProductColorDropDown($index)
{
// return some kind of form list
}
public function showProductSpecs($index)
{
// Get the initial results from the parent method
$specs = parent::showProductSpecs($index);
// Now refine it in some way and return it
return $specs;
}
public function displayProductPrice($index)
{
// Pricing just for Acme
}
}
Worked in a new one in there for you, “parent”. Using parent with our fancy Name Space Identifier double colon thingy, we can get mom (might be dad) to do all the hard work for us. Since both parent and child have this method with the exact same name you have to have some way of telling PHP which one you mean. Using the keyword “parent” does that very thing.
Are My Parents Keeping Secrets
Child classes can’t directly access either methods or members that have been marked “private”. Earlier when I stated that a child gets everything that the parent provides, what this really means is what it provides that isn’t private.
Along the same line of thinking as when I first presented the notion of setting permissions for methods and members, this all holds true even for child classes. There are just some things that parents don’t want their children to know about.
The notion of inheritance brings up a third kind of permission that only makes sense when you will be extending an object. Instead of public or private, you can set a permission of “protected”.
- A method or member that is set to “protected” may only be accessed within the same or inherited class.
Let’s see this in action with our now heavily abused Product class.
class Product
{
private $model = “”;
private $description = “”;
protected $index = 0;
public function __construct($index)
{
$this->index = $index;
$this->pullFromDatabase();
}
public function showProductSpecs()
{
// Some clever display code
}
public function displayProductPrice()
{
// Clever price calculations returned
}
protected function fetchPrice()
{
// Do something database'ish to get the raw product price
}
private function pullFromDatabase()
{
// Fancy database query would run here
$this->$model = $dataRow[“model”];
$this->$description = $dataRow[“description”];
}
}
I’ve changed 2 things in here. Before we get into the permissions bit, notice how I’ve changed the constructor. It now sets a member variable called $index then calls to the database pull. In doing so, I now no longer need to send that argument along to each and every method, as any of these methods can look up $this->index to get the value.
Both the fetchPrice() method and the $index member are protected. So, if you were to try the following bit of code, expect failure in your future.
$p = new Product(12); $price = $p->fetchPrice(); // Don't have permissions to do that here!
However, the following is perfectly okay to do. In fact, this is what setting that “protected” permission is all about.
class AcmeProduct extends Product
{
public function generateProductColorDropDown()
{
// return some kind of form list
}
public function showProductSpecs()
{
// Get the initial results from the parent method
$specs = parent::showProductSpecs();
// Now refine it in some way and return it
return $specs;
}
public function displayProductPrice()
{
$price = $this->fetchPrice();
// Pricing just for Acme
return $acmePrice;
}
}
The public method displayProductPrice() now calls a protected method from the parent as though it were a method within this very same class. We don’t use the “parent” identifier in this case since we don’t have to resolve a naming conflict like when you override a method.
Getting Abstract
Earlier I suggested the notion that a class at the top of the tree might be more useful as a container for methods and members to be shared by child classes. What if you wanted to enforce this. Say you wanted to make sure nobody ever tried to create a new Product object. You only want inherited children to use that class, then folks would create objects from there. Simple enough. Just state that the class is “Abstract”.
abstract class Product
{
....
}
With that in place, you can inherit everything provided by the Product class, you just can’t directly create an object from it. In this case, it would make a lot of sense to do this with a nice generic class like Product, with the thinking being that you only want more refined code actually getting utilized as objects.
Abstraction also allows you to force children to take care of some chores on their own. By defining a method without a body you are saying that a child must implement that method in order to inherit from this class. You know you’ve heard mom or dad say, “do as I say, not as I do”. This looks like:
abstract class Product
{
public function childMustDo();
....
}
Why on earth would you want to do something like that? For this article I’m going to dodge that question, as it gets into the notion of an object contract as well as polymorphism. A couple of topics going beyond the scope of what I’m presenting at this time.
I should also mention here that if any method within a class is abstract, in that it doesn’t have a body, then the entire class must be marked abstract. Making the class abstract does not require you to have any abstract methods though.
When An Object Doesn’t Want Children
In the case of our Product class, child classes really made a lot of sense. In some cases not only do children not make sense, but you actually want to enforce the notion that the class in question can not be inherited from. Mark that class “final” and no more inheritance is possible.
final class Product
{
....
}
That’s all well and good, except that we know this Product class is something we want to extend. If the case is that we just want to have certain methods that cannot be overridden by a child class, you can modify just that method with “final”.
class Product
{
final public function neverToBeOveridden()
{
// perfectly formed code goes here
}
....
}
Note that marking a method as private does not prevent a child class from overriding it. A private method just isn’t visible at all to anything outside of the class that declares it. Normally you would be better off avoiding overriding private methods due to the increased ambiguity. Be explicit in all that you do, and code will be usable and reusable for years to come.
What Got Left Out
I don’t want this to be the final word on Object Oriented design for you. My intention here was to help you with jumping some of the conceptual hurdles in going from a procedural methodology to an Object Oriented strategy. So as to keep you around to this point I’ve left a few concepts out of here so as to leave them to more qualified authors to address.
- I’ve totally dodged out of talking about Interfaces. They’re important, but not for the initial understanding of OO design.
- When do you inherit and when do you include objects? This gets into the realm of “IS A” and “HAS A” relationships.
- Various best practices and design patterns were all but totally vacant. Too many good books out there on these topics.
- There are several more magic methods I didn’t discuss. The two I did talk about I believe are the ones that are utilized far more often than any others. Some of these methods aren’t especially wise to use, which is why I didn’t bring up stuff like the Overloading methods.
- Class autoloading got left out. It’s a simple enough concept to read about in the official PHP manual.
- Static and constant data member variables.
- The extremely important topic of exception handling got left out.
- Lots of implementation details that are better handled elsewhere.
Summary
Even with a lot of the details left out I hope I have presented to you a reasonable introduction into the world of Object Oriented design. More importantly, if you can begin to see how you can utilize these concepts into your next project, or perhaps revamp an already existing project, I would consider this a success.
In a language like PHP the notion of traditional functions doesn’t go away now that PHP 5 has brought to us some real OO tools. Try not to get lost into the notion of either OO or procedural methods as the one true path. Combining the two is where you get the most bang for your efforts. Knowing when to apply one strategy or the other is a matter of experience and study.
Further Reading
There are a lot of new books coming out that get into the Object Oriented functionality in PHP. From what I’ve seen, many of these are garbage. Some are not much more than barely updated versions of previous PHP 4 books that just barely touch on how dramatic a change PHP 5 has brought to us lowly web developers. Here are a few good ones I feel comfortable in recommending.
“Head First Java”
by: Kathy Sierra & Bert Bates
O’Reilly
A Java book to learn about PHP? You betcha. If there are some awful PHP books out there, that number is easily tripled when talking about Java books. Here is an exception. This is a wonderful introduction into Java and Object Oriented design. The biggest mental translation to make here is that PHP is not a type safe language, where as Java requires that you declare everything explicitly. Beyond that, the Object Oriented discussions mostly apply to any OO capable language.
“Object-Oriented PHP”
by: Peter Lavin
No Starch Press
Although I found this author’s approach to be weak in how he introduces OO design, it’s an outstanding follow up to better understanding the specifics of how PHP 5 handles things. Just keep in mind that his first couple of chapters has examples using PHP 4. The remainder of the book is pure PHP 5.
“PHP Objects, Patterns, and Practice”
by: Matt Zendra
Apress
Once you have what you think is a firm handle on what all is going on with OO design it would be about time to give some time over to this book. Here is where you’ll pick up many of the best of PHP’s best practices.
Is It All Worth It
For just the sake of PHP developing, it’s worth it and then some. The more that you utilize this approach to designing software, the more time you’ll save. You’ll find it far easier to pass code around to various projects, even projects you may never be directly involved with. You can now utilize the rich variety of classes available on the Internet, and extend them as needed.
Perhaps even more importantly, other languages like Java, Python, and even C++ start making a lot more sense. Most OO design techniques cross the language barrier with mostly syntax details changing between them. There’s certainly more than just minor syntax details that divide these languages, but my point is that the concepts cross those barriers. This is one of the reasons why something like SOAP is able to work at all. The object concept bridges across the divide.
[...] http://fmpub.net/contact.php?to=jb wrote an interesting post today onHere’s a quick excerptI hated the notion of Object Oriented (OO) programming. I’d been using PHP since early version 3, and I had put together some very usable web sites with it. Every book I read on the topic seemed to be rich with metaphors about dogs, cars, and various other things, but not much on why I would want to abandon my happy procedural methods. Not that these authors didn’t try. The arguments they made just didn’t hit home with me at the time. I think I may now understand why this was. Most of the efforts I’ve seen to describe object oriented design are coming from the perspective of someone who thinks in an OO way. I’m not saying this was a bad approach, it just really didn’t work for me. Perhaps if I hadn’t been so deeply entrenched in the methodologies I had been using for years I would have been […] [...]
Pingback by PHP - From Procedural to Object Oriented — March 7, 2008 @ 11:02 pm |