Hi All,
For my pet project, an IRC bot in PHP, I want to be able to allow addons to be made for it, and I want the addons to be able to hook into events that occur in the bot.
The basic structure I'm considering now is this:
1. A module or add on will hook into the main app something like this:
function addon(&$bot) { ... addon functionality here ... }
$hook['eventtype']=EVENT1;
$hook['functionname']='addon' ;
$bot->AddHook($hook) ;
2. $bot will keep lists of all functions that should be called for each event type. E.g. $EVENT1[]='addon' ;
3. When something occurs that could trigger a hook (e.g. a message is received), hooks will be called via arrays of event types.
e.g.
if (EVENTX has occured)
foreach ($EVENT1 as $function) {
$function($this) ;
}
Is this method of variable functions with a by-reference copy of the entire bot the best way to call the external addons?
Also wondering whether I should include the ability for the addons to define the user levels of users that they want to be called for in the $hook[] definition, or let them determine that in their own code (since they'll be able to access $bot->current_user_level or something similar) ?
Any other input on structures for implementing hooks or examples of existing code appreciated.
Implementing hooks for modular 'add ons'
Moderator: General Moderators
I think you'll end up with some events and event handlers.
this way you only have to plugin eventlisteners (which implement the listener interface).
In GUI libraries like MFC and QT they use "signals (and slots)". you can read more on that at for example at http://doc.trolltech.com/3.3/signalsandslots.html
this way you only have to plugin eventlisteners (which implement the listener interface).
Code: Select all
interface CEListener
{
function isConnect($connected)
}
class someCEListener implements CEListener
{
function isConnect($connected)
{
// do something
}
}
class Generator
{
private CEListeners;
function addCEListener(CEListener &$listener)
{
$this->CEListeners[] =& $listener;
}
function main(....)
{
// if CE rises
if (....)
{
foreach($CEListeners as $listener)
{
$listener->isConnect(...);
}
}
}
}In GUI libraries like MFC and QT they use "signals (and slots)". you can read more on that at for example at http://doc.trolltech.com/3.3/signalsandslots.html
From Tim's code, you may want to be careful with this.
Based on how this is coded, I can see that Tim is thinking in a PHP5 way, but in PHP4 this foreach construct won't work if the listener is an object. Use this instead...
The reason? In P5, foreach returns objects by reference. In P4, it's by copy.
Just some FYI from someone with fresh wounds.
Code: Select all
<?php
function main(....)
{
// if CE rises
if (....)
{
foreach($CEListeners as $listener)
{
$listener->isConnect(...);
}
}
}
?>Code: Select all
<?php
function main(....)
{
// if CE rises
if (....)
{
foreach($CEListeners as $listener => $listenerObject)
{
$CEListeners[$listener]->isConnect(...);
}
}
}
?>Just some FYI from someone with fresh wounds.
The Observer pattern would be worth a look (there's also a lot of other pattern info to be found in each of the following links).
http://www.horde.org/papers/kongress200 ... _patterns/
http://www.phppatterns.com/index.php/article/archive/1/
http://www.javaworld.com/channel_conten ... ndex.shtml
With Observer, rather than storing a list of functions, the observable (Bot) stores a list of objects all having the same "listen" interface:
If your "hooks" are objects you have better encapsulation. With hooks as functions, Bot may have to know some details about how to call them (unless each hook corresponds to a single, simple function). As objects, Bot can simply pass on the event and doesn't need to know anything about them.
If only one handler should ever be active, passing $event along a Chain of Responsibility is a better fit. That might be more what you are looking for. Create a single event handler class for each event type which encapsulates all the functions for that type - and possibly subdivide into factory objects, if needed.
http://www.javaworld.com/javaworld/jw-0 ... terns.html http://www.javaworld.com/javaworld/jw-0 ... chain.html
http://www.horde.org/papers/kongress200 ... _patterns/
http://www.phppatterns.com/index.php/article/archive/1/
http://www.javaworld.com/channel_conten ... ndex.shtml
With Observer, rather than storing a list of functions, the observable (Bot) stores a list of objects all having the same "listen" interface:
Code: Select all
class Bot
{
function registerObserver(&$observer)
{
$this->_observers[] =& $observer;
}
function notify()
{
foreach(array_keys($this->_observers) as $key)
{
$this->_observers[$key]->listen($this->event);
}
}
etc..
}
class Foo // an observer
{
function listen(&$event)
{
// do something with $event
}
etc..
}
// Or, instead, load them by factory:
class Bot
{
function Bot()
{
$this->_observers[] =& $this->_foo();
etc..
}
function &_foo()
{
include(..path to Foo class..);
return new Foo;
}
etc..
}If only one handler should ever be active, passing $event along a Chain of Responsibility is a better fit. That might be more what you are looking for. Create a single event handler class for each event type which encapsulates all the functions for that type - and possibly subdivide into factory objects, if needed.
http://www.javaworld.com/javaworld/jw-0 ... terns.html http://www.javaworld.com/javaworld/jw-0 ... chain.html