%PDF- %PDF-
Direktori : /home/bitrix/www/bitrix/modules/sale/lib/location/util/ |
Current File : //home/bitrix/www/bitrix/modules/sale/lib/location/util/process.php |
<? /** * This class is for internal use only, not a part of public API. * It can be changed at any time without notification. * * @access private */ namespace Bitrix\Sale\Location\Util; use Bitrix\Main; abstract class Process { const JUST_SHOW_STAGES = false; const MIN_TIME_LIMIT = 5; const DEBUG_MODE = false; const DEBUG_FOLDER = '%BX_ROOT%/tmp/'; const DEBUG_FILE = '%SESSION_KEY%_process.txt'; const LOCK_FILE = '%SESSION_KEY%_lock'; const CALLBACK_TYPE_MANUAL = 'manual'; const CALLBACK_TYPE_QUOTA = 'quota'; protected $stages = array(); protected $stagesByCode = array(); protected $stage = 0; protected $step = 0; protected $data = array(); protected $time = 0; protected $timeLimit = 20; // in seconds protected $sessionKey = 'long_process'; protected $useLock = false; protected $options = array(); public function __construct($options = array()) { if(isset($options['INITIAL_TIME'])) $this->time = intval($options['INITIAL_TIME']); else $this->time = time(); $this->useLock = !!$options['USE_LOCK']; $this->options = $options; $this->restore(); if(isset($options['STEP']) && $options['STEP'] == 0) $this->reset(); $this->logMessage('#############################', false); $this->logMessage('HIT STARTED '.$this->getTimeStampString(), false); if(intval($options['TIME_LIMIT'])) $this->setTimeLimit(intval($options['TIME_LIMIT'])); $this->saveStartTime(); $this->saveMemoryPeak(); } public function addStage($params) { if(empty($params['CODE']) || empty($params['CALLBACK'])) throw new Main\SystemException('Not enought params to add stage'); $ss = intval($params['STEP_SIZE']); $this->stages[] = array( 'STEP_SIZE' => $ss ? $ss : 1, 'PERCENT' => intval($params['PERCENT']), 'CODE' => $params['CODE'], 'ORDER' => count($this->stages), 'TYPE' => strlen($params['TYPE']) ? $params['TYPE'] : static::CALLBACK_TYPE_MANUAL, 'CALLBACK' => $params['CALLBACK'], 'SUBPERCENT_CALLBACK' => $params['SUBPERCENT_CALLBACK'], 'ON_BEFORE_CALLBACK' => strlen($params['ON_BEFORE_CALLBACK']) ? $params['ON_BEFORE_CALLBACK'] : false, 'ON_AFTER_CALLBACK' => strlen($params['ON_AFTER_CALLBACK']) ? $params['ON_AFTER_CALLBACK'] : false ); $this->stagesByCode[$params['CODE']] =& $this->stages[count($this->stages) - 1]; } public function restore() { if(!isset($_SESSION[$this->sessionKey]['STAGE'])) $_SESSION[$this->sessionKey]['STAGE'] = 0; if(!isset($_SESSION[$this->sessionKey]['STEP'])) $_SESSION[$this->sessionKey]['STEP'] = 0; if(!isset($_SESSION[$this->sessionKey]['DATA'])) $_SESSION[$this->sessionKey]['DATA'] = array(); $this->stage =& $_SESSION[$this->sessionKey]['STAGE']; $this->step =& $_SESSION[$this->sessionKey]['STEP']; $this->data =& $_SESSION[$this->sessionKey]['DATA']; } // reset current condition public function reset() { $this->stage = 0; $this->step = 0; $this->data = array(); $this->clearLogFile(); } public function performStage() { return $this->performIteration(); } public function performIteration() { if($this->stage == 0 && $this->step == 0) { $this->lockProcess(); if(static::DEBUG_MODE) { $logDir = $this->getLogFileDir(); if(!file_exists($logDir)) mkdir($logDir, 755, true); $this->logMessage('PROCESS STARTED, STAGE '.$this->stages[0]['CODE']); } } $this->onBeforePerformIteration(); if(!isset($this->stages[$this->stage])) throw new Main\SystemException('No more stages to perform'); if(self::JUST_SHOW_STAGES) $this->nextStage(); else { $stage = $this->stage; if($this->stages[$stage]['ON_BEFORE_CALLBACK'] != false) call_user_func(array($this, $this->stages[$stage]['ON_BEFORE_CALLBACK'])); if($this->stages[$this->stage]['TYPE'] == static::CALLBACK_TYPE_MANUAL) call_user_func(array($this, $this->stages[$this->stage]['CALLBACK'])); elseif($this->stages[$this->stage]['TYPE'] == static::CALLBACK_TYPE_QUOTA) { while($this->checkQuota()) { $result = call_user_func(array($this, $this->stages[$this->stage]['CALLBACK'])); $this->nextStep(); if($result) break; } if($result) $this->nextStage(); } if($this->stages[$stage]['ON_AFTER_CALLBACK'] != false) call_user_func(array($this, $this->stages[$stage]['ON_AFTER_CALLBACK'])); } $this->onAfterPerformIteration(); $percent = $this->getPercent(); $this->saveMemoryPeak(); $this->logMessage('HIT ENDED '.$this->getTimeStampString(), false); if($percent == 100) $this->unLockProcess(); return $percent; } ///////////////////////////////////////////////// /// Staging ///////////////////////////////////////////////// public function setStepSize($code, $stepSize) { if(!isset($this->stagesByCode[$code])) throw new Main\SystemException('Unknown stage code passed'); if(($stepSize = intval($stepSize)) <= 0) throw new Main\SystemException('Bad step size passed'); $this->stagesByCode[$code]['STEP_SIZE'] = $stepSize; } // move to next stage public function nextStage() { $this->stage++; $this->step = 0; $this->logMessage('### NEXT STAGE >>> '.$this->stages[$this->stage]['CODE'].' in '.$this->getElapsedTimeString().', mem peak = '.$this->getMemoryPeakString().' mb'); } // move to next step public function nextStep() { $this->step++; } public function isStage($code) { return $this->stages[$this->stage]['CODE'] == $code; } protected function stageCompare($code, $way) { $currIndex = $this->stages[$this->stage]['ORDER']; $stageIndex = $this->stagesByCode[$code]['ORDER']; if($currIndex == $stageIndex) return true; if($way) // gt return $currIndex > $stageIndex; else // lt return $currIndex < $stageIndex; } // $this->stage <= $code public function stageLT($code) { return $this->stageCompare($code, false); } // $code <= $this->stage public function stageGT($code) { return $this->stageCompare($code, true); } public function setStage($stage) { foreach($this->stages as $sId => $info) { if($info['CODE'] == $stage) { $this->stage = $sId; $this->step = 0; break; } } } public function onBeforePerformIteration() { } public function onAfterPerformIteration() { } public function getStageCode() { return $this->stages[$this->stage]['CODE']; } public function getCurrStageIndex() { return $this->stage; } public function getStep() { return $this->step; } public function getStage($code) { return $this->stagesByCode[$code]; } public function getCurrStageStepSize() { return $this->stages[$this->stage]['STEP_SIZE']; } ///////////////////////////////////////////////// /// Percentage ///////////////////////////////////////////////// public function getStagePercent($sNum = false) { if($sNum === false) $stage = $this->stages[$this->stage]['PERCENT']; else $stage = is_numeric($sNum) ? $this->stages[$sNum]['PERCENT'] : $this->stagesByCode[$sNum]['PERCENT']; return $stage ? $stage : 0; } public function getPercentBetween($codeFrom, $codeTo) { return $this->getStagePercent($codeTo) - $this->getStagePercent($codeFrom); } public function getPercentFromToCurrent($codeFrom){ return $this->getStagePercent($this->stage - 1) - $this->getStagePercent($codeFrom); } public function getCurrentPercentRange() { return $this->getStagePercent($this->stage) - $this->getStagePercent($this->stage - 1); } public function getPercent() { $percent = $this->stage > 0 ? $this->stages[$this->stage - 1]['PERCENT'] : 0; $addit = 0; $cb = $this->stages[$this->stage]['SUBPERCENT_CALLBACK']; if(strlen($cb) && method_exists($this, $cb)) $addit = $this->$cb(); return $percent + $addit; } public function calcSubPercent($range) { if(!$range) return 0; return round(($this->step / $range)*($this->getStagePercent($this->stage) - $this->getStagePercent($this->stage - 1))); } public function getSubPercentByTotalAndDone($total, $done = 0) { if(!$done || !$total) return 0; $pRange = $this->getCurrentPercentRange(); $part = round($pRange * ($done / $total)); return $part >= $pRange ? $pRange : $part; } ///////////////////////////////////////////////// /// Quotas info ///////////////////////////////////////////////// public function checkQuota() { return (time() - $this->time) < $this->timeLimit; } public function setTimeLimit($timeLimit) { if($timeLimit == intval($timeLimit)) { if($timeLimit < static::MIN_TIME_LIMIT) $timeLimit = static::MIN_TIME_LIMIT; $this->timeLimit = $timeLimit; } } public function getMemoryPeak() { return $this->data['memory_peak']; } protected function saveStartTime() { if(!isset($this->data['process_time'])) $this->data['process_time'] = time(); } protected function saveMemoryPeak() { $mp = memory_get_peak_usage(false); if(!isset($this->data['memory_peak'])) $this->data['memory_peak'] = $mp; else { if($this->data['memory_peak'] < $mp) $this->data['memory_peak'] = $mp; } } ///////////////////////////////////////////////// /// Logging ///////////////////////////////////////////////// public function clearLogFile() { $logDir = $this->getLogFileDir(); if(!Main\IO\Directory::isDirectoryExists($logDir)) Main\IO\Directory::createDirectory($logDir); $logFile = $this->getLogFilePath(); Main\IO\File::putFileContents($logFile, ''); } public function getLogFileDir() { return $_SERVER['DOCUMENT_ROOT'].'/'.str_replace('%BX_ROOT%', BX_ROOT, self::DEBUG_FOLDER); } public function getLogFilePath() { return $this->getLogFileDir().str_replace('%SESSION_KEY%', $this->sessionKey, self::DEBUG_FILE); } public function logMessage($message = '', $addTimeStamp = true) { if(!static::DEBUG_MODE || !strlen($message)) return; file_put_contents( $this->getLogFilePath(), ($addTimeStamp ? $this->getTimeStampString().' ' : '').$message.PHP_EOL, FILE_APPEND ); } public function logMemoryUsage() { $this->logMessage('MEMORY USAGE: '.(memory_get_usage(false) / (1024 * 1024)).' MB', false); } public function logFinalResult() { $this->logMessage('ALL DONE!'); $this->logMessage('TOTAL PROCESS TIME: '.$this->getElapsedTimeString(), false); $this->logMessage('MEMORY PEAK (mb): '.$this->getMemoryPeakString(), false); } ///////////////////////////////////////////////// /// Lock ///////////////////////////////////////////////// public function getLockFilePath() { return $this->getLogFileDir().str_replace('%SESSION_KEY%', $this->sessionKey, self::LOCK_FILE); } public function lockProcess() { if(!$this->useLock) return; file_put_contents($this->getLockFilePath(), '1'); } public function unLockProcess() { if(!$this->useLock) return; $file = $this->getLockFilePath(); if(file_exists($file)) unlink($file); } public function checkProcessLocked() { return $this->useLock && file_exists($this->getLockFilePath()); } ///////////////////////////////////////////////// /// Diagnostics tools ///////////////////////////////////////////////// protected function getHitTime() { return time() - $this->time; } protected function getProcessTime() { return time() - $this->data['process_time']; } protected function getProcessTimeString() { return $this->getTimeString($this->getProcessTime()); } protected function getHitTimeString() { return $this->getTimeString($this->getHitTime()); } protected function getElapsedTimeString() { $time = time() - $this->data['process_time']; return $this->getProcessTimeString(); } protected function getTimeString($time = 0) { $h = floor($time / 3600); $m = floor(($time - $h * 3600) / 60); $s = $time - $h * 3600 - $m * 60; if(strlen($m) == 1) $m = '0'.$m; if(strlen($s) == 1) $s = '0'.$s; return $h.':'.$m.':'.$s; } protected function getTimeStampString() { return '['.date('H:i:s').']'; } protected function getMemoryPeakString() { return $this->getMemoryPeak() / 1048576; } ///////////////////////////////////////////////// /// Util ///////////////////////////////////////////////// public function getData() { return $this->data; } // special case for array protected function getBlock($from) { $step = $this->step; for($i = 0; $i < $step; $i++) next($from); $block = array(); $hadSmth = false; for($i = $step; $i <= $step + $this->getCurrStageStepSize(); $i++) { list($code, $elem) = each($from); if(!isset($code)) break; $hadSmth = true; $block[$code] = $elem; $this->nextStep(); } if(!$hadSmth) { $this->nextStage(); return false; } return $block; } }