I recently added PISG stats generation to my IRC bot, Pointything. Instead of making a crontab entry which is not controlled by him, I put the command to generate stats right into pointything himself. In order to make the stats generate at regular intervals, I had to devise a way to run the function once every 24 hours. Taking a few pages from Qt, I set up a system of signals and slots. However, the system is variously different. A general overseer, the eventPump works the magic. You call eventPump::connect($this,'signalName',$callback). Note that $callback can be any kind of callback. The eventPump does not check to see if the signal or slot actually exists (Checking the slot is a limitation of PHP). With this in place, I am able to create a timer that trips every so often.
$t=new timer(3600);
eventPump::connect($t,'tick',array($this,'generateStats'));
$t->start();
$this->timers[]=$t;
The timer class is a simple class that checks if the interval between ticks is more than a given number of seconds. If so, it calls eventPump::raise('tick',array($this)). The array passed is the set of arguments to be passed to the slot via call_user_func_array(). I can’t pass regular parameters because func_get_params() is screwy with objects passed.
PHP is not threadable. All that you are able to do is fork(). This makes it nigh impossible to do more than one thing at the same time, such as real timers. This is where this whole event system comes into play. Pointything’s run() function is where all the action happens. stream_select() is called in there, which makes him able to be on more than one server, and also to run some telnet sessions. After the streams are dealt with and the IRC data processed, eventPump::tick() is called. This in turn calls eventPump::raise(eventPump::instance(),'tick'). In the timer’s __construct() method, it connects its tick() slot to this event. The tick() slot keeps track of time elapsed, and then raises its own ‘tick’ event to the listeners.
It means that I now have the ability to break free of the functional programming paradigm. Instead of calling a function to check if something has changed and then act upon it, PHP acts upon it the instant a change is known. The stack pointer is now jumping all over the code, working on things as they happen instead of forcing them to wait until a request is made to check if anything needs done. Theoretically, I don’t need pointything’s method. I could just keep an infinite recursion of events and timers perpetuating him.
By far, this is not the cleanest system possible. This could be considered a “I wrote it because I could” project. I could be using the Standard PHP Library’s classes to do this in a much faster and efficient way. However, it works nicely the way it is.