Completed
Push — php7.2-travis ( 46fda5...100f27 )
by
unknown
249:00 queued 239:17
created

ar_eventsInstance   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 47
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 45.45%

Importance

Changes 0
Metric Value
dl 0
loc 47
ccs 15
cts 33
cp 0.4545
rs 10
c 0
b 0
f 0
wmc 9
lcom 1
cbo 4

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A setEventProperties() 0 5 1
A call() 0 9 3
A listen() 0 4 1
A capture() 0 4 1
A match() 0 4 1
A fire() 0 3 1
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 10 and the first side effect is on line 3.

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.

Loading history...
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 {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
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 */
0 ignored issues
show
Coding Style introduced by
Comment refers to a FIXME task "add a add() method, which re-adds the listener, potentially as last in the list */"
Loading history...
159
	}
160
161
	class ar_eventsInstance extends arBase {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
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 {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

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