Setup a testing mail server using PHP on Mac OS X

You may already know this feeling. You've spent a few hours developing a shell script to send out billing emails to customers and after pressing "enter" you suddenly realise that your script is sending out hundreds of dummy emails to your customers (you had copied some data from the live server "just to test things out a bit").

You quickly stop the script, but the damage is already done! You're now getting confused emails from your customers... time to start writing that apology email!....... OR... you could implement a testing mail server so this situation NEVER HAPPENS AGAIN!

Read more

SWFUpload jQuery Plugin

Deprecated

This plugin is now deprecated. I recommend Plupload as it has HTML5 uploads with fallbacks to Flash, Silverlight and HTML4 for older browsers.


When I first stumbled across SWFUpload about two years ago I was impressed by how easy it was to implement. However, their example code has always bugged me as being rather crap, having to assign a separate global event handler for each event, and the lack of multiple handlers for a single event.

Using jQuery (the solution to all things painful), I've written a plugin to create a real event dispatcher for SWFUpload without modifying the SWFUpload core!

Read more

Macquarie online banking interface sucks

My wife and I have a few bank accounts with Macquarie, and although we think we're getting a pretty good deal with no bank fees and higher interest than most standard savings account, it's obvious their online banking interface is stuck in the mid-90's listening to Informer on a Sony Walkman.

Fortunately it's now 2009 and we have the ability to run custom javascript on any website we want, so stop complaining and fix your online banking experience today!

Read more

Handy jQuery Plugins/Snippets

It seems almost the entire world has switched to using jQuery, and for good reason too! Here are some simple but helpful plugins and snippets I've written recently.

jQuery.fn.otherwise

jQuery first-timers always seem to be asking "How can I check if my selection didn't match anything?", to which the common answer is "just check the .length property". Try the following for a chainable solution instead.

Usage Example


$('button').click(function(){
    $('#msg p:first')
        .remove()
        .otherwise(function(){
            $('#msg').html('<div class="error">There are no more paragraphs to remove!</div>');
        });
});
 


<button type="button">Click me!</button>

<div id="msg">
    <p>This is the first paragraph.</p>
    <p>This is the second paragraph.</p>
    <p>This is the third paragraph.</p>
</div>

See it in action!

Plugin Code


jQuery.fn.otherwise = function(func) {
    if (!this.length) {
        func.apply(this);
    }
    return this;
};

jQuery.fn.followLink

This was my first attempt at a jQuery plugin. I wanted to simulate a link being clicked, however .click() only triggers jQuery click handlers and doesn't change the current page.

Usage Example


// navigate prev/next depending on left/right arrows pressed
$(document).keydown(function(e){
    if (e.which == 37) {
        $('#prev').followLink();
    } else if (e.which == 39) {
        $('#next').followLink();
    }
});


<a href="prev.html" id="prev">Go to the previous page</a><br />
<a href="next.html" id="next">Go to the next page</a>

See it in action!

Plugin Code


jQuery.fn.followLink = function(){
    this.attr('href', function(){
        window.location = this.href;
    });
};

jQuery.fromXMLString

Ever had a string of XML that you'd love to use jQuery on? Although you can pass an XML string to jQuery in Firefox without additional code, it doesn't work in IE7. Try this baby instead!

Usage Example


var strXML = '<people><person id="69"><name>Trent</name><location>Punchy</location></person></people>';
$('#location').text(
    $.fromXMLString(strXML).find("person[id='69'] location").text()
);


<p>Trent is from <span id="location"></span>.</p>

See it in action!

Plugin Code


jQuery.fromXMLString = function(strXML){
    if (window.DOMParser) {
        return jQuery(new DOMParser().parseFromString(strXML, "text/xml"));
    } else if (window.ActiveXObject) {
        var doc = new ActiveXObject("Microsoft.XMLDOM");
        doc.async = "false";
        doc.loadXML(strXML);
        return jQuery(doc);
    } else {
        return jQuery(strXML);
    }
};

Open external website links in a new window

Want all links away from your site to open in a new window? Although this is not as "jQuery-like" as the previous examples, it's fast and gets the job done.

It only modifies links that don't have a "target" attribute already set. I also like to make all pdfs open in a new window.


$(function(){
    $('A, AREA').filter(function(){
        return (!this.target && (this.href.indexOf(window.location.hostname) == -1 || this.href.match(/\.pdf$/i)));
    }).attr('target', '_blank');
});

See it in action!

New CakePHP “multitask” Plugin

What is multitask?

Multitask is a CakePHP plugin designed as a proof of concept task manager for non-interactive tasks.

It consists of:

  1. MultitaskerShell - a daemon that manages the tasks/threads
  2. MultitaskQueuedTask - a model for adding queued tasks
  3. ThreadedTask - a base class for your tasks

What does it do?

The multitasker shell acts as a daemon, getting a task from a model, executing it, and updating its status when done.

It could be used for executing long-running tasks in the background, such as tasks involving significant network activity or encoding video, etc.

Installation & Usage

  1. Copy the "multitask" folder into the plugins folder of your CakePHP app.
  2. Import the database schema from config/multitask.sql
  3. Run "multitasker" shell from the command line.


cd cake/console/
./cake multitasker -app ../../app

Then add some tasks into your database and it will execute them. :)

Here is some code that will add a task into the queue. Look in vendors/shells/tasks/echo.php for the task being executed.


$this-&gt;MultitaskQueuedTask = ClassRegistry::init('Multitask.MultitaskQueuedTask');

$task = array(
'task' => 'echo',
'data' => 'Hello, World!',
);

$this->MultitaskQueuedTask->create($task, true);
$this->MultitaskQueuedTask->save();

$task = array(
'task' => 'echo',
'method' => 'delayed',
'data' => array('duration' => 3, 'message' => 'That was a nice sleep!'),
);

$this->MultitaskQueuedTask->create($task, true);
$this->MultitaskQueuedTask->save();

Known Issues

I've not used this in production and don't recommend you do either! Heck, I haven't used it aside from a few small tests, so use with caution.

The plugin is in its elementary stages, and doesn't handle scripting errors, task progress, task dependencies, task priorities, etc.

Requirements

PHP needs to be configured with pcntl and shmop extensions, as it uses PHP_Fork to handle threading.

I have only tested it on PHP5. Please let me know if you get it working on PHP4.

Windows Compatibility

Although the pcntl and shmop extensions don't run on Windows, I have added "linear threading" (ie. no threading) so you can test the functionality of your tasks on Windows.

Where can I get it?

UPDATE: I've now pushed it to github - http://github.com/ifunk/Multitask for easy downloading...

Optimising php-faker to be 10x faster!

In a link trail following the sequence of helper callbacks debacle yesterday, I stumbled across an interesting php library for generating random data - php-faker, which is a port of Perl's Data::Faker.

At first glance it looked pretty handy, but after testing it a few times I noticed it would repeat the same "random" data in a sequence, over and over again. I think it must have been a problem with my wamp installation, however it made me peruse the code to discover an explanation.

What I immediately noticed was the inefficiency of the class over many iterations. Changing a few simple things enabled the code to execute 10x faster in my test environment.

What I changed

  1. Moved arrays from inside methods to static variables. This gave a massive speed boost, especially for the larger datasets.
  2. Cached instances of the classes instead of creating a new object for each request.
  3. Optimised the core methods random(), rand_num(), rand_letter() to use mt_rand() and simplified the code.
  4. Replaced unneccessary create_function() call with a more efficient alternative.

There is still a fair bit that could be optimised as I left the actual logic intact, but the above is simple, simple stuff.

Download my version

I probably should have installed git and created a patch, however forking the project was easier for me since I could edit the files online (thanks github!). Caius has already integrated my changes into his project, so happy days, just get it from him!

Git repository: http://github.com/caius/php-faker/tree/master

Little Frog Live Show

This week Bigfish performed a show at the Brisbane Powerhouse as part of the Powerkidz Festival, aimed at kids and held during the school holidays. I went along with my wife and 3 yr old daughter and had a great time.

The show consists of a live host, a guitarist, a few plasma screens, some short animated songs, and a live 3d host called "Wert" who appears on one of the plasmas. Apart from my daughter being initially wary of Wert, she had a great time and sang and danced to the tunes she knows off by heart.

You can see all the episodes and play the little frog game on the Little Frog website. We'll be putting up some new photos and videos of the event soon so keep an eye out!

And don't worry, I expect to put up some more CakePHP goodness soon, just have a few pressing deadlines to meet!

I’m going to webDU 2008!

I can't believe my luck!

Less than a month ago I stumbled across this competition on Flash Den which was offering a full-conference ticket to webDU. On a whim I decided to enter and lo-and-behold, a few weeks later I found out I am the chosen one!

I'm very excited to be going down to Sydney for the conference - I don't get out much. My flights and accommodation are booked, thanks to my boss.

If you're going to be there this year give me a shout and we can evangelise CakePHP during the Coldfusion sessions! (justKIDDING™)

CakePHP 1.2 Sessions and SWFUpload

Have you ever used SWFUpload to upload more than one file at once without refreshing the page? Of course you have! If you haven't, then I suggest you go read up about it here and here before continuing with this tutorial.

One issue many people run into when using SWFUpload with CakePHP is that sessions are often lost in the upload process. Fortunately there is a way to prevent these incessant session-less side-effects.

Read more

April Fools!

I'm usually too preoccupied to put any effort into April Fools, however this year I decided to do something funny since I'm now working in the office a few days a week.

This morning I placed a roll of toilet paper below each person's desk and sent the following email to everyone, addressed from the general manager, Colin.

SUBJECT: some house-keeping

Hi all,

After reviewing the amount of petty cash spent on toilet paper each fortnight, I will now be enforcing a new TP Ration Program to ensure we don't exceed our toilet paper budget.

You will be responsible for your own roll of toilet paper, which will be kept underneath your desk for safe keeping while not in use. If you squander your allocated roll before the fortnight is over, a replacement roll will be purchased and docked automatically from your next pay.

You will also be responsible for any clients' toiletry requirements that are incurred whilst meeting with you. If a client is meeting with more than one person, each staff member will contribute an equal percentage of TP.

I appreciate your efforts in making Bigfish a cost-efficient working environment.

Cheers,
Colin

PS. For those unsure of the recommended rate of consumption, I suggest a maximum of 2 sheets for number 1's, and a maximum of 8 sheets for number 2's.

Next Page →