Eliminate the Cruft

on 04 November 2009

Packing for a move across the country from Gainesville, FL to Indianapolis has got me thinking about something (other than the fact that I may be crazy for moving to Indianapolis in November). You see, in my past life, I was a pack rat. And I was that especially dangerous type of pack rat that's also an engineer and therefore finds it necessary to hold onto every piece of electrical or mechanical equipment that could one day be used to build an automated egg fryer (see "Honey, I Shrunk the Kids" for reference) or a scale replica of Beijing or whatever other “weekend” project I swear I'm gonna do one day. Needless to say, I had a lot of junk. But that's the old me. That was me v1.0, or maybe it was v1.5, but it wasn't the new(ish) me (with added cupholders!).

The new me hates all that accumulated shit. But unfortunately, the new me is still stuck with a lot of it. So I've spent the past few days going through it and getting rid of the stuff I truly don't need and setting aside the valuable stuff that I don't need to be sold later, and re-storing the stuff I may still need one day. And I feel much better about it. The new me can fit all of his possessions into a 4'x8' U-Haul trailer.

The reason I'm writing about this on a programming blog is because I've also spent the last few weeks eliminating cruft from the website of one of my clients. Having had several designers work on their site over the past few years, there was a lot of it. No one seemed to have taken the time to clean up their code, delete old files, reorganize the file structure, or anything like that. It was a mess. It took hours to clean it all up, and despite the fact that these fixes are completely unnoticeable to their customers, or even my clients to some extent, I feel great about it. The end result is that the site will now be much easier to maintain, and will save my client money in the long run.

The lesson here is not that we should go back and clean up all of our old, messy code (though we should), but rather that we shouldn't let the cruft accumulate in the first place. When a site needs to changes, it's easy to make the fix and tell yourself that you'll clean it up tomorrow when things have slowed down. But there's no shortage of fires to put out, and the chances you'll actually have time to get back to it later are next to none. And the more you put it off, the harder it gets to make changes in the future.So do yourself a favor and get rid of the cruft before it accumulates. We'll all be happier and more productive with less clutter in the world.

Tired of writing getInstance() methods and overloading the __construct() and __clone() methods for all of my Singletons, today I developed a Singleton that can be extended.  The result is as follows:

class Singleton
{
	protected static $_instance = array();
	
	private function __construct()
	{
	}
	private function __clone()
	{
	}
	
	public static function getInstance()
	{
		$class = get_called_class();
		if (null === self::$_instance[$class]) {
			self::$_instance[$class] = new $class();
		}
		return self::$_instance[$class];
	}
}

The $_instance variable is an array because, as a static variable, it wouldn’t allow for more than one class to extend it. For instance, suppose two classes, Widget and Gadget, extend Singleton. When Widget is first created with Widget::getInstance(), if it weren’t an array, $_instance would be set to a Widget object. Since this variable is static, calling Gadget::getInstance() would just return the previously instantiated Widget object. So the solution to this problem was to make $_instance an associative array with the keys being the name of the calling class.

I use the get_called_class function to retrieve the name of the class calling getInstance. So if you call Widget::getInstance(), the result of get_called_class would be “Widget”. If you used the __CLASS__ magic constant, you would get “Singleton” which is not what we want. The problem with this is that get_called_class() was only implemented in PHP v5.3, so this won’t work with prior versions of PHP (which is what I’m using). Luckily I found this post which creates a suitable get_called_class() function if it doesn’t exist. The code is duplicated below, thanks to Septuro:

if(!function_exists('get_called_class')) {
	class class_tools {
		static $i = 0;
		static $fl = null;

		static function get_called_class() {
			$bt = debug_backtrace();

			if (self::$fl == $bt[2]['file'].$bt[2]['line']) {
				self::$i++;
			} else {
				self::$i = 0;
				self::$fl = $bt[2]['file'].$bt[2]['line'];
			}

			$lines = file($bt[2]['file']);
			preg_match_all('/([a-zA-Z0-9\_]+)::'.$bt[2]['function'].'/', $lines[$bt[2]['line']-1], $matches);
			return $matches[1][self::$i];
		}
	}

	function get_called_class() {
		return class_tools::get_called_class();
	}
}

It should be noted that if the child class has a constructor, it should be declared protected so the Singleton class can instantiate it upon the first call to getInstance().