ar_events::event()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 0
cts 3
cp 0
crap 2
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) {
0 ignored issues
show
Unused Code introduced by
The parameter $objectType is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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