1
|
|
|
<?php |
|
|
|
|
2
|
|
|
|
3
|
|
|
ar_pinp::allow( 'ar_events', array( |
4
|
|
|
'listen', 'capture', 'fire', 'get', 'event' |
5
|
|
|
) ); |
6
|
|
|
ar_pinp::allow( 'ar_eventsInstance' ); |
7
|
|
|
ar_pinp::allow( 'ar_eventsEvent' ); |
8
|
|
|
ar_pinp::allow( 'ar_eventsListener' ); |
9
|
|
|
|
10
|
|
|
class ar_events extends arBase { |
11
|
|
|
protected static $listeners = array(); |
12
|
|
|
protected static $event = null; |
13
|
|
|
|
14
|
1 |
|
public static function listen( $eventName, $objectType = null, $capture = false ) { |
15
|
1 |
|
$path = ar::context()->getPath(); |
16
|
1 |
|
return new ar_eventsInstance( $path, $eventName, $objectType, $capture ); |
17
|
|
|
} |
18
|
|
|
|
19
|
|
|
public static function capture( $eventName, $objectType = null ) { |
20
|
|
|
return self::listen( $eventName, $objectType, true ); |
21
|
|
|
} |
22
|
|
|
|
23
|
15 |
|
public static function fire( $eventName, $eventData = array(), $objectType = null, $path = '') { |
24
|
15 |
|
if ( !isset(self::$listeners['capture'][$eventName]) |
25
|
15 |
|
&& !isset(self::$listeners['listen'][$eventName]) ) { |
26
|
15 |
|
return $eventData; // no listeners for this event, so dont bother searching |
27
|
|
|
} |
28
|
|
|
$prevEvent = null; |
29
|
|
|
if ( self::$event ) { |
30
|
|
|
$prevEvent = self::$event; |
31
|
|
|
} |
32
|
|
|
$path = ar::context()->getPath( array( 'path' => $path ) ); |
33
|
|
|
$me = ar::context()->getObject( array( 'path' => $path ) ); |
34
|
|
|
if ( $me && !isset($objectType) ) { |
35
|
|
|
$objectType = $me->type; |
36
|
|
|
} else if ( !$objectType ) { // when set to false to prevent automatic filling of the objectType, reset it to null |
37
|
|
|
$objectType = null; |
38
|
|
|
} |
39
|
|
|
self::$event = new ar_eventsEvent( $eventName, $eventData, $objectType, $path, $me ); |
40
|
|
|
if ( self::walkListeners( self::$listeners['capture'][$eventName], $path, $objectType, true ) ) { |
41
|
|
|
self::walkListeners( self::$listeners['listen'][$eventName], $path, $objectType, false ); |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
if ( self::$event->preventDefault ) { |
45
|
|
|
$result = false; |
46
|
|
|
} else if ( self::$event->data ) { |
47
|
|
|
$result = self::$event->data; |
48
|
|
|
} else { |
49
|
|
|
$result = true; |
50
|
|
|
} |
51
|
|
|
self::$event = $prevEvent; |
52
|
|
|
return $result; |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
protected static function walkListeners( $listeners, $path, $objectType, $capture ) { |
56
|
|
|
$strObjectType = (string) $objectType; |
57
|
|
|
$objectTypeStripped = $strObjectType; |
58
|
|
|
$pos = strpos( $strObjectType, '.'); |
59
|
|
|
if ( $pos !== false ) { |
60
|
|
|
$objectTypeStripped = substr($strObjectType, 0, $pos); |
61
|
|
|
} |
62
|
|
|
$pathticles = explode( '/', $path ); |
63
|
|
|
$pathlist = array( '/' ); |
64
|
|
|
$prevpath = '/'; |
65
|
|
|
foreach ( $pathticles as $pathticle ) { |
66
|
|
|
if ( $pathticle ) { |
67
|
|
|
$prevpath .= $pathticle . '/'; |
68
|
|
|
$pathlist[] = $prevpath; |
69
|
|
|
} |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
if ( !$capture ) { |
73
|
|
|
$pathlist = array_reverse( $pathlist ); |
74
|
|
|
} |
75
|
|
|
reset($pathlist); |
76
|
|
|
|
77
|
|
|
do { |
78
|
|
|
$currentPath = current( $pathlist ); |
79
|
|
|
if ( is_array( $listeners[$currentPath] ) ) { |
80
|
|
|
foreach ( $listeners[$currentPath] as $listener ) { |
81
|
|
|
if ( !isset($listener['type']) || |
82
|
|
|
( $listener['type'] == $strObjectType ) || |
83
|
|
|
( $listener['type'] == $objectTypeStripped ) || |
84
|
|
|
( is_a( $strObjectType, $listener['type'] ) ) ) |
85
|
|
|
{ |
86
|
|
|
$continue = true; |
87
|
|
|
if ( count($listener['filters']) ) { |
88
|
|
|
$continue = false; |
89
|
|
|
foreach( $listener['filters'] as $filter ) { |
90
|
|
|
if ( $filter instanceof \Closure ) { |
91
|
|
|
$continue = $filter( self::$event ); |
92
|
|
|
if ( $continue ) { |
93
|
|
|
continue; |
94
|
|
|
} |
95
|
|
|
} else if ( ar_filter::match(self::$event, $filter) ) { |
96
|
|
|
$continue = true; |
97
|
|
|
continue; |
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
} |
101
|
|
|
if ( $continue ) { |
102
|
|
|
$result = call_user_func_array( $listener['method'], $listener['args'] ); |
103
|
|
|
if ( $result === false ) { |
104
|
|
|
return false; |
105
|
|
|
} |
106
|
|
|
} |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
} |
110
|
|
|
} while( next( $pathlist ) ); |
111
|
|
|
return true; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
public static function event() { |
115
|
|
|
return self::$event; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
public static function get( $path ) { |
119
|
|
|
return new ar_eventsInstance( $path ); |
120
|
|
|
} |
121
|
|
|
|
122
|
1 |
|
public static function addListener( $path, $eventName, $objectType, $method, $args, $capture = false, $filters = array() ) { |
123
|
1 |
|
$when = ($capture) ? 'capture' : 'listen'; |
124
|
1 |
|
self::$listeners[$when][$eventName][$path][] = array( |
125
|
1 |
|
'type' => $objectType, |
126
|
1 |
|
'method' => $method, |
127
|
1 |
|
'args' => $args, |
128
|
|
|
'filters' => $filters |
129
|
1 |
|
); |
130
|
1 |
|
return new ar_eventsListener( $eventName, $path, $capture, count(self::$listeners[$when][$eventName][$path])-1 ); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
public static function removeListener( $name, $path, $capture, $id ) { |
134
|
|
|
$when = ($capture) ? 'capture' : 'listen'; |
135
|
|
|
unset( self::$listeners[$when][$name][$path][$id] ); |
136
|
|
|
} |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
class ar_eventsListener extends arBase { |
|
|
|
|
140
|
|
|
private $capture = false; |
141
|
|
|
private $path = ''; |
142
|
|
|
private $name = ''; |
143
|
|
|
private $id = null; |
144
|
|
|
|
145
|
1 |
|
public function __construct( $name, $path, $capture, $id ) { |
146
|
1 |
|
$this->name = $name; |
147
|
1 |
|
$this->path = $path; |
148
|
1 |
|
$this->capture = $capture; |
149
|
1 |
|
$this->id = $id; |
150
|
1 |
|
} |
151
|
|
|
|
152
|
|
|
public function remove() { |
153
|
|
|
if ( isset($this->id) ) { |
154
|
|
|
ar_events::removeListener( $this->name, $this->path, $this->capture, $this->id ); |
155
|
|
|
} |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/* FIXME: add a add() method, which re-adds the listener, potentially as last in the list */ |
|
|
|
|
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
class ar_eventsInstance extends arBase { |
|
|
|
|
162
|
|
|
private $path = '/'; |
163
|
|
|
private $eventName = null; |
164
|
|
|
private $objectType = null; |
165
|
|
|
private $capture = false; |
166
|
|
|
private $filters = array(); |
167
|
|
|
|
168
|
1 |
|
public function __construct( $path, $eventName = null, $objectType = null, $capture = false ) { |
169
|
1 |
|
$this->path = $path; |
170
|
1 |
|
$this->setEventProperties( $eventName, $objectType, $capture); |
171
|
1 |
|
} |
172
|
|
|
|
173
|
1 |
|
public function call( $method, $args = array() ) { |
174
|
1 |
|
if ( is_string($method) ) { |
175
|
1 |
|
$method = ar_pinp::getCallback( $method, array_keys($args) ); |
176
|
1 |
|
} |
177
|
1 |
|
if ( !( $method instanceof \Closure ) ) { |
178
|
|
|
return ar_error::raiseError('Illegal event listener method', 500); |
179
|
|
|
} |
180
|
1 |
|
return ar_events::addListener( $this->path, $this->eventName, $this->objectType, $method, $args, $this->capture, $this->filters ); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
public function listen( $eventName, $objectType = null ) { |
184
|
|
|
$this->setEventProperties( $eventName, $objectType, false); |
185
|
|
|
return $this; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
public function capture( $eventName, $objectType = null ) { |
189
|
|
|
$this->setEventProperties( $eventName, $objectType, true); |
190
|
|
|
return $this; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
public function match( $filter ) { |
194
|
|
|
$this->filters[] = $filter; |
195
|
|
|
return $this; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
public function fire($eventName, $eventData, $objectType = null) { |
|
|
|
|
199
|
|
|
return ar_events::fire($eventName, $eventData, $objectType = null, $this->path); |
200
|
|
|
} |
201
|
|
|
|
202
|
1 |
|
private function setEventProperties($eventName, $objectType, $capture) { |
203
|
1 |
|
$this->eventName = $eventName; |
204
|
1 |
|
$this->objectType = $objectType; |
205
|
1 |
|
$this->capture = (bool)$capture; |
206
|
1 |
|
} |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
class ar_eventsEvent extends arBase { |
|
|
|
|
210
|
|
|
public $data = null; |
211
|
|
|
private $name = ''; |
212
|
|
|
private $type = null; |
213
|
|
|
private $preventDefault = false; |
214
|
|
|
private $path = null; |
215
|
|
|
private $target = null; |
216
|
|
|
|
217
|
|
|
public function __construct( $name, $data = null, $type = null, $path = null, $target = null ) { |
218
|
|
|
$this->name = $name; |
219
|
|
|
$this->data = $data; |
220
|
|
|
$this->type = $type; |
221
|
|
|
$this->path = $path; |
222
|
|
|
$this->target = $target; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
public function preventDefault() { |
226
|
|
|
$this->preventDefault = true; |
227
|
|
|
return false; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
public function __get( $name ) { |
231
|
|
|
if ( in_array($name, array('preventDefault','name','type','path','target') ) ) { |
232
|
|
|
return $this->{$name}; |
233
|
|
|
} |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
} |
237
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.