BxDolLiveUpdates.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup UnaCore UNA Core
  7. * @{
  8. */
  9. class BxDolLiveUpdates extends BxDolFactory implements iBxDolSingleton
  10. {
  11. protected $_oQuery;
  12. protected $_aSystems;
  13. protected $_aSystemsActive;
  14. protected $_iProfileId;
  15. protected $_iInterval;
  16. protected $_iCacheTTL;
  17. protected $_sCacheKey;
  18. protected $_sJsClass;
  19. protected $_sJsObject;
  20. protected function __construct()
  21. {
  22. parent::__construct();
  23. /**
  24. * Note. Currently Live Updates are associated with profiles (Profile ID) and therefore they are used for logged in members only.
  25. * If it's needed Session ID can be used instead of Profile ID. In this case Live Updates can be used for visitors too. Don't forget
  26. * to update BxBaseLiveUpdates::init if Session ID will be used.
  27. */
  28. $this->_iProfileId = (int)bx_get_logged_profile_id();
  29. if(!$this->_iProfileId)
  30. return;
  31. $this->_oQuery = new BxDolLiveUpdatesQuery();
  32. $this->_iInterval = (int)$this->_oQuery->getParam('sys_live_updates_interval');
  33. $this->_iCacheTTL = 86400;
  34. $this->_sCacheKey = 'sys_live_updates_' . $this->_iProfileId;
  35. $this->_sJsClass = 'BxDolLiveUpdates';
  36. $this->_sJsObject = 'oLiveUpdates';
  37. $this->_aSystems = $this->_getCachedSystems();
  38. $this->_aSystemsActive = array_keys($this->_aSystems);
  39. }
  40. /**
  41. * get live updates instanse
  42. * @return ready to use class instance
  43. */
  44. public static function getInstance()
  45. {
  46. if(!isset($GLOBALS['bxDolClasses'][__CLASS__]))
  47. $GLOBALS['bxDolClasses'][__CLASS__] = new BxTemplLiveUpdates();
  48. return $GLOBALS['bxDolClasses'][__CLASS__];
  49. }
  50. /**
  51. *
  52. * Is called via Ajax Requests only.
  53. */
  54. public function perform()
  55. {
  56. $iIndex = (int)bx_get('index');
  57. $mixedSystemsActive = bx_get('systems_active');
  58. if($mixedSystemsActive !== false)
  59. $this->_aSystemsActive = array_keys($mixedSystemsActive);
  60. $this->_aSystems = $this->_getCachedSystems();
  61. if(empty($this->_aSystems) || !is_array($this->_aSystems))
  62. return array();
  63. $aCurrent = $mixedSystemsActive;
  64. if(empty($aCurrent) || !is_array($aCurrent))
  65. $aCurrent = $this->_getCachedData();
  66. $aRequested = $this->_getRequestedData($iIndex, true, $aCurrent);
  67. $aResult = array();
  68. $bUpdateCache = false;
  69. foreach($aRequested as $sName => $aData) {
  70. if(isset($aCurrent[$sName])) {
  71. if((int)$aCurrent[$sName] == (int)$aData['count'])
  72. continue;
  73. $aResultData = array('count_new' => $aData['count'], 'count_old' => $aCurrent[$sName]);
  74. if(isset($aData['data']))
  75. $aResultData = array_merge($aResultData, $aData['data']);
  76. $aResult[] = array(
  77. 'system' => $sName,
  78. 'data' => $aResultData,
  79. 'method' => $aData['method']
  80. );
  81. }
  82. $aCurrent[$sName] = $aData['count'];
  83. $bUpdateCache = true;
  84. }
  85. if($bUpdateCache)
  86. $this->_updateCached('data', $aCurrent);
  87. return $aResult;
  88. }
  89. /**
  90. *
  91. * Add transient live update for current user on this current page.
  92. * @param string $sName - unique name.
  93. * @param integer $iFrequency - call frequency.
  94. * @param string $sServiceCall - serialized service call.
  95. * @param boolean $bActive - add active/not active live update.
  96. */
  97. public function add($sName, $iFrequency, $sServiceCall, $bActive = true)
  98. {
  99. if(!$this->_iProfileId)
  100. return false;
  101. if(!in_array($sName, $this->_aSystemsActive))
  102. $this->_aSystemsActive[] = $sName;
  103. if(empty($this->_aSystems[$sName])) {
  104. $this->_aSystems[$sName] = array(
  105. 'name' => $sName,
  106. 'frequency' => $iFrequency,
  107. 'service_call' => $sServiceCall,
  108. 'active' => $bActive ? 1 : 0
  109. );
  110. $this->_updateCached('systems', $this->_aSystems);
  111. }
  112. $mixedResponce = $this->_getRequestedDataBySystem($this->_aSystems[$sName]);
  113. if($mixedResponce !== false) {
  114. $aCachedData = $this->_getCachedData();
  115. $aCachedData[$sName] = $mixedResponce['count'];
  116. $this->_updateCached('data', $aCachedData);
  117. }
  118. return true;
  119. }
  120. protected function _getCacheInfo()
  121. {
  122. return array(
  123. $this->_oQuery->getDbCacheObject(),
  124. $this->_oQuery->genDbCacheKey($this->_sCacheKey),
  125. $this->_iCacheTTL
  126. );
  127. }
  128. protected function _getCached($sType)
  129. {
  130. list($oCache, $sCacheKey, $iCacheTtl) = $this->_getCacheInfo();
  131. $aCached = $oCache->getData($sCacheKey, $iCacheTtl);
  132. if(empty($aCached[$sType])) {
  133. switch($sType) {
  134. case 'systems':
  135. $aCached[$sType] = $this->_oQuery->getSystems();
  136. break;
  137. case 'data':
  138. if(!isset($aCached[$sType]))
  139. $aCached[$sType] = array();
  140. $aRequested = $this->_getRequestedData();
  141. foreach($this->_aSystems as $sName => $aSystem)
  142. $aCached[$sType][$sName] = !empty($aRequested[$sName]['count']) ? (int)$aRequested[$sName]['count'] : 0;
  143. break;
  144. }
  145. if(!empty($aCached[$sType]))
  146. $oCache->setData($sCacheKey, $aCached, $iCacheTtl);
  147. }
  148. return $aCached[$sType];
  149. }
  150. protected function _getCachedSystems()
  151. {
  152. $aSystems = $this->_getCached('systems');
  153. if(!empty($aSystems) && !empty($this->_aSystemsActive))
  154. $aSystems = array_intersect_key($aSystems, array_flip($this->_aSystemsActive));
  155. return $aSystems;
  156. }
  157. protected function _getCachedData()
  158. {
  159. return $this->_getCached('data');
  160. }
  161. protected function _updateCached($sKey, $aData)
  162. {
  163. list($oCache, $sCacheKey, $iCacheTtl) = $this->_getCacheInfo();
  164. $aCached = $oCache->getData($sCacheKey, $iCacheTtl);
  165. $aCached[$sKey] = $aData;
  166. $oCache->setData($sCacheKey, $aCached, $iCacheTtl);
  167. }
  168. protected function _getRequestedData($iIndex = 0, $bIndexCheck = false, $aCachedData = array())
  169. {
  170. $aResult = array();
  171. foreach($this->_aSystems as $sName => $aSystem) {
  172. if(empty($aSystem) || !is_array($aSystem) || (int)$aSystem['active'] != 1)
  173. continue;
  174. if($bIndexCheck && $iIndex % (int)$aSystem['frequency'] != 0)
  175. continue;
  176. $mixedResponce = $this->_getRequestedDataBySystem($aSystem, (!empty($aCachedData) && isset($aCachedData[$sName]) ? (int)$aCachedData[$sName] : 0));
  177. if($mixedResponce === false)
  178. continue;
  179. $aResult[$sName] = $mixedResponce;
  180. }
  181. return $aResult;
  182. }
  183. protected function _getRequestedDataBySystem($aSystem, $iCachedData = 0)
  184. {
  185. if(!BxDolService::isSerializedService($aSystem['service_call']))
  186. return false;
  187. $aResponce = BxDolService::callSerialized($aSystem['service_call'], array('count' => (int)$iCachedData));
  188. if(empty($aResponce) || !is_array($aResponce) || !isset($aResponce['count'], $aResponce['method']))
  189. return false;
  190. return $aResponce;
  191. }
  192. }
  193. /** @} */