1<?php
2/**
3 * Zend Framework (http://framework.zend.com/)
4 *
5 * @link      http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license   http://framework.zend.com/license/new-bsd New BSD License
8 */
9
10namespace Zend\EventManager;
11
12use Zend\Stdlib\CallbackHandler;
13use Zend\Stdlib\PriorityQueue;
14
15/**
16 * Shared/contextual EventManager
17 *
18 * Allows attaching to EMs composed by other classes without having an instance first.
19 * The assumption is that the SharedEventManager will be injected into EventManager
20 * instances, and then queried for additional listeners when triggering an event.
21 */
22class SharedEventManager implements
23    SharedEventAggregateAwareInterface,
24    SharedEventManagerInterface
25{
26    /**
27     * Identifiers with event connections
28     * @var array
29     */
30    protected $identifiers = array();
31
32    /**
33     * Attach a listener to an event
34     *
35     * Allows attaching a callback to an event offered by one or more
36     * identifying components. As an example, the following connects to the
37     * "getAll" event of both an AbstractResource and EntityResource:
38     *
39     * <code>
40     * $sharedEventManager = new SharedEventManager();
41     * $sharedEventManager->attach(
42     *     array('My\Resource\AbstractResource', 'My\Resource\EntityResource'),
43     *     'getAll',
44     *     function ($e) use ($cache) {
45     *         if (!$id = $e->getParam('id', false)) {
46     *             return;
47     *         }
48     *         if (!$data = $cache->load(get_class($resource) . '::getOne::' . $id )) {
49     *             return;
50     *         }
51     *         return $data;
52     *     }
53     * );
54     * </code>
55     *
56     * @param  string|array $id Identifier(s) for event emitting component(s)
57     * @param  string $event
58     * @param  callable $callback PHP Callback
59     * @param  int $priority Priority at which listener should execute
60     * @return CallbackHandler|array Either CallbackHandler or array of CallbackHandlers
61     */
62    public function attach($id, $event, $callback, $priority = 1)
63    {
64        $ids = (array) $id;
65        $listeners = array();
66        foreach ($ids as $id) {
67            if (!array_key_exists($id, $this->identifiers)) {
68                $this->identifiers[$id] = new EventManager($id);
69            }
70            $listeners[] = $this->identifiers[$id]->attach($event, $callback, $priority);
71        }
72        if (count($listeners) > 1) {
73            return $listeners;
74        }
75        return $listeners[0];
76    }
77
78    /**
79     * Attach a listener aggregate
80     *
81     * Listener aggregates accept an EventManagerInterface instance, and call attachShared()
82     * one or more times, typically to attach to multiple events using local
83     * methods.
84     *
85     * @param  SharedListenerAggregateInterface $aggregate
86     * @param  int $priority If provided, a suggested priority for the aggregate to use
87     * @return mixed return value of {@link ListenerAggregateInterface::attachShared()}
88     */
89    public function attachAggregate(SharedListenerAggregateInterface $aggregate, $priority = 1)
90    {
91        return $aggregate->attachShared($this, $priority);
92    }
93
94    /**
95     * Detach a listener from an event offered by a given resource
96     *
97     * @param  string|int $id
98     * @param  CallbackHandler $listener
99     * @return bool Returns true if event and listener found, and unsubscribed; returns false if either event or listener not found
100     */
101    public function detach($id, CallbackHandler $listener)
102    {
103        if (!array_key_exists($id, $this->identifiers)) {
104            return false;
105        }
106        return $this->identifiers[$id]->detach($listener);
107    }
108
109    /**
110     * Detach a listener aggregate
111     *
112     * Listener aggregates accept a SharedEventManagerInterface instance, and call detachShared()
113     * of all previously attached listeners.
114     *
115     * @param  SharedListenerAggregateInterface $aggregate
116     * @return mixed return value of {@link SharedListenerAggregateInterface::detachShared()}
117     */
118    public function detachAggregate(SharedListenerAggregateInterface $aggregate)
119    {
120        return $aggregate->detachShared($this);
121    }
122
123    /**
124     * Retrieve all registered events for a given resource
125     *
126     * @param  string|int $id
127     * @return array
128     */
129    public function getEvents($id)
130    {
131        if (!array_key_exists($id, $this->identifiers)) {
132            //Check if there are any id wildcards listeners
133            if ('*' != $id && array_key_exists('*', $this->identifiers)) {
134                return $this->identifiers['*']->getEvents();
135            }
136            return false;
137        }
138        return $this->identifiers[$id]->getEvents();
139    }
140
141    /**
142     * Retrieve all listeners for a given identifier and event
143     *
144     * @param  string|int $id
145     * @param  string|int $event
146     * @return false|PriorityQueue
147     */
148    public function getListeners($id, $event)
149    {
150        if (!array_key_exists($id, $this->identifiers)) {
151            return false;
152        }
153        return $this->identifiers[$id]->getListeners($event);
154    }
155
156    /**
157     * Clear all listeners for a given identifier, optionally for a specific event
158     *
159     * @param  string|int $id
160     * @param  null|string $event
161     * @return bool
162     */
163    public function clearListeners($id, $event = null)
164    {
165        if (!array_key_exists($id, $this->identifiers)) {
166            return false;
167        }
168
169        if (null === $event) {
170            unset($this->identifiers[$id]);
171            return true;
172        }
173
174        return $this->identifiers[$id]->clearListeners($event);
175    }
176}
177