Вычисление математических формул на PHP и Javascript

Привет всем. Сегодня речь пойдет о задаче вычисления заданной пользователем функции. Например, мы делаем сервис для постройки графиков с возможностью задать функцию, или делаем некий аналог Excel, в котором по заданным формулам необходимо провести вычисления.

Решение мы будем искать на самых простых серверных и клиентских языках - PHP и Javascript.

 

Итак – нам надо каким-то образом распарсить строку, введенную пользователем. При этом убедится, что строка – настоящая формула. Мы можем при помощи хитрых манипуляций разбить строку на операции, функции, пытаться вычислить вложенность, найти сопоставление уже созданным математическим функциям и найти ответ. Все это слишком сложно. Для данных языков – существует злой оператор eval().

Eval– позволяет исполнить произвольную строку. Зло этой функции заключается в том, что пользователь может в качестве строки указать много вредоносного кода и таким образом получить доступ, или обрушить что-нибудь на сервере. По этому – если решать задачу через eval – нужно лишь удостовериться что пользователь ввел допустимую строку.

Мы, кстати, можем также позволить пользователю ссылаться в формуле на допустимые переменные конкретного кода, а также собственные пользовательские функции. Нужно лишь предварительно объявить круг этих переменных и заранее описать функции.

Примерная формула может выглядеть следующим образом

function myfunc($x){ // пользовательская функция
    return $x/4;
}
$myvar=16; // пользовательская переменная
$expr='23.5 / 0+{myvar} *sqrt({myvar}/4)+myfunc({myvar})';

В формуле – мы заранее говорим пользователю, что переменную можно использовать только через кавычки. Это позволит нам не допустить ошибок, связанных со слиянием переменных с функциями, или действиями.

Деление на ноль допустимо в PHP, оно просто вернет 0. Остальные операторы будут суммироваться отдельно. На Javascript - деление на 0 выдаст бесконечность, но случай частный и не несет никакой смысловой нагрузки, главное чтобы программа не спотыкнулась, а продолжила свое выполнение.

Чтобы выполнить проверку – мы можем прибегнуть к регулярным выражениям, узнав, содержим ли формула только допустимые символы, или что-то еще. Но это долго с точки зрения вычислений. Мы можем пойти проще. Можем заменить все разрешенные конструкции на пустышки и сравнить в конце строку с пустотой. Если символы остаются – значит – ошибка, введены запрещенные символы. Мы также можем проверить на число открытых и закрытых скобок. Можем выполнить еще ряд проверок, но для безопасности хватит и этого. Если что-то в формуле не задастся  - будет выведен пустой результат.

Для реализации вышеследующего – перечислим все разрешенные конструкции. Только порядок замены должен идти от общего к частному. То есть сначала – наиболее общие выражения, например «asin(», затем «sin(» , затем «(» .

$test=strtr($expr,array(
    '{myvar}'=>'', // разрешенные переменные
    'myfunc('=>'', // разрешенные пользовательские функции
    '1'=>'',
    '2'=>'',
    '3'=>'',
    '4'=>'',
    '5'=>'',
    '6'=>'',
    '7'=>'',
    '8'=>'',
    '9'=>'',
    '0'=>'',
    '.'=>'',
    '+'=>'',
    '-'=>'',
    '*'=>'',
    '/'=>'',
    '%'=>'',
    'asin('=>'',
    'sin('=>'',
    'acos('=>'',
    'cos('=>'',
    'abs('=>'',
    'ceil('=>'',
    'exp('=>'',
    'floor('=>'',
    'tan('=>'',
    'round('=>'',
    'sqrt('=>'',
    ')'=>'',
    '('=>'',
    ' '=>'',
));
if ($test!='')die('Ошибка. Запрещенные символы '.$test);
if (substr_count($expr,'(')!=substr_count($expr,')'))die('Ошибка. Число открытых и закрытых скобок не совпадает');

Вот и все проверки. Остается только произвести замену нашей переменной и выполнить злую операцию вычисления

$expr=strtr($expr,array(
    '{inc}'=>'$inc',
    '++'=>'',
    '--'=>'',
));
eval('$result='.$expr.';');
echo $result;

++ и -- мы запрещаем по причине, что они изменяют исходную переменную, что может быть недопустимо. На . В начале строки проверку можно не делать, т.к. изначально строка будет пустая и мы будет ее очищать если будет использоваться цикл.

Можно использовать это как угодно, загнать в функцию, в цикл. Для Javascript все аналогично.

Успехов в создании идеальных сервисов.

  • Автор: kosmom
  • Рейтинг: 0
  • Просмотров: 4177
  • Комментариев: 0
  • Создан: 28.05.2013 16:12

Комментарии (0)

Ваши предложения и пожелания пишите на pro@kosmom.ru

Теги

backup bootstrap core framework eloquent excel home project html ios javascript keep-alive kpi laravel legacy mvp orm php rip scroll timestamp undefined vue vuetify watch безопасность биометрический паспорт ваша любаша для путешествий загран на 10 лет загран паспорт загранпаспорт нового образца зимние книги как заполнить анкеты кеширование книги на новый год логирование мцф недвижимость новогодние книги образец заполнения антеты паспорт для путешествий паспорт нового поколения печать продукт проектирование прокси разработка ремонт ремонт в апартаментах ремонт нежилого помещения самокат сдача сколько стоил ремонт апартаментов спорт стандарты таблица финансы хостинг цена ремонта что почитать зимой юзабилити

Случайный пост

07.04.2012 21:04
Что делать с ненужными книгами