Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
// +-----------------------------------------------------------------------+
3
// | Copyright (c) 2005, Bertrand Mansion                                  |
4
// | All rights reserved.                                                  |
5
// |                                                                       |
6
// | Redistribution and use in source and binary forms, with or without    |
7
// | modification, are permitted provided that the following conditions    |
8
// | are met:                                                              |
9
// |                                                                       |
10
// | o Redistributions of source code must retain the above copyright      |
11
// |   notice, this list of conditions and the following disclaimer.       |
12
// | o Redistributions in binary form must reproduce the above copyright   |
13
// |   notice, this list of conditions and the following disclaimer in the |
14
// |   documentation and/or other materials provided with the distribution.|
15
// | o The names of the authors may not be used to endorse or promote      |
16
// |   products derived from this software without specific prior written  |
17
// |   permission.                                                         |
18
// |                                                                       |
19
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
20
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
21
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
23
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
25
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
28
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
30
// |                                                                       |
31
// +-----------------------------------------------------------------------+
32
// | Author: Bertrand Mansion <bmansion@mamasam.com>                       |
33
// |         Stephan Schmidt <schst@php.net>                               |
34
// +-----------------------------------------------------------------------+
35
//
36
// $Id: Dispatcher.php 284686 2009-07-24 05:22:17Z clockwerx $
37
 
38
require_once 'Event/Notification.php';
39
 
40
/**
41
 * Pseudo 'static property' for Notification object
42
 * @global array $GLOBALS["_Event_Dispatcher"]
43
 */
44
$GLOBALS['_Event_Dispatcher'] = array(
45
                                  'NotificationClass' => 'Event_Notification'
46
                                     );
47
 
48
/**
49
 * Registers a global observer
50
 */
51
define('EVENT_DISPATCHER_GLOBAL', '');
52
 
53
/**
54
 * Dispatch notifications using PHP callbacks
55
 *
56
 * The Event_Dispatcher acts acts as a notification dispatch table.
57
 * It is used to notify other objects of interesting things, if
58
 * they meet certain criteria. This information is encapsulated
59
 * in {@link Event_Notification} objects. Client objects register
60
 * themselves with the Event_Dispatcher as observers of specific
61
 * notifications posted by other objects. When an event occurs,
62
 * an object posts an appropriate notification to the Event_Dispatcher.
63
 * The Event_Dispatcher dispatches a message to each
64
 * registered observer, passing the notification as the sole argument.
65
 *
66
 * The Event_Dispatcher is actually a combination of three design
67
 * patterns: the Singleton, {@link http://c2.com/cgi/wiki?MediatorPattern Mediator},
68
 * and Observer patterns. The idea behind Event_Dispatcher is borrowed from
69
 * {@link http://developer.apple.com/documentation/Cocoa/Conceptual/Notifications/index.html Apple's Cocoa framework}.
70
 *
71
 * @category   Event
72
 * @package    Event_Dispatcher
73
 * @author     Bertrand Mansion <bmansion@mamasam.com>
74
 * @author     Stephan Schmidt <schst@php.net>
75
 * @copyright  1997-2005 The PHP Group
76
 * @license    http://www.opensource.org/licenses/bsd-license.php BSD License
77
 * @version    Release: @package_version@
78
 * @link       http://pear.php.net/package/Event_Dispatcher
79
 */
80
class Event_Dispatcher
81
{
82
    /**
83
     * Registered observer callbacks
84
     * @var array
85
     * @access private
86
     */
87
    var $_ro = array();
88
 
89
    /**
90
     * Pending notifications
91
     * @var array
92
     * @access private
93
     */
94
    var $_pending = array();
95
 
96
    /**
97
     * Nested observers
98
     * @var array
99
     * @access private
100
     */
101
    var $_nestedDispatchers = array();
102
 
103
    /**
104
     * Name of the dispatcher
105
     * @var string
106
     * @access private
107
     */
108
    var $_name = null;
109
 
110
    /**
111
     * Class used for notifications
112
     * @var string
113
     * @access private
114
     */
115
    var $_notificationClass = null;
116
 
117
    /**
118
     * PHP4 constructor
119
     *
120
     * Please use {@link getInstance()} instead.
121
     *
122
     * @access  private
123
     * @param   string      Name of the notification dispatcher.
124
     */
125
    function Event_Dispatcher($name)
126
    {
127
        Event_Dispatcher::__construct($name);
128
    }
129
 
130
    /**
131
     * PHP5 constructor
132
     *
133
     * Please use {@link getInstance()} instead.
134
     *
135
     * @access  private
136
     * @param   string      Name of the notification dispatcher.
137
     */
138
    function __construct($name)
139
    {
140
        $this->_name = $name;
141
        $this->_notificationClass = $GLOBALS['_Event_Dispatcher']['NotificationClass'];
142
    }
143
 
144
    /**
145
     * Returns a notification dispatcher singleton
146
     *
147
     * There is usually no need to have more than one notification
148
     * center for an application so this is the recommended way
149
     * to get a Event_Dispatcher object.
150
     *
151
     * @param string    Name of the notification dispatcher.
152
     *                  The default notification dispatcher is named __default.
153
     *
154
     * @return object Event_Dispatcher
155
     */
156
    function &getInstance($name = '__default')
157
    {
158
        static $dispatchers = array();
159
 
160
        if (!isset($dispatchers[$name])) {
161
            $dispatchers[$name] = new Event_Dispatcher($name);
162
        }
163
 
164
        return $dispatchers[$name];
165
    }
166
 
167
    /**
168
     * Registers an observer callback
169
     *
170
     * This method registers a {@link http://www.php.net/manual/en/language.pseudo-types.php#language.types.callback callback}
171
     * which is called when the notification corresponding to the
172
     * criteria given at registration time is posted.
173
     * The criteria are the notification name and eventually the
174
     * class of the object posted with the notification.
175
     *
176
     * If there are any pending notifications corresponding to the criteria
177
     * given here, the callback will be called straight away.
178
     *
179
     * If the notification name is empty, the observer will receive all the
180
     * posted notifications. Same goes for the class name.
181
     *
182
     * @access  public
183
     * @param   mixed       A PHP callback
184
     * @param   string      Expected notification name, serves as a filter
185
     * @param   string      Expected contained object class, serves as a filter
186
     * @return void
187
     */
188
    function addObserver($callback, $nName = EVENT_DISPATCHER_GLOBAL, $class = null)
189
    {
190
        if (is_array($callback)) {
191
            if (is_object($callback[0])) {
192
                // Note : PHP4 does not allow correct object comparison so
193
                // only the class name is used for registration checks.
194
                $reg = get_class($callback[0]).'::'.$callback[1];
195
            } else {
196
                $reg = $callback[0].'::'.$callback[1];
197
            }
198
        } else {
199
            $reg = $callback;
200
        }
201
 
202
        $this->_ro[$nName][$reg] = array(
203
                                    'callback' => $callback,
204
                                    'class'    => $class
205
                                    );
206
 
207
        // Post eventual pending notifications for this observer
208
        if (isset($this->_pending[$nName])) {
209
            foreach (array_keys($this->_pending[$nName]) as $k) {
210
                $notification =& $this->_pending[$nName][$k];
211
                if (!$notification->isNotificationCancelled()) {
212
                    $objClass = get_class($notification->getNotificationObject());
213
                    if (empty($class) || strcasecmp($class, $objClass) == 0) {
214
                        call_user_func_array($callback, array(&$notification));
215
                        $notification->increaseNotificationCount();
216
                    }
217
                }
218
            }
219
        }
220
    }
221
 
222
    /**
223
     * Creates and posts a notification object
224
     *
225
     * The purpose of the optional associated object is generally to pass
226
     * the object posting the notification to the observers, so that the
227
     * observers can query the posting object for more information about
228
     * the event.
229
     *
230
     * Notifications are by default added to a pending notification list.
231
     * This way, if an observer is not registered by the time they are
232
     * posted, it will still be notified when it is added as an observer.
233
     * This behaviour can be turned off in order to make sure that only
234
     * the registered observers will be notified.
235
     *
236
     * The info array serves as a container for any kind of useful
237
     * information. It is added to the notification object and posted along.
238
     *
239
     * @access  public
240
     * @param   object      Notification associated object
241
     * @param   string      Notification name
242
     * @param   array       Optional user information
243
     * @param   bool        Whether the notification is pending
244
     * @param   bool        Whether you want the notification to bubble up
245
     * @return  object  The notification object
246
     */
247
    function &post(&$object, $nName, $info = array(), $pending = true, $bubble = true)
248
    {
249
        $notification =& new $this->_notificationClass($object, $nName, $info);
250
        return $this->postNotification($notification, $pending, $bubble);
251
    }
252
 
253
    /**
254
     * Posts the {@link Event_Notification} object
255
     *
256
     * @access  public
257
     * @param   object      The Notification object
258
     * @param   bool        Whether to post the notification immediately
259
     * @param   bool        Whether you want the notification to bubble up
260
     * @see Event_Dispatcher::post()
261
     * @return  object  The notification object
262
     */
263
    function &postNotification(&$notification, $pending = true, $bubble = true)
264
    {
265
        $nName = $notification->getNotificationName();
266
        if ($pending === true) {
267
            $this->_pending[$nName][] =& $notification;
268
        }
269
        $objClass = get_class($notification->getNotificationObject());
270
 
271
        // Find the registered observers
272
        if (isset($this->_ro[$nName])) {
273
            foreach (array_keys($this->_ro[$nName]) as $k) {
274
                $rObserver =& $this->_ro[$nName][$k];
275
                if ($notification->isNotificationCancelled()) {
276
                    return $notification;
277
                }
278
                if (empty($rObserver['class']) ||
279
                	strcasecmp($rObserver['class'], $objClass) == 0) {
280
                    call_user_func_array($rObserver['callback'], array(&$notification));
281
                    $notification->increaseNotificationCount();
282
                }
283
            }
284
        }
285
 
286
        // Notify globally registered observers
287
        if (isset($this->_ro[EVENT_DISPATCHER_GLOBAL])) {
288
            foreach (array_keys($this->_ro[EVENT_DISPATCHER_GLOBAL]) as $k) {
289
                $rObserver =& $this->_ro[EVENT_DISPATCHER_GLOBAL][$k];
290
                if ($notification->isNotificationCancelled()) {
291
                    return $notification;
292
                }
293
                if (empty($rObserver['class']) ||
294
                	strcasecmp($rObserver['class'], $objClass) == 0) {
295
                    call_user_func_array($rObserver['callback'], array(&$notification));
296
                    $notification->increaseNotificationCount();
297
                }
298
            }
299
        }
300
 
301
        if ($bubble === false) {
302
            return $notification;
303
        }
304
 
305
        // Notify in nested dispatchers
306
        foreach (array_keys($this->_nestedDispatchers) as $nested) {
307
            $notification =& $this->_nestedDispatchers[$nested]->postNotification($notification, $pending);
308
        }
309
 
310
        return $notification;
311
    }
312
 
313
    /**
314
     * Removes a registered observer that correspond to the given criteria
315
     *
316
     * @access  public
317
     * @param   mixed       A PHP callback
318
     * @param   string      Notification name
319
     * @param   string      Contained object class
320
     * @return  bool    True if an observer was removed, false otherwise
321
     */
322
    function removeObserver($callback, $nName = EVENT_DISPATCHER_GLOBAL, $class = null)
323
    {
324
        if (is_array($callback)) {
325
            if (is_object($callback[0])) {
326
                $reg = get_class($callback[0]).'::'.$callback[1];
327
            } else {
328
                $reg = $callback[0].'::'.$callback[1];
329
            }
330
        } else {
331
            $reg = $callback;
332
        }
333
 
334
        $removed = false;
335
        if (isset($this->_ro[$nName][$reg])) {
336
            if (!empty($class)) {
337
                if (strcasecmp($this->_ro[$nName][$reg]['class'], $class) == 0) {
338
                    unset($this->_ro[$nName][$reg]);
339
                    $removed = true;
340
                }
341
            } else {
342
                unset($this->_ro[$nName][$reg]);
343
                $removed = true;
344
            }
345
        }
346
 
347
        if (isset($this->_ro[$nName]) && count($this->_ro[$nName]) == 0) {
348
            unset($this->_ro[$nName]);
349
        }
350
        return $removed;
351
    }
352
 
353
   /**
354
    * Check, whether the specified observer has been registered with the
355
    * dispatcher
356
    *
357
     * @access  public
358
     * @param   mixed       A PHP callback
359
     * @param   string      Notification name
360
     * @param   string      Contained object class
361
     * @return  bool        True if the observer has been registered, false otherwise
362
    */
363
    function observerRegistered($callback, $nName = EVENT_DISPATCHER_GLOBAL, $class = null)
364
    {
365
        if (is_array($callback)) {
366
            if (is_object($callback[0])) {
367
                $reg = get_class($callback[0]).'::'.$callback[1];
368
            } else {
369
                $reg = $callback[0].'::'.$callback[1];
370
            }
371
        } else {
372
            $reg = $callback;
373
        }
374
 
375
        if (!isset($this->_ro[$nName][$reg])) {
376
            return false;
377
        }
378
        if (empty($class)) {
379
            return true;
380
        }
381
        if (strcasecmp($this->_ro[$nName][$reg]['class'], $class) == 0) {
382
            return true;
383
        }
384
        return false;
385
    }
386
 
387
   /**
388
    * Get all observers, that have been registered for a notification
389
    *
390
     * @access  public
391
     * @param   string      Notification name
392
     * @param   string      Contained object class
393
     * @return  array       List of all observers
394
    */
395
    function getObservers($nName = EVENT_DISPATCHER_GLOBAL, $class = null)
396
    {
397
        $observers = array();
398
        if (!isset($this->_ro[$nName])) {
399
            return $observers;
400
        }
401
        foreach ($this->_ro[$nName] as $reg => $observer) {
402
        	if ($class == null || $observer['class'] == null ||  strcasecmp($observer['class'], $class) == 0) {
403
        		$observers[] = $reg;
404
        	}
405
        }
406
        return $observers;
407
    }
408
 
409
    /**
410
     * Get the name of the dispatcher.
411
     *
412
     * The name is the unique identifier of a dispatcher.
413
     *
414
     * @access   public
415
     * @return   string     name of the dispatcher
416
     */
417
    function getName()
418
    {
419
        return $this->_name;
420
    }
421
 
422
    /**
423
     * add a new nested dispatcher
424
     *
425
     * Notifications will be broadcasted to this dispatcher as well, which
426
     * allows you to create event bubbling.
427
     *
428
     * @access   public
429
     * @param    Event_Dispatcher    The nested dispatcher
430
     */
431
    function addNestedDispatcher(&$dispatcher)
432
    {
433
        $name = $dispatcher->getName();
434
        $this->_nestedDispatchers[$name] =& $dispatcher;
435
    }
436
 
437
   /**
438
    * remove a nested dispatcher
439
    *
440
    * @access   public
441
    * @param    Event_Dispatcher    Dispatcher to remove
442
    * @return   boolean
443
    */
444
    function removeNestedDispatcher($dispatcher)
445
    {
446
        if (is_object($dispatcher)) {
447
            $dispatcher = $dispatcher->getName();
448
        }
449
        if (!isset($this->_nestedDispatchers[$dispatcher])) {
450
            return false;
451
        }
452
        unset($this->_nestedDispatchers[$dispatcher]);
453
        return true;
454
    }
455
 
456
    /**
457
     * Changes the class used for notifications
458
     *
459
     * You may call this method on an object to change it for a single
460
     * dispatcher or statically, to set the default for all dispatchers
461
     * that will be created.
462
     *
463
     * @access   public
464
     * @param    string     name of the notification class
465
     * @return   boolean
466
     */
467
    function setNotificationClass($class)
468
    {
469
        if (isset($this) && is_a($this, 'Event_Dispatcher')) {
470
            $this->_notificationClass = $class;
471
            return true;
472
        }
473
        $GLOBALS['_Event_Dispatcher']['NotificationClass'] = $class;
474
        return true;
475
    }
476
 
477
}
478
?>