Secure math expressions using PHP eval() PHP's eval() is general considered insecure with any user input.

However, with filtering of input (i.e. [-+/*0-9]) you can make eval() secure for a small subset of PHP; mathematical expressions.

I've tried to think of a way to extend that by allowing a limited set of math functions and I believe I've found a secure way of doing it.

function expression($expression) {
    static $function_map = array(
        'floor'     =&gt; 'floor',
        'ceil'      =&gt; 'ceil',
        'round'     =&gt; 'round',         
        'sin'       =&gt; 'sin',
        'cos'       =&gt; 'cos',
        'tan'       =&gt; 'tan',           
        'asin'      =&gt; 'asin',
        'acos'      =&gt; 'acos',
        'atan'      =&gt; 'atan',          
        'abs'       =&gt; 'abs',
        'log'       =&gt; 'log',           
        'pi'        =&gt; 'pi',
        'exp'       =&gt; 'exp',
        'min'       =&gt; 'min',
        'max'       =&gt; 'max',
        'rand'      =&gt; 'rand',
        'fmod'      =&gt; 'fmod',
        'sqrt'      =&gt; 'sqrt',
        'deg2rad'   =&gt; 'deg2rad',
        'rad2deg'   =&gt; 'rad2deg',
    );

    // Remove any whitespace
    $expression = strtolower(preg_replace('~\s+~', '', $expression));

    // Empty expression
    if ($expression === '') {
        trigger_error('Empty expression', E_USER_ERROR);
        return null;
    }

    // Illegal function
    $expression = preg_replace_callback('~\b[a-z]\w*\b~', function($match) use($function_map) {
        $function = $match[0];
        if (!isset($function_map[$function])) {
            trigger_error("Illegal function '{$match[0]}'", E_USER_ERROR);
            return '';
        }
        return $function_map[$function];
    }, $expression);

    // Invalid function calls
    if (preg_match('~[a-z]\w*(?![\(\w])~', $expression, $match) &gt; 0) {
        trigger_error("Invalid function call '{$match[0]}'", E_USER_ERROR);
        return null;
    }

    // Legal characters
    if (preg_match('~[^-+/%*&amp;|&lt;&gt;!=.()0-9a-z,]~', $expression, $match) &gt; 0) {
        trigger_error("Illegal character '{$match[0]}'", E_USER_ERROR);
        return null;
    }       

    return eval("return({$expression});");
}


It's use would be simple: `$result = expression('floor(3 / 2)') and anything but math/boolean operators, numbers and a limited set of functions should be filtered out.

I can't find a way of hacking this expression() function, but I'd really like to have a second opinion on this.

Is the above expression function secure for user input?
It looks pretty reasonable to me. A couple of points:


You seem to be assuming that a function start with a letter. Functions CAN start with an underscore. Are you sure there is no such function that can be exploited?
The regex in the section 'Invalid function calls' seems like it could be simpler. You are trying to check that any 'word' ends with a '(', is this right? If so, I don't think you need the second \w


Finally although this seems like a good job, the ideally ideal way to do something like this would be to make your own tokenizer. A tokenizer looks at a string char by char and decides at each point whether it's looking at part of an identifier, an operator, etc. You would simply need to call your checks at each point that it decides it has identified a complete token. The advantage of this is that it would be easy to extend the syntax you accept and if needed to make translations from the language you accept to valid PHP for eval().