Confessions of a Converted PHP Developer: Mixing Like a Baker

Tweet

One of the things that always made me shudder in PHP was the lack of being able to implement generic class mixins in an easy way. The ability to avoid writing code multiple times is important to me as a developer, as it makes me more efficient at my job, so I can do more important code in the same amount of time, or slack off more while maintaining the same code output (this mainly depends on my mood at any one time).

If I had a dollar for every time I had to write a set of functions that I knew I’d written before, but also knew there was no way of avoiding, I’d have… well… not really that much, but at least enough to cover a night out. Bear with me on this one, there’s a fair bit of PHP code. (For the record I’d usually hunt down the functions and copy paste them.)

Here is a View class with a basic set of functions which allow me to implement something akin to the mixin design pattern.

class HtmlView {
 	protected $_helpers = array();
	public function addHelper($obj)
  	{
 		array_push($this->_helpers, $obj);
		return $this;
	}

	public function __call($name, $params)
	{
		foreach($this->_helpers as $helper)
		{
			if(is_callable(array($helper, $name )))
				return call_user_func_array(array($helper, $name), $params);
		}

		throw new Exception('Attempted to call nonexistent method ' . $name . ' on ' . __CLASS__);
	}
}

I could now create a helper class with a bunch of methods that I want to be able to access from my HtmlView.

class TagViewHelper{
	public function linkFor($url, $name)
	{
		return sprintf('<a href="%s">%s', $url, $name);
	}
	# the rest of my bunch of methods here...
}

I could either add the TagViewHelper to my HtmlView class at instantiation, or pop it into the constructor.

public function __construct()
{
 	$this->addHelper(new TagViewHelper);
	# Add other view helpers here
}

Now I’m able to call the methods in the helper class from an instantiated HtmlView object.

<!-- somewhere deep inside code being called by our HtmlView object--&gt;
Check out my awesome &lt;?PHP echo $this-&gt;linkFor('http://malstaxidermy.com', 'Stuffed Mongooses'); ?&gt;.
</code>

This would happily create a link for my stuffed mongooses website.

And the crowd goes wild…

This is pretty common stuff, and while some of you might be staring at your screens, screaming “Why didn’t you just create an abstract GenericMixin class and then extend from that?” I’d like to start by saying “Woah, kind sir! I have a point!”

While there are many reasons this isn’t an ideal solution, I’m just going to pick on two.

  1. 90% of the time, I’m not in control of the objects I’m extending. The PHP framework we use consists of quite a few submodules which all work in whacky and wonderful (read: annoying) ways, which creates headaches when editing the root class. Alternatively this could be a library that we are using, and generally you want to avoid editing external dependencies if at all possible.
  2. What if I wanted to add in different functionality which was often used? Drop in a flyweight / object caching implementation? PHP doesn’t handle multiple inheritance, so at this point we’re back at point one – placing the code directly in our classes, or having some ridiculous base abstract class with as many pattern implementations as possible.

What’s your point? I’m here for the Ruby!

The good news is that, like a lot of languages, Ruby has built in support for mixins. It’s as easy as defining a module, then including it in our class definition. The include statement in Ruby is nothing like the include statement in PHP. Whereas in PHP this would include a file, what we’re doing in Ruby is pulling in all the methods from the module into the class.

Let’s recreate our HtmlView functionality in Ruby.

module TagViewHelper
	def tag_for(url, title)
		"<a href='%s'&gt;%s&lt;/a>" % [ url, title ]
	end
end

class HtmlView
	include TagViewHelper
end

Now I could call tag_for(“http://malstaxidermy.com”, “Solid Snakes”) from code inside an instance of my HtmlView. At no point did I have to worry about the handling of Mixins, because this is handled by Ruby.

You’ve got to admit, that’s a lot less code. Mixin modules are one of the great features of Ruby, and allow us to consolidate common behaviour into a set of shared objects.

You might be wondering what happens if you had several methods with the same name. Ruby handles this by simply overwriting all previous methods with the last one added.

Do you have any stories, or advice about mixins in PHP or Ruby?

Note: This is a very basic example designed to show how recreating some functionality that takes a fair amount of code in PHP can be handled very quickly and easily with Ruby.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.BlaineSch.com Blaine

    In the PHP example you add each object into an array. It may be better to add into a “hash” (technically still an array in PHP), then calling it based on the object name, method, and parameters.
    I assume this would take a slightly longer time to add new objects to the helper array, however you wouldn’t have to reiterate through all of them when calling it, and possibly calling the wrong one if the method name is ambiguous.

    • http://mal.co.nz Mal Curtis

      Hey Blaine,

      The objects stored in the array can have multiple methods, so the key used to store it would be irrelevant – as the main class is unaware of which methods are available in which helper objects.

  • http://joshadell.com Josh

    Hopefully, PHP’s “traits” functionality will appear on the scene sooner rather than later (http://simas.posterous.com/new-to-php-54-traits). It seems like more an more languages are pining away for something like Javascript’s half-classical/half-prototypical inheritance, though maybe with a cleaner syntax.

  • KingCrunch

    Thought about implementing something like this in PHP5.3 for about a week :)

  • Vladislav Veselinov

    There are traits in PHP’s trunk and will be available in PHP 5.4 :)

  • http://github.com/shuber Sean Huber

    Nice! I’ve been working on something similar (mixins + other rubyisms in php) – https://github.com/shuber/phuby

    It’s definitely an awesome learning experience working on projects like this. Makes you appreciate ruby more too!