<?php
/*
* File: Subscriber.php
*
* Copyright (c) 2007 by Daniel Kraft <dk@d9t.de>
*
* GNU General Public License (GPL)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*
* If you find bugs, please feel free to report them by mail or on
* irc: freenode://#d9t
*
*
* Subscriber Pattern
* ==================
*
* This is the subscriber-pattern implemented in php4 (and php5 if you're willing to
* accept the wrong documentation and use __construct instead of the class-name as
* constructor). It's not clean, but it will probarbly work.
* Usage:
*
* Define your events:
*
* >>> class MyEvent {
* ... // This is just a event with a piece of data.
* ... var $a;
* ... function MyEvent($a) {
* ... $this->a = $a;
* ... }
* ... }
* ... class AnotherEvent {
* ... // Whew - just another event!
* ... }
*
* Then define your subscribers. This can either be a globaly defined function or a class,
* which should derive from Subscriber.
* A function takes the event as argument.
* A class must implement the __call__ method, which takes the event as argument.
* __call__ will be called upon notification.
*
* Function use-case:
*
* >>> function doSomething($event) {
* ... print "I'm doing something ".$event->a."\n";
* ... }
*
* Class use-case:
*
* >>> class DoSomethingClass extends Subscriber {
* ... function DoSomething($var1, $var2) {
* ... // initialize whatever
* ... }
* ...
* ... function __call__($event) {
* ... // This is actually called upon notification.
* ... print "Hey, I'm a fully functional object!\n";
* ... }
* ... }
*
* Now you may subscribe to your event:
*
* >>> subscribe(MyEvent, doSomething);
* >>> subscribe(MyEvent, new DoSomething(1,2));
*
* We can also subscribe to other events.
*
* >>> subscribe(AnotherEvent, new DoSomething(4,5));
*
* Then, at some time, we simply notify the event.
*
* >>> notify(new MyEvent(44));
* I'm doing something 44
* Hey, I'm a fully functional object!
*
* >>> notify(AnotherEvent);
* Hey, I'm a fully functional object!
*
* Please take care to pass existing instances by reference, or you will copy the state of the object
* due to php's difficulties with references:
*
* >>> class CounterEvent {
* ... $increment = 1;
* ... function CounterEvent($increment=1) {
* ... $this->increment = $increment;
* ... }
* ... }
* >>> class Counter extends Subscriber {
* ... var $i=0;
* ... function __call__($event) {
* ... $this->i += $event->increment;
* ... print "Called ".$this->i." times\n";
* ... }
* ... }
*
* >>> $counter = new Counter();
* >>> subscribe(CounterEvent, &$counter); // see the "&"?
* >>> notify(new CounterEvent());
* Called 1 times
*
* >>> notify(new CounterEvent());
* Called 2 times
*
* >>> notify(new CounterEvent(3));
* Called 5 times
*
* TODO:
* - unsubscription
* - get list of subscribers
* - obscure the global variable somehow so that namespace collision is less likely
*
*/
function subscribe($event, $obj) {
/*
* This method subscribes the obj to an event.
*/
// php4 uses SMALL klass names. WHY?!
if (version_compare(phpversion(), '5.0.0', '<') == 1)
$event = strtolower($event);
if (!is_array($GLOBALS["_subscribers"])) $GLOBALS["_subscribers"] = array();
if (!is_array($GLOBALS["_subscribers"][$event])) $GLOBALS["_subscribers"][$event] = array();
$GLOBALS["_subscribers"][$event][] =& $obj;
}
function notify($event) {
/*
* This notifies an event, i.e. calls all subscribers.
* Per specification this calls functions with the event as argument and
* classes' __call__ method also with event as argument.
*/
$klass = get_class($event);
if (!is_array($GLOBALS["_subscribers"])) return;
if (!is_array($GLOBALS["_subscribers"][$klass])) return;
for ($i=0; $i<count($GLOBALS["_subscribers"][$klass]); $i++) {
$subscriber =& $GLOBALS["_subscribers"][$klass][$i];
// call instance
if (method_exists($subscriber, '__call__'))
$subscriber->__call__($event);
// call function
else
$subscriber($event);
}
}
class Subscriber {
function __call__($event) {
// Implement whatever you need here.
}
}
?>