/**
  * Checks for broken counters.
  * Expirity counter is broken if it is < 0, or if it is more than tasks count in list of expired tasks
  * Other counters is broken if it is < 0, or if it is != tasks count in list of respective tasks
  *
  * Method is called inside CTask::GetList() to perform recounting of broken counters.
  * 
  * @param $arFilter Filter was used in GetList() call
  * @param $tasksCountInList Number of records returned by GetList() call
  */
 public static function onTaskGetList($arFilter, $tasksCountInList)
 {
     if (!CTaskCountersProcessorInstaller::isInstallComplete()) {
         return;
     }
     // Is there our marker?
     if (!(array_key_exists('::MARKERS', $arFilter) && array_key_exists(self::MARKER_ID, $arFilter['::MARKERS']) && $tasksCountInList !== null)) {
         return;
     }
     $tasksCountInList = (int) $tasksCountInList;
     $counterOwnerUserId = $arFilter['::MARKERS'][self::MARKER_ID]['userId'];
     $counterId = $arFilter['::MARKERS'][self::MARKER_ID]['counterId'];
     $counterValue = (int) CUserCounter::GetValue($counterOwnerUserId, $counterId, $site_id = '**');
     if (in_array($counterId, array(CTaskCountersProcessor::COUNTER_TASKS_MY_EXPIRED, CTaskCountersProcessor::COUNTER_TASKS_ACCOMPLICE_EXPIRED, CTaskCountersProcessor::COUNTER_TASKS_AUDITOR_EXPIRED, CTaskCountersProcessor::COUNTER_TASKS_ORIGINATOR_EXPIRED, CTaskCountersProcessor::COUNTER_TASKS_MY_EXPIRED_CANDIDATES, CTaskCountersProcessor::COUNTER_TASKS_ACCOMPLICE_EXPIRED_CANDIDATES), true)) {
         $isExpirityCounter = true;
     } else {
         $isExpirityCounter = false;
     }
     $isCounterBrokeDetected = false;
     $realTasksCount = null;
     // Is checksum correct?
     $filterCheksum = $arFilter['::MARKERS'][self::MARKER_ID]['filterCheksum'];
     $realCheksum = self::calcFilterChecksum($arFilter);
     // break detection part
     if ($filterCheksum === $realCheksum) {
         $realTasksCount = $tasksCountInList;
         if ($counterValue < 0 || $tasksCountInList != $counterValue) {
             $isCounterBrokeDetected = true;
         }
     } else {
         if (isset($arFilter['SAME_GROUP_PARENT'], $arFilter['ONLY_ROOT_TASKS']) && $arFilter['SAME_GROUP_PARENT'] === 'Y' && $arFilter['ONLY_ROOT_TASKS'] === 'Y') {
             // unset the corresponding fields and try to compare checksums again
             unset($arFilter['SAME_GROUP_PARENT']);
             unset($arFilter['ONLY_ROOT_TASKS']);
             $realCheksum = self::calcFilterChecksum($arFilter);
             if ($filterCheksum === $realCheksum) {
                 // tasks count in list shouldn't be more than registered in counter
                 // and counter shouldn't be less than zero
                 if ($counterValue < 0 || $tasksCountInList > $counterValue) {
                     $isCounterBrokeDetected = true;
                 } else {
                     if (static::getCountersRecheckForSubTasksNeed()) {
                         $rsTasksCount = CTasks::getCount($arFilter, array('bIgnoreDbErrors' => true, 'bSkipUserFields' => true, 'bSkipExtraTables' => true));
                         if ($rsTasksCount && ($arTasksCount = $rsTasksCount->fetch()) && isset($arTasksCount['CNT'])) {
                             $realTasksCount = (int) $arTasksCount['CNT'];
                             if ($realTasksCount != $counterValue) {
                                 // and finally check
                                 $isCounterBrokeDetected = true;
                             }
                         }
                     }
                 }
             }
         }
     }
     /*
     if ( ! $isCounterBrokeDetected )
     {
     	if ($counterValue < 0)
     	{
     		$isCounterBrokeDetected = true;
     	}
     	else if ($realTasksCount !== null)
     	{
     		if ($isExpirityCounter)
     		{
     			if ($realTasksCount < $counterValue)
     				$isCounterBrokeDetected = true;
     		}
     		else
     		{
     			if ($realTasksCount !== $counterValue)
     				$isCounterBrokeDetected = true;
     		}
     	}
     }
     */
     if ($isCounterBrokeDetected) {
         ob_start();
         // a special way for correction of 'deadline expired' counters
         if ($isExpirityCounter) {
             // pend counters reinstalling (agent is used)
             self::pendCountersRecalculation();
         } else {
             if ($realTasksCount !== null) {
                 $delta = $realTasksCount - $counterValue;
                 CTaskCountersQueue::push($counterId, CTaskCountersQueue::OP_INCREMENT, array($counterOwnerUserId), $delta);
                 CTaskCountersQueue::execute();
             } else {
                 CTaskAssert::logError('[0x97e63b37] counter "' . $counterId . '" was mistimed for user ' . $counterOwnerUserId . '. But no correct data available for recount.');
             }
         }
         ob_end_clean();
     }
 }
 public static function execute()
 {
     // In case of excepted execution flow queue must be empty
     // when calling code gets control
     $queue = self::$queue;
     self::$queue = array();
     $lengthBeforeOprimization = count($queue);
     $queue = self::optimizeQueue($queue);
     $lengthAfterOprimization = count($queue);
     // $s = 'Execute queue, length: ' . $lengthAfterOprimization
     //	. ' (removed elements during optimization: '
     //	. ($lengthBeforeOprimization - $lengthAfterOprimization)
     //	. ')';
     //soundex($s);
     $arUsersAffected = array();
     foreach ($queue as $request) {
         $arUsersAffected = array_merge($arUsersAffected, $request['arUsers']);
         self::processUsersCounters($request['counterId'], $request['operation'], $request['arUsers'], $request['delta']);
     }
     $arUsersAffected = array_unique($arUsersAffected);
     foreach ($arUsersAffected as $userId) {
         self::refreshTotalCounter($userId);
     }
 }