403Webshell
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/lib/Calc/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/cvetdv.ru/bitrix/modules/bizproc/lib/Calc/Parser.php
<?php

namespace Bitrix\Bizproc\Calc;

class Parser
{
	private const Operation = 0;
	private const Variable = 1;
	private const Constant = 2;

	private \CBPActivity $activity;
	private array $errors = [];
	private array $priority = [
		'(' => 0,
		')' => 1,
		'@' => 2,
		';' => 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 array $functions;

	private array $errorMessages = [
		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#"',
	];

	public function __construct(\CBPActivity $activity)
	{
		$this->activity = $activity;
		$this->functions = Functions::getList();
	}

	public function getActivity(): \CBPActivity
	{
		return $this->activity;
	}

	private function getVariableValue($variable)
	{
		$variable = trim($variable);
		if (!preg_match(\CBPActivity::ValuePattern, $variable))
		{
			return null;
		}

		return $this->activity->parseValue($variable);
	}

	private function setError($errorCode, $message = ''): void
	{
		$this->errors[] = [$errorCode, str_replace('#STR#', $message, $this->errorMessages[$errorCode])];
	}

	public function getErrors(): array
	{
		return $this->errors;
	}

	/*
	Return array of polish notation
	array(
		key => array(value, self::Operation | self::Variable | self::Constant)
	)
	*/
	private function getPolishNotation($text)
	{
		$text = trim($text);
		if (mb_strpos($text, '=') === 0)
		{
			$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;
		}

		$notation = [];
		$stack = [];
		$prev = '';

		$isFunctionArgs = function ($stack)
		{
			return (
				isset($stack[0], $stack[1], $this->functions[$stack[1][0]]) && $stack[0][0] === '('
			);
		};

		$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);
				[$name] = explode('(', $str);
				$name = trim($name);
				if (isset($this->functions[$name]))
				{
					if ($stack)
					{
						while ($this->priority['f'] <= $stack[0][1])
						{
							$op = array_shift($stack);
							$notation[] = [$op[0], self::Operation];
							if (!$stack)
							{
								break;
							}
						}
					}
					array_unshift($stack, [$name, $this->priority['f']]);
				}
				else
				{
					$this->setError(3, $name);

					return false;
				}
				$str = '(';
			}

			if ($str === '-' || $str === '+')
			{
				if (
					$prev === ''
					|| in_array($prev, ['(', ';', '=', '<=', '>=', '<>', '<', '>', '&', '+', '-', '*', '/', '^'])
				)
				{
					$str .= 'm';
				}
			}

			switch ($str)
			{
				case '(':
					array_unshift($stack, ['(', $this->priority['(']]);
					array_unshift($stack, ['@', $this->priority['@']]);
					break;
				case ')':
					$hasComma = false;
					$hasArguments = $prev !== '(';
					//trailing comma
					if ($prev === ';')
					{
						array_shift($stack);
					}

					while ($op = array_shift($stack))
					{
						if ($op[0] === '(')
						{
							break;
						}
						if ($op[0] === ';')
						{
							$hasComma = true;
						}

						$isInFunction = $isFunctionArgs($stack);

						if (
							$op[0] === '@'
							&& (
								(!$hasComma && !$isInFunction)
								|| (!$hasArguments && $isInFunction)
							)
						)
						{
							continue;
						}
						$notation[] = [$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 (!$stack)
					{
						array_unshift($stack, [$str, $this->priority[$str]]);
						if ($str === ';')
						{
							$notation[] = ['@', self::Operation];
						}
						break;
					}
					while ($this->priority[$str] <= $stack[0][1])
					{
						$op = array_shift($stack);
						$notation[] = [$op[0], self::Operation];
						if (!$stack)
						{
							break;
						}
					}
					array_unshift($stack, [$str, $this->priority[$str]]);
					break;
				default:
					if (mb_strpos($str, '0') === 0 || (int)$str)
					{
						$notation[] = [(float)$str, self::Constant];
						break;
					}
					if (mb_strpos($str, '"') === 0 || mb_strpos($str, "'") === 0)
					{
						$notation[] = [mb_substr($str, 1, -1), self::Constant];
						break;
					}
					$notation[] = [$str, self::Variable];
			}
			$text = mb_substr($text, mb_strlen($match[0]));
			$prev = $str;
		}
		while ($op = array_shift($stack))
		{
			if ($op[0] === '(')
			{
				$this->setError(5);

				return false;
			}
			$notation[] = [$op[0], self::Operation];
		}

		return $notation;
	}

	public function calculate($text)
	{
		if (!$notation = $this->getPolishNotation($text))
		{
			return null;
		}

		$stack = [];
		foreach ($notation 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 '@':
							$arg = array_shift($stack);
							array_unshift($stack, [$arg]);
							break;
						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 = \CBPHelper::stringify(array_shift($stack));
							$arg1 = \CBPHelper::stringify(array_shift($stack));
							array_unshift($stack, $arg1 . $arg2);
							break;
						case '+':
							$arg2 = (float)($this->toSingleValue(array_shift($stack)));
							$arg1 = (float)($this->toSingleValue(array_shift($stack)));
							array_unshift($stack, $arg1 + $arg2);
							break;
						case '-':
							$arg2 = (float)($this->toSingleValue(array_shift($stack)));
							$arg1 = (float)($this->toSingleValue(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)($this->toSingleValue(array_shift($stack)));
							$arg1 = (float)($this->toSingleValue(array_shift($stack)));
							array_unshift($stack, $arg1 * $arg2);
							break;
						case '/':
							$arg2 = (float)($this->toSingleValue(array_shift($stack)));
							$arg1 = (float)($this->toSingleValue(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, $arg1 ** $arg2);
							break;
						case '%':
							$arg = (float)array_shift($stack);
							array_unshift($stack, $arg / 100);
							break;
						default:
							$func = $this->functions[$item[0]]['func'];
							$functionArgs = new Arguments($this);
							if (!empty($this->functions[$item[0]]['args']))
							{
								$args = array_shift($stack);
								$functionArgs->setArgs(is_array($args) ? $args : [$args]);
							}

							$val = $func($functionArgs);

							$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 toSingleValue($argument)
	{
		if (is_array($argument))
		{
			reset($argument);
			return current($argument);
		}

		return $argument;
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit