Существуют ли решения по математическим вычислениям из строки?

Сейчас пользуюсь:
https://stackoverflow.com/questions/18880772/calcu...

Использование:
$Cal = new Field_calculate();

$result = $Cal->calculate('5+7'); // 12
$result = $Cal->calculate('(5+9)*5'); // 70
$result = $Cal->calculate('(10.2+0.5*(2-0.4))*2+(2.1*4)'); // 30.4


Код тут
class Field_calculate {
const PATTERN = '/(?:\-?\d+(?:\.?\d+)?[\+\-\*\/])+\-?\d+(?:\.?\d+)?/';
const PARENTHESIS_DEPTH = 10;
public function calculate($input){
if(strpos($input, '+') != null || strpos($input, '-') != null || strpos($input, '/') != null || strpos($input, '*') != null){
// Remove white spaces and invalid math chars
$input = str_replace(',', '.', $input);
$input = preg_replace('[^0-9\.\+\-\*\/\(\)]', '', $input);
// Calculate each of the parenthesis from the top
$i = 0;
while(strpos($input, '(') || strpos($input, ')')){
$input = preg_replace_callback('/\(([^\(\)]+)\)/', 'self::callback', $input);
$i++;
if($i > self::PARENTHESIS_DEPTH) break;
}
// Calculate the result
if(preg_match(self::PATTERN, $input, $match)) return $this->compute($match[0]);
// To handle the special case of expressions surrounded by global parenthesis like "(1+1)"
if(is_numeric($input)) return $input;
return 0;
}
return $input;
}

private function compute($input){
$compute = create_function('', 'return '.$input.';');
//$compute = function($input){return $input;}; попытка заменить create_function
//или убрать $compute=.. а return заменить
//return 0 + eval('return '.$input.';');
return 0 + $compute();
}

private function callback($input){
if(is_numeric($input[1])) return $input[1];
elseif(preg_match(self::PATTERN, $input[1], $match)) return $this->compute($match[0]);
return 0;
}
}


Но проблема, что используя php7.3 получаю ошибку, что функция устарела:
Function create_function() is deprecated

Конкретно на строку из кода:
$compute = create_function('', 'return '.$input.';');


Подскажите, на что заменить код, чтобы избавится от ошибки, либо другое решение.

PS. eval не предлагайте
  • Вопрос задан
  • 52 просмотра
Решения вопроса 1
Tolly
@Tolly Автор вопроса
Нашел решение, которое меня устроило:
https://ru.stackoverflow.com/questions/454598/%D0%...

код
// Исключения для парсера выражений
class AriphmeticException extends Exception {
function __construct($msg, $code) {
return parent::__construct($msg, $code);
}
function __toString() {
return get_class($this) . '('
. $this->code . '): '
. $this->message;
}
}

// Собственно сам вычислитель выражений
function calculate($statement) {
if (!is_string($statement)) {
throw new AriphmeticException('Wrong type', 1);
}
$calcQueue = array();
$operStack = array();
$operPriority = array(
'(' => 0,
')' => 0,
'+' => 1,
'-' => 1,
'*' => 2,
'/' => 2,
);
$token = '';
foreach (str_split($statement) as $char) {
// Если цифра, то собираем из цифр число
if ($char >= '0' && $char <= '9') {
$token .= $char;
} else {
// Если число накопилось, сохраняем в очереди вычисления
if (strlen($token)) {
array_push($calcQueue, $token);
$token = '';
}
// Если найденный символ - операция (он есть в списке приоритетов)
if (isset($operPriority[$char])) {
if (')' == $char) {
// Если символ - закрывающая скобка, переносим операции из стека в очередь вычисления пока не встретим открывающую скобку
while (!empty($operStack)) {
$oper = array_pop($operStack);
if ('(' == $oper) {
break;
}
array_push($calcQueue, $oper);
}
if ('(' != $oper) {
// Упс! А открывающей-то не было. Сильно ругаемся (18+)
throw new AriphmeticException('Unexpected ")"', 2);
}
} else {
// Встретили операцию кроме скобки. Переносим операции с меньшим приоритетом в очередь вычисления
while (!empty($operStack) && '(' != $char) {
$oper = array_pop($operStack);
if ($operPriority[$char] > $operPriority[$oper]) {
array_push($operStack, $oper);
break;
}
if ('(' != $oper) {
array_push($calcQueue, $oper);
}
}
// Кладем операцию на стек операций
array_push($operStack, $char);
}
} elseif (strpos(' ', $char) !== FALSE) {
// Игнорируем пробелы (можно добавить что еще игнорируем)
} else {
// Встретили что-то непонятное (мы так не договаривались). Опять ругаемся
throw new AriphmeticException('Unexpected symbol "' . $char . '"', 3);
}
}

}
// Вроде все разобрали, но если остались циферки добавляем их в очередь вычисления
if (strlen($token)) {
array_push($calcQueue, $token);
$token = '';
}
// ... и оставшиеся в стеке операции
if (!empty($operStack)) {
while ($oper = array_pop($operStack)) {
if ('(' == $oper) {
// ... кроме открывающих скобок. Это верный признак отсутствующей закрывающей
throw new AriphmeticException('Unexpected "("', 4);
}
array_push($calcQueue, $oper);
}
}
$calcStack = array();
// Теперь вычисляем все то, что напарсили
// Тут ошибки не ловил, но они могут быть (это домашнее задание)
foreach ($calcQueue as $token) {
switch ($token) {
case '+':
$arg2 = array_pop($calcStack);
$arg1 = array_pop($calcStack);
array_push($calcStack, $arg1 + $arg2);
break;
case '-':
$arg2 = array_pop($calcStack);
$arg1 = array_pop($calcStack);
array_push($calcStack, $arg1 - $arg2);
break;
case '*':
$arg2 = array_pop($calcStack);
$arg1 = array_pop($calcStack);
array_push($calcStack, $arg1 * $arg2);
break;
case '/':
$arg2 = array_pop($calcStack);
$arg1 = array_pop($calcStack);
array_push($calcStack, $arg1 / $arg2);
break;
default:
array_push($calcStack, $token);
}
}
return array_pop($calcStack);
}
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
Adamos
@Adamos
Окей, гугл. Пишем калькулятор на... да хотя бы и на пыхе, какая разница?
Ответ написан
Комментировать
Sanasol
@Sanasol Куратор тега PHP
нельзя просто так взять и загуглить ошибку
PS. eval не предлагайте


https://www.php.net/manual/ru/function.create-func...
Эта функция внутри себя делает вызов функции eval(), а значит имеет такие же проблемы с безопасностью, как и eval().


Т.е. eval смущает, а create_function норм?)

А так парсить и вот это всё.
В том же вопросе на SO есть решение ниже без евала
https://stackoverflow.com/a/53103580/1603055
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы