PHP에서 문자열로 전달된 수식을 어떻게 평가하나요?
PHP에서 평가하는 두 가지 주요 방법이 있습니다. eval( ) 및 create_function().
사용 eval()
function calculator1($str){ eval("$str = $str;"); return $str; }
create_function() 사용
function calculator2($str){ $fn = create_function("", "return ({$str});" ); return $fn(); };
두 방법 모두 악성 코드 실행을 방지하려면 문자열 정리가 필요합니다.
대안 접근 방식
또는 다음 클래스를 사용하여 수학적 표현식을 안전하게 평가할 수 있습니다.
class EvalMath { var $suppress_errors = false; var $last_error = null; var $v = array('e'=>2.71,'pi'=>3.14); // variables (and constants) var $f = array(); // user-defined functions var $vb = array('e', 'pi'); // constants var $fb = array( // built-in functions 'sin','sinh','arcsin','asin','arcsinh','asinh', 'cos','cosh','arccos','acos','arccosh','acosh', 'tan','tanh','arctan','atan','arctanh','atanh', 'sqrt','abs','ln','log'); function EvalMath() { // make the variables a little more accurate $this->v['pi'] = pi(); $this->v['e'] = exp(1); } function e($expr) { return $this->evaluate($expr); } function evaluate($expr) { $this->last_error = null; $expr = trim($expr); if (substr($expr, -1, 1) == ';') $expr = substr($expr, 0, strlen($expr)-1); // strip semicolons at the end //=============== // is it a variable assignment? if (preg_match('/^\s*([a-z]\w*)\s*=\s*(.+)$/', $expr, $matches)) { if (in_array($matches[1], $this->vb)) { // make sure we're not assigning to a constant return $this->trigger("cannot assign to constant '$matches[1]'"); } if (($tmp = $this->pfx($this->nfx($matches[2]))) === false) return false; // get the result and make sure it's good $this->v[$matches[1]] = $tmp; // if so, stick it in the variable array return $this->v[$matches[1]]; // and return the resulting value //=============== // is it a function assignment? } elseif (preg_match('/^\s*([a-z]\w*)\s*\(\s*([a-z]\w*(?:\s*,\s*[a-z]\w*)*)\s*\)\s*=\s*(.+)$/', $expr, $matches)) { $fnn = $matches[1]; // get the function name if (in_array($matches[1], $this->fb)) { // make sure it isn't built in return $this->trigger("cannot redefine built-in function '$matches[1]()'"); } $args = explode(",", preg_replace("/\s+/", "", $matches[2])); // get the arguments if (($stack = $this->nfx($matches[3])) === false) return false; // see if it can be converted to postfix for ($i = 0; $i<count($stack); $i++) { // freeze the state of the non-argument variables $token = $stack[$i]; if (preg_match('/^[a-z]\w*$/', $token) and !in_array($token, $args)) { if (array_key_exists($token, $this->v)) { $stack[$i] = $this->v[$token]; } else { return $this->trigger("undefined variable '$token' in function definition"); } } } $this->f[$fnn] = array('args'=>$args, 'func'=>$stack); return true; //=============== } else { return $this->pfx($this->nfx($expr)); // straight up evaluation, woo } } function vars() { $output = $this->v; unset($output['pi']); unset($output['e']); return $output; } function funcs() { $output = array(); foreach ($this->f as $fnn=>$dat) $output[] = $fnn . '(' . implode(',', $dat['args']) . ')'; return $output; } //===================== HERE BE INTERNAL METHODS ====================\ // Convert infix to postfix notation function nfx($expr) { $index = 0; $stack = new EvalMathStack; $output = array(); // postfix form of expression, to be passed to pfx() $expr = trim(strtolower($expr)); $ops = array('+', '-', '*', '/', '^', '_'); $ops_r = array('+'=>0,'-'=>0,'*'=>0,'/'=>0,'^'=>1); // right-associative operator? $ops_p = array('+'=>0,'-'=>0,'*'=>1,'/'=>1,'_'=>1,'^'=>2); // operator precedence $expecting_op = false; // we use this in syntax-checking the expression // and determining when a - is a negation if (preg_match("/[^\w\s+*^\/()\.,-]/", $expr, $matches)) { // make sure the characters are all good return $this->trigger("illegal character '{$matches[0]}'"); } while(1) { // 1 Infinite Loop ;) $op = substr($expr, $index, 1); // get the first character at the current index // find out if we're currently at the beginning of a number/variable/function/parenthesis/operand $ex = preg_match('/^([a-z]\w*\(?|\d+(?:\.\d*)?|\.\d+|\()/', substr($expr, $index), $match); //=============== if ($op == '-' and !$expecting_op) { // is it a negation instead of a minus? $stack->push('_'); // put a negation on the stack $index++; } elseif ($op == '_') { // we have to explicitly deny this, because it's legal on the stack return $this->trigger("illegal character '_'"); // but not in the input expression //=============== } elseif ((in_array($op, $ops) or $ex) and $expecting_op) { // are we putting an operator on the stack? if ($ex) { // are we expecting an operator but have a number/variable/function/opening parethesis? $op = '*'; $index--; // it's an implicit multiplication } // heart of the algorithm: while($stack->count > 0 and ($o2 = $stack->last()) and in_array($o2, $ops) and ($ops_r[$op] ? $ops_p[$op] < $ops_p[$o2] : $ops_p[$op] <= $ops_p[$o2])) { $output[] = $stack->pop(); // pop stuff off the stack into the output } // many thanks: http://en.wikipedia.org/wiki/Reverse_Polish_notation#The_algorithm_in_detail $stack->push($op); // finally put OUR operator onto the stack $index++; $expecting_op = false; //=============== } elseif ($op == ')' and $expecting_op) { // ready to close a parenthesis? while (($o2 = $stack->pop()) != '(') { // pop off the stack back to the last ( if (is_null($o2)) return $this->trigger("unexpected ')'"); else $output[] = $o2; }
위 내용은 PHP에서 문자열로 전달된 수학 공식을 안전하게 평가하는 방법은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!