1<?php
2
3/**
4 * Copyright 2014 Fabian Grutschus. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without modification,
7 * are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice, this
10 *   list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 *   this list of conditions and the following disclaimer in the documentation
14 *   and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 * The views and conclusions contained in the software and documentation are those
28 * of the authors and should not be interpreted as representing official policies,
29 * either expressed or implied, of the copyright holders.
30 *
31 * @author    Fabian Grutschus <f.grutschus@lubyte.de>
32 * @copyright 2014 Fabian Grutschus. All rights reserved.
33 * @license   BSD
34 * @link      http://github.com/fabiang/xmpp
35 */
36
37namespace Fabiang\Xmpp\Event;
38
39use Fabiang\Xmpp\Exception\InvalidArgumentException;
40
41/**
42 * Event manager.
43 *
44 * The EventManager holds and triggers events.
45 *
46 * @package Xmpp\Event
47 */
48class EventManager implements EventManagerInterface
49{
50
51    const WILDCARD = '*';
52
53    /**
54     * Attached events.
55     *
56     * @var array
57     */
58    protected $events = array(self::WILDCARD => array());
59
60    /**
61     * Event object.
62     *
63     * @var EventInterface
64     */
65    protected $eventObject;
66
67    /**
68     * Constructor sets default event object.
69     *
70     * @param EventInterface $eventObject Event object
71     */
72    public function __construct(EventInterface $eventObject = null)
73    {
74        if (null === $eventObject) {
75            $eventObject = new Event;
76        }
77
78        $this->eventObject = $eventObject;
79    }
80
81    /**
82     * {@inheritDoc}
83     */
84    public function attach($event, $callback)
85    {
86        if (!is_callable($callback, true)) {
87            throw new InvalidArgumentException(
88                'Second argument of "' . __CLASS__ . '"::attach must be a valid callback'
89            );
90        }
91
92        if (!isset($this->events[$event])) {
93            $this->events[$event] = array();
94        }
95
96        if (!in_array($callback, $this->events[$event], true)) {
97            $this->events[$event][] = $callback;
98        }
99    }
100
101    /**
102     * {@inheritDoc}
103     */
104    public function trigger($event, $caller, array $parameters)
105    {
106        if (empty($this->events[$event]) && empty($this->events[self::WILDCARD])) {
107            return;
108        }
109
110        $events = array();
111        if (!empty($this->events[$event])) {
112            $events = $this->events[$event];
113        }
114
115        $callbacks = array_merge($events, $this->events[self::WILDCARD]);
116        $previous  = array();
117
118        $eventObject = clone $this->getEventObject();
119        $eventObject->setName($event);
120        $eventObject->setTarget($caller);
121        $eventObject->setParameters($parameters);
122
123        do {
124            $current = array_shift($callbacks);
125
126            call_user_func($current, $eventObject);
127
128            $previous[]  = $current;
129            $eventObject = clone $eventObject;
130            $eventObject->setEventStack($previous);
131        } while (count($callbacks) > 0);
132    }
133
134    /**
135     * {@inheritDoc}
136     */
137    public function getEventObject()
138    {
139        return $this->eventObject;
140    }
141
142    /**
143     * {@inheritDoc}
144     */
145    public function setEventObject(EventInterface $eventObject)
146    {
147        $this->eventObject = $eventObject;
148        return $this;
149    }
150
151    /**
152     * Return list of events.
153     *
154     * @return array
155     */
156    public function getEventList()
157    {
158        return $this->events;
159    }
160}
161