Login

PHP 5 and Polymorphism

This article explains what polymorphism is and how it applies to object oriented design in particular. It also explains the pros and cons of polymorphism when working with certain versions of PHP.

NOTE: The latest versions of PHP, and I am not sure in which exact version this was corrected, support late binding properly. There are still tons of issues with using late binding in cahoots with a bytecode cache, for reasons beyond my understanding or concern. Those of you using an older version of PHP (I still have 5.0.1 on a server) can see for yourselves the lack of late binding support. I initially rescinded this article after learning that late binding is properly supported in current versions, but it is important that you be aware of the possibility that it may not work in your particular PHP 5 install.

PHP 5 and Polymorphism

This article is going to cover one of the most important parts of object oriented programming and design–polymorphism–using PHP 5 for demonstration purposes. Before you continue, be warned that this article is not entirely positive in regard to PHP. While the language is great for rapid development and has made tremendous strides in the last two major versions, its object support still has a way to go before being on par with more mature languages like C++ or Java.

If you are an object programming guru this article probably isn’t for you, since polymorphism is one of those things you learn light-switch style – suddenly it’s all clear, and once you understand it, you never forget. If you are looking to learn a bit about object programming and design and you aren’t really sure exactly what it means when someone says an object is polymorphic, this article will help.

By the end of this article, you should know what polymorphism is and how it applies to object oriented design in particular, and you should know about the pros and cons of PHP 5 object programming as it pertains to polymorphism.

{mospagebreak title=What is Polymorphism?}

Polymorphism – “The occurrence of different forms, stages, or types in individual organisms or in organisms of the same species, independent of sexual variations.” (dictionary.com). By that definition, we could assume polymorphism is a programmatic way to represent the same object through multiple states or stages. That’s great, but probably still unclear to many of us. What it really means is this: programming to an interface or base class without regard to an object’s concrete class.

If you are familiar with design patterns, even on a rudimentary level, you should be getting a picture in your mind of what this means. In fact, polymorphism is probably the single greatest facilitator for patterns-based programming. It allows us to organize similar objects in a logical way such that calling code does not have to worry about specific types; rather, we code to an expected interface or base class. The more a software application is abstracted, the more flexible it becomes – and polymorphism is one of the best ways to abstract behaviors.

For example, consider a class called Person. We can subclass Person with classes called David, Charles, and Alejandro. Person has an abstract method called AcceptFeedback(), which all subclasses implement. That means that any code using any subclasses of the Person base class can call the AcceptFeedback() method with confidence, knowing that the class itself handles logic that would otherwise appear in a conditional. You do not have to check whether the object is a David or an Alejandro, just that it is a Person. The effect is that your code is written to the lowest common denominator – the Person class.

The Person class in this example could also be created as an interface. There are some differences, primarily that an interface imparts no behavior, only a set of rules, so to speak. A Person interface would say “you must support the AddFeedback() method” whereas a Person class could provide some default code for the AddFeedback() method, saying “if you choose not to support AddFeedback(), you will be provided with a default implementation.” Choosing interfaces or base classes is the subject for another article entirely, but in general, if you need a default implementation for a method, provide it through a base class. If you are simply outlining a set of expectations for your classes, then use an interface.

{mospagebreak title=Applying Polymorphic Design}

Continuing with our Person base class example, let’s take a look at a non-polymorphic implementation. The following example shows a really poor way to create an application that uses different types of Person objects. Note that the actual Person classes are omitted; we’re only concerned with the calling code for now.

This example shows objects that behave differently and a switch statement to differentiate between the different classes of Person, performing the correct operation on each. Note that the feedback comment in each condition is different. That probably would not be the case in a real application; it’s simply done to elucidate the differences in the class implementations.

Note the lack of the switch statement and, of greater importance, the lack of concern regarding what type of object Person::GetPerson() returned. Person::AddFeedback() is a polymorphic method. The behavior is one hundred percent encapsulated by the concrete class. Remember, whether we’re working with David, Charles or Alejandro, calling code never has to know the concrete class to function, only the base class.

While I’m sure there are better examples than mine, I think it demonstrates the basic use of polymorphism from the perspective of calling code. We now need to take into consideration the internals of the classes. One of the greatest aspects of inheriting from a base class is that the inheriting class is able to access the behaviors of the parent class, which often serve as nothing more than defaults, but can also be chained to inheriting methods to create more sophisticated behaviors. Below is a simple demonstration of this.

Here we have chained the David::AddFeedback() method to the Person::AddFeedback method. You may note that it resembles overloaded methods in C++, Java, or C#. Remember that this is a simplified example, and that the actual code you write will of course be completely dependent on your project.

{mospagebreak title=Late Binding in PHP 5, or, the Lack Thereof}

In my opinion, late binding is what makes Java and C# so spectacular. They allow base class methods to invoke methods in “this”, or $this, even if they do not exist in the base class–or, better yet, to invoke a method from the base class that may be replaced by another version in an inherited class. You may think something like this would work in PHP:

PARSE ERROR! The method ParseFeedback does not exist, or something along those lines. Sigh! So much for late binding in PHP 5! Wait–you probably want to know what late binding is.

Late binding means that method invocations bind to the target object at the last possible minute, which means those objects already have a concrete type when the method is invoked by the runtime. In our example above, you would be calling David::AddFeedback(), and since $this in David::AddFeedback() references a David object, you would logically assume that the ParseFeedback() method exists–but it does not, because AddFeedback() was defined in Person, and calls ParseFeedback() from the Person class.

Unfortunately there is no simple way to skate around this rotten behavior in PHP 5, which means you’re somewhat neutered when it comes to creating a flexible polymorphic class heirarchy.

I must point out that I chose PHP 5 as the language for this article simply for this reason: it’s not very good for abstracting object concepts! Consider it a cautionary tale from someone who learned it the hard way, beginning with PHP 5 when it was still in alpha and assuming that since they added abstract classes and interfaces, late binding was a given.

The End

You should now have a basic understanding of polymorphism and why PHP 5 is not very good at implementing it. You should, generally speaking, know how to encapsulate conditional behavior with a polymorphic object model. Ultimately what you should gain from this is flexibility in your objects, which of course means less code overall. You may end up creating a few more class and a few more class methods, but your calling code will be greatly reduced, which almost always results in time and effort savings. You also increase the clarity of your code by encapsulating conditional behavior-–behavior based on an object’s state-–within the object itself, rather than handling it with code that, for all intents and purposes, should not know enough about the object to make any real decisions on such matters.

If, like myself, you find yourself to be passionately interested in object oriented design, read what I consider to be the three fundamentals: “Design Patterns” (ISBN: 0201633612), “Refactoring” (ISBN: 0201485672), and “Refactoring to Patterns” (ISBN: 0321213351). I would suggest reading them in the stated order. They provide a far more extensive and expert opinion than my own, and will set you on the path to good object oriented software design.