PHP and the Prototype Pattern

March 23rd, 2008

Posted at 6:47pm by Stan

About a mont ago at one of my contract jobs we had a discussion about the Prototype design pattern and mixins. The discussion took place around javascript, which is by language construct a prototyped language. For those who don't know about the Prototype design pattern let me quote from the Gang-of-Four, "Specify the kinds of objects to create using a prototypical instance, and create new objects by coping this prototype." Or, Wikipedia, "Prototype-based programming is a style of object-oriented programming in which classes are not present, and behavior reuse (known as inheritance in class-based languages) is performed via a process of cloning existing objects that serve as prototypes. This model can also be known as class-less, prototype-oriented or instance-based programming."

Ultimately what prototyping solves is the problems incurred by inheritance. Object-oriented programmers around the world will be twitching at that very sentence, but bare with me. In Java we typically implement interfaces as much as possible over and above inheriting classes, because on occasion with inheritance we get stuck in a position where we would really like method A from class Z and method B from class Y, and we can't. Subsequently, we define that functionality by the usage of interfaces so that with our new class C we can check that it is an instanceof Z and Y, thus providing functionality for methods A and B. Did that confuse you? Think of it this way, you can't inherit two classes into one class in many languages (ie. Java, PHP), but you may want functionality already offered in both of those classes and yet you want to still be able to test generically for said functionality by those originating classes. Bottom line is, you just can't. Prototype, in part, helps solve this dilemma.

Javascript is natively a prototype class, you can move functions around from object to object simply by variable assignment. Subsequently you can define Behavior A in a function and apply it to objects Q, R and S simply by setting Q.behaviorName = Behavior A. That comes in really handy, especially when you want to develop things with a mixin pattern.

So, back to my work conversation... One of our developers was unfamiliar with what we meant when we spoke of "prototype" and "mixin". His background was mostly in PHP and since that conversation I've been trying to think of ways to explain these design patterns within PHP. PHP is not natively a prototyped language, and if I were you I wouldn't even use the example I'm about to show you! Subsequently, I wrote out some code to show Prototype usage within PHP. This example requires PHP 5 because it's dependent upon the new OO features in the language, so if you're still running the legacy 4 than get out your compilers!

First, we're going to define a class called "Prototype", this will server as our base class for any prototype object we develop, here it is:

class Prototype { private $prototype = array(); public function __construct() {} public function __get( $key ) { if (isset($this->prototype[ $key ])) { return $this->prototype[ $key ]; } else { throw new Prototype_Exception("{$func}() not found!"); } } public function __set( $key , $value ) { $this->prototype[ $key ] = $value; } public function __call( $func , $args ) { if (isset($this->prototype[ $func ])) { call_user_func_array( $this->prototype[ $func ] , $args ); } else { throw new Prototype_Exception("{$func}() not found!"); } } } class Prototype_Exception extends Exception { }

Nothing really spectacular here, we're basically creating a dummy class with magic methods and a stack which we'll use to keep track of the functions we define for this object. Now, we'll create a series of methods which we might use for our new Prototyped class.

function foo() { print "Foo!\ "; } function bar() { print "Bar!\ "; } function test() { print "Hello world!\ "; } function methodTakesVar($a) { print "Variable : {$a}\ "; }

Definitely nothing exciting here, so let's keep moving on and create two new prototypes.

$SamplePrototype = new Prototype(); $SamplePrototype->foo = 'foo'; $SamplePrototype->bar = 'bar'; $SamplePrototype->foo(); $SamplePrototype->bar(); print "\ \ ---\ \ "; $AnotherPrototype = new Prototype(); $AnotherPrototype->foo = 'foo'; $AnotherPrototype->bar = 'test'; $AnotherPrototype->test = 'test'; $AnotherPrototype->foo(); $AnotherPrototype->bar(); $AnotherPrototype->test();

If you create a new php file and paste the Prototype class, the functions we defined and the above code and then execute it, you should get something like this:

Foo! Bar! --- Foo! Hello world! Hello world!

See how we've defined a method and then just glued it onto our new prototype object like so? Now, let's go one step further and make a mixin of our prototype class, first we'll define a mixin method:

function mixin( $obj ) { $obj->addedMethod = 'methodTakesVar'; }

Next, we're going to take our $AnotherPrototype object and use it as a starting point to create a $Mixin object, and then we'll mixin our new method.

$Mixin = clone $AnotherPrototype; mixin($Mixin); $Mixin->addedMethod("Randomly chosen");

Again, if you execute this it should look like (Note: the method addedMethod() was defined above.):

Variable : Randomly chosen

Another good example is to define a method that observes certain events, we'll call that method "observer", and our method to apply the mixin will be called "observabalize", generically let's say our foo() method calls "observer()", not knowing anything about what that method does. So, create another method called "doSomething" and have it print "Hello World", then set $Mixin->observer = 'doSomething'. On another object we could create an observer and set it to "doAnything" which prints out "Whatever...". We would make each object observable with a different mixin. This might be convoluting my examples, so my apologies if it is.

The big-wig programmer who live and breath by their design patterns will probably see the obvious faults of my example, so please don't consider this a canonical example of prototyping. For those who write in PHP and are wondering what the heck a prototype is all about, hopefully this example has served you some benefit. And again, for what it is worth, I definitely would not use this any type of a production environment - there's a reason PHP doesn't inherently do prototyping.

tags: patterns, php

Comments:

Posted on June 05, 2008 04:33pm by Andrea Giammarchi
The true prototype, with injected scope, is possible with runkit extension.
Read more in webreflection ;-)

Post Comment:


Enter the code you see in the image below.