1<?php 2 3/** 4 * Hoa 5 * 6 * 7 * @license 8 * 9 * New BSD License 10 * 11 * Copyright © 2007-2017, Hoa community. All rights reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions are met: 15 * * Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * * Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * * Neither the name of the Hoa nor the names of its contributors may be 21 * used to endorse or promote products derived from this software without 22 * specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37namespace Hoa\Event; 38 39use Hoa\Consistency; 40 41/** 42 * Class \Hoa\Event\Event. 43 * 44 * Events are asynchronous at registration, anonymous at use (until we 45 * receive a bucket) and useful to largely spread data through components 46 * without any known connection between them. 47 * 48 * @copyright Copyright © 2007-2017 Hoa community 49 * @license New BSD License 50 */ 51class Event 52{ 53 /** 54 * Event ID key. 55 * 56 * @const int 57 */ 58 const KEY_EVENT = 0; 59 60 /** 61 * Source object key. 62 * 63 * @const int 64 */ 65 const KEY_SOURCE = 1; 66 67 /** 68 * Static register of all observable objects, i.e. \Hoa\Event\Source 69 * object, i.e. object that can send event. 70 * 71 * @var array 72 */ 73 private static $_register = []; 74 75 /** 76 * Callables, i.e. observer objects. 77 * 78 * @var array 79 */ 80 protected $_callable = []; 81 82 83 84 /** 85 * Privatize the constructor. 86 * 87 */ 88 private function __construct() 89 { 90 return; 91 } 92 93 /** 94 * Manage multiton of events, with the principle of asynchronous 95 * attachments. 96 * 97 * @param string $eventId Event ID. 98 * @return \Hoa\Event\Event 99 */ 100 public static function getEvent($eventId) 101 { 102 if (!isset(self::$_register[$eventId][self::KEY_EVENT])) { 103 self::$_register[$eventId] = [ 104 self::KEY_EVENT => new self(), 105 self::KEY_SOURCE => null 106 ]; 107 } 108 109 return self::$_register[$eventId][self::KEY_EVENT]; 110 } 111 112 /** 113 * Declare a new object in the observable collection. 114 * Note: Hoa's libraries use hoa://Event/AnID for their observable objects; 115 * 116 * @param string $eventId Event ID. 117 * @param \Hoa\Event\Source|string $source Observable object or class. 118 * @return void 119 * @throws \Hoa\Event\Exception 120 */ 121 public static function register($eventId, $source) 122 { 123 if (true === self::eventExists($eventId)) { 124 throw new Exception( 125 'Cannot redeclare an event with the same ID, i.e. the event ' . 126 'ID %s already exists.', 127 0, 128 $eventId 129 ); 130 } 131 132 if (is_object($source) && !($source instanceof Source)) { 133 throw new Exception( 134 'The source must implement \Hoa\Event\Source ' . 135 'interface; given %s.', 136 1, 137 get_class($source) 138 ); 139 } else { 140 $reflection = new \ReflectionClass($source); 141 142 if (false === $reflection->implementsInterface('\Hoa\Event\Source')) { 143 throw new Exception( 144 'The source must implement \Hoa\Event\Source ' . 145 'interface; given %s.', 146 2, 147 $source 148 ); 149 } 150 } 151 152 if (!isset(self::$_register[$eventId][self::KEY_EVENT])) { 153 self::$_register[$eventId][self::KEY_EVENT] = new self(); 154 } 155 156 self::$_register[$eventId][self::KEY_SOURCE] = $source; 157 158 return; 159 } 160 161 /** 162 * Undeclare an object in the observable collection. 163 * 164 * @param string $eventId Event ID. 165 * @param bool $hard If false, just delete the source, else, 166 * delete source and attached callables. 167 * @return void 168 */ 169 public static function unregister($eventId, $hard = false) 170 { 171 if (false !== $hard) { 172 unset(self::$_register[$eventId]); 173 } else { 174 self::$_register[$eventId][self::KEY_SOURCE] = null; 175 } 176 177 return; 178 } 179 180 /** 181 * Attach an object to an event. 182 * It can be a callable or an accepted callable form (please, see the 183 * \Hoa\Consistency\Xcallable class). 184 * 185 * @param mixed $callable Callable. 186 * @return \Hoa\Event\Event 187 */ 188 public function attach($callable) 189 { 190 $callable = xcallable($callable); 191 $this->_callable[$callable->getHash()] = $callable; 192 193 return $this; 194 } 195 196 /** 197 * Detach an object to an event. 198 * Please see $this->attach() method. 199 * 200 * @param mixed $callable Callable. 201 * @return \Hoa\Event\Event 202 */ 203 public function detach($callable) 204 { 205 unset($this->_callable[xcallable($callable)->getHash()]); 206 207 return $this; 208 } 209 210 /** 211 * Check if at least one callable is attached to an event. 212 * 213 * @return bool 214 */ 215 public function isListened() 216 { 217 return !empty($this->_callable); 218 } 219 220 /** 221 * Notify, i.e. send data to observers. 222 * 223 * @param string $eventId Event ID. 224 * @param \Hoa\Event\Source $source Source. 225 * @param \Hoa\Event\Bucket $data Data. 226 * @return void 227 * @throws \Hoa\Event\Exception 228 */ 229 public static function notify($eventId, Source $source, Bucket $data) 230 { 231 if (false === self::eventExists($eventId)) { 232 throw new Exception( 233 'Event ID %s does not exist, cannot send notification.', 234 3, 235 $eventId 236 ); 237 } 238 239 $data->setSource($source); 240 $event = self::getEvent($eventId); 241 242 foreach ($event->_callable as $callable) { 243 $callable($data); 244 } 245 246 return; 247 } 248 249 /** 250 * Check whether an event exists. 251 * 252 * @param string $eventId Event ID. 253 * @return bool 254 */ 255 public static function eventExists($eventId) 256 { 257 return 258 array_key_exists($eventId, self::$_register) && 259 self::$_register[$eventId][self::KEY_SOURCE] !== null; 260 } 261} 262 263/** 264 * Flex entity. 265 */ 266Consistency::flexEntity('Hoa\Event\Event'); 267