PHP Observer Pattern

This week, I learnt a new design pattern to add to my toolbox. It is called the Observer Pattern.

The example I will go through is based on the one given in the book, PHP Master - Write Cutting Edge Code, which I would highly recommend anyone learning PHP.

What is the Observer Pattern?

It is basically a way of triggering code to run when certain events happen. If you are familiar with Javascript, then this concept will be familiar.

In Javascript, you might attach a function to a "click" event of a button which gets run every time that particular button is pressed.

This pattern allows you to do something similar in PHP. You can register a "listener" and then trigger it when you like. Let's take a look how this works.

The Event Class

This class will be responsible for storing and running all of the code. It will be made up of static properties and functions so that it can be accessed anywhere in the code base.

class Event
{
    static private $callbacks = [];
}

First we have an array that will store all of the event names and the functions to run when they are invoked.

static public function register(string $eventName, $callback)
{
    if (!is_callable($callback)) {
        throw new Exception('Must register a callable, '.gettype($callback).' given.');
    }
    self::$callbacks[strtolower($eventName)][] = $callback;
}

Next we have our register function. It takes 2 parameters: a name and a callback. The callback is typically expected to be a closure (which I covered here), but can also be an object.

The function throws an Exception if it is not a piece of code that can be run. Otherwise, it adds it to the array. Note that I have allowed for multiple functions to be run with the same event name.

Next we need a way to trigger it. So there is another function:

static public function trigger(string $eventName, $data = null)
{
    if (isset(self::$callbacks[strtolower($eventName)])) {
        foreach(self::$callbacks[strtolower($eventName)] as $callback) {
            $callback($data);
        }
    }
}

}

Again, very simple. It takes 2 parameters, the name and, optionally, data that you wish to pass into the callback function.

If the name exists in the listener, it loops through all the attached functions and runs them.

So simple.

Using the Event Class

In my very simplistic example, I am just going to register and even called "my_event" using the class that I just made:

Event::register('my_event', function($data) {
    print $data."\n";
});

You can see that I pass the function as the second parameter directly and all the function does is print the data parameter.

Now let's say somewhere else in the code I have a function that does something, I can now trigger the event each time it is run:

function doSomething()
{
    print "I did something\n";
    Event::trigger('my_event', date("Y-m-d"));
}

So this function prints some text then triggers the event "my_event", passing the date as the parameter. As we set up the event earlier, it will just print this.

So if we run this function:

doSomething();

We get the following output:

Summary

Obviously in this very simple demo, it seems silly to use events at all, but this is very useful pattern to use in your more complex systems and very easy to set up.

For example, you can trigger things like writing to a log or clearing some cache or sending an alert. It can make your code far more tidy having this all in one place!


© 2012-2018