Nickolay.info. PHP. Статьи. Простейший калькулятор на функции PHP eval()

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

Что умеет и чего не умеет калькулятор: знает арифметические операции + / * - % (остаток от деления) ^ (степень), функции sin, sinh, cos, cosh, tan, tanh, abs, acos, acosh, asin, asinh, atan, atanh, exp, log, log10, deg2rad, rad2deg, sqrt, ceil, floor, round, число pi, скобки. Не знает: вычислений по цепочке, то есть, (1+rad2deg(sin(pi/2)))/2 наш калькулятор вычислит, (1+2)+3 - тоже, а вот 1+2+3 - уже нет. Чтобы научить его это делать, поправьте шаблоны в исходнике.

Вся реализация, собственно, вот:

//Строка ввода в переменной $text
$number = '(?:\d+(?:[,.]\d+)?|pi)'; //Шаблон для числа
$functions = '(?:sinh?|cosh?|tanh?|abs|acosh?|asinh?|atanh?|exp|log10|log|deg2rad|rad2deg|sqrt|ceil|floor|round)'; 
 //Разрешенные функции PHP
$operators = '[+\/*\^%-]'; //Разрешенные математические действия
$regexp = '/^(('.$number.'|'.$functions.'\s*\((?1)+\)|\((?1)+\))(?:'.$operators.'(?2))?)+$/'; 
 //Окончательная оценка
if (preg_match($regexp, $text)) {
 $text = preg_replace('!pi!', 'pi()', $text); //pi придётся записать функцией
 $text = preg_replace('~([0-9]+)\^([0-9]+)~e', 'pow("\\1", "\\2")', $text); //и учесть степень
 eval('$result = '.$text.';'); //Сам калькулятор :)
}
else { $result = 'Error!'; }
echo '<p align="center"><b>Result=</b>'.$result.'</p>';

Конечно, этот код очень примитивен, служит только для примера и может улучшаться. Так, приведённый фрагмент "понимает" возведение только целого числа в целую в степень, знак "-" перед числом не входит в шаблон, следовательно, не может быть отличён скриптом от операции вычитания и т.д.

 Калькулятор PHP на функции eval() в работе

 Исходник калькулятора (2 Кб)

P.S. В обсуждении этого подхода один из слушателей сразу же высказал мнение, что нужно исходить из того, что без пробелов в коде и с 50-ю символами ввода (строка $text у нас на всякий случай обрезается на стороне сервера) злой хакер едва-ли сумеет что-нибудь сделать, так что процитированную выше часть скрипта вполне можно сделать и такой:

//Строка ввода в переменной $text
$result='';
@eval('$result = '.$text.';'); //Сам калькулятор :)
if (!is_numeric($result)) $result = 'Error!';
echo '<p align="center"><b>Result=</b>'.$result.'</p>';

- то есть, просто не выводить результат, если он не числовой, а выполнять всё подряд... увы, простейшее

unlink('calc.php');

в поле ввода данную идею начисто опровергло :)

Рейтинг@Mail.ru

вверх гостевая; E-mail