Programming events with PHP

2006-10-24

Article explains, how to implement events with the PHP programming language, using the call_user_func() function.

The way how events are raised and how listeners are attached on the events is a part of a core in many modern applications. It plays an important role in some enterprise design patterns (MVC, for example). This article intoduces the basics of the event model and describes sample implementation of the event systems, suitable for PHP.

The basic event terminology includes the following terms:

  • Publisher or provider or source - An object that fires an event.
  • Event object - A set of data that represents fired event and which is sent to event subscribers by the publisher.
  • Subscriber or listener or consumer or handler or callback - An object that is being notified or informed when the publisher fires an event.
  • Firing or trigging or notifying - A process of notifying subscribers by the publisher about the event.
  • Attaching/detaching or (un)subscribing or (un)registering - A process of binding/unbinding event subscribers with the publisher, so that they could be notified about the particular event when it is fired.

As you may notice, there are too much different names for the same things in the definitions. Practically, the each name, more or less, is used together with other relevant names in the different programming technologies. For example, event listeners are attached to event source, not subscribed. Similar frequently used definition sets and idioms are the following: publisher/subscribing/subscriber, source/attaching/listener (Java), provider/consumer, registering callback (PHP), notifying listeners (Java), etc.

The most common way of the event-driven object collaboration is the following:

  • There is a host object that fires an event. This object contains the list of event consumers, which are registered as event callbacks. The event callbacks may be either functions or methods.
  • Under some conditions (database reading started or completed, user clicks on a button), host object prepares an event data (creates an event object) and executes all callbacks, which are registered as event consumers. In other words, which are in the corresponding array-like structures.

After the few theory words, there are some details about how to do event-driven programming in the PHP language.

The key part of the event-driven programming is a process of calling the callback function or method on the particular event. And, additionally to the traditional by-name calling, PHP provides you with the two more calling methods:

// Traditional by-name calling
myFunction();
$myObject->myMethod();
MyClass::MyStaticMethod();
 
// By-var calling
$var = 'myFunction';
$var();
$var = 'myMethod';
$myObject->$var();
$var = 'myStaticMethod';
MyClass::$var();
 
// By-callback calling
$callback = 'myFunction';
call_user_func($callback);
$callback = array($myObject, 'myMethod');
call_user_func($callback);
$callback = array('MyClass', 'myStaticMethod');
call_user_func($callback);

Additionally to the above, the call_user_func_array() function, which differs from call_user_func() on how the arguments are handled, can be used for callback calling. There is also an eval()-based calling, which, however, cannot be recommended because it is slower then other methods.

The class in the following code fragment shows a basic PHP callback usage for notifying consumers about the event.

class MyEventSource {
 
    protected $_eventCallbacks = array();
 
    function registerEventCallback($callback) {
        $this->_callbacks[] = $callback;
    }
 
    function fireEvent() {
        foreach ($this->_callbacks as $callback) {
            call_user_func($callback);
        }
    }
}
 
$source = new MyEventSource();
foreach (range(1, 5) as $i) {
    $source->registerEventCallback('myFunction');
}
// will execute myFunction 5 times
$source->fireEvent();

When defining a new class event, the following conventions should be followed:

  • Define protected or private $<eventName>Callbacks array.
  • Define protected or public register<eventName>Callback method.
  • Define fire<eventName> method.