How to run custom PHP code in WordPress – the right way

I’ve recently been asked to look at integrating some complex forms into a WordPress powered site. None of the current plugins satisfied our (multi-lingual) needs, so the next simplest approach appeared to be integrating PHP code with WordPress. (I had considered integrating Kohana, my favoured PHP framework, as there is a module that allows easy integration, but this seemed overkill for a few forms).

Running PHP code from within WordPress is nothing new. There are numerous plugins. I opted for Exec-PHP, partly because it gets high user ratings and the developer has provided feedback on his site, and partly because it worked for me first time.

Having installed the plugin, it was simple to create a Page, add some PHP code using the traditional PHP tags . However, once I started writing code in anger, I began to understand that to run the code efficiently, and to give the end-user a good experience, simply placing code in the page (blog entry) is too simplistic.

The key issues, is that the PHP code is evaluated at run-time, using the PHP eval() function. This function is highly inefficient. So, the first improvement was to move the bulk of the code to a separate PHP file and include it. This way, the only code evaluated is the include statement.

I chose to put my included code in a folder called custom-php within the WordPress wp-content folder. The best way to reference the file is using the ABSPATH constant provided by WordPress, as follows:

<? require_once(ABSPATH. '/wp-content/custom-php/contact-form.php'); ?>

Now I can simply place my PHP code in the external file for inclusion. So far, so good. But it wasn’t long until I hit my next hurdle. I need to send an email when the form is submitted. Nothing too taxing there. In fact, this is super simple thanks to the @wp_mail WordPress tag.

Now, normally when writing a PHP form script, the form is POSTed to the server, the server sends the email using some SMTP library, and then the script renders an HTML page ( or View if using an MVC framework). And if you’re a good developer, you’ll actually perform an HTTP redirect instead, to prevent double form submission (see this article on Wikipedia).

However when PHP code is executed within a WordPress page, some of the response has already been sent. This has two side effects:

  1. Firstly, the page renders in the browser until it reaches the script; waits until the email has been sent, and then continues rendering the rest of the page/
  2. Secondly, as part of the response has already been sent, it is not possible to modify the HTTP headers, and hence a redirect cannot be sent.

So, we need a way to run the script before the response is sent. Here’s my solution:

Firstly, the PHP page included in the WordPress page, contains an HTML form. I add to the form a hidden field so that it can easily identified when POSTed e.g.:

<form method="POST" action="<?php echo $_SERVER['REQUEST_URI']; ?>">
<input type="hidden" name="form-name" value="contact" />
<div style="text-align:left; border: 1px solid #bbb; padding:10px;">
	<div>
		<label for="name">name</label>
		<input type="text" name="name" id="name" />
	</div>
	<div>
		<label for="email">email</label>
		<input type="text" name="email" id="email" />
	</div>
	<div>
		<label for="message">message</label>
		<textarea name="message" id="message" style="width:250px; height:100px"></textarea>
	</div>
	<input type="submit" value="go" />
</div>
</form>

Now, when the form is submitted, I am able to detect it, and therefore process the form submission prior to rendering the page. I do this by adding some code to the WordPress page template. I opted to add some more include code to the first line of the header.php file within my theme, like so:

<?php require_once(ABSPATH. '/wp-content/custom-php/contact-form-test.php'); ?>

There may be a better place to put this code (wp-config, wp-settings ?). I’m no WordPress guru, and I’m happy to be educated.

The contact-form-test.php code is as follows:

<? 
    session_start();
    if($_POST['form-name'] AND "contact" === $_POST['form-name']) {
         require_once(ABSPATH. '/wp-content/custom-php/contact-form-functions.php');
	 send_message();
     }
?>

This is fairly simple code. We test to see if our form was posted, and if so include the code to send the email. No point including it unless we need to. The key is, the code is now included before any other WordPress code executes.

For completeness, the contact-form-functions.php is as follows:

<?
function send_message()
{
	$subject = "Test email form WordPress";
	$body = $_POST['message'];
	$recipient = "rocky-racoon@abbeyroad.co.uk";

	$success = @wp_mail($recipient, $subject, $body);

	if($success) {
		session_start();
 		$_SESSION["mail_message"] = "Thank you. your message has been sent.";
		header( 'Location:  '.$_SERVER['HTTP_REFERER'] ) ;
	}
	else {
		$GLOBALS["mail_message"] = "Euston, we have a problem.";
	}
}
?>

We use WordPress’ mail functionality to send the email. If it sends OK, we perform an HTTP redirect. First we also set a flashvar using the PHP $_SESSION as, due to the redirect, we cannot use a $GLOBAL as it obviously goes out of scope.

One final change is required to contact-form.php to retrieve the success (or error) message. The following code is placed before the HTML form:

<?php
	if(isset($GLOBALS["mail_message"])) {
 		echo "<p>".$GLOBALS["mail_message"]."</p>";
	}

	if(isset($_SESSION["mail_message"])) {
 		echo "<p>".$_SESSION["mail_message"]."</p>";
		unset($_SESSION["mail_message"]);
	}
?>

This solution is useful for an custom PHP code that is server-intensive, be it database queries, web service calls or sending email.

Hope you found this useful

Tags: ,

17 Comments

David Lyle commented on November 11th, 2009 at 5:38 pm

Excellent tip to Include the php code rather than just drop it in the page/post!

Thanks for sharing

carlitos commented on November 17th, 2009 at 9:24 am

I’ve got a problem, I got this page done topxxname.php and uploaded it to wpcontent/themes/mytheme/ and when i try to open on browser that file my wp send me to the 404 page!

How i can add custom php pages to the themes folder and run them?

If i do ur tutorial it will work ?

Thanks

badlyDrawnToy commented on November 17th, 2009 at 10:49 pm

@carlitos – you can’t just upload a file and access it using WP. Everything goes through the controller, index.php. You need to create a PAge within WP, and run your code within this.

carlitos commented on November 19th, 2009 at 6:09 am

yeah thank you i finally figure it out

Michael commented on November 20th, 2009 at 11:48 pm

Thanks for taking the time to post this. It has saved me hours and at my age…that is important :)

Joe commented on December 1st, 2009 at 8:01 pm

Thanks for this article. Makes sense but I cannot get it to work.

When I first display the form page it begins with this error:

Warning: session_start() [function.session-start]: Cannot send session cache limiter – headers already sent (output started at /home/myacctname/public_html/mywpsitename/wp-content/themes/formtheme/header.php:8) in /home/myacctname/public_html/mywpsitename/wp-content/custom-php/contact-form.php on line 2

In my themes header.php the first line reads:

…and in “contact-form-test.php” the entire module is

…and in “contact-form-functions.php” the entire module is

The error message appears, followed by the contact form itself. If I fill out the contact form and click GO then I get the “Thank you. your message has been sent” message, but I never get the email.

As a test I tried commenting out the session-start command at the beginning of “contact-form.php”. and that did eliminate the error message, but now when I fill out the form and click GO I no longer get the ‘thank you’ message.

I researched the error message and read that it might be caused by stray nonblank characters either before the but it looks like my Notepad++ is set to Unix/ANSI so I don’t think that is the problem.

Any suggestions on 1) why the error message, and 2) why I got the “…your message has been sent” and yet received no email?

TIA

badlyDrawnToy commented on December 1st, 2009 at 8:21 pm

Hi Joe. You’re on the right track with the stray characters. To start a session the headers need modifying in the HTTP response. This cannot happen once the response has started being written. This is a classic gotcha with PHP when you are mixing server-side code with client-side HTML. If you have so much as a space or carriage return outside of the PHP tags, prior to the session_start call, then you are actually sending a response. You have to work through ALL the files that might be loaded before the session_start call. This includes those loaded by WordPress such as index.php, wp-config.php. If there’s a new line after the closing PHP tag, this cvould ber the culprit. Often, you’ll find there is no closing tag, to avoid this error. Quite likely it’s header.php, contact-form-test.php, contact-form-functions.php or contact-form.php.

If sessions aren’t working, then you will not see the message. It needs to be set in session so it can subsequently be pulled out after the redirect.

As to why you’re not receiving email, the code I posted is using Worpress’ underlying mail function. This means that whatever WP uses, the function uses. Can you send mail outside of this script? If not, you might need to configure WP with an SMTP plugin, rather than using the native sendmail() function.

god luck

Joe commented on December 2nd, 2009 at 3:54 am

Thanks for the encouragement, but I looked at the header.php and the other files you mentioned but no luck: I’m still getting the “headers already sent” message whenever I display the contact form page (all other site pages are fine).

