About two years ago I wrote one of my first “true” object oriented php projects. I say “true”, because before that I had dabbled and played with OO, but never had I had a REAL project with it. Well “real” project is perhaps overstating it a bit, but it ended up with about 2000 lines of code, which is the borderline where something becomes a project in my book. The rest is just proof of concept or small scripts.
To start with some honesty, if I look at it now, and I am looking at it now, a lot of it is crap. But the one thing that started this project is still a nice implementation and also the subject of today’s blog post.
Many of the projects I have done in the past have started with a theoretical problem and the problem with theoretical problems is that they are not really problems; not in a way as for example building a bridge is a problem. So I invented a program idea that would present me with just that problem. The problem that day was that I could not seem to figure out how to do a plugin architecture. Now to some, this may sound simple, but at that time I simply couldn’t seem to figure it out.
So I started this project called libPhoto. LibPhoto was meant to become a generic back-end library you could use to manipulate images via an easy interface that abstracted complex modifications to the image. Ok ok, I admit, I also was interested in computer graphic algorithms at the time. But that’s for a different day.
Now one of the key aspects of this libPhoto would be that each modification would be a “plugin” for the library, so that it could easily be extended. The other key aspect was that libPhoto didn’t really needed to understand these modifications, it would only understand “versions” of an image. When talking to libPhoto you could tell him what a specific “version” of an image was called and what attributes it had.
Here some code to show how that looked in practice.
$libphoto = new libPhoto();
$resize = $libphoto->newMutator(‘resize’);
$resize->config(array(‘width’ => 200, ‘width’ => 200, ‘keepAspect’ => false));
$dropshadow = $libphoto->newMutator(‘dropshadow’);
$dropshadow->config(‘gausSize’ => 3, ‘offset’ => 15);
$thumb = $libphoto->newImageVersion(‘thumb’);
$thumb->addMutator($resize);
$thumb->addMutator($dropshadow);
$libphoto->setImageVersion($thumb);
$imageVersions = $libphoto->processFile(‘/some/image/file’);
Now the nice thing about the above, is that libPhoto, the codebase itself, has no idea how mutators worked, nor does it care. What you are seeing above is the end result of such buzzword patterns as the factory method pattern and command pattern. Or at the very least, I like to think it does. And as to explain why I think it does, i’ll go over what is happening behind this code.
$libphoto = new libPhoto();
Well, pretty obvious, it instantiates the libPhoto class into an object, which then does all kinds of stuff behind the scene like scanning to see what Mutators it has.
$resize = $libphoto->newMutator('resize');
$resize->config(array('width' => 200, 'width' => 200, 'keepAspect' => false));
Now here we can really begin. When requesting a new mutator object from libPhoto, libphoto will check if it actually exists and if it does, will instantiate it for you and supply it with some default configuration options and will ask it about some small details like if the current php installation contains the requirements to run that mutator. Which in most cases equated to “does it have GDlib”. After it prepared it for the harsh world outside, it returns it back to the code we are reading. At which point we could supply it with some extra configuration options. We could have asked it for those configuration options, but since I wrote the damn thing, I should know what its config options are.
Now I could of course have trusted on the puppy dog eyes of developers everywhere to implement some default methods, but in a more sane approach I actually implemented an interface to which all mutators needed to comply. Furthermore all Mutators extended from a baseMutator which already implemented various default methods like reading the config options and storing them as private variables.
So in the end your average mutator looked roughly like this
/**
* This mutation makes the image greyscaled
*/
class mutatorResize extends baseMutator {
private $ConfigOptions = array(
‘width’ => ‘int’,
‘height’ => ‘int’,
‘keepAspect’ => ‘bool’
);
/**
* This turns the image grey.
*
* @param gd image resource $image
* @return gd image resource
*/
function process($image) {
// … snip …
return $image;
}
}
As you can see, it really only defines some options that the process will use to do its job and after that only define a process which does the job.
All the tedious stuff like reading those config options or explaining them to the underlying system where all already generically defined in the baseMutator.
$thumb = $libphoto->newImageVersion('thumb');
$thumb->addMutator($resize);
$thumb->addMutator($dropshadow);
$libphoto->setImageVersion($thumb);
$imageVersions = $libphoto->processFile(‘/some/image/file’);
Now the first line is more of the same as the previous one with the mutators. It instantiates an new imageVersion object which get’s a few default config options before being passed along. Then you add some Mutator objects, and basically tell libPhoto there is an new image version. After having done all that we tell libPhoto to process an image file.
LibPhoto will then iterate over all the different image versions it has and tell them to do their thing. The imageVersion object will open the imagefile and create a GDimage resource and iterate over its mutators and pass them the file and tell them to do their thing.
The mutators will gladly accept the GDimage resource, do their magic with it, and pass it back. In the end the imageVersion writes the GDimage resource back as an image file and passes that file location back to libPhoto.
This is I think a perfect example of encapsulation and the command pattern. None of the objects even remotely care what the other is doing but just know how to tell it to do its “thing”.
I hope you enjoyed reading a bit about this, i know it’s all pretty basic stuff but i hope i’ll be able to expand into something a bit more complex when the time comes.
-edit-
Disabled comments because I was only getting spam. If you want to comment just do so on another article and add that it was actually for this one and i’ll move it or something.
admin PHP command pattern, design patterns, factory method pattern, libphoto, object oriented programming