Skip to a section of this page:

Archive for the ‘Web’ Category

WordPress performance

Friday, December 4th, 2009

As promised, here’s a quick rundown of the WordPress performance plugins I’ve been using.

  • DB Cache Reloaded = Big performance wins. Especially on shared hosting.
  • Hyper Cache = Less noticeable improvement than DB Cache, but still noticeable.
  • WP Minify = Essential. Every website should combine, minify then cache static text assets (like CSS and JavaScript) for production.

Note: I have used WP Super Cache in the past and have run into problems. Apparently it doesn’t perform too well on shared hosting setups. Also, in my experience, DB Cache outperforms Super Cache every time.

I have other thoughts about web performance that I’d like to share, but not today. Let me gather my thoughts some more and get back to you.

Know of any other good WordPress performance tricks? Leave a comment.

So long MT, G’Day DH

Friday, December 4th, 2009

Well, it wasn’t easy, but I’ve done it: I have migrated my sites away from MediaTemple (MT) to DreamHost (DH).

Why the change? Performance mainly.

MediaTemple has given me decent stability and good support over the last few years. And most of all I’ll miss that gorgeous, minimal admin control panel.

But the performance has been poor at best. Even with WordPress caching plugins installed, page renders averaged 3 seconds, not including the fairly frequent spikes (of up to 30 seconds) which would occur every 5 attempts or so.

And while they seem to have more constant outages (two of which have affected me already), the overall page render performance at DreamHost has been at least twice as good (< 1 second for page render on average) as MediaTemple and far more consistent (i.e: no spikes). For those who are interested, without caching plugins installed, the difference was far more pronounced (averages of 13 seconds for MT and 2 seconds for DH).

The DreamHost control panel isn't quite as minimal or as gorgeous, but it is much better organised than others I tried, and has all the functionality I require.

I should note here that these performance results (especially the DreamHost results) are based on a limited period of testing. So I’ll keep you posted as to the service and performance longer term. But it is safe to say that the performance of my previous provider has been less-than-ideal for a long time, hence the move.

For those interested in WordPress performance, I’ll post a follow up about the plugins I’m using.

Slightly nicer URLs

Saturday, October 17th, 2009

As we know, all unique online resources should be addressable with a unique URL.

However, not all URLs were created equal. Some URLs are “nicer” than others. For example, URLs with query string parameters are often considered to belong to the “not so nice” URL category: http://example.com/?p=1234&vH=10&Session_ID=er5DKJn838JK2dfs

In general, what I consider to be “nice” or “not so nice” URLs is a lengthy topic, and I’ll only touch on part of it today. Suffice to say, that for some purposes, I believe using query string parameters is not the worst crime you can commit. In fact, in some cases, I believe they are perfectly acceptable.

Take the following URL for instance: http://example.com/books/?format=html&order=alphabetical&page=2. Although query string parameters mean this URL is a little tricky to read, at least it uses human-readable parameter keys and values. And because slashes / in URLs imply heirarchy, the only good alternative for this type of URL would be a Matrix URL, like this: http://example.com/books/;format=html;order=alphabetical;page=2.

Implementing Matrix URLs within web applications can be difficult, requiring extra server-side redirects or client-side trickery because by default, a HTML form won’t submit data formatted as a Matrix URL.

That’s why I believe query strings aren’t so bad, sometimes they really come in handy.

Repeated parameters

That said, when using checkboxes (or heaven-forbid) multi-select controls to submit data using the GET method, some server-side languages (like PHP) require that you add [] to the end of the name attribute of each control, for example: <input type="checkbox" name="items[]" value="item1" /><input type="checkbox" name="items[]" value="item2" />

For my money, this results in “not so nice” URLs, for example: http://example.com/books/?items[]=item1&items[]=item2

I know it’s a subtle difference, but I much prefer: http://example.com/books/?items=item1&items=item2

The other benefit is that your HTML wouldn’t need to contain the [] either: <input type="checkbox" name="items" value="item1" /><input type="checkbox" name="items" value="item2" />

A problem

The problem is, by default, if [] doesn’t appear in your URLs, only the last ‘items’ parameter will be accessible to PHP in the $_GET array.

A solution

I spent some time thinking about this, and decided the best thing to do would be to parse the URL myself.

/**
 * Returns query string parameters more intelligently from the URL than by using the $_GET array.
 *
 * When multiple parameters are encountered with the same name, they are stacked into an
 * array. This means all URL data can be accessed without using brackets in name attributes
 * For example, typically you would use: <input name="items[]" /> resulting in &items[]=id1&items[]=id2
 * However, using this method you can use: <input name="items" /> resulting in &items=id1&items=id2
 *
 * @author Andrew Ramsden
 * @see: http://irama.org/news/2009/10/17/slightly-nicer-urls/
 * @license GNU GENERAL PUBLIC LICENSE (GPL) <http://www.gnu.org/licenses/gpl.html>
 *
 * @param String $url (optional) A URL to parse for query string variables. If not set, the
 *        current requested URI will be parsed.
 * @return Array An associative array with all query string variables. Multiple parameters
 *         are stacked into a nested array.
 */
function getURLVariables ($url='') {
	$url = !empty($url) ? parse_url($url) : parse_url($_SERVER['REQUEST_URI']);
	$result = array();
	$queryStrParams = explode('&',$url['query']);
	foreach ($queryStrParams as $param) {
		$paramKeyVals = explode('=',$param, 2);
		if (!isset($paramKeyVals[0])) continue;
		$key = $paramKeyVals[0];
		$val = isset($paramKeyVals[1])?$paramKeyVals[1]:'';
		if (substr($key,-6) == '%5B%5D') { // support ugly urls too
			$result[substr($key,0,-6)][] = $val;
		} else if (!isset($result[$key])) { // add new param to the results array
			$result[$key] = $val;
		} else { // this param already exists, stack into an array
				if (is_array($result[$key])) {
					$result[$key][] = $val; // add to existing array
				} else {
					$result[$key] = array($result[$key], $val); // create new array
				}
		}
	}
	return $result;
}

Now instead of using: $items = $_GET['items']; you can use $items = getURLVariables()['items']; and access all the data from your slightly nicer URLs.

Feedback appreciated, let me know what you think.

Not-so-compact documentation

Monday, March 30th, 2009

I just finished fleshing out the documentation for the compact content widget.

In particular, now each presentation type (tabbed, slideshow and slider) has its own page, that outlines:

  1. How to use that presentation type.
  2. Options that can be set independently for each widget instance.
  3. Configuration that applies globally for all instances of a particular presentation type.
  4. The markup generated when a widget is initialised.

The documentation is fairly detailed, but hopefully this level of detail is useful.

Who started it?

Wednesday, March 25th, 2009

I spent some time this evening thinking about ideas for progressing form validation code.

Specifically, I was trying to solve the conundrum: When the submit event for a form is triggered, how can you tell which user interface element triggered the event?

This problem typically arises when you have multiple submit buttons on a form, when any submit button is activated, the submit event is triggered, but the form element is passed as a property of the event object, not the element (in this case a button) that originally triggered the event.

To complicate matters further, pressing enter while focussed on any input element will also trigger the submit event in many user agents.

Finding a complete robust solution became a bit of an emotional roller-coaster, let me explain…

  1. My initial investigation revealed that jQuery was returning a useful object eventObj.originalEvent.explicitOriginalTarget. :)

    For example:

    $('form').submit(eventObj) {
    	var iStartedIt = eventObj.originalEvent.explicitOriginalTarget;
    };

    Unfortunately, it turned out that explicitOriginalTarget is a Mozilla-specific property, with no cross-browser alternative. :(

  2. My next thought was that the currently focussed element would have to be the element that triggered the submit event! :)

    Unfortunately, jQuery doesn’t have a :focus selector. :(

  3. I found a proposed solution for extending jQuery with a :focus selector. :)

    Unfortunately, it relies on document.activeElement, which is a property only supported in IE, and modern browsers that have implemented parts of the HTML5 draft specification. :(

  4. Luckily, a good fellow proposed a way to support document.activeElement in older browsers that don’t implement it themselves. :)

    And… even better, this seemed to work (well, mostly – see below).

All together now

So put all this together and we get:

// Add document.activeElement support to browsers that don't support it.
if (
	typeof document.activeElement == 'undefined' &&
	document.addEventListener
) {
	document.addEventListener("focus", function(e){
		if (e && e.target) {
			document.activeElement = e.target == document
				? null : e.target;
		}
	}, true);
}
// Extend jQuery to support :focus selector
jQuery.extend(jQuery.expr[':'], {'focus': function(e) {
	return (document.activeElement)
		? e == document.activeElement : false ;
}});
// Now on submit, we can find the element that triggered this event
$('form').submit(function(eventObj) {
	// if a submit button triggered the submit event, find it
	if ((focussedSubmit = $(this).find(':submit:focus')).size() > 0) {
		var iStartedIt = focussedSubmit;
	} else {
		// otherwise find the first submit button
		var iStartedIt = form.find(':submit').eq(0);
	}
	// The iStartedIt variable now contains a reference to the element
	// that triggered the submit event!
};

Tested and working in Firefox 2, Firefox 3, Opera 9.5 but not working correctly in Chrome. IE versions were not tested at this time, but in theory IE6+ should be fine.