1<?php 2/** 3 * @package Joomla.Platform 4 * @subpackage Event 5 * 6 * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. 7 * @license GNU General Public License version 2 or later; see LICENSE 8 */ 9 10defined('JPATH_PLATFORM') or die; 11 12/** 13 * Class to handle dispatching of events. 14 * 15 * This is the Observable part of the Observer design pattern 16 * for the event architecture. 17 * 18 * @see JPlugin 19 * @since 3.0.0 20 * @deprecated 4.0 The CMS' Event classes will be replaced with the `joomla/event` package 21 */ 22class JEventDispatcher extends JObject 23{ 24 /** 25 * An array of Observer objects to notify 26 * 27 * @var array 28 * @since 3.0.0 29 */ 30 protected $_observers = array(); 31 32 /** 33 * The state of the observable object 34 * 35 * @var mixed 36 * @since 3.0.0 37 */ 38 protected $_state = null; 39 40 /** 41 * A multi dimensional array of [function][] = key for observers 42 * 43 * @var array 44 * @since 3.0.0 45 */ 46 protected $_methods = array(); 47 48 /** 49 * Stores the singleton instance of the dispatcher. 50 * 51 * @var JEventDispatcher 52 * @since 3.0.0 53 */ 54 protected static $instance = null; 55 56 /** 57 * Returns the global Event Dispatcher object, only creating it 58 * if it doesn't already exist. 59 * 60 * @return JEventDispatcher The EventDispatcher object. 61 * 62 * @since 3.0.0 63 */ 64 public static function getInstance() 65 { 66 if (self::$instance === null) 67 { 68 self::$instance = new static; 69 } 70 71 return self::$instance; 72 } 73 74 /** 75 * Get the state of the JEventDispatcher object 76 * 77 * @return mixed The state of the object. 78 * 79 * @since 3.0.0 80 */ 81 public function getState() 82 { 83 return $this->_state; 84 } 85 86 /** 87 * Registers an event handler to the event dispatcher 88 * 89 * @param string $event Name of the event to register handler for 90 * @param string $handler Name of the event handler 91 * 92 * @return void 93 * 94 * @since 3.0.0 95 * @throws InvalidArgumentException 96 */ 97 public function register($event, $handler) 98 { 99 // Are we dealing with a class or callback type handler? 100 if (is_callable($handler)) 101 { 102 // Ok, function type event handler... let's attach it. 103 $method = array('event' => $event, 'handler' => $handler); 104 $this->attach($method); 105 } 106 elseif (class_exists($handler)) 107 { 108 // Ok, class type event handler... let's instantiate and attach it. 109 $this->attach(new $handler($this)); 110 } 111 else 112 { 113 throw new InvalidArgumentException('Invalid event handler.'); 114 } 115 } 116 117 /** 118 * Triggers an event by dispatching arguments to all observers that handle 119 * the event and returning their return values. 120 * 121 * @param string $event The event to trigger. 122 * @param array $args An array of arguments. 123 * 124 * @return array An array of results from each function call. 125 * 126 * @since 3.0.0 127 */ 128 public function trigger($event, $args = array()) 129 { 130 $result = array(); 131 132 /* 133 * If no arguments were passed, we still need to pass an empty array to 134 * the call_user_func_array function. 135 */ 136 $args = (array) $args; 137 138 $event = strtolower($event); 139 140 // Check if any plugins are attached to the event. 141 if (!isset($this->_methods[$event]) || empty($this->_methods[$event])) 142 { 143 // No Plugins Associated To Event! 144 return $result; 145 } 146 147 // Loop through all plugins having a method matching our event 148 foreach ($this->_methods[$event] as $key) 149 { 150 // Check if the plugin is present. 151 if (!isset($this->_observers[$key])) 152 { 153 continue; 154 } 155 156 // Fire the event for an object based observer. 157 if (is_object($this->_observers[$key])) 158 { 159 $args['event'] = $event; 160 $value = $this->_observers[$key]->update($args); 161 } 162 // Fire the event for a function based observer. 163 elseif (is_array($this->_observers[$key])) 164 { 165 $value = call_user_func_array($this->_observers[$key]['handler'], array_values($args)); 166 } 167 168 if (isset($value)) 169 { 170 $result[] = $value; 171 } 172 } 173 174 return $result; 175 } 176 177 /** 178 * Attach an observer object 179 * 180 * @param object $observer An observer object to attach 181 * 182 * @return void 183 * 184 * @since 3.0.0 185 */ 186 public function attach($observer) 187 { 188 if (is_array($observer)) 189 { 190 if (!isset($observer['handler']) || !isset($observer['event']) || !is_callable($observer['handler'])) 191 { 192 return; 193 } 194 195 // Make sure we haven't already attached this array as an observer 196 foreach ($this->_observers as $check) 197 { 198 if (is_array($check) && $check['event'] === $observer['event'] && $check['handler'] === $observer['handler']) 199 { 200 return; 201 } 202 } 203 204 $this->_observers[] = $observer; 205 $methods = array($observer['event']); 206 } 207 else 208 { 209 if (!($observer instanceof JEvent)) 210 { 211 return; 212 } 213 214 // Make sure we haven't already attached this object as an observer 215 $class = get_class($observer); 216 217 foreach ($this->_observers as $check) 218 { 219 if ($check instanceof $class) 220 { 221 return; 222 } 223 } 224 225 $this->_observers[] = $observer; 226 $methods = array_diff(get_class_methods($observer), get_class_methods('JPlugin')); 227 } 228 229 end($this->_observers); 230 $key = key($this->_observers); 231 232 foreach ($methods as $method) 233 { 234 $method = strtolower($method); 235 236 if (!isset($this->_methods[$method])) 237 { 238 $this->_methods[$method] = array(); 239 } 240 241 $this->_methods[$method][] = $key; 242 } 243 } 244 245 /** 246 * Detach an observer object 247 * 248 * @param object $observer An observer object to detach. 249 * 250 * @return boolean True if the observer object was detached. 251 * 252 * @since 3.0.0 253 */ 254 public function detach($observer) 255 { 256 $retval = false; 257 258 $key = array_search($observer, $this->_observers); 259 260 if ($key !== false) 261 { 262 unset($this->_observers[$key]); 263 $retval = true; 264 265 foreach ($this->_methods as &$method) 266 { 267 $k = array_search($key, $method); 268 269 if ($k !== false) 270 { 271 unset($method[$k]); 272 } 273 } 274 } 275 276 return $retval; 277 } 278} 279