I also tried relocating the “require_once(ABSPATH. ‘/wp-content/custom-php/contact-form-test.php” to the wp-configure.php, and also deactivating all plugins but still I get the same “headers already sent” error message.

If you have any other ideas – or perhaps know of some diagnostic tool to perhaps capture the outgoing stream and that would reveal the culprit (i.e., the code that is possibly sending the stray character(s) after the php closing bracket) then I’m all ears.

Shaun Tarves commented on March 10th, 2010 at 5:48 pm

I think the problem Joe is having is the same one I am having. When you try to use session_start() in contact-form.php where contact_form.php isn’t the only thing included on the page, PHP complains that headers are already sent.

My form sends just fine, but I can’t extract the error message to display it. Here’s my set up:

contact-form-test.php is the first line in header.php

contact-form-functions.php does its job and sends the mail through WordPress

contact-form.php is included from index.php since I want the form to appear on my home page, toward the bottom. So when contact-form.php loads, the session_start() in line 2 of contact-form.php that should resume the session to pull out the error message makes PHP complain that headers have already been sent out (since it’s almost done rendering the entire page). I can’t see how any of this would actually work.

Maybe you can shed some light on how you got this going? Perhaps showing the code for the page that calls contact-form.php would be helpful.

Shaun Tarves commented on March 10th, 2010 at 7:38 pm

TO ANYONE HAVING PROBLEMS WITH THIS

I finally fixed this after nearly a day of debugging. Here’s the strategy:

in the header.php of my template, I have:
if (!session_id()) {
session_start();
}
require_once(ABSPATH. ‘…/contact-form-test.php’);

contact-form-test.php:
if($_POST['form-name'] AND “contact” === $_POST['form-name']) {
session_register(‘mail_message’);
require_once(ABSPATH. ‘…/contact-form-functions.php’);
send_message();
}

contact-form-functions.php
function send_message()
{
$subject = ‘Test email form WordPress’;
$body = $_POST['message'];
$recipient = ‘shaun@tarves.net’;

$success = @wp_mail($recipient, $subject, $body);

if($success) {
$_SESSION['mail_message'] = ‘Thank you. your message has been sent.’;
session_write_close();
header( ‘Location: ‘.$_SERVER['HTTP_REFERER'].’#contact’);
}
else {
$GLOBALS['mail_message'] = ‘Euston, we have a problem.’;
}
}

Doing it this way allows you to only have to include session_start() one time, and the key here is session_write_close(). For some reason, upon page redirection, PHP often loses session data. Ensuring the write is closed makes everything work.

Thanks so much for this great tutorial!

badlyDrawnToy commented on March 11th, 2010 at 9:56 am

Shaun, Joe

I just took a look at my code, and I had modified it since the orignal post. I moved the session_start() call to the contact-form-test.php include. Looking at your fix, this might be the reason. (I’ve modified this post to show the update).

and yes, PHP will lose all session data unless you tell it otherwise. Check out the wp_unregister_GLOBALS() function in wp-settings.php

tudoquemegustaeillegal commented on March 23rd, 2010 at 2:53 pm

hi,

the only way I got this to work was to put the session_start() in the index.php of my theme ? http://bugs.php.net/bug.php?id=14636

Neil commented on March 29th, 2010 at 11:57 am

Hi
Thanks for this.
I also found a plugin which inserts a shortcode for php includes.
http://www.amberpanther.com/contributions/wp-include-file/
It would be good to hear your thoughts on this. Goo didea or not?
thanks

badlyDrawnToy commented on March 29th, 2010 at 12:27 pm

Neil

Plugin looks good. Only thing to watch is *when* it executes your code. If it calls the include part way through the page, you could have session issues

Koleksi Web commented on May 7th, 2010 at 10:42 am

Thanks for the tips! I will try this script in my WP blog

Lenin commented on June 12th, 2010 at 8:43 am

Let me try ;) I am switching from blogspot to wordpress

Blog Setup Service commented on August 19th, 2010 at 11:36 am

I’m also giving it a try. Great article by the way.

Leave a Reply

CAPTCHA Image

Tethering the iPhone : My Experience plus o2′s secret little bmi.js file

I recently moved house, and in the process lost my broadband connection for longer than anticipated. Having looked into the various USB Dongle solutions, I decided to opt for a tethered connection form my iPhone. I learned a lot from my week or so working this way – about my productivity, and about how affective tethering is.

Productivity

The thing I noticed the most was how much more productive I was. We all know how easy it is to get distracted by email, RSS, Twitter, Facebook, YouTube, and web surfing. And many of us know we should practice better time management. Well, next time you laugh at friend who tells you that where they work they don’t have internet access, I’ll have to admit, I completely understand why management make such decisions (unless your a web developer of course!).

Here’s my 2 tips to remaining productive:

  • I really recommend only checking you mail periodically – it’s so easy to get into a conversation over email these days. I’d rather just fire-and-forget.
  • Treat Twitter, Blogs etc. as a break from work, or something to read over breakfast/lunch/midnight feast depending on your working habits. I now read twitter feeds like RSS feeds – scanning for something interesting to pick up on.

Tethering

My experience was pretty good. I must admit, I’ve not used USB Dongles in anger, and form what I’ve seen, results vary widely. I’ve moved to Didsbury which has a strong 3G signal, and tethered over USB. My iPhone is the 3G, not 3GS. Here’s my experiences:

Speed

Connection is really good. I even managed to watch an episode of Dragon’s Den on my Mac with very little buffering. Of course, I couldn’t have done this on the phone itself.

Large File Downloads

Whilst streaming was fine, downloading wasn’t too good. I did try to download the whole episode, but it would have taken hours.

Image Quality

The biggest surprise was the poor quality of graphics. The quality is drastically reduced/pixelated. Hovering over the images reveals a hint that:

Shift+R improves the quality of this image. Shift+A improves the quality of all images on this page.

This intrigued me, so I did some digging and found that in the source of every web page, o2 is adding a call to a Javascript file in the header. This file, bmi.js, appears to replace images with low res versions. Not sure how, but it does seem to try to use iframes. It seems fairly harmless, but I did, on one occasion, find it broke a web page I was developing.

A quick look on Google reveals that thsi same file is used by Vodafone.

Battery Life

Whilst tethering, the phone does stay rather warm. With regards to battery life, as I was connected via USB, the phone remained fully charged. Interestingly, if the laptop is on battery power it still charges the phone, so the laptop loses juice pretty quickly.

Receiving Calls

Making ands Receiving calls whilst connected is fine. However, I did accidentally turn off 3G once. Whilst I could still browse OK, if the phone rings, you are disconnected.

Bugs

There is (or was, as I just downloaded the latest upgrade) a small bug with the address book. Every other letter in the right-hand alpha list became a bullet point. Odd.

Conclusion

Whether you think the £20/month tethering fee is justifiable or not is a different matter. It’s certainly cheaper than buying a USB Dongle if you only plan to use it the odd month or two. Regardless, if you have a good 3G connection I can recommend it – unless you hate low res images!

Tags: , , , ,

3 Comments

Remy Sharp commented on December 9th, 2009 at 11:59 pm

What I found really interesting is that it was inlining all the external scripts, with the exception of jQuery, the server side is obviously sniffing that script and allow it to be an exception. Odd but interesting the pull of jQuery (i.e. it’s expecting it’s possibly cached?).

Huey commented on May 29th, 2010 at 11:06 pm

I don’t think it is worth the fee, I have figured out how to tether for free so there’s no need to complain about the price. But the imaging is a huge problem as because of the layout of some sites it becomes impossible to get the full res. I’m still looking for a way to disable this…

Huey commented on June 30th, 2010 at 11:18 am

I have found a solution! :D

On your iPhone go to

Settings > General > Network > Cellular Data Network

Then Under “Cellular Data” change the APN to “mobile.o2.co.uk”
And change the Username to “bypass” (no quotes in both cases)

Then save the settings and restart your iPhone and the bmi.js file should no longer show up while tethering. Leaving you with full resolution browsing! The iPhone browser also appears a lot better since it was also affected by the js file.

Leave a Reply

CAPTCHA Image

BT F*cks Up Again

What a surprise. BT has f*cked up again. They really are the worst utility company I’ve ever had to deal with. I have no wish to deal with them, but I am forced to. When people go to work at BT, they must be lobotomised to have the words ‘customer’ and ‘service’ permanently removed from their brains.

I’ve moved house. A stressful experience at the best of times. We’ve finally sold our house in Macclesfield, and are now renting whilst looking to buy a new property. Being a web developer, one of my biggest concerns was getting my phone line and broadband up and running as soon as possible.

The rental property I’ve moved to didn’t have an active line, so I needed to pay for one to be installed. BT wanted £123 for this task. I was prepared for this expense (even though it is a fair whack). What I wasn’t prepared for was how BT wanted to shaft me for a 12 month line rental contract. We’ve been customer’s for a decade, and we could transfer our account to the new property, but we still needed to take out a new 12 month contract. Worse still, the contract is not transferable to the next property, nor the next tenant!

Absolutely outrageous. If we move in the next 3 months, I’ll have forked out in excess of £265 to get my phone line activated (without call or broadband costs). I’d then potentially need to fork out another £265 at my next property for the privilege. Maybe that lobotomy adds the words ‘daylight’ and ‘robbery’ into their brains.

After some research, I found a better alternative – the Post Office. The connection fee was cheaper at £107, and there is no minimum contract. On top of this, there’s some great free calls given as part of the bundle (mobile, international etc.).

So, having sorted out the phone line, I now needed to deal with my broadband. My provider told me that so long as there was 5 days notice, if I got hold of the Linked Order Reference from BT/Post Office then they should be able to get the broadband running the same day. Well, I’d been told it was 10 days to get the phone line installed, so I went back to the Post Office, got the Order Reference, and told my broadband provider.

Over the next few days, I got several emails confirming all was in order. Last night, I phoned to check. Everything was in order.

Today was the day it was all to come together. The engineer was booked between 8am and 1pm. He arrived at 8:30am and by 10am the phone line was active. Broadband however, was not active. The engineer informed me it could happen any time. The guy in the exchange would be working through 20 or so jobs, until around 4:30pm. So, I waited patiently.

At 12pm an email arrived from my broadband provider:

We would like to inform you that the simultaneous provide order for Linked Order Reference XXX has been cancelled by BT. The reason for this is that our order with the quoted reference could not be matched with the telephone order. Please call BT for more information and update us when your line will be activated.

WTF? Several expensive phone calls later, my broadband provider has provided no more insight as to why this has happened beyond ‘BT’s system doesn’t work’. And so, I must now put in a new order and wait 7 days for activation. I could speed this up to 48 hours if I pay an extra £100.

No explanation from BT
No redress from BT
No customer service from BT

Absolutely pathetic. Almost as incompetent as Macclesfield Borough/East Cheshire Council

3 Comments

Alun commented on July 31st, 2009 at 1:27 pm

Could be worse, imagine if parcel force moved into the telecoms industry…

Darren commented on August 3rd, 2009 at 11:55 am

Truly a bunch of absolute wasters!

We’re moving house ourselves at the moment too and it has to be said Sky/Orange haven’t been a great deal better either!

Still, at least NTL are no more!!

Josh commented on October 15th, 2009 at 4:49 pm

Shame on BT – as usual, customers are a low priority. On the positive side, thanks for flagging up the Post Office as a place with no minimum contract.

Leave a Reply

CAPTCHA Image

grid960 – a grid overlay bookmarklet for 960.gs

A couple of days ago I released a jQuery plugin that provides a grid overlay when working with the 960.gs css grid framework. The plugin seems to have been quite popular, and as a result I now have a host of new Twitter followers – Hello to everyone in Romania!

I’m glad that others have found the plugin useful. A couple of people have suggested I release the overlay as a bookmarklet, so I spent yesterday learning how to develop one. So here it is: grid960 – a grid overlay bookmarklet for 960.gs

grid-bookmarklet

To install the bookmarklet simply drag the link below to your bookmarks toolbar. In Internet Explorer, right click, select ‘Add to favourites’ and then select ‘Links’.

grid960

You can test it on this page.

The bookmarklet works slightly differently to the original jQuery plugin. Firstly, if jQuery is not installed, it will inject it for you. If jQuery is already installed it will use this library – it will dynamically upgrade the library if it is not the latest vesion (1.3.2).

Secondly, the bookmarklet provides a small configuration menu on the page. When the bookmarklet loads, it will defaults to the 12 column grid. Through the menu, you can switch to a 16 column grid, and also alter the opacity of the gridlines.

The bookmarklet does not work in IE6 (yeah, whatever).

Any problems or suggestions, let me know.

Tags: , , , ,

17 Comments

Ian commented on April 23rd, 2009 at 1:23 pm

Very cool and quick work on the bookmarklet implementation! As a feature suggestion would it be possible to allow the user to select the width, gutter, and columns? This would then make it useful for aligning no matter which grid your working with, not just 960.

| Caldeas Blog commented on April 23rd, 2009 at 5:51 pm

[...] badlyDrawnToy created a bookmarket for the 960.gs grid overlay. I modified it for Bluetrip. Just drag the link below to your bookmarks toolbar. In Internet Explorer, right click, select ‘Add to favourites’ and then select ‘Links’. It does not work with IE6. [...]

Simon Rumble commented on April 24th, 2009 at 7:01 am

Nice work Howie! (Google Reader filtered out your code, mind.)

Simon Rumble commented on April 28th, 2009 at 6:47 am

Very likely reason it doesn’t work in IE6 is that it has a 508 char limit on bookmarklets and yours is 657 chars. If you reduce it down to just load your library, and have all the guts in there, you’ll get under that limit:

javascript:(function(){document.body.appendChild(document.createElement(‘script’)).src=’http://www.yoursite.com/stuff/bookmarklet.js’;})();

badlyDrawnToy commented on April 28th, 2009 at 8:32 am

Simon. The code is a little more complex than simply writing in the script element, as it first loads jQuery and then the custom script.

The code fist checks to see if jQuery is already loaded; if not the script is added. If it does exist, it checks the version, and updates if necessary. One further complication, is that the code must wait for jQuery to initialise before downloaded the second custom script.

Simon Rumble commented on April 28th, 2009 at 8:39 am

Hmmm, much trickier than mine! But couldn’t all that be done in a download, which triggers further downloads? The key here is to just get the Bookmarklet component down to under 508 chars for broken IE.

(The reason I care about IE6 is it’s a critical target platform for me. It’s STILL in the SOE here. Gah! Not that I need your bookmarklet for work purposes, just had to solve the problem myself just now.)

badlyDrawnToy commented on April 28th, 2009 at 8:48 am

@Simon – Yes, I guess I could/should have done this. I decided that web developers are unlikely to be designing their grid layouts in IE6. Could be wrong, which would be scary!

Seemed like overkill to make 3 HTTP requests to load the script. Here’s the code used

http://www.badlydrawntoy.com/static/960grid/js/960grid-bookmarklet.js

Simon Rumble commented on April 28th, 2009 at 8:54 am

When I first started this and was trying to get Firefox et al installed, I was told by the support people to develop in IE6 and Word… Yeesh!

The reason IE6 might be handy is for debugging a bog-standard IE6 install, which is fiendishly difficult. I find XRAY incredibly handy for this (http://www.westciv.com/xray/)

Milan Andric commented on May 5th, 2009 at 1:08 am

I’m looking for a javascript grid-based page editor, for lack of a better term. Some javascript code that takes a set of vanilla html div blocks and allows you to order them/move them/expand contract and maintains the grid system in a fixed width interface, like 12 column 960. The javascript generates the right grid code and you can save it or do an XMLHTTPRequest post to save it. Any ideas or have you seen something like this?

robert commented on May 22nd, 2009 at 12:52 am

do you have a decompressed version of the javascript so I can add a 24 colun grid option? I can likely edit it myself unless you want to add it which would be nice heh. Email me if you do, thanks.

DemoGeek commented on September 18th, 2009 at 3:48 pm

That is indeed very handy particularly with the 12 and 16 grid option. Would really help if there is a way to adjust the color overlay as if you have the same color on the page you might find it a bit harder to track it.

David commented on September 30th, 2009 at 2:29 am

Awesome. THANK YOU!

Ravoof commented on November 8th, 2009 at 9:56 am

Really useful. Thanks.

Katie Dixon commented on December 7th, 2009 at 1:50 pm

Works great and so useful! Thank you very much!

Surftipps commented on February 20th, 2010 at 9:12 pm

Absoluty perfect. Thanks

Faramarz commented on May 8th, 2010 at 11:06 am

Brilliant. It would be great if grid colours could be chosen.
Really nice job, thanks for sharing it with everybody else.

Phil commented on June 19th, 2010 at 9:19 pm

Perhaps Howie is a Manchester City supporter. He wouldn’t want to give you a choice of colour in case you chose one that wasn’t blue.

Leave a Reply

CAPTCHA Image

Colophon

badlyDrawnToy is the blog of Howie Weiner, a knackered, old web developer, based in Manchester.

Howie is Technical Director at JP74 and specialises in PHP (Kohana) and Java (Spring) web applications, Search Engine Optimisation, custom CMS and web development.

View Howie Weiner's profile on LinkedIn
View Howie's twitter feed