Server IP : 80.87.202.40 / Your IP : 216.73.216.169 Web Server : Apache System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64 User : bitrix ( 600) PHP Version : 8.2.27 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/main/lib/update/ |
Upload File : |
<?php namespace Bitrix\Main\Update; use Bitrix\Main; use Bitrix\Main\HttpApplication; use Bitrix\Main\Web\Json; use Bitrix\Main\Config\Option; use Bitrix\Main\Context; use Bitrix\Main\Localization\Loc; /** * Class Stepper * @package Bitrix\Main\Update * This class can be used if only you do not alter tables in DB. Agent will not be executed if module is not installed. * @example Code to bind agent in updater: * \Bitrix\Main\Update\Stepper::bindClass('Bitrix\Tasks\Update1701', 'tasks'); * or * if ($updater->CanUpdateDatabase()) * { * $basePath = $updater->CanUpdateKernel() ? $updater->curModulePath.'/lib/somepath' : BX_ROOT.'/modules/lists/lib/somepath'; * if(include_once($_SERVER["DOCUMENT_ROOT"].$basePath."ecrmpropertyupdate.php")) * { * \Bitrix\Lists\SomePath\EcrmPropertyUpdate::bind(); * } * } */ abstract class Stepper { const CONTINUE_EXECUTION = true; const FINISH_EXECUTION = false; const THRESHOLD_TIME = 20.0; const DELAY_COEFFICIENT = 0.5; protected static $moduleId = "main"; private static $countId = 0; protected $outerParams = []; /** * Returns HTML to show updates. * @param array|string $ids * @param string $title * @return string */ public static function getHtml($ids = [], $title = "") { if (static::class !== __CLASS__) { $title = static::getTitle(); $ids = [static::$moduleId => [static::class]]; return call_user_func([__CLASS__, "getHtml"], $ids, $title); } $return = []; $count = 0; $steps = 0; if (is_string($ids)) { $ids = [$ids => null]; } foreach ($ids as $moduleId => $classesId) { $options = []; $category = "main.stepper." . $moduleId; if (is_string($classesId)) { $classesId = [$classesId]; } if (is_array($classesId)) { foreach ($classesId as $classId) { if (($option = Option::get($category, $classId)) !== "") { $options[$classId] = $option; } } } elseif (is_null($classesId)) { $options = Option::getForModule($category); } foreach ($options as $classId => $option) { $option = unserialize($option, ['allowed_classes' => false]); if (is_array($option)) { $return[] = [ "moduleId" => $moduleId, "class" => $classId, "title" => $option["title"] ?? '', "steps" => $option["steps"], "count" => $option["count"], ]; $count += $option["count"]; $steps += ($option["count"] > $option["steps"] ? $option["steps"] : $option["count"]); } } } $result = ''; if (!empty($return)) { $id = ++self::$countId; \CJSCore::Init(['update_stepper']); $title = empty($title) ? self::getTitle() : $title; $progress = $count > 0 ? intval($steps * 100 / $count) : 0; $result .= <<<HTML <div class="main-stepper main-stepper-show" id="{$id}-container" data-bx-steps-count="{$count}"> <div class="main-stepper-info" id="{$id}-title">{$title}</div> <div class="main-stepper-inner"> <div class="main-stepper-bar"> <div class="main-stepper-bar-line" id="{$id}-bar" style="width:{$progress}%;"></div> </div> <div class="main-stepper-steps"><span id="{$id}-steps">{$steps}</span> / <span id="{$id}-count">{$count}</span></div> <div class="main-stepper-error-text" id="{$id}-error"></div> </div> </div> HTML; $return = Json::encode($return); $result = <<<HTML <div class="main-stepper-block">{$result} <script>BX.ready(function(){ if (BX && BX["UpdateStepperRegister"]) { BX.UpdateStepperRegister({$id}, {$return}); }});</script> </div> HTML; } return $result; } public static function getTitle() { return Loc::getMessage("STEPPER_TITLE"); } /** * Executes an agent. * @return string */ public static function execAgent() { global $pPERIOD; $updater = self::createInstance(); $className = get_class($updater); $option = Option::get("main.stepper." . $updater->getModuleId(), $className); if ($option !== "") { $option = unserialize($option, ['allowed_classes' => false]); } $option = is_array($option) ? $option : []; $updater->setOuterParams(func_get_args()); $startTime = microtime(true); if ($updater->execute($option) === self::CONTINUE_EXECUTION) { $executeTime = microtime(true) - $startTime; $threshold = (float)($option["thresholdTime"] ?? self::THRESHOLD_TIME); if ($executeTime > $threshold) { // delaying next execution time proportionally to the last execution time $delayCoefficient = (float)($option["delayCoefficient"] ?? self::DELAY_COEFFICIENT); /** @see main/classes/general/agent.php:498 */ $pPERIOD = (int)round($executeTime + $executeTime * $delayCoefficient); } $option["steps"] = (int)($option["steps"] ?? 0); $option["count"] = (int)($option["count"] ?? 0); $option["lastTime"] = $executeTime; $option["totalTime"] = (float)($option["totalTime"] ?? 0.0) + $executeTime; $option["title"] = $updater::getTitle(); Option::set("main.stepper." . $updater->getModuleId(), $className, serialize($option)); return $className . '::execAgent(' . $updater::makeArguments($updater->getOuterParams()) . ');'; } Option::delete('main.stepper.' . $updater->getModuleId(), ['name' => $className]); Option::delete('main.stepper.' . $updater->getModuleId(), ['name' => '\\' . $className]); return ''; } /** * @deprecated Does nothing. */ public function __destruct() { } /** * Executes some action, and if return value is false, agent will be deleted. * @param array $option Array with main data to show if it is necessary like {steps : 35, count : 7}, where steps is an amount of iterations, count - current position. * @return boolean */ abstract function execute(array &$option); public function setOuterParams(array $outerParams): void { $this->outerParams = $outerParams; } public function getOuterParams(): array { return $this->outerParams; } /** * It is possible to pass only integer and string values for now. But you can make your own method or extend this one. * @param array $arguments * @return string */ public static function makeArguments($arguments = []): string { if (is_array($arguments)) { foreach ($arguments as $key => $val) { if (is_string($val)) { $arguments[$key] = "'" . EscapePHPString($val, "'") . "'"; } else { $arguments[$key] = intval($val); } } return implode(", ", $arguments); } return ""; } /** * Just a fabric method. * @return Stepper */ public static function createInstance() { return new static; } /** * Wrap-function to get moduleId. * @return string */ public static function getModuleId() { return static::$moduleId; } /** * Adds agent for current class. * @param int $delay Delay for running agent * @param array $withArguments Data that will available in $stepper->outerParams * @return void */ public static function bind($delay = 300, $withArguments = []) { /** @var Stepper | string $c */ $c = get_called_class(); self::bindClass($c, $c::getModuleId(), $delay, $withArguments); } /** * Adds agent for class $className for $moduleId module. Example for updater: \Bitrix\Main\Stepper::bindClass('\Bitrix\SomeModule\SomeClass', 'somemodule'). * @param string $className Class like \Bitrix\SomeModule\SomeClass extends Stepper. * @param string $moduleId Module ID like somemodule. * @param int $delay Delay for running agent * @param array $withArguments * @return void */ public static function bindClass($className, $moduleId, $delay = 300, $withArguments = []) { if (class_exists("\CAgent")) { $addAgent = true; $withArguments = is_array($withArguments) ? $withArguments : []; $delay = (int)$delay; if ($delay <= 0) { /** @var Stepper $className */ $addAgent = ('' !== call_user_func_array([$className, "execAgent"], $withArguments)); } if ($addAgent) { if (Option::get("main.stepper." . $moduleId, $className) === "") { Option::set("main.stepper." . $moduleId, $className, serialize([])); } \CTimeZone::Disable(); \CAgent::AddAgent( $className . '::execAgent(' . (empty($withArguments) ? '' : call_user_func_array([$className, "makeArguments"], [$withArguments])) . ');', $moduleId, "Y", 1, "", "Y", date(Main\Type\Date::convertFormatToPhp(\CSite::GetDateFormat()), time() + $delay), 100, false, false ); \CTimeZone::Enable(); } } else { $connection = \Bitrix\Main\Application::getConnection(); $helper = $connection->getSqlHelper(); $arguments = ''; if (!empty($withArguments)) { $arguments = class_exists($className) ? call_user_func_array([$className, "makeArguments"], [$withArguments]) : self::makeArguments($withArguments); } $name = $helper->forSql($className . '::execAgent(' . $arguments . ');', 2000); $className = $helper->forSql($className); $moduleId = $helper->forSql($moduleId); $agent = $connection->query("SELECT ID FROM b_agent WHERE MODULE_ID='" . $moduleId . "' AND NAME = '" . $name . "' AND USER_ID IS NULL")->fetch(); if (!$agent) { $connection->query( "INSERT INTO b_agent (MODULE_ID, SORT, NAME, ACTIVE, AGENT_INTERVAL, IS_PERIOD, NEXT_EXEC) VALUES ('" . $moduleId . "', 100, '" . $name . "', 'Y', 1, 'Y', " . ($delay > 0 ? $helper->addSecondsToDateTime($delay) : $helper->getCurrentDateTimeFunction()) . ")" ); $merge = $helper->prepareMerge( 'b_option', ['MODULE_ID', 'NAME'], [ 'MODULE_ID' => 'main.stepper.' . $moduleId, 'NAME' => $className, 'VALUE' => 'a:0:{}', ], [ 'VALUE' => 'a:0:{}', ] ); if ($merge) { $connection->Query($merge[0]); } } } } /** * Just method to check request. * @return void */ public static function checkRequest() { $result = []; $data = Context::getCurrent()->getRequest()->getPost("stepper"); if (is_array($data)) { foreach ($data as $stepper) { if (($option = Option::get("main.stepper." . $stepper["moduleId"], $stepper["class"])) !== "" && ($res = unserialize($option, ['allowed_classes' => false])) && is_array($res)) { $r = [ "moduleId" => $stepper["moduleId"], "class" => $stepper["class"], "steps" => $res["steps"], "count" => $res["count"], ]; $result[] = $r; } } } self::sendJson($result); } /** * Sends json. * @param $result * @return void */ private static function sendJson($result) { global $APPLICATION; $APPLICATION->RestartBuffer(); header('Content-Type:application/json; charset=UTF-8'); echo Json::encode($result); \CMain::finalActions(); die; } protected function writeToLog(\Exception $exception) { $application = HttpApplication::getInstance(); $exceptionHandler = $application->getExceptionHandler(); $exceptionHandler->writeToLog($exception); } }