1<?php
2
3/**
4 * Hoa
5 *
6 *
7 * @license
8 *
9 * New BSD License
10 *
11 * Copyright © 2007-2017, Hoa community. All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions are met:
15 *     * Redistributions of source code must retain the above copyright
16 *       notice, this list of conditions and the following disclaimer.
17 *     * Redistributions in binary form must reproduce the above copyright
18 *       notice, this list of conditions and the following disclaimer in the
19 *       documentation and/or other materials provided with the distribution.
20 *     * Neither the name of the Hoa nor the names of its contributors may be
21 *       used to endorse or promote products derived from this software without
22 *       specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37namespace Hoa\Event;
38
39/**
40 * Class \Hoa\Event\Listener.
41 *
42 * A contrario of events, listeners are synchronous, identified at use and
43 * useful for close interactions between one or some components.
44 *
45 * @copyright  Copyright © 2007-2017 Hoa community
46 * @license    New BSD License
47 */
48class Listener
49{
50    /**
51     * Source of listener (for Bucket).
52     *
53     * @var \Hoa\Event\Listenable
54     */
55    protected $_source    = null;
56
57    /**
58     * All listener IDs and associated listeners.
59     *
60     * @var array
61     */
62    protected $_callables = [];
63
64
65
66    /**
67     * Build a listener.
68     *
69     * @param   \Hoa\Event\Listenable  $source    Source (for Bucket).
70     * @param   array                  $ids       Accepted ID.
71     */
72    public function __construct(Listenable $source, array $ids)
73    {
74        $this->_source = $source;
75        $this->addIds($ids);
76
77        return;
78    }
79
80    /**
81     * Add acceptable ID (or reset).
82     *
83     * @param   array  $ids    Accepted ID.
84     * @return  void
85     */
86    public function addIds(array $ids)
87    {
88        foreach ($ids as $id) {
89            $this->_callables[$id] = [];
90        }
91
92        return;
93    }
94
95    /**
96     * Attach a callable to a listenable component.
97     *
98     * @param   string  $listenerId    Listener ID.
99     * @param   mixed   $callable      Callable.
100     * @return  \Hoa\Event\Listener
101     * @throws  \Hoa\Event\Exception
102     */
103    public function attach($listenerId, $callable)
104    {
105        if (false === $this->listenerExists($listenerId)) {
106            throw new Exception(
107                'Cannot listen %s because it is not defined.',
108                0,
109                $listenerId
110            );
111        }
112
113        $callable                                            = xcallable($callable);
114        $this->_callables[$listenerId][$callable->getHash()] = $callable;
115
116        return $this;
117    }
118
119    /**
120     * Detach a callable from a listenable component.
121     *
122     * @param   string  $listenerId    Listener ID.
123     * @param   mixed   $callable      Callable.
124     * @return  \Hoa\Event\Listener
125     */
126    public function detach($listenerId, $callable)
127    {
128        unset($this->_callables[$listenerId][xcallable($callable)->getHash()]);
129
130        return $this;
131    }
132
133    /**
134     * Detach all callables from a listenable component.
135     *
136     * @param  string  $listenerId    Listener ID.
137     * @return \Hoa\Event\Listener
138     */
139    public function detachAll($listenerId)
140    {
141        unset($this->_callables[$listenerId]);
142
143        return $this;
144    }
145
146    /**
147     * Check if a listener exists.
148     *
149     * @param   string  $listenerId    Listener ID.
150     * @return  bool
151     */
152    public function listenerExists($listenerId)
153    {
154        return array_key_exists($listenerId, $this->_callables);
155    }
156
157    /**
158     * Send/fire a bucket to a listener.
159     *
160     * @param   string             $listenerId    Listener ID.
161     * @param   \Hoa\Event\Bucket  $data          Data.
162     * @return  array
163     * @throws  \Hoa\Event\Exception
164     */
165    public function fire($listenerId, Bucket $data)
166    {
167        if (false === $this->listenerExists($listenerId)) {
168            throw new Exception(
169                'Cannot fire on %s because it is not defined.',
170                1,
171                $listenerId
172            );
173        }
174
175        $data->setSource($this->_source);
176        $out = [];
177
178        foreach ($this->_callables[$listenerId] as $callable) {
179            $out[] = $callable($data);
180        }
181
182        return $out;
183    }
184}
185