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/cvetdv.ru/bitrix/modules/bizproc/classes/general/ |
Upload File : |
<?php use Bitrix\Main; use Bitrix\Bizproc; class CBPCalc { /** @var CBPActivity $activity */ private $activity; private $arErrorsList = []; private static $weekHolidays; private static $yearHolidays; private static $startWorkDay; private static $endWorkDay; private static $yearWorkdays; // Operation priority private $arPriority = [ '(' => 0, ')' => 1, ';' => 2, '=' => 3, '<' => 3, '>' => 3, '<=' => 3, '>=' => 3, '<>' => 3, '&' => 4, '+' => 5, '-' => 5, '*' => 6, '/' => 6, '^' => 7, '%' => 8, '-m' => 9, '+m' => 9, ' ' => 10, ':' => 11, 'f' => 12, ]; // Allowable functions private $arAvailableFunctions = [ 'abs' => ['args' => true, 'func' => 'FunctionAbs'], 'and' => ['args' => true, 'func' => 'FunctionAnd'], 'date' => ['args' => true, 'func' => 'FunctionDate'], 'dateadd' => ['args' => true, 'func' => 'FunctionDateAdd'], 'datediff' => ['args' => true, 'func' => 'FunctionDateDiff'], 'false' => ['args' => false, 'func' => 'FunctionFalse'], 'if' => ['args' => true, 'func' => 'FunctionIf'], 'intval' => ['args' => true, 'func' => 'FunctionIntval'], 'floatval' => ['args' => true, 'func' => 'FunctionFloatval'], 'numberformat' => ['args' => true, 'func' => 'FunctionNumberFormat'], 'min' => ['args' => true, 'func' => 'FunctionMin'], 'max' => ['args' => true, 'func' => 'FunctionMax'], 'rand' => ['args' => true, 'func' => 'FunctionRand'], 'round' => ['args' => true, 'func' => 'FunctionRound'], 'ceil' => ['args' => true, 'func' => 'FunctionCeil'], 'floor' => ['args' => true, 'func' => 'FunctionFloor'], 'not' => ['args' => true, 'func' => 'FunctionNot'], 'or' => ['args' => true, 'func' => 'FunctionOr'], 'substr' => ['args' => true, 'func' => 'FunctionSubstr'], 'strpos' => ['args' => true, 'func' => 'FunctionStrpos'], 'strlen' => ['args' => true, 'func' => 'FunctionStrlen'], 'implode' => ['args' => true, 'func' => 'FunctionImplode'], 'explode' => ['args' => true, 'func' => 'FunctionExplode'], 'randstring' => ['args' => true, 'func' => 'FunctionRandString'], 'true' => ['args' => false, 'func' => 'FunctionTrue'], 'convert' => ['args' => true, 'func' => 'FunctionConvert'], 'merge' => ['args' => true, 'func' => 'FunctionMerge'], 'addworkdays' => ['args' => true, 'func' => 'FunctionAddWorkDays'], 'workdateadd' => ['args' => true, 'func' => 'FunctionWorkDateAdd'], 'isworkday' => ['args' => true, 'func' => 'FunctionIsWorkDay'], 'isworktime' => ['args' => true, 'func' => 'FunctionIsWorkTime'], 'urlencode' => ['args' => true, 'func' => 'FunctionUrlencode'], 'touserdate' => ['args' => true, 'func' => 'FunctionToUserDate'], 'getuserdateoffset' => ['args' => true, 'func' => 'FunctionGetUserDateOffset'], 'strtolower' => ['args' => true, 'func' => 'FunctionStrtolower'], 'strtoupper' => ['args' => true, 'func' => 'FunctionStrtoupper'], 'ucwords' => ['args' => true, 'func' => 'FunctionUcwords'], 'ucfirst' => ['args' => true, 'func' => 'FunctionUcfirst'], 'strtotime' => ['args' => true, 'func' => 'FunctionStrtotime'], 'locdate' => ['args' => true, 'func' => 'FunctionLocDate'], 'shuffle' => ['args' => true, 'func' => 'FunctionShuffle'], 'firstvalue'=> ['args' => true, 'func' => 'FunctionFirstValue'], 'swirl' => ['args' => true, 'func' => 'FunctionSwirl'], 'getdocumenturl' => ['args' => true, 'func' => 'FunctionGetDocumentUrl'], 'trim' => ['args' => true, 'func' => 'functionTrim'], ]; // Allowable errors private $arAvailableErrors = [ 0 => 'Incorrect variable name - "#STR#"', 1 => 'Empty', 2 => 'Syntax error "#STR#"', 3 => 'Unknown function "#STR#"', 4 => 'Unmatched closing bracket ")"', 5 => 'Unmatched opening bracket "("', 6 => 'Division by zero', 7 => 'Incorrect order of operands', 8 => 'Incorrect arguments of function "#STR#"', ]; const Operation = 0; const Variable = 1; const Constant = 2; public function __construct(CBPActivity $activity) { $this->activity = $activity; } private function getVariableValue($variable) { $variable = trim($variable); if (!preg_match(CBPActivity::ValuePattern, $variable)) return null; return $this->activity->ParseValue($variable); } private function setError($errnum, $errstr = '') { $this->arErrorsList[] = [$errnum, str_replace('#STR#', $errstr, $this->arAvailableErrors[$errnum])]; } public function getErrors() { return $this->arErrorsList; } /* Return array of polish notation array( key => array(value, self::Operation | self::Variable | self::Constant) ) */ private function getPolishNotation($text) { $text = trim($text); if (mb_substr($text, 0, 1) === '=') $text = mb_substr($text, 1); if (mb_strpos($text, '{{=') === 0 && mb_substr($text, -2) == '}}') { $text = mb_substr($text, 3); $text = mb_substr($text, 0, -2); } if ($text == '') { $this->SetError(1); return false; } $arPolishNotation = $arStack = []; $prev = ''; $preg = '/ \s*\(\s* | \s*\)\s* | \s*,\s* | # Combine ranges of variables \s*;\s* | # Combine ranges of variables \s*=\s* | \s*<=\s* | \s*>=\s* | \s*<>\s* | \s*<\s* | \s*>\s* | \s*&\s* | # String concatenation \s*\+\s* | # Addition or unary plus \s*-\s* | \s*\*\s* | \s*\/\s* | \s*\^\s* | # Exponentiation \s*%\s* | # Percent \s*[\d\.]+\s* | # Numbers \s*\'[^\']*\'\s* | # String constants in apostrophes \s*"[^"]*"\s* | # String constants in quotes (\s*\w+\s*\(\s*) | # Function names \s*'.CBPActivity::ValueInternalPattern.'\s* | # Variables (?<error>.+) # Any erroneous substring /xi'; while (preg_match($preg, $text, $match)) { if (isset($match['error'])) { $this->SetError(2, $match['error']); return false; } $str = trim($match[0]); if ($str === ",") $str = ";"; if (isset($match[1]) && $match[1]) { $str = mb_strtolower($str); list($name, $left) = explode('(', $str); $name = trim($name); if (isset($this->arAvailableFunctions[$name])) { if (!$arStack) { array_unshift($arStack, [$name, $this->arPriority['f']]); } else { while ($this->arPriority['f'] <= $arStack[0][1]) { $op = array_shift($arStack); $arPolishNotation[] = [$op[0], self::Operation]; if (!$arStack) break; } array_unshift($arStack, [$name, $this->arPriority['f']]); } } else { $this->SetError(3, $name); return false; } $str = '('; } if ($str == '-' || $str == '+') { if ($prev == '' || in_array($prev, ['(', ';', '=', '<=', '>=', '<>', '<', '>', '&', '+', '-', '*', '/', '^'])) $str .= 'm'; } $prev = $str; switch ($str) { case '(': array_unshift($arStack, ['(', $this->arPriority['(']]); break; case ')': while ($op = array_shift($arStack)) { if ($op[0] == '(') break; $arPolishNotation[] = [$op[0], self::Operation]; } if ($op == null) { $this->SetError(4); return false; } break; case ';' : case '=' : case '<=': case '>=': case '<>': case '<' : case '>' : case '&' : case '+' : case '-' : case '+m': case '-m': case '*' : case '/' : case '^' : case '%' : if (!$arStack) { array_unshift($arStack, [$str, $this->arPriority[$str]]); break; } while ($this->arPriority[$str] <= $arStack[0][1]) { $op = array_shift($arStack); $arPolishNotation[] = [$op[0], self::Operation]; if (!$arStack) break; } array_unshift($arStack, [$str, $this->arPriority[$str]]); break; default: if (mb_substr($str, 0, 1) == '0' || (int) $str) { $arPolishNotation[] = [(float)$str, self::Constant]; break; } if (mb_substr($str, 0, 1) == '"' || mb_substr($str, 0, 1) == "'") { $arPolishNotation[] = [mb_substr($str, 1, -1), self::Constant]; break; } $arPolishNotation[] = [$str, self::Variable]; } $text = mb_substr($text, mb_strlen($match[0])); } while ($op = array_shift($arStack)) { if ($op[0] == '(') { $this->SetError(5); return false; } $arPolishNotation[] = [$op[0], self::Operation]; } return $arPolishNotation; } public function calculate($text) { if (!$arPolishNotation = $this->GetPolishNotation($text)) return null; $stack = []; foreach ($arPolishNotation as $item) { switch ($item[1]) { case self::Constant: array_unshift($stack, $item[0]); break; case self::Variable: array_unshift($stack, $this->GetVariableValue($item[0])); break; case self::Operation: switch ($item[0]) { case ';': $arg2 = array_shift($stack); $arg1 = array_shift($stack); if (!is_array($arg1) || !isset($arg1[0])) $arg1 = [$arg1]; $arg1[] = $arg2; array_unshift($stack, $arg1); break; case '=': $arg2 = array_shift($stack); $arg1 = array_shift($stack); array_unshift($stack, $arg1 == $arg2); break; case '<=': $arg2 = array_shift($stack); $arg1 = array_shift($stack); array_unshift($stack, $arg1 <= $arg2); break; case '>=': $arg2 = array_shift($stack); $arg1 = array_shift($stack); array_unshift($stack, $arg1 >= $arg2); break; case '<>': $arg2 = array_shift($stack); $arg1 = array_shift($stack); array_unshift($stack, $arg1 != $arg2); break; case '<': $arg2 = array_shift($stack); $arg1 = array_shift($stack); array_unshift($stack, $arg1 < $arg2); break; case '>': $arg2 = array_shift($stack); $arg1 = array_shift($stack); array_unshift($stack, $arg1 > $arg2); break; case '&': $arg2 = (string) array_shift($stack); $arg1 = (string) array_shift($stack); array_unshift($stack, $arg1 . $arg2); break; case '+': $arg2 = (float) array_shift($stack); $arg1 = (float) array_shift($stack); array_unshift($stack, $arg1 + $arg2); break; case '-': $arg2 = (float) array_shift($stack); $arg1 = (float) array_shift($stack); array_unshift($stack, $arg1 - $arg2); break; case '+m': $arg = (float) array_shift($stack); array_unshift($stack, $arg); break; case '-m': $arg = (float) array_shift($stack); array_unshift($stack, (-$arg)); break; case '*': $arg2 = (float) array_shift($stack); $arg1 = (float) array_shift($stack); array_unshift($stack, $arg1 * $arg2); break; case '/': $arg2 = (float) array_shift($stack); $arg1 = (float) array_shift($stack); if (0 == $arg2) { $this->SetError(6); return null; } array_unshift($stack, $arg1 / $arg2); break; case '^': $arg2 = (float) array_shift($stack); $arg1 = (float) array_shift($stack); array_unshift($stack, pow($arg1, $arg2)); break; case '%': $arg = (float) array_shift($stack); array_unshift($stack, $arg / 100); break; default: $func = $this->arAvailableFunctions[$item[0]]['func']; if ($this->arAvailableFunctions[$item[0]]['args']) { $arg = array_shift($stack); $val = $this->$func($arg); } else { $val = $this->$func(); } $error = is_float($val) && (is_nan($val) || is_infinite($val)); if ($error) { $this->SetError(8, $item[0]); return null; } array_unshift($stack, $val); } } } if (count($stack) > 1) { $this->SetError(7); return null; } return array_shift($stack); } private function arrgsToArray($args) { if (!is_array($args)) return [$args]; $result = []; foreach ($args as $arg) { if (!is_array($arg)) { $result[] = $arg; } else { foreach ($this->ArrgsToArray($arg) as $val) $result[] = $val; } } return $result; } /* Math */ private function functionAbs($num) { return abs((float) $num); } private function functionRound($args) { $ar = $this->ArrgsToArray($args); $val = (float)array_shift($ar); $precision = (int)array_shift($ar); return round($val, $precision); } private function functionCeil($num) { return ceil((double)$num); } private function functionFloor($num) { return floor((double)$num); } private function functionMin($args) { if (!is_array($args)) return (float) $args; foreach ($args as &$arg) $arg = (float) $arg; $args = $this->ArrgsToArray($args); return $args ? min($args) : false; } private function functionMax($args) { if (!is_array($args)) return (float) $args; foreach ($args as &$arg) $arg = (float) $arg; $args = $this->ArrgsToArray($args); return $args ? max($args) : false; } private function functionRand($args) { $ar = $this->ArrgsToArray($args); $min = (int)array_shift($ar); $max = (int)array_shift($ar); if (!$max) { $max = mt_getrandmax(); } return mt_rand($min, $max); } private function functionIntval($num) { return intval($num); } private function functionFloatval($num) { return floatval($num); } /* Logic */ private function functionTrue() { return true; } private function functionFalse() { return false; } private function functionIf($args) { if (!is_array($args)) return null; $expression = (boolean) array_shift($args); $ifTrue = array_shift($args); $ifFalse = array_shift($args); return $expression ? $ifTrue : $ifFalse; } private function functionNot($arg) { return !((boolean) $arg); } private function functionAnd($args) { if (!is_array($args)) return (boolean) $args; $args = $this->ArrgsToArray($args); foreach ($args as $arg) { if (!$arg) return false; } return true; } private function functionOr($args) { if (!is_array($args)) return (boolean) $args; $args = $this->ArrgsToArray($args); foreach ($args as $arg) { if ($arg) return true; } return false; } /* Date */ private function functionDateAdd($args) { if (!is_array($args)) { $args = [$args]; } $ar = $this->ArrgsToArray($args); $date = array_shift($ar); $offset = $this->getDateTimeOffset($date); $interval = array_shift($ar); if (($date = $this->makeTimestamp($date)) === false) { return null; } if (empty($interval)) { return $date; // new Bizproc\BaseType\Value\DateTime($date, $offset); } // 1Y2M3D4H5I6S, -4 days 5 hours, 1month, 5h $interval = trim($interval); $bMinus = false; if (mb_substr($interval, 0, 1) === "-") { $interval = mb_substr($interval, 1); $bMinus = true; } static $arMap = ["y" => "YYYY", "year" => "YYYY", "years" => "YYYY", "m" => "MM", "month" => "MM", "months" => "MM", "d" => "DD", "day" => "DD", "days" => "DD", "h" => "HH", "hour" => "HH", "hours" => "HH", "i" => "MI", "min" => "MI", "minute" => "MI", "minutes" => "MI", "s" => "SS", "sec" => "SS", "second" => "SS", "seconds" => "SS", ]; $arInterval = []; while (preg_match('/\s*([\d]+)\s*([a-z]+)\s*/i', $interval, $match)) { $match2 = mb_strtolower($match[2]); if (array_key_exists($match2, $arMap)) { $arInterval[$arMap[$match2]] = ($bMinus ? -intval($match[1]) : intval($match[1])); } $p = mb_strpos($interval, $match[0]); $interval = mb_substr($interval, $p + mb_strlen($match[0])); } $date += $offset; // to server $newDate = AddToTimeStamp($arInterval, $date); $newDate -= $offset; // to user timezone return new Bizproc\BaseType\Value\DateTime($newDate, $offset); } private function functionWorkDateAdd($args) { if (!is_array($args)) $args = [$args]; $ar = $this->ArrgsToArray($args); $date = array_shift($ar); $offset = $this->getDateTimeOffset($date); $paramInterval = array_shift($ar); $user = array_shift($ar); if ($user) { $date = $this->FunctionToUserDate([$user, $date]); $offset = $this->getDateTimeOffset($date); } if (($date = $this->makeTimestamp($date, true)) === false) return null; if (empty($paramInterval) || !CModule::IncludeModule('calendar')) return $date; $paramInterval = trim($paramInterval); $multiplier = 1; if (mb_substr($paramInterval, 0, 1) === "-") { $paramInterval = mb_substr($paramInterval, 1); $multiplier = -1; } $workDayInterval = $this->getWorkDayInterval(); $intervalMap = ["d" => $workDayInterval, "day" => $workDayInterval, "days" => $workDayInterval, "h" => 3600, "hour" => 3600, "hours" => 3600, "i" => 60, "min" => 60, "minute" => 60, "minutes" => 60, ]; $interval = 0; while (preg_match('/\s*([\d]+)\s*([a-z]+)\s*/i', $paramInterval, $match)) { $match2 = mb_strtolower($match[2]); if (array_key_exists($match2, $intervalMap)) $interval += intval($match[1]) * $intervalMap[$match2]; $p = mb_strpos($paramInterval, $match[0]); $paramInterval = mb_substr($paramInterval, $p + mb_strlen($match[0])); } if (date('H:i:s', $date) === '00:00:00') { //add start work day seconds $date += $this->getCalendarWorkTime()[0]; } $date = $this->getNearestWorkTime($date, $multiplier); if ($interval) { $days = (int) floor($interval / $workDayInterval); $hours = $interval % $workDayInterval; $remainTimestamp = $this->getWorkDayRemainTimestamp($date, $multiplier); if ($days) $date = $this->addWorkDay($date, $days * $multiplier); if ($hours > $remainTimestamp) { $date += $multiplier < 0 ? -$remainTimestamp -60 : $remainTimestamp + 60; $date = $this->getNearestWorkTime($date, $multiplier) + (($hours - $remainTimestamp) * $multiplier); } else $date += $multiplier * $hours; } $date -= $offset; return new Bizproc\BaseType\Value\DateTime($date, $offset); } private function functionAddWorkDays($args) { if (!is_array($args)) $args = [$args]; $ar = $this->ArrgsToArray($args); $date = array_shift($ar); $offset = $this->getDateTimeOffset($date); $days = (int) array_shift($ar); if (($date = $this->makeTimestamp($date)) === false) return null; if ($days === 0 || !CModule::IncludeModule('calendar')) return $date; $date = $this->addWorkDay($date, $days); return new Bizproc\BaseType\Value\DateTime($date, $offset); } private function functionIsWorkDay($args) { if (!CModule::IncludeModule('calendar')) return null; if (!is_array($args)) $args = [$args]; $ar = $this->ArrgsToArray($args); $date = array_shift($ar); $user = array_shift($ar); if ($user) { $date = $this->FunctionToUserDate([$user, $date]); } if (($date = $this->makeTimestamp($date, true)) === false) return null; return !$this->isHoliday($date); } private function functionIsWorkTime($args) { if (!CModule::IncludeModule('calendar')) return null; if (!is_array($args)) $args = [$args]; $ar = $this->ArrgsToArray($args); $date = array_shift($ar); $user = array_shift($ar); if ($user) { $date = $this->FunctionToUserDate([$user, $date]); } if (($date = $this->makeTimestamp($date, true)) === false) return null; return !$this->isHoliday($date) && $this->isWorkTime($date); } private function functionDateDiff($args) { if (!is_array($args)) $args = [$args]; $ar = $this->ArrgsToArray($args); $date1 = array_shift($ar); $date2 = array_shift($ar); $format = array_shift($ar); if ($date1 == null || $date2 == null) return null; $date1Formatted = $this->getDateTimeObject($date1); $date2Formatted = $this->getDateTimeObject($date2); if ($date1Formatted === false || $date2Formatted === false) { return null; } $interval = $date1Formatted->diff($date2Formatted); return $interval === false ? null : $interval->format($format); } private function functionDate($args) { $ar = $this->ArrgsToArray($args); $format = array_shift($ar); $date = array_shift($ar); if (!$format || !is_string($format)) { return null; } $ts = $date ? $this->makeTimestamp($date, true) : time(); if (!$ts) { return null; } return date($format, $ts); } private function functionToUserDate($args) { if (!is_array($args)) { $args = [$args]; } $ar = $this->ArrgsToArray($args); $user = array_shift($ar); $date = array_shift($ar); if (!$user) { return null; } if (!$date) { $date = time(); } elseif (($date = $this->makeTimestamp($date)) === false) { return null; } $userId = CBPHelper::ExtractUsers($user, $this->activity->GetDocumentId(), true); $offset = $userId ? CTimeZone::GetOffset($userId, true) : 0; return new Bizproc\BaseType\Value\DateTime($date, $offset); } private function functionGetUserDateOffset($args) { if (!is_array($args)) { $args = [$args]; } $ar = $this->ArrgsToArray($args); $user = array_shift($ar); if (!$user) { return null; } $userId = CBPHelper::ExtractUsers($user, $this->activity->GetDocumentId(), true); if (!$userId) { return null; } return CTimeZone::GetOffset($userId, true); } private function functionStrtotime($args) { $ar = $this->ArrgsToArray($args); $datetime = (string)array_shift($ar); $baseDate = array_shift($ar); $baseTimestamp = $baseDate ? $this->makeTimestamp($baseDate, true) : time(); if (!$baseTimestamp) { return null; } $timestamp = strtotime($datetime, (int)$baseTimestamp); if ($timestamp === false) { return null; } return new Bizproc\BaseType\Value\DateTime($timestamp); } private function functionLocDate($args) { $ar = $this->ArrgsToArray($args); $format = array_shift($ar); $date = array_shift($ar); if (!$format || !is_string($format)) { return null; } $reformFormat = $this->frameSymbolsInDateFormat($format); $timestamp = $date ? $this->makeTimestamp($date, true) : time(); if (!$timestamp) { return null; } $formattedDate = date($reformFormat, $timestamp); if ($formattedDate === false) { return null; } return $this->replaceDateToLocDate($formattedDate, $reformFormat); } /* Date - Helpers */ private function makeTimestamp($date, $appendOffset = false) { if (!$date) { return false; } //serialized date string if (is_string($date) && Bizproc\BaseType\Value\Date::isSerialized($date)) { $date = new Bizproc\BaseType\Value\Date($date); } if ($date instanceof Bizproc\BaseType\Value\Date) { return $date->getTimestamp() + ($appendOffset? $date->getOffset() : 0); } if (intval($date)."!" === $date."!") return $date; if (($result = MakeTimeStamp($date, FORMAT_DATETIME)) === false) { if (($result = MakeTimeStamp($date, FORMAT_DATE)) === false) { if (($result = MakeTimeStamp($date, "YYYY-MM-DD HH:MI:SS")) === false) { $result = MakeTimeStamp($date, "YYYY-MM-DD"); } } } return $result; } private function getWorkDayTimestamp($date) { return date('H', $date) * 3600 + date('i', $date) * 60; } private function getWorkDayRemainTimestamp($date, $multiplier = 1) { $dayTs = $this->getWorkDayTimestamp($date); list ($startSeconds, $endSeconds) = $this->getCalendarWorkTime(); return $multiplier < 0 ? $dayTs - $startSeconds :$endSeconds - $dayTs; } private function getWorkDayInterval() { list ($startSeconds, $endSeconds) = $this->getCalendarWorkTime(); return $endSeconds - $startSeconds; } private function isHoliday($date) { [$yearWorkdays] = $this->getCalendarWorkdays(); [$weekHolidays, $yearHolidays] = $this->getCalendarHolidays(); $dayOfYear = date('j.n', $date); if (in_array($dayOfYear, $yearWorkdays, true)) { return false; } $dayOfWeek = date('w', $date); if (in_array($dayOfWeek, $weekHolidays)) { return true; } $dayOfYear = date('j.n', $date); if (in_array($dayOfYear, $yearHolidays, true)) { return true; } return false; } private function isWorkTime($date) { $dayTs = $this->getWorkDayTimestamp($date); list ($startSeconds, $endSeconds) = $this->getCalendarWorkTime(); return ($dayTs >= $startSeconds && $dayTs <= $endSeconds); } private function getNearestWorkTime($date, $multiplier = 1) { $reverse = $multiplier < 0; list ($startSeconds, $endSeconds) = $this->getCalendarWorkTime(); $dayTimeStamp = $this->getWorkDayTimestamp($date); if ($this->isHoliday($date)) { $date -= $dayTimeStamp; $date += $reverse? -86400 + $endSeconds : $startSeconds; $dayTimeStamp = $reverse? $endSeconds : $startSeconds; } if (!$this->isWorkTime($date)) { $date -= $dayTimeStamp; if ($dayTimeStamp < $startSeconds) { $date += $reverse? -86400 + $endSeconds : $startSeconds; } else { $date += $reverse? $endSeconds : 86400 + $startSeconds; } } if ($this->isHoliday($date)) $date = $this->addWorkDay($date, $reverse? -1 : 1); return $date; } private function addWorkDay($date, $days) { $delta = 86400; if ($days < 0) $delta *= -1; $days = abs($days); $iterations = 0; while ($days > 0 && $iterations < 1000) { ++$iterations; $date += $delta; if ($this->isHoliday($date)) continue; --$days; } return $date; } private function getCalendarHolidays() { if (static::$yearHolidays === null) { $calendarSettings = CCalendar::GetSettings(); $weekHolidays = [0, 6]; $yearHolidays = []; if (isset($calendarSettings['week_holidays'])) { $weekDays = ['SU' => 0, 'MO' => 1, 'TU' => 2, 'WE' => 3, 'TH' => 4, 'FR' => 5, 'SA' => 6]; $weekHolidays = []; foreach ($calendarSettings['week_holidays'] as $day) $weekHolidays[] = $weekDays[$day]; } if (isset($calendarSettings['year_holidays'])) { foreach (explode(',', $calendarSettings['year_holidays']) as $yearHoliday) { $date = explode('.', trim($yearHoliday)); if (count($date) == 2 && $date[0] && $date[1]) $yearHolidays[] = (int)$date[0] . '.' . (int)$date[1]; } } static::$weekHolidays = $weekHolidays; static::$yearHolidays = $yearHolidays; } return [static::$weekHolidays, static::$yearHolidays]; } private function getCalendarWorkTime() { if (static::$startWorkDay === null) { $startSeconds = 0; $endSeconds = 24 * 3600 - 1; $calendarSettings = CCalendar::GetSettings(); if (!empty($calendarSettings['work_time_start'])) { $time = explode('.', $calendarSettings['work_time_start']); $startSeconds = $time[0] * 3600; if (!empty($time[1])) $startSeconds += $time[1] * 60; } if (!empty($calendarSettings['work_time_end'])) { $time = explode('.', $calendarSettings['work_time_end']); $endSeconds = $time[0] * 3600; if (!empty($time[1])) $endSeconds += $time[1] * 60; } static::$startWorkDay = $startSeconds; static::$endWorkDay = $endSeconds; } return [static::$startWorkDay, static::$endWorkDay]; } private function getCalendarWorkdays() { if (static::$yearWorkdays === null) { $yearWorkdays = []; $calendarSettings = CCalendar::GetSettings(); $calendarYearWorkdays = $calendarSettings['year_workdays'] ?? ''; foreach (explode(',', $calendarYearWorkdays) as $yearWorkday) { $date = explode('.', trim($yearWorkday)); if (count($date) === 2 && $date[0] && $date[1]) { $yearWorkdays[] = (int)$date[0] . '.' . (int)$date[1]; } } static::$yearWorkdays = $yearWorkdays; } return [static::$yearWorkdays]; } private function getDateTimeObject($date) { if ($date instanceof Bizproc\BaseType\Value\Date) { return (new \DateTime())->setTimestamp($date->getTimestamp()); } $df = Main\Type\DateTime::getFormat(); $df2 = Main\Type\Date::getFormat(); $date1Formatted = \DateTime::createFromFormat($df, $date); if ($date1Formatted === false) { $date1Formatted = \DateTime::createFromFormat($df2, $date); if ($date1Formatted) { $date1Formatted->setTime(0, 0); } } return $date1Formatted; } private function getDateTimeOffset($date) { if ($date instanceof Bizproc\BaseType\Value\Date) { return $date->getOffset(); } return 0; } private function frameSymbolsInDateFormat($format) { $complexSymbols = ['j F', 'd F', 'jS F']; $symbols = ['D', 'l', 'F', 'M', 'r']; $frameRule = []; foreach ($symbols as $symbol) { $frameRule[$symbol] = '#' . $symbol . '#'; $frameRule['\\' . $symbol] = '\\' . $symbol; } foreach ($complexSymbols as $symbol) { $frameRule[$symbol] = substr($symbol, 0, -1) . '#' . $symbol[-1] . '_1#'; $frameRule['\\' . $symbol] = '\\' . substr($symbol, 0, -1) . '#' . $symbol[-1] . '#'; } return strtr($format, $frameRule); } private function frameNamesInFormattedDateRFC2822($formattedDate) { $matches = []; $pattern = "/#(\w{3}), \d{2} (\w{3}) \d{4} \d{2}:\d{2}:\d{2} [+-]\d{4}#/"; if (preg_match_all($pattern, $formattedDate, $matches)) { foreach ($matches[0] as $key => $match) { $day = $matches[1][$key]; $month = $matches[2][$key]; $reformMatch = str_replace( [$day, $month], ['#' . $day . '#', '#' . $month . '#'], $match ); $reformMatch = substr($reformMatch, 1, -1); $formattedDate = str_replace($match, $reformMatch, $formattedDate); } } return $formattedDate; } private function replaceDateToLocDate($formattedDate, $format) { $lenShortName = 3; $dayNames = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; $monthNames = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ]; if (strpos($format, '#r#') !== false) { $formattedDate = $this->frameNamesInFormattedDateRFC2822($formattedDate); } $replacementRule = []; foreach (array_merge($dayNames, $monthNames) as $name) { $replacementRule['#' . $name . '#'] = GetMessage( 'BPCGCALC_LOCDATE_' . strtoupper($name) ); $shortName = substr($name, 0, $lenShortName); $replacementRule['#' . $shortName . '#'] = GetMessage( 'BPCGCALC_LOCDATE_' . strtoupper($shortName) . '_SHORT' ); } foreach ($monthNames as $monthName) { $replacementRule['#' . $monthName . '_1' . '#'] = GetMessage( 'BPCGCALC_LOCDATE_' . strtoupper($monthName) . '_1' ); } return strtr($formattedDate, $replacementRule); } /* String & Formatting */ private function functionNumberFormat($args) { $ar = $this->ArrgsToArray($args); $number = (float) array_shift($ar); $decimals = (int) (array_shift($ar) ?: 0); $decPoint = array_shift($ar); if ($decPoint === null) { $decPoint = '.'; } $decPoint = (string) $decPoint; $thousandsSeparator = array_shift($ar); if ($thousandsSeparator === null) { $thousandsSeparator = ','; } $thousandsSeparator = (string) $thousandsSeparator; return number_format($number, $decimals, $decPoint, $thousandsSeparator); } private function functionRandString($args) { $ar = $this->ArrgsToArray($args); $len = (int)array_shift($ar); return \randString($len); } private function functionSubstr($args) { if (!is_array($args)) $args = [$args]; $ar = $this->ArrgsToArray($args); $str = array_shift($ar); $pos = (int)array_shift($ar); $len = (int)array_shift($ar); if (($str == null) || ($str === "")) return null; if ($len) { return mb_substr($str, $pos, $len); } return mb_substr($str, $pos); } private function functionStrpos($args) { $ar = $this->ArrgsToArray($args); $haystack = (string)array_shift($ar); if (empty($haystack)) { return false; } $maxOffset = mb_strlen($haystack); $minOffset = -1 * $maxOffset; $needle = (string)array_shift($ar); $offset = max($minOffset, min($maxOffset, (int)array_shift($ar))); return mb_strpos($haystack, $needle, $offset); } private function functionStrlen($args) { $ar = $this->ArrgsToArray($args); $str = array_shift($ar); if (!is_scalar($str)) { return null; } $str = (string) $str; return mb_strlen($str); } private function functionImplode($args) { $ar = (array) $args; $glue = (string)array_shift($ar); $pieces = \CBPHelper::MakeArrayFlat(array_shift($ar)); if (!$pieces) { return ''; } return implode($glue, $pieces); } private function functionExplode($args) { $ar = (array) $args; $delimiter = array_shift($ar); $str = array_shift($ar); if (is_array($str)) { $str = reset($str); } if (is_array($delimiter)) { $delimiter = reset($delimiter); } if (empty($delimiter) || !is_scalar($str) || !is_scalar($delimiter)) { return null; } $str = (string) $str; return explode($delimiter, $str); } private function functionUrlencode($args) { $ar = $this->ArrgsToArray($args); $str = array_shift($ar); if (!is_scalar($str)) { if (is_array($str)) { $str = implode(", ", CBPHelper::MakeArrayFlat($str)); } else { return null; } } $str = (string) $str; return urlencode($str); } private function functionConvert($args) { if (!is_array($args)) $args = [$args]; $ar = $this->ArrgsToArray($args); $val = array_shift($ar); $type = array_shift($ar); $attr = array_shift($ar); $type = mb_strtolower($type); if ($type === 'printableuserb24') { $result = []; $users = CBPHelper::StripUserPrefix($val); if (!is_array($users)) $users = [$users]; foreach ($users as $userId) { $db = CUser::GetByID($userId); if ($ar = $db->GetNext()) { $ix = randString(5); $attr = (!empty($attr) ? 'href="'.$attr.'"' : 'href="#" onClick="return false;"'); $result[] = '<a class="feed-post-user-name" id="bp_'.$userId.'_'.$ix.'" '.$attr.' bx-post-author-id="'.$userId.'" bx-post-author-gender="'.$ar['PERSONAL_GENDER'].'" bx-tooltip-user-id="'.$userId.'">'.CUser::FormatName(CSite::GetNameFormat(false), $ar, false).'</a>'; } } $result = implode(", ", $result); } elseif ($type == 'printableuser') { $result = []; $users = CBPHelper::StripUserPrefix($val); if (!is_array($users)) $users = [$users]; foreach ($users as $userId) { $db = CUser::GetByID($userId); if ($ar = $db->GetNext()) $result[] = CUser::FormatName(CSite::GetNameFormat(false), $ar, false); } $result = implode(", ", $result); } else { $result = $val; } return $result; } private function functionStrtolower($args) { $ar = $this->ArrgsToArray($args); $str = array_shift($ar); if (!is_scalar($str)) { return null; } return mb_strtolower((string) $str); } private function functionStrtoupper($args) { $ar = $this->ArrgsToArray($args); $str = array_shift($ar); if (!is_scalar($str)) { return null; } return mb_strtoupper((string) $str); } private function functionUcwords($args) { $ar = $this->ArrgsToArray($args); $str = array_shift($ar); if (!is_scalar($str)) { return null; } return mb_convert_case((string) $str, MB_CASE_TITLE); } private function functionUcfirst($args) { $ar = $this->ArrgsToArray($args); $str = array_shift($ar); if (!is_scalar($str)) { return null; } return $this->mb_ucfirst((string) $str); } private function mb_ucfirst($str) { $len = mb_strlen($str); $firstChar = mb_substr($str, 0, 1); $otherChars = mb_substr($str, 1, $len - 1); return mb_strtoupper($firstChar) . $otherChars; } private function functionTrim($args) { $array = $this->ArrgsToArray($args); if (empty($array)) { return null; } $result = []; foreach ($array as $str) { if (is_scalar($str) || (is_object($str) && method_exists($str, '__toString'))) { $result[] = trim((string)$str); continue; } return null; } return count($result) > 1 ? $result : $result[0]; } /* Complex values */ private function functionMerge($args) { if (!is_array($args)) $args = []; foreach ($args as &$a) { $a = is_object($a) ? [$a] : (array)$a; } return call_user_func_array('array_merge', $args); } private function functionShuffle($args) { if (!is_array($args) || $args === []) { return null; } $array = $this->ArrgsToArray($args); shuffle($array); return $array; } private function functionFirstValue($args) { $ar = $this->ArrgsToArray($args); return $ar[0] ?? null; } private function functionSwirl($args) { $ar = $this->ArrgsToArray($args); if (count($ar) <= 1) { return $ar[0] ?? null; } return array_merge(array_slice($ar, 1), [$ar[0]]); } private function functionGetDocumentUrl($args) { $ar = $this->ArrgsToArray($args); $format = array_shift($ar); $external = array_shift($ar); $url = $this->activity->workflow->getService('DocumentService')->GetDocumentAdminPage( $this->activity->getDocumentId() ); $name = null; if ($external) { $url = Main\Engine\UrlManager::getInstance()->getHostUrl() . $url; } if ($format === 'bb' || $format === 'html') { $name = $this->activity->workflow->getService('DocumentService')->getDocumentName( $this->activity->getDocumentId() ); } if ($format === 'bb') { return sprintf( '[url=%s]%s[/url]', $url, $name ); } if ($format === 'html') { return sprintf( '<a href="%s" target="_blank">%s</a>', $url, htmlspecialcharsbx($name) ); } return $url; } }