Verzeichnisstruktur phpBB-3.2.0
- Veröffentlicht
- 06.01.2017
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
EventManager.php
001 <?php
002 /**
003 * Zend Framework (http://framework.zend.com/)
004 *
005 * @link http://github.com/zendframework/zf2 for the canonical source repository
006 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
007 * @license http://framework.zend.com/license/new-bsd New BSD License
008 */
009
010 namespace Zend\EventManager;
011
012 use ArrayAccess;
013 use ArrayObject;
014 use Traversable;
015 use Zend\Stdlib\CallbackHandler;
016 use Zend\Stdlib\PriorityQueue;
017
018 /**
019 * Event manager: notification system
020 *
021 * Use the EventManager when you want to create a per-instance notification
022 * system for your objects.
023 */
024 class EventManager implements EventManagerInterface
025 {
026 /**
027 * Subscribed events and their listeners
028 * @var array Array of PriorityQueue objects
029 */
030 protected $events = array();
031
032 /**
033 * @var string Class representing the event being emitted
034 */
035 protected $eventClass = 'Zend\EventManager\Event';
036
037 /**
038 * Identifiers, used to pull shared signals from SharedEventManagerInterface instance
039 * @var array
040 */
041 protected $identifiers = array();
042
043 /**
044 * Shared event manager
045 * @var false|null|SharedEventManagerInterface
046 */
047 protected $sharedManager = null;
048
049 /**
050 * Constructor
051 *
052 * Allows optionally specifying identifier(s) to use to pull signals from a
053 * SharedEventManagerInterface.
054 *
055 * @param null|string|int|array|Traversable $identifiers
056 */
057 public function __construct($identifiers = null)
058 {
059 $this->setIdentifiers($identifiers);
060 }
061
062 /**
063 * Set the event class to utilize
064 *
065 * @param string $class
066 * @return EventManager
067 */
068 public function setEventClass($class)
069 {
070 $this->eventClass = $class;
071 return $this;
072 }
073
074 /**
075 * Set shared event manager
076 *
077 * @param SharedEventManagerInterface $sharedEventManager
078 * @return EventManager
079 */
080 public function setSharedManager(SharedEventManagerInterface $sharedEventManager)
081 {
082 $this->sharedManager = $sharedEventManager;
083 StaticEventManager::setInstance($sharedEventManager);
084 return $this;
085 }
086
087 /**
088 * Remove any shared event manager currently attached
089 *
090 * @return void
091 */
092 public function unsetSharedManager()
093 {
094 $this->sharedManager = false;
095 }
096
097 /**
098 * Get shared event manager
099 *
100 * If one is not defined, but we have a static instance in
101 * StaticEventManager, that one will be used and set in this instance.
102 *
103 * If none is available in the StaticEventManager, a boolean false is
104 * returned.
105 *
106 * @return false|SharedEventManagerInterface
107 */
108 public function getSharedManager()
109 {
110 // "false" means "I do not want a shared manager; don't try and fetch one"
111 if (false === $this->sharedManager
112 || $this->sharedManager instanceof SharedEventManagerInterface
113 ) {
114 return $this->sharedManager;
115 }
116
117 if (!StaticEventManager::hasInstance()) {
118 return false;
119 }
120
121 $this->sharedManager = StaticEventManager::getInstance();
122 return $this->sharedManager;
123 }
124
125 /**
126 * Get the identifier(s) for this EventManager
127 *
128 * @return array
129 */
130 public function getIdentifiers()
131 {
132 return $this->identifiers;
133 }
134
135 /**
136 * Set the identifiers (overrides any currently set identifiers)
137 *
138 * @param string|int|array|Traversable $identifiers
139 * @return EventManager Provides a fluent interface
140 */
141 public function setIdentifiers($identifiers)
142 {
143 if (is_array($identifiers) || $identifiers instanceof Traversable) {
144 $this->identifiers = array_unique((array) $identifiers);
145 } elseif ($identifiers !== null) {
146 $this->identifiers = array($identifiers);
147 }
148 return $this;
149 }
150
151 /**
152 * Add some identifier(s) (appends to any currently set identifiers)
153 *
154 * @param string|int|array|Traversable $identifiers
155 * @return EventManager Provides a fluent interface
156 */
157 public function addIdentifiers($identifiers)
158 {
159 if (is_array($identifiers) || $identifiers instanceof Traversable) {
160 $this->identifiers = array_unique(array_merge($this->identifiers, (array) $identifiers));
161 } elseif ($identifiers !== null) {
162 $this->identifiers = array_unique(array_merge($this->identifiers, array($identifiers)));
163 }
164 return $this;
165 }
166
167 /**
168 * Trigger all listeners for a given event
169 *
170 * @param string|EventInterface $event
171 * @param string|object $target Object calling emit, or symbol describing target (such as static method name)
172 * @param array|ArrayAccess $argv Array of arguments; typically, should be associative
173 * @param null|callable $callback Trigger listeners until return value of this callback evaluate to true
174 * @return ResponseCollection All listener return values
175 * @throws Exception\InvalidCallbackException
176 */
177 public function trigger($event, $target = null, $argv = array(), $callback = null)
178 {
179 if ($event instanceof EventInterface) {
180 $e = $event;
181 $event = $e->getName();
182 $callback = $target;
183 } elseif ($target instanceof EventInterface) {
184 $e = $target;
185 $e->setName($event);
186 $callback = $argv;
187 } elseif ($argv instanceof EventInterface) {
188 $e = $argv;
189 $e->setName($event);
190 $e->setTarget($target);
191 } else {
192 $e = new $this->eventClass();
193 $e->setName($event);
194 $e->setTarget($target);
195 $e->setParams($argv);
196 }
197
198 if ($callback && !is_callable($callback)) {
199 throw new Exception\InvalidCallbackException('Invalid callback provided');
200 }
201
202 // Initial value of stop propagation flag should be false
203 $e->stopPropagation(false);
204
205 return $this->triggerListeners($event, $e, $callback);
206 }
207
208 /**
209 * Trigger listeners until return value of one causes a callback to
210 * evaluate to true
211 *
212 * Triggers listeners until the provided callback evaluates the return
213 * value of one as true, or until all listeners have been executed.
214 *
215 * @param string|EventInterface $event
216 * @param string|object $target Object calling emit, or symbol describing target (such as static method name)
217 * @param array|ArrayAccess $argv Array of arguments; typically, should be associative
218 * @param callable $callback
219 * @return ResponseCollection
220 * @deprecated Please use trigger()
221 * @throws Exception\InvalidCallbackException if invalid callable provided
222 */
223 public function triggerUntil($event, $target, $argv = null, $callback = null)
224 {
225 trigger_error(
226 'This method is deprecated and will be removed in the future. Please use trigger() instead.',
227 E_USER_DEPRECATED
228 );
229 return $this->trigger($event, $target, $argv, $callback);
230 }
231
232 /**
233 * Attach a listener to an event
234 *
235 * The first argument is the event, and the next argument describes a
236 * callback that will respond to that event. A CallbackHandler instance
237 * describing the event listener combination will be returned.
238 *
239 * The last argument indicates a priority at which the event should be
240 * executed. By default, this value is 1; however, you may set it for any
241 * integer value. Higher values have higher priority (i.e., execute first).
242 *
243 * You can specify "*" for the event name. In such cases, the listener will
244 * be triggered for every event.
245 *
246 * @param string|array|ListenerAggregateInterface $event An event or array of event names. If a ListenerAggregateInterface, proxies to {@link attachAggregate()}.
247 * @param callable|int $callback If string $event provided, expects PHP callback; for a ListenerAggregateInterface $event, this will be the priority
248 * @param int $priority If provided, the priority at which to register the callable
249 * @return CallbackHandler|mixed CallbackHandler if attaching callable (to allow later unsubscribe); mixed if attaching aggregate
250 * @throws Exception\InvalidArgumentException
251 */
252 public function attach($event, $callback = null, $priority = 1)
253 {
254 // Proxy ListenerAggregateInterface arguments to attachAggregate()
255 if ($event instanceof ListenerAggregateInterface) {
256 return $this->attachAggregate($event, $callback);
257 }
258
259 // Null callback is invalid
260 if (null === $callback) {
261 throw new Exception\InvalidArgumentException(sprintf(
262 '%s: expects a callback; none provided',
263 __METHOD__
264 ));
265 }
266
267 // Array of events should be registered individually, and return an array of all listeners
268 if (is_array($event)) {
269 $listeners = array();
270 foreach ($event as $name) {
271 $listeners[] = $this->attach($name, $callback, $priority);
272 }
273 return $listeners;
274 }
275
276 // If we don't have a priority queue for the event yet, create one
277 if (empty($this->events[$event])) {
278 $this->events[$event] = new PriorityQueue();
279 }
280
281 // Create a callback handler, setting the event and priority in its metadata
282 $listener = new CallbackHandler($callback, array('event' => $event, 'priority' => $priority));
283
284 // Inject the callback handler into the queue
285 $this->events[$event]->insert($listener, $priority);
286 return $listener;
287 }
288
289 /**
290 * Attach a listener aggregate
291 *
292 * Listener aggregates accept an EventManagerInterface instance, and call attach()
293 * one or more times, typically to attach to multiple events using local
294 * methods.
295 *
296 * @param ListenerAggregateInterface $aggregate
297 * @param int $priority If provided, a suggested priority for the aggregate to use
298 * @return mixed return value of {@link ListenerAggregateInterface::attach()}
299 */
300 public function attachAggregate(ListenerAggregateInterface $aggregate, $priority = 1)
301 {
302 return $aggregate->attach($this, $priority);
303 }
304
305 /**
306 * Unsubscribe a listener from an event
307 *
308 * @param CallbackHandler|ListenerAggregateInterface $listener
309 * @return bool Returns true if event and listener found, and unsubscribed; returns false if either event or listener not found
310 * @throws Exception\InvalidArgumentException if invalid listener provided
311 */
312 public function detach($listener)
313 {
314 if ($listener instanceof ListenerAggregateInterface) {
315 return $this->detachAggregate($listener);
316 }
317
318 if (!$listener instanceof CallbackHandler) {
319 throw new Exception\InvalidArgumentException(sprintf(
320 '%s: expected a ListenerAggregateInterface or CallbackHandler; received "%s"',
321 __METHOD__,
322 (is_object($listener) ? get_class($listener) : gettype($listener))
323 ));
324 }
325
326 $event = $listener->getMetadatum('event');
327 if (!$event || empty($this->events[$event])) {
328 return false;
329 }
330 $return = $this->events[$event]->remove($listener);
331 if (!$return) {
332 return false;
333 }
334 if (!count($this->events[$event])) {
335 unset($this->events[$event]);
336 }
337 return true;
338 }
339
340 /**
341 * Detach a listener aggregate
342 *
343 * Listener aggregates accept an EventManagerInterface instance, and call detach()
344 * of all previously attached listeners.
345 *
346 * @param ListenerAggregateInterface $aggregate
347 * @return mixed return value of {@link ListenerAggregateInterface::detach()}
348 */
349 public function detachAggregate(ListenerAggregateInterface $aggregate)
350 {
351 return $aggregate->detach($this);
352 }
353
354 /**
355 * Retrieve all registered events
356 *
357 * @return array
358 */
359 public function getEvents()
360 {
361 return array_keys($this->events);
362 }
363
364 /**
365 * Retrieve all listeners for a given event
366 *
367 * @param string $event
368 * @return PriorityQueue
369 */
370 public function getListeners($event)
371 {
372 if (!array_key_exists($event, $this->events)) {
373 return new PriorityQueue();
374 }
375 return $this->events[$event];
376 }
377
378 /**
379 * Clear all listeners for a given event
380 *
381 * @param string $event
382 * @return void
383 */
384 public function clearListeners($event)
385 {
386 if (!empty($this->events[$event])) {
387 unset($this->events[$event]);
388 }
389 }
390
391 /**
392 * Prepare arguments
393 *
394 * Use this method if you want to be able to modify arguments from within a
395 * listener. It returns an ArrayObject of the arguments, which may then be
396 * passed to trigger().
397 *
398 * @param array $args
399 * @return ArrayObject
400 */
401 public function prepareArgs(array $args)
402 {
403 return new ArrayObject($args);
404 }
405
406 /**
407 * Trigger listeners
408 *
409 * Actual functionality for triggering listeners, to which trigger() delegate.
410 *
411 * @param string $event Event name
412 * @param EventInterface $e
413 * @param null|callable $callback
414 * @return ResponseCollection
415 */
416 protected function triggerListeners($event, EventInterface $e, $callback = null)
417 {
418 $responses = new ResponseCollection;
419 $listeners = $this->getListeners($event);
420
421 // Add shared/wildcard listeners to the list of listeners,
422 // but don't modify the listeners object
423 $sharedListeners = $this->getSharedListeners($event);
424 $sharedWildcardListeners = $this->getSharedListeners('*');
425 $wildcardListeners = $this->getListeners('*');
426 if (count($sharedListeners) || count($sharedWildcardListeners) || count($wildcardListeners)) {
427 $listeners = clone $listeners;
428
429 // Shared listeners on this specific event
430 $this->insertListeners($listeners, $sharedListeners);
431
432 // Shared wildcard listeners
433 $this->insertListeners($listeners, $sharedWildcardListeners);
434
435 // Add wildcard listeners
436 $this->insertListeners($listeners, $wildcardListeners);
437 }
438
439 foreach ($listeners as $listener) {
440 $listenerCallback = $listener->getCallback();
441
442 // Trigger the listener's callback, and push its result onto the
443 // response collection
444 $responses->push(call_user_func($listenerCallback, $e));
445
446 // If the event was asked to stop propagating, do so
447 if ($e->propagationIsStopped()) {
448 $responses->setStopped(true);
449 break;
450 }
451
452 // If the result causes our validation callback to return true,
453 // stop propagation
454 if ($callback && call_user_func($callback, $responses->last())) {
455 $responses->setStopped(true);
456 break;
457 }
458 }
459
460 return $responses;
461 }
462
463 /**
464 * Get list of all listeners attached to the shared event manager for
465 * identifiers registered by this instance
466 *
467 * @param string $event
468 * @return array
469 */
470 protected function getSharedListeners($event)
471 {
472 if (!$sharedManager = $this->getSharedManager()) {
473 return array();
474 }
475
476 $identifiers = $this->getIdentifiers();
477 //Add wildcard id to the search, if not already added
478 if (!in_array('*', $identifiers)) {
479 $identifiers[] = '*';
480 }
481 $sharedListeners = array();
482
483 foreach ($identifiers as $id) {
484 if (!$listeners = $sharedManager->getListeners($id, $event)) {
485 continue;
486 }
487
488 if (!is_array($listeners) && !($listeners instanceof Traversable)) {
489 continue;
490 }
491
492 foreach ($listeners as $listener) {
493 if (!$listener instanceof CallbackHandler) {
494 continue;
495 }
496 $sharedListeners[] = $listener;
497 }
498 }
499
500 return $sharedListeners;
501 }
502
503 /**
504 * Add listeners to the master queue of listeners
505 *
506 * Used to inject shared listeners and wildcard listeners.
507 *
508 * @param PriorityQueue $masterListeners
509 * @param array|Traversable $listeners
510 * @return void
511 */
512 protected function insertListeners($masterListeners, $listeners)
513 {
514 foreach ($listeners as $listener) {
515 $priority = $listener->getMetadatum('priority');
516 if (null === $priority) {
517 $priority = 1;
518 } elseif (is_array($priority)) {
519 // If we have an array, likely using PriorityQueue. Grab first
520 // element of the array, as that's the actual priority.
521 $priority = array_shift($priority);
522 }
523 $masterListeners->insert($listener, $priority);
524 }
525 }
526 }
527