1: <?php
2: /**
3: * This code is licensed under AGPLv3 license or Afterlogic Software License
4: * if commercial version of the product was purchased.
5: * For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
6: */
7:
8: namespace Aurora\System;
9:
10: /**
11: * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
12: * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
13: * @copyright Copyright (c) 2019, Afterlogic Corp.
14: */
15: class EventEmitter
16: {
17: /**
18: *
19: */
20: protected static $self = null;
21:
22: /**
23: * @var array
24: */
25: private $aListenersResult;
26:
27: /**
28: * @var int
29: */
30: private $iEventLevel = 0;
31:
32: /**
33: * @var array
34: */
35: private $aListeners;
36:
37: /**
38: *
39: * @return \self
40: */
41: public static function getInstance()
42: {
43: if (is_null(self::$self)) {
44: self::$self = new self();
45: }
46:
47: return self::$self;
48: }
49:
50: /**
51: *
52: * @return \self
53: */
54: public static function createInstance()
55: {
56: return new self();
57: }
58:
59: /**
60: *
61: * @return array
62: */
63: public function getListeners()
64: {
65: return $this->aListeners;
66: }
67:
68: /**
69: * @return array
70: */
71: public function getListenersResult()
72: {
73: return $this->aListenersResult;
74: }
75:
76: public function strPad($sText, $iCount, $sPadText, $iPadType = STR_PAD_LEFT)
77: {
78: return str_pad($sText, strlen($sText) + $iCount, $sPadText, $iPadType);
79: }
80:
81: /**
82: *
83: * @return array
84: */
85: public function getListenersByEvent($sModule, $sEvent)
86: {
87: $aListeners = [];
88:
89: if (isset($this->aListeners[$sEvent])) {
90: $aListeners = $this->aListeners[$sEvent];
91: }
92: $sEvent = $sModule . Module\AbstractModule::$Delimiter . $sEvent;
93: if (isset($this->aListeners[$sEvent])) {
94: $aListeners = array_merge($aListeners, $this->aListeners[$sEvent]);
95: }
96:
97: return $aListeners;
98: }
99:
100: /**
101: * Subscribe to an event.
102: *
103: * When the event is triggered, we'll call all the specified callbacks.
104: * It is possible to control the order of the callbacks through the
105: * priority argument.
106: *
107: * This is for example used to make sure that the authentication plugin
108: * is triggered before anything else. If it's not needed to change this
109: * number, it is recommended to ommit.
110: *
111: * @param string $sEvent
112: * @param callback $fCallback
113: * @param int $iPriority
114: * @return void
115: */
116: public function on($sEvent, $fCallback, $iPriority = 100)
117: {
118: if (!isset($this->aListeners[$sEvent])) {
119: $this->aListeners[$sEvent] = [];
120: }
121: while (isset($this->aListeners[$sEvent][$iPriority])) {
122: $iPriority++;
123: }
124: $this->aListeners[$sEvent][$iPriority] = $fCallback;
125: \ksort($this->aListeners[$sEvent]);
126: }
127:
128: public function onAny($aListeners)
129: {
130: foreach ($aListeners as $sKey => $mListener) {
131: if (is_array($mListener) && is_callable($mListener[1])) {
132: if (isset($mListener[2])) {
133: $this->on($mListener[0], $mListener[1], $mListener[2]);
134: } else {
135: $this->on($mListener[0], $mListener[1]);
136: }
137: }
138: }
139: }
140:
141: /**
142: * Broadcasts an event
143: *
144: * This method will call all subscribers. If one of the subscribers returns false, the process stops.
145: *
146: * The arguments parameter will be sent to all subscribers
147: *
148: * @param string $sEvent
149: * @param array $aArguments
150: * @param mixed $mResult
151: * @param callback $mCountinueCallback
152: * @return boolean
153: */
154: public function emit($sModule, $sEvent, &$aArguments = [], &$mResult = null, $mCountinueCallback = null, $bSkipIsAllowedModuleCheck = false)
155: {
156: $bResult = false;
157: $mListenersResult = null;
158:
159: $aListeners = $this->getListenersByEvent($sModule, $sEvent);
160: if (count($aListeners) > 0) {
161: $this->iEventLevel = $this->iEventLevel + 4;
162: \Aurora\System\Api::Log($this->strPad("Emit $sModule::$sEvent", $this->iEventLevel, "-"), Enums\LogLevel::Full, 'subscriptions-');
163: \Aurora\System\Api::Log($this->strPad("START Execute subscriptions", $this->iEventLevel, "-"), Enums\LogLevel::Full, 'subscriptions-');
164: foreach ($aListeners as $fCallback) {
165: $bIsAllowedModule = true;
166: if (!$bSkipIsAllowedModuleCheck) {
167: $bIsAllowedModule = Api::GetModuleManager()->IsAllowedModule($fCallback[0]::GetName());
168: }
169: if (\is_callable($fCallback) && $bIsAllowedModule) {
170: \Aurora\System\Api::Log($this->strPad($fCallback[0]::GetName() . Module\AbstractModule::$Delimiter . $fCallback[1], $this->iEventLevel + 2, "-"), Enums\LogLevel::Full, 'subscriptions-');
171:
172: $mCallBackResult = \call_user_func_array(
173: $fCallback,
174: [
175: &$aArguments,
176: &$mResult,
177: &$mListenersResult
178: ]
179: );
180:
181: if (\is_callable($mCountinueCallback)) {
182: $mCountinueCallback(
183: $fCallback[0]::GetName(),
184: $aArguments,
185: $mCallBackResult
186: );
187: }
188:
189: if (isset($mListenersResult)) {
190: $this->aListenersResult[$fCallback[0]::GetName() . Module\AbstractModule::$Delimiter . $fCallback[1]] = $mListenersResult;
191: }
192:
193: if ($mCallBackResult) {
194: $bResult = $mCallBackResult;
195:
196: break;
197: }
198: }
199: }
200: \Aurora\System\Api::Log($this->strPad('END Execute subscriptions', $this->iEventLevel, "-"), Enums\LogLevel::Full, 'subscriptions-');
201:
202: $this->iEventLevel = $this->iEventLevel - 4;
203: }
204:
205: return $bResult;
206: }
207: }
208: