1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Webino™ (http://webino.sk) |
4
|
|
|
* |
5
|
|
|
* @link https://github.com/webino for the canonical source repository |
6
|
|
|
* @copyright Copyright (c) 2015-2017 Webino, s.r.o. (http://webino.sk) |
7
|
|
|
* @author Peter Bačinský <[email protected]> |
8
|
|
|
* @license BSD-3-Clause |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace WebinoAppLib\Application\Traits; |
12
|
|
|
|
13
|
|
|
use WebinoAppLib\Application; |
14
|
|
|
use WebinoAppLib\Exception; |
15
|
|
|
use WebinoAppLib\Log; |
16
|
|
|
use Zend\EventManager\Event; |
17
|
|
|
use Zend\EventManager\EventManagerInterface; |
18
|
|
|
use Zend\EventManager\ListenerAggregateInterface; |
19
|
|
|
use Zend\Stdlib\CallbackHandler; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* Trait EventEmitterTrait |
23
|
|
|
* @TODO redesign, remove CallbackHandler support, cause is deprecated by Zend |
24
|
|
|
*/ |
25
|
|
|
trait EventsTrait |
26
|
|
|
{ |
27
|
|
|
/** |
28
|
|
|
* @var EventManagerInterface |
29
|
|
|
*/ |
30
|
|
|
private $events; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @var \Zend\EventManager\ListenerAggregateInterface[] |
34
|
|
|
*/ |
35
|
|
|
protected $listeners = []; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @param string $service |
39
|
|
|
* @return mixed |
40
|
|
|
*/ |
41
|
|
|
abstract public function get($service); |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @param mixed $service |
45
|
|
|
* @param null $factory |
46
|
|
|
* @return $this |
47
|
|
|
*/ |
48
|
|
|
abstract public function set($service, $factory = null); |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @param $service |
52
|
|
|
* @return bool |
53
|
|
|
*/ |
54
|
|
|
abstract public function has($service); |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @param mixed|\WebinoLogLib\Message\MessageInterface $level |
58
|
|
|
* @param mixed ...$args |
59
|
|
|
* @return \Psr\Log\LoggerInterface |
60
|
|
|
*/ |
61
|
|
|
abstract public function log($level = null, ...$args); |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Require service from services into application |
65
|
|
|
* |
66
|
|
|
* @param string $service Service name |
67
|
|
|
* @throws Exception\DomainException Unable to get service |
68
|
|
|
*/ |
69
|
|
|
abstract protected function requireService($service); |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @return EventManagerInterface |
73
|
|
|
*/ |
74
|
|
|
public function getEvents() |
75
|
|
|
{ |
76
|
|
|
if (null === $this->events) { |
77
|
|
|
$this->requireService(Application::EVENTS); |
78
|
|
|
} |
79
|
|
|
return $this->events; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* @param EventManagerInterface $events |
84
|
|
|
*/ |
85
|
|
|
protected function setEvents(EventManagerInterface $events) |
86
|
|
|
{ |
87
|
|
|
$this->events = $events; |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Attach a listener to an event |
92
|
|
|
* |
93
|
|
|
* @param string|\Zend\EventManager\ListenerAggregateInterface $event |
94
|
|
|
* @param string|callable|int $callback If string $event provided, expects PHP callback; |
95
|
|
|
* @param int $priority Invocation priority |
96
|
|
|
* @return \Zend\Stdlib\CallbackHandler|mixed CallbackHandler if attaching callable |
97
|
|
|
* (to allow later unsubscribe); mixed if attaching aggregate |
98
|
|
|
*/ |
99
|
|
|
public function bind($event, $callback = null, $priority = 1) |
100
|
|
|
{ |
101
|
|
|
$aggregate = (null === $callback) ? $this->resolveListenerAggregate($event) : null; |
102
|
|
|
|
103
|
|
|
if ($aggregate instanceof ListenerAggregateInterface) { |
104
|
|
|
$this->log(Log\AttachAggregateListener::class, [$aggregate]); |
105
|
|
|
$aggregate->attach($this->getEvents()); |
106
|
|
|
return $this; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
unset($aggregate); |
110
|
|
|
|
111
|
|
|
if ($event instanceof CallbackHandler) { |
112
|
|
|
$callback = $event->getCallback(); |
113
|
|
|
$mData = $event->getMetadata(); |
114
|
|
|
$this->log(Log\AttachListener::class, [$mData['event'], $callback, $mData['priority']]); |
115
|
|
|
return $this->listeners[] = $this->getEvents()->attach( |
116
|
|
|
$mData['event'], |
117
|
|
|
$callback, |
118
|
|
|
$mData['priority'] |
119
|
|
|
); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
$normalized = $this->normalizeCallback($callback); |
123
|
|
|
$this->log(Log\AttachListener::class, [$event, $normalized, $priority]); |
124
|
|
|
$this->listeners[] = $this->getEvents()->attach($event, $normalized, $priority); |
|
|
|
|
125
|
|
|
|
126
|
|
|
return $this; |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* {@inheritdoc} |
131
|
|
|
*/ |
132
|
|
|
public function unbind($event = null, $callback = null, $priority = null) |
133
|
|
|
{ |
134
|
|
|
$aggregate = (null === $callback) ? $this->resolveListenerAggregate($event) : null; |
135
|
|
|
|
136
|
|
|
if ($aggregate instanceof ListenerAggregateInterface) { |
137
|
|
|
$this->log(Log\DetachAggregateListener::class, [$aggregate]); |
138
|
|
|
$aggregate->detach($this->getEvents()); |
139
|
|
|
return $this; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
unset($aggregate); |
143
|
|
|
|
144
|
|
|
if (is_callable($event)) { |
145
|
|
|
$_callback = $event; |
146
|
|
|
$event = null; |
147
|
|
|
|
148
|
|
|
} elseif ($callback instanceof CallbackHandler) { |
149
|
|
|
$_callback = $callback->getCallback(); |
150
|
|
|
|
151
|
|
|
} else { |
152
|
|
|
$_callback = $this->normalizeCallback($callback); |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
if ($_callback instanceof CallbackHandler) { |
156
|
|
|
$_callback = $_callback->getCallback(); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** @var \Zend\Stdlib\CallbackHandler $listener */ |
160
|
|
|
foreach ($this->listeners as $index => $listener) { |
161
|
|
|
$mData = $listener->getMetadata(); |
162
|
|
|
|
163
|
|
|
if ($event && $event !== $mData['event']) { |
164
|
|
|
continue; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
if ($_callback !== $listener->getCallback()) { |
168
|
|
|
continue; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
if (null !== $priority && $priority !== $mData['priority']) { |
172
|
|
|
continue; |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
$this->log(Log\DetachListener::class, [$event, $_callback, $priority]); |
176
|
|
|
$this->getEvents()->detach($listener); |
177
|
|
|
unset($this->listeners[$index]); |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
return $this; |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* {@inheritdoc} |
185
|
|
|
*/ |
186
|
|
|
public function emit($event, $argv = [], $callback = null) |
187
|
|
|
{ |
188
|
|
|
$target = $this; |
189
|
|
|
if ($event instanceof Event) { |
190
|
|
|
$event->getTarget() or $event->setTarget($this); |
|
|
|
|
191
|
|
|
$target = $callback; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
$this->log(Log\TriggerEvent::class, [$event]); |
195
|
|
|
|
196
|
|
|
if (is_callable($argv)) { |
197
|
|
|
return $this->getEvents()->trigger($event, $target, [], $argv); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
return $this->getEvents()->trigger($event, $target, $argv, $callback); |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* Normalize the callback argument |
205
|
|
|
* |
206
|
|
|
* If the callback is a string, register it to the services then return it. |
207
|
|
|
* |
208
|
|
|
* @param mixed $callback |
209
|
|
|
* @return mixed |
210
|
|
|
*/ |
211
|
|
|
private function normalizeCallback($callback) |
212
|
|
|
{ |
213
|
|
|
if (is_string($callback)) { |
214
|
|
|
$this->has($callback) || $this->set($callback); |
215
|
|
|
return $this->get($callback); |
216
|
|
|
} |
217
|
|
|
return $callback; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* @param mixed $event |
222
|
|
|
* @return mixed|string |
223
|
|
|
*/ |
224
|
|
|
private function resolveListenerAggregate($event) |
225
|
|
|
{ |
226
|
|
|
if (is_string($event) && class_exists($event)) { |
227
|
|
|
if (empty(class_implements($event)[ListenerAggregateInterface::class])) { |
228
|
|
|
return $event; |
229
|
|
|
} |
230
|
|
|
return $this->normalizeCallback($event); |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
return $event; |
234
|
|
|
} |
235
|
|
|
} |
236
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.