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/classes/general/ |
Upload File : |
<?php /** * Bitrix Framework * @package bitrix * @subpackage main * @copyright 2001-2024 Bitrix */ use Bitrix\Main\Localization\Loc; Loc::loadMessages(__FILE__); class CAllAgent { protected const LOCK_TIME = 600; public static function AddAgent( $name, // PHP function name $module = "", // module $period = "N", // check for agent execution count in period of time $interval = 86400, // time interval between execution $datecheck = "", // first check for execution time $active = "Y", // is the agent active or not $next_exec = "", // first execution time $sort = 100, // order $user_id = false, // user $existError = true // return error, if agent already exist ) { global $DB, $APPLICATION; $z = $DB->Query(" SELECT ID FROM b_agent WHERE NAME = '".$DB->ForSql($name)."' AND USER_ID".($user_id? " = ".(int)$user_id: " IS NULL") ); if (!($agent = $z->Fetch())) { $arFields = array( "MODULE_ID" => $module, "SORT" => $sort, "NAME" => $name, "ACTIVE" => $active, "AGENT_INTERVAL" => $interval, "IS_PERIOD" => $period, "USER_ID" => $user_id, ); $next_exec = (string)$next_exec; if ($next_exec != '') $arFields["NEXT_EXEC"] = $next_exec; $ID = CAgent::Add($arFields); return $ID; } else { if (!$existError) return $agent['ID']; if ($APPLICATION instanceof CMain) { $e = new CAdminException(array( array( "id" => "agent_exist", "text" => ($user_id ? Loc::getMessage("MAIN_AGENT_ERROR_EXIST_FOR_USER", array('#AGENT#' => $name, '#USER_ID#' => $user_id)) : Loc::getMessage("MAIN_AGENT_ERROR_EXIST_EXT", array('#AGENT#' => $name)) ) ) )); $APPLICATION->throwException($e); } return false; } } public static function Add($arFields) { global $DB, $CACHE_MANAGER; if (CAgent::CheckFields($arFields)) { if (!is_set($arFields, "NEXT_EXEC")) $arFields["~NEXT_EXEC"] = $DB->GetNowDate(); if (CACHED_b_agent !== false) $CACHE_MANAGER->CleanDir("agents"); $ID = $DB->Add("b_agent", $arFields); foreach (GetModuleEvents("main", "OnAfterAgentAdd", true) as $arEvent) ExecuteModuleEventEx($arEvent, array( $arFields, )); return $ID; } return false; } public static function RemoveAgent($name, $module = "", $user_id = false) { global $DB; if (trim($module) == '') $module = "AND (MODULE_ID is null or ".$DB->Length("MODULE_ID")." = 0)"; else $module = "AND MODULE_ID = '".$DB->ForSql($module, 50)."'"; $strSql = " DELETE FROM b_agent WHERE NAME = '".$DB->ForSql($name)."' ".$module." AND USER_ID".($user_id ? " = ".(int)$user_id : " IS NULL"); $DB->Query($strSql); } public static function Delete($id) { global $DB; $id = intval($id); if ($id <= 0) return false; $DB->Query("DELETE FROM b_agent WHERE ID = ".$id); return true; } public static function RemoveModuleAgents($module) { global $DB; if ($module <> '') { $strSql = "DELETE FROM b_agent WHERE MODULE_ID='".$DB->ForSql($module,255)."'"; $DB->Query($strSql); } } public static function Update($ID, $arFields) { global $DB, $CACHE_MANAGER; $ign_name = false; $ID = intval($ID); if(is_set($arFields, "ACTIVE") && $arFields["ACTIVE"]!="Y") $arFields["ACTIVE"]="N"; if(is_set($arFields, "IS_PERIOD") && $arFields["IS_PERIOD"]!="Y") $arFields["IS_PERIOD"]="N"; if(!is_set($arFields, "NAME")) $ign_name = true; if(CAgent::CheckFields($arFields, $ign_name)) { if(CACHED_b_agent !== false) $CACHE_MANAGER->CleanDir("agents"); $strUpdate = $DB->PrepareUpdate("b_agent", $arFields); $strSql = "UPDATE b_agent SET ".$strUpdate." WHERE ID=".$ID; $res = $DB->Query($strSql); return $res; } return false; } public static function GetById($ID) { return CAgent::GetList(Array(), Array("ID"=>intval($ID))); } public static function GetList($arOrder = Array("ID" => "DESC"), $arFilter = array()) { global $DB; $arSqlSearch = array(); $arSqlOrder = array(); $arOFields = array( "ID" => "A.ID", "ACTIVE" => "A.ACTIVE", "IS_PERIOD" => "A.IS_PERIOD", "NAME" => "A.NAME", "MODULE_ID" => "A.MODULE_ID", "USER_ID" => "A.USER_ID", "LAST_EXEC" => "A.LAST_EXEC", "AGENT_INTERVAL" => "A.AGENT_INTERVAL", "NEXT_EXEC" => "A.NEXT_EXEC", "DATE_CHECK" => "A.DATE_CHECK", "SORT" => "A.SORT" ); if (!is_array($arFilter)) { $arFilter = []; } foreach ($arFilter as $key => $val) { if ((string)$val == '') { continue; } switch(strtoupper($key)) { case "ID": $arSqlSearch[] = "A.ID=".(int)$val; break; case "ACTIVE": $t_val = mb_strtoupper($val); if($t_val == "Y" || $t_val == "N") $arSqlSearch[] = "A.ACTIVE='".$t_val."'"; break; case "IS_PERIOD": $t_val = mb_strtoupper($val); if($t_val=="Y" || $t_val=="N") $arSqlSearch[] = "A.IS_PERIOD='".$t_val."'"; break; case "NAME": $arSqlSearch[] = "A.NAME LIKE '".$DB->ForSQLLike($val)."'"; break; case "=NAME": $arSqlSearch[] = "A.NAME = '".$DB->ForSQL($val)."'"; break; case "MODULE_ID": $arSqlSearch[] = "A.MODULE_ID = '".$DB->ForSQL($val)."'"; break; case "USER_ID": $arSqlSearch[] = "A.USER_ID ".(intval($val)<=0?"IS NULL":"=".intval($val)); break; case "LAST_EXEC": $arr = ParseDateTime($val, CLang::GetDateFormat()); if($arr) { $date2 = mktime(0, 0, 0, $arr["MM"], $arr["DD"]+1, $arr["YYYY"]); $arSqlSearch[] = "A.LAST_EXEC>=".$DB->CharToDateFunction($DB->ForSql($val), "SHORT")." AND A.LAST_EXEC<".$DB->CharToDateFunction(ConvertTimeStamp($date2), "SHORT"); } break; case "NEXT_EXEC": $arr = ParseDateTime($val); if($arr) { $date2 = mktime(0, 0, 0, $arr["MM"], $arr["DD"]+1, $arr["YYYY"]); $arSqlSearch[] = "A.NEXT_EXEC>=".$DB->CharToDateFunction($DB->ForSql($val), "SHORT")." AND A.NEXT_EXEC<".$DB->CharToDateFunction(ConvertTimeStamp($date2), "SHORT"); } break; } } foreach($arOrder as $by => $order) { $by = mb_strtoupper($by); $order = mb_strtoupper($order); if (isset($arOFields[$by])) { if ($order != "ASC") { $order = "DESC"; } $arSqlOrder[] = $arOFields[$by]." ".$order; } } $strSql = "SELECT A.ID, A.MODULE_ID, A.USER_ID, B.LOGIN, B.NAME as USER_NAME, B.LAST_NAME, A.SORT, ". "A.NAME, A.ACTIVE, A.RUNNING, ". $DB->DateToCharFunction("A.LAST_EXEC")." as LAST_EXEC, ". $DB->DateToCharFunction("A.NEXT_EXEC")." as NEXT_EXEC, ". $DB->DateToCharFunction("A.DATE_CHECK")." as DATE_CHECK, ". "A.AGENT_INTERVAL, A.IS_PERIOD, A.RETRY_COUNT ". "FROM b_agent A LEFT JOIN b_user B ON(A.USER_ID = B.ID)"; $strSql .= !empty($arSqlSearch) ? " WHERE ".implode(" AND ", $arSqlSearch) : ""; $strSql .= !empty($arSqlOrder) ? " ORDER BY ".implode(", ", $arSqlOrder) : ""; $res = $DB->Query($strSql); return $res; } public static function CheckFields($arFields, $ign_name = false) { global $DB, $APPLICATION; $errMsg = array(); if(!$ign_name && (!is_set($arFields, "NAME") || mb_strlen(trim($arFields["NAME"])) <= 2)) $errMsg[] = array("id" => "NAME", "text" => Loc::getMessage("MAIN_AGENT_ERROR_NAME")); if( array_key_exists("NEXT_EXEC", $arFields) && ( $arFields["NEXT_EXEC"] == "" || !$DB->IsDate($arFields["NEXT_EXEC"], false, false, "FULL") ) ) { $errMsg[] = array("id" => "NEXT_EXEC", "text" => Loc::getMessage("MAIN_AGENT_ERROR_NEXT_EXEC")); } if( array_key_exists("DATE_CHECK", $arFields) && $arFields["DATE_CHECK"] <> "" && !$DB->IsDate($arFields["DATE_CHECK"], false, false, "FULL") ) { $errMsg[] = array("id" => "DATE_CHECK", "text" => Loc::getMessage("MAIN_AGENT_ERROR_DATE_CHECK")); } if( array_key_exists("LAST_EXEC", $arFields) && $arFields["LAST_EXEC"] <> "" && !$DB->IsDate($arFields["LAST_EXEC"], false, false, "FULL") ) { $errMsg[] = array("id" => "LAST_EXEC", "text" => Loc::getMessage("MAIN_AGENT_ERROR_LAST_EXEC")); } if(!empty($errMsg)) { if ($APPLICATION instanceof CMain) { $e = new CAdminException($errMsg); $APPLICATION->ThrowException($e); } return false; } return true; } /** * Three states: no cron (null), on cron (true), on hit (false). * @return bool|null */ protected static function OnCron() { if (COption::GetOptionString('main', 'agents_use_crontab', 'N') == 'Y' || (defined('BX_CRONTAB_SUPPORT') && BX_CRONTAB_SUPPORT === true)) { return (defined('BX_CRONTAB') && BX_CRONTAB === true); } return null; } public static function CheckAgents() { define("START_EXEC_AGENTS_1", microtime(true)); define("BX_CHECK_AGENT_START", true); //For a while agents will execute only on primary cluster group if((defined("NO_AGENT_CHECK") && NO_AGENT_CHECK===true) || (defined("BX_CLUSTER_GROUP") && BX_CLUSTER_GROUP !== 1)) return null; $res = CAgent::ExecuteAgents(); define("START_EXEC_AGENTS_2", microtime(true)); return $res; } public static function ExecuteAgents() { global $DB, $CACHE_MANAGER, $pPERIOD, $USER; $cron = static::OnCron(); if ($cron !== null) { $str_crontab = ($cron ? " AND IS_PERIOD='N' " : " AND IS_PERIOD='Y' "); } else { $str_crontab = ""; } $saved_time = 0; $cache_id = "agents".$str_crontab; if (CACHED_b_agent !== false && $CACHE_MANAGER->Read(CACHED_b_agent, $cache_id, "agents")) { $saved_time = $CACHE_MANAGER->Get($cache_id); if (time() < $saved_time) { return ""; } } $connection = \Bitrix\Main\Application::getConnection(); $helper = $connection->getSqlHelper(); $strSql = " SELECT 'x' FROM b_agent WHERE ACTIVE = 'Y' AND NEXT_EXEC <= " . $helper->getCurrentDateTimeFunction() . " AND (DATE_CHECK IS NULL OR DATE_CHECK <= " . $helper->getCurrentDateTimeFunction() . ") ".$str_crontab." LIMIT 1 "; $db_result_agents = $DB->Query($strSql); if ($db_result_agents->Fetch()) { if(!$connection->lock('agent')) { return ""; } } else { if (CACHED_b_agent !== false) { $rs = $DB->Query("SELECT UNIX_TIMESTAMP(NEXT_EXEC)-UNIX_TIMESTAMP(" . $helper->getCurrentDateTimeFunction() . ") DATE_DIFF FROM b_agent WHERE ACTIVE = 'Y' " . $str_crontab . " ORDER BY NEXT_EXEC LIMIT 1"); $ar = $rs->Fetch(); if (!$ar || $ar["DATE_DIFF"] < 0) $date_diff = 0; elseif ($ar["DATE_DIFF"] > CACHED_b_agent) $date_diff = CACHED_b_agent; else $date_diff = $ar["DATE_DIFF"]; if ($saved_time > 0) { $CACHE_MANAGER->Clean($cache_id, "agents"); $CACHE_MANAGER->Read(CACHED_b_agent, $cache_id, "agents"); } $CACHE_MANAGER->Set($cache_id, intval(time()+$date_diff)); } return ""; } $strSql = "SELECT ID, NAME, AGENT_INTERVAL, IS_PERIOD, MODULE_ID, RETRY_COUNT ". "FROM b_agent ". "WHERE ACTIVE = 'Y' ". " AND NEXT_EXEC <= " . $helper->getCurrentDateTimeFunction() . " ". " AND (DATE_CHECK IS NULL OR DATE_CHECK <= " . $helper->getCurrentDateTimeFunction() . ") ". $str_crontab. " ORDER BY RUNNING ASC, SORT desc "; // limit selection to prevent excessive UPDATE $limit = ($cron ? COption::GetOptionInt("main", "agents_cron_limit", 1000) : COption::GetOptionInt("main", "agents_limit", 100)); if ($limit > 0) { $strSql .= ' LIMIT ' . $limit; } $db_result_agents = $DB->Query($strSql); $ids = ''; $agents_array = array(); while ($db_result_agents_array = $db_result_agents->Fetch()) { $agents_array[] = $db_result_agents_array; $ids .= ($ids <> ''? ', ':'').$db_result_agents_array["ID"]; } if ($ids <> '') { $strSql = "UPDATE b_agent SET DATE_CHECK = " . $helper->addSecondsToDateTime(self::LOCK_TIME) . " WHERE ID IN (".$ids.")"; $DB->Query($strSql); } $connection->unlock('agent'); /** @var callable|false $logFunction */ $logFunction = (defined("BX_AGENTS_LOG_FUNCTION") && function_exists(BX_AGENTS_LOG_FUNCTION)? BX_AGENTS_LOG_FUNCTION : false); ignore_user_abort(true); $startTime = time(); foreach ($agents_array as $arAgent) { if (time() - $startTime > self::LOCK_TIME - 30) { // locking time control; 30 seconds delta is for the possibly last agent break; } if ($logFunction) { $logFunction($arAgent, "start"); } if ($arAgent["MODULE_ID"] <> '' && $arAgent["MODULE_ID"]!="main") { if (!CModule::IncludeModule($arAgent["MODULE_ID"])) continue; } //update the agent to the running state - if it fails it'll go to the end of the list on the next try $DB->Query("UPDATE b_agent SET RUNNING = 'Y', RETRY_COUNT = RETRY_COUNT+1 WHERE ID = ".$arAgent["ID"]); //these vars can be assigned within agent code $pPERIOD = $arAgent["AGENT_INTERVAL"]; CTimeZone::Disable(); // global $USER should not be available here $USER = null; try { $eval_result = ""; $e = eval("\$eval_result=".$arAgent["NAME"]); } catch (Throwable $exception) { CTimeZone::Enable(); $application = \Bitrix\Main\Application::getInstance(); $exceptionHandler = $application->getExceptionHandler(); $exceptionHandler->writeToLog(new \Bitrix\Main\SystemException("Error in agent {$arAgent["NAME"]}.", 0, '', 0, $exception)); continue; } CTimeZone::Enable(); if ($logFunction) { $logFunction($arAgent, "finish", $eval_result, $e); } if ($e === false) { continue; } elseif ($eval_result == '') { $strSql = "DELETE FROM b_agent WHERE ID = ".$arAgent["ID"]; } else { if ($logFunction && function_exists('token_get_all')) { if (count(token_get_all("<?php ".$eval_result)) < 3) { //probably there is an error in the result $logFunction($arAgent, "not_callable", $eval_result, $e); } } $strSql = " UPDATE b_agent SET NAME = '".$DB->ForSQL($eval_result)."', LAST_EXEC = " . $helper->getCurrentDateTimeFunction() . ", NEXT_EXEC = " . $helper->addSecondsToDateTime($pPERIOD, $arAgent["IS_PERIOD"]=="Y"? "NEXT_EXEC" : null) . ", DATE_CHECK = NULL, RUNNING = 'N', RETRY_COUNT = 0 WHERE ID = ".$arAgent["ID"]; } $DB->Query($strSql); } return null; } } class CAgent extends CAllAgent { }