EmitterTrait.php 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. <?php
  2. declare(strict_types=1);
  3. namespace Sabre\Event;
  4. /**
  5. * Event Emitter Trait.
  6. *
  7. * This trait contains all the basic functions to implement an
  8. * EventEmitterInterface.
  9. *
  10. * Using the trait + interface allows you to add EventEmitter capabilities
  11. * without having to change your base-class.
  12. *
  13. * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
  14. * @author Evert Pot (http://evertpot.com/)
  15. * @license http://sabre.io/license/ Modified BSD License
  16. */
  17. trait EmitterTrait
  18. {
  19. /**
  20. * Subscribe to an event.
  21. */
  22. public function on(string $eventName, callable $callBack, int $priority = 100)
  23. {
  24. if (!isset($this->listeners[$eventName])) {
  25. $this->listeners[$eventName] = [
  26. true, // If there's only one item, it's sorted
  27. [$priority],
  28. [$callBack],
  29. ];
  30. } else {
  31. $this->listeners[$eventName][0] = false; // marked as unsorted
  32. $this->listeners[$eventName][1][] = $priority;
  33. $this->listeners[$eventName][2][] = $callBack;
  34. }
  35. }
  36. /**
  37. * Subscribe to an event exactly once.
  38. */
  39. public function once(string $eventName, callable $callBack, int $priority = 100)
  40. {
  41. $wrapper = null;
  42. $wrapper = function () use ($eventName, $callBack, &$wrapper) {
  43. $this->removeListener($eventName, $wrapper);
  44. return \call_user_func_array($callBack, \func_get_args());
  45. };
  46. $this->on($eventName, $wrapper, $priority);
  47. }
  48. /**
  49. * Emits an event.
  50. *
  51. * This method will return true if 0 or more listeners were successfully
  52. * handled. false is returned if one of the events broke the event chain.
  53. *
  54. * If the continueCallBack is specified, this callback will be called every
  55. * time before the next event handler is called.
  56. *
  57. * If the continueCallback returns false, event propagation stops. This
  58. * allows you to use the eventEmitter as a means for listeners to implement
  59. * functionality in your application, and break the event loop as soon as
  60. * some condition is fulfilled.
  61. *
  62. * Note that returning false from an event subscriber breaks propagation
  63. * and returns false, but if the continue-callback stops propagation, this
  64. * is still considered a 'successful' operation and returns true.
  65. *
  66. * Lastly, if there are 5 event handlers for an event. The continueCallback
  67. * will be called at most 4 times.
  68. */
  69. public function emit(string $eventName, array $arguments = [], ?callable $continueCallBack = null): bool
  70. {
  71. if (\is_null($continueCallBack)) {
  72. foreach ($this->listeners($eventName) as $listener) {
  73. $result = \call_user_func_array($listener, $arguments);
  74. if (false === $result) {
  75. return false;
  76. }
  77. }
  78. } else {
  79. $listeners = $this->listeners($eventName);
  80. $counter = \count($listeners);
  81. foreach ($listeners as $listener) {
  82. --$counter;
  83. $result = \call_user_func_array($listener, $arguments);
  84. if (false === $result) {
  85. return false;
  86. }
  87. if ($counter > 0) {
  88. if (!$continueCallBack()) {
  89. break;
  90. }
  91. }
  92. }
  93. }
  94. return true;
  95. }
  96. /**
  97. * Returns the list of listeners for an event.
  98. *
  99. * The list is returned as an array, and the list of events are sorted by
  100. * their priority.
  101. *
  102. * @return callable[]
  103. */
  104. public function listeners(string $eventName): array
  105. {
  106. if (!isset($this->listeners[$eventName])) {
  107. return [];
  108. }
  109. // The list is not sorted
  110. if (!$this->listeners[$eventName][0]) {
  111. // Sorting
  112. \array_multisort($this->listeners[$eventName][1], SORT_NUMERIC, $this->listeners[$eventName][2]);
  113. // Marking the listeners as sorted
  114. $this->listeners[$eventName][0] = true;
  115. }
  116. return $this->listeners[$eventName][2];
  117. }
  118. /**
  119. * Removes a specific listener from an event.
  120. *
  121. * If the listener could not be found, this method will return false. If it
  122. * was removed it will return true.
  123. */
  124. public function removeListener(string $eventName, callable $listener): bool
  125. {
  126. if (!isset($this->listeners[$eventName])) {
  127. return false;
  128. }
  129. foreach ($this->listeners[$eventName][2] as $index => $check) {
  130. if ($check === $listener) {
  131. unset($this->listeners[$eventName][1][$index]);
  132. unset($this->listeners[$eventName][2][$index]);
  133. return true;
  134. }
  135. }
  136. return false;
  137. }
  138. /**
  139. * Removes all listeners.
  140. *
  141. * If the eventName argument is specified, all listeners for that event are
  142. * removed. If it is not specified, every listener for every event is
  143. * removed.
  144. */
  145. public function removeAllListeners(?string $eventName = null)
  146. {
  147. if (!\is_null($eventName)) {
  148. unset($this->listeners[$eventName]);
  149. } else {
  150. $this->listeners = [];
  151. }
  152. }
  153. /**
  154. * The list of listeners.
  155. *
  156. * @var array
  157. */
  158. protected $listeners = [];
  159. }