There and Back Again

Using Eval in PHP

PHP contains an eval function, and since it lets build PHP code at runtime it allows for some very neat tricks, such as creating mock objects or soap proxy classes at runtime. Though eval can be useful that doesn’t mean its not exteremly dangerous, in this post im going to talk about when I think eval should be used, and some of its security concerns.

Eval’s Inherent Security Risk

Eval by its nature is always going to be a security concern. You taking a string from an external source and bringing it into your PHP script, you can think of attacks of this nature to being equivalent to SQL injection though they can generally cause a lot more damage ($GLOBALS generally contains your DB password and PHP has lots of filesystem functions). Now proper escaping and data cleaning should mitigate these risks but its easier to just avoid them whenever possible.

Uses of Eval

Eval has 3 primary uses, providing compatability between php versions, generating class definitions and logic at runtime, and call functions with variable names. Now when we look at these 3 use cases the actual when to use eval choice breaks down like this: using eval for compat is great, for logic is good if there is no other way, and NEVER to call functions.

Using Eval to provide Compat

This is a pretty straight forward use case, based on phpversion check you either use eval to create a function or execute some code at runtime. Most compat can be provided but without eval since a function definition inside an if statement is conditionally declared, but in cases where a new PHP5 keyword is used eval must be used to prevent PHP4 compile errors.

PHP_Compat in PEAR provides a number of compat functions, its clone method shows using eval to prevent compile errors, whiles its call_user_func_array method shows using eval to solve a problem when there is no other solution.

Using Eval to generate class/function definitions at runtime

This is an area where you end up using Eval because there is no other solution, but before you make the choice you might want to look at your design. For example you might think that using eval to run say store validation rules in a database and combine them automatically is a good idea, but this same solution could easily be done though a function naming scheme and just storing the name of the rule in the db. There are lots of cases like this, I tend to make the decision like this, if the solution can be done any other way try that before looking to Eval.

A good example of when eval is required is generating a proxy object from WSDL data (you could actually write the data out and include it as well but that only makes sense if there is an integrated cache, and has the same security problems). Without eval there is no way to create a class definition at runtime, so what you do is pulldown the xml definition of the class, generate a PHP class from that and then include it.

This brings up lots of security concerns, the biggest being that your taking data from a non-trusted source and generating PHP from it. In this case its alleviated by the fact that the actual body of the functions is just a stub that calls another worker method, in fact in PHP5 you could use __call instead of this approach (though im guessing generating the proxy is faster). So this means you can do very strict validation of the function, an example function is show below (note this isn’t the real from WSDL.php its just example code).

<?php

$function = "
// generated stub for the $funcName method
function $funcName() {
    soapCall('$funcName',func_get_args());
}
eval($function);

?>

As you can see we have just one variable to worry about cleaning, its a string and it can only be the characters allowed in a function name. Thats an important way of looking at cleaning data your going to eval, you could say it can’t contain a ; or a -, but you might miss something important, its always easier to make a whitelist rather then a black list. An example of cleaning the input is show below using preg_replace, I like to use preg_replace for this sort of thing, since regexs allow for an easy whitelist and you can decide to either replace offending characters with a default placeholder like an _ just remove them, or keep the same basic syntax, switch to preg_match and throw an error when an offending character is some instead of just cleaning it out.

<?php

$funcName = preg_replace("/[^A-Za-z0-9_]/","",$funcName)]

?>

Note: I couldn’t find a list of allowed characters in the PHP manual if anyone knows where its located please let me know
Note: While this escaping prevents security problems, it doesn’t prevent all errors since it will let a function name starting with a number though and thats not allowed.

Now if you were using user content to generate more then just function stubs you’d have to be more careful about cleaning and escaping your input, any strings should always be escaped with addslashes and any variable names with similar escaping to the code above.

Using eval to call variable functions

This is the simple case just never do it, PHP offers a couple of different ways to call functions with the name of a variable so unless you stuck on PHP < 4.0.4 eval shouldn't be needed.
For pretty much any use you can just use call_user_func_array, but you might also find just using variable functions is easier in some cases, the same syntax can also be used for objects.

The speed of eval

Besides security concerns eval also has the problem of being incredibly slow. In my testing on PHP 4.3.10 its 10 times slower then normal code and 28 times slower on PHP 5.1 beta1. This means if you have to use eval, you should avoid using it inline in any performance sensitive code. Any easy way to cancel the performance penality is to create a function in eval and just call that, now an extra function call does have some performance overhead but its pretty small and depending on the design can be non-existant since you would be calling some function anyway.

Raw Speed Data:

[jeichorn@bluga ~]$ php -v
PHP 4.3.10 (cli) (built: Jun 13 2005 21:25:09)
Copyright (c) 1997-2004 The PHP Group
Zend Engine v1.3.0, Copyright (c) 1998-2004 Zend Technologies

[jeichorn@bluga ~]$ php evalspeed.php
Eval: 1000000 times took 8.7586531639099
Same code not eval:  1000000 times took 0.99519085884094
Eval: 1000000 times took 2.1635251045227


[jeichorn@bluga ~]$ php5 -v
PHP 5.1.0b1 (cli) (built: Jun 13 2005 12:41:46)
Copyright (c) 1997-2005 The PHP Group
Zend Engine v2.1.0b1, Copyright (c) 1998-2004 Zend Technologies

[jeichorn@bluga ~]$ php5 evalspeed.php
Eval: 1000000 times took 8.6683559417725
Same code not eval:  1000000 times took 0.32248520851135
Eval: 1000000 times took 1.1273620128632

Simple Speedtest script:

<?php

/**
 * Simple function to replicate PHP 5 behaviour
 */
function microtime_float()
{
   list($usec, $sec) = explode(" ", microtime());
   return ((float)$usec + (float)$sec);
}

$rounds = 1000000;

$start = microtime_float();
for($i = 0; $i < $rounds; $i++) {
        eval('$b = $i;');
}
$end = microtime_float();
echo "Eval: $rounds times took ".($end-$start)."n";


$start = microtime_float();
for($i = 0; $i < $rounds; $i++) {
        $b = $i;
}
$end = microtime_float();
echo "Same code not eval:  $rounds times took ".($end-$start)."n";


$start = microtime_float();
eval('function test($i) { $b = $i; }');
for($i = 0; $i < $rounds; $i++) {
        test($i);
}
$end = microtime_float();
echo "Eval in function: $rounds times took ".($end-$start)."n";

?>

Security Reference

I’m currently looking for articles and reference about using eval securely, please send me links if you know of anything.

20 thoughts on “Using Eval in PHP

  1. Travis Swicegood

    Hey Josh,

    Interesting info. I’ve just skimmed the data at the top, but was caught by the benchmark. I adjusted it to attempt asserts to compare that $b PHP 5.0.4 (cli) (built: May 24 2005 13:33:34)
    Copyright (c) 1997-2004 The PHP Group
    Zend Engine v2.0.4-dev, Copyright (c) 1998-2004 Zend Technologies

    Eval: 1000000 times took 37.7924017906
    Same code not eval: 1000000 times took 1.51829910278
    Eval in function: 1000000 times took 5.09292197227

    Looks like I have some code adjustments to make to get rid of all my single-quotes in the assert’s I’m so fond of :-)

    As for using eval to generate dynamic code on the fly – why not implement a stream? Unless the class and/or method signatures need to change (which why would they?), you could store you data in a database, then create a stream wrapper around it, and just call include(“mysqlstream:/table/classToGenerate”);. I don’t know how PHP would react to that time-wise, but it might be interesting to see.

  2. Joshua Eichorn Post author

    There are lots of tricks you can do with streams but I wouldn’t do anything like that, code belongs on the filesystem and you should design things to dynamically select it.

    But it is important to remember that dynamically generating code and storing it somewhere might solve the speed problems but it doesn’t solve of any of the security ones.

  3. Donald Lobo

    great article. I’m wondering how you would implement the following without using eval:

    call a static method bar in class foo and pass in a few parameters by reference?

    $a = new a( );
    $c =& foo::bar( $a );

    class foo {
    function &bar( &$object ) {
    c = new c();
    return c;
    }
    }

    now make the assumption that foo is typically a variable (i.e. u r repeating the same pattern across multiple classes)

  4. Joshua Eichorn Post author

    I belive the only option is call_user_func_array

    Something like below seems to work:

    class Test {
    function blah(&$test) {
    $test->blah = “blah was here”;
    }
    }

    $i = new Test();

    call_user_func_array(array(‘Test’,'blah’),array(&$i));

    var_dump($i->blah);

  5. Pingback: HotPHPPER News

  6. Hayley Watson

    “its always easier to make a blacklist rather then a white list.”
    Haven’t you got those back to front? A whitelist (in this context) being the charaters that ARE allowed in a function name, and a blacklist being the ones that are NOT. It’s easy enough to make a list of all the allowable characters (the whitelist), but the only reliable way of making the blacklist would be to start with a list containing every character and subtract the whitelist.

    Whitelisting gives you a list of everything you allow, while blacklisting requires guessing every possible thing that you shouldn’t allow.

  7. Hayley Watson

    Returns a list of all (uppercase) characters that are allowed to start function names. It is surprisingly extensive, but do you have to allow ALL of them in function names you create at runtime?

    Adapting it to find legal subsequent characters is straightforward. The only glitch you might experience is with _(), if you have the gettext extension installed.

  8. Joshua Eichorn Post author

    Hayley thanks for the catch, that was just flipping things around, I say whitelist in the next sentence as well. Also it I think your second comment got cut off, what returns the list of characters.

    Also you can limit things however you want, its really just a matter of keeping out anything that will cause a security problem and meeting the needs of your situation.

  9. Jorge

    Hello, its a very interesting article. I am building a cms and considered eval to allow the cms admin to write modules on the fly using php and eval. I know that eval is quite dangerous and that’s why I’m lookig for information about coding it safely. What do you think about it? Should I try something different? Do you think it is possible to make it safely anyway?

    Jorge

  10. Joshua Eichorn Post author

    Jorge:
    In a situation like that is no great way to garentee security. What you’ll want to do is remove the runtime eval by just writing the new code to files (helps performance too) and then focus on making sure that only the admin can write new code.

  11. Stefan Braunewell

    Eval is not inherently slow, it just has a small constant run-time every time it’s called. To assess the speed of eval() php parsing itself, you can put the for-loop inside the eval function. Results:

    Eval: 1000000 times took 8.4494090080261
    Same code not eval: 1000000 times took 0.83726000785828
    Loop in eval took 1.0076489448547

    You can see: executing code in eval is not the problem – envoking it so many times is.

  12. Pingback: PHP security | TheStruggle

  13. sharpskater69

    good. consider using /\W/, much easier in your example. good though.

  14. John Kleijn

    This benchmark is useless. You can’t compare the performance of already parsed code with unparsed code. Compare it against ‘include’ and we have something that actually says something other than, “yes, parsing code takes time”.

  15. Erwin Haantjes

    Hello, i have made a class function that makes it easier to deal with constructors and parent functions, it is also using eval to call the parent function. It is called inherited(), see example code below. But the question is, is it real slower? Does have anybody an idea (when speed is an issue) to get better perfomance?

    Here it is:
    Hi, i wanted to get rid off the parent::methodName(myVar1, myVar2, myVar3 etc…..) construction so i wrote a shortcut to this notation. The only thing you have to do is calling $this->inherited() (or parent::inherited()) to call the same function with the same parameters in the parent class. It is also possible to specify other parameters if you like (also by reference). You can also use this inside constructors/destructors. It is also possible (without rewriting an existing php4 ‘base’ class and it’s constructors/destructors) to use a php4 class as base class in php5, anyway look at the source code below.

    IMPORTANT TO KNOW:
    - $this->raise() is a custom error function, it displays trace info when called
    - There are some defines used to display, these are not included in this demo (but doesn’t care)
    - TObject is an custom base object that have the inherited() function as described below.

    The function (used in base class of all objects):
    public function inherited()
    {
    // Use DEBUG backtrace to trace caller function
    $bt = debug_backtrace($this);
    $bt = $bt[ 1 ]; // List is in reversed order, 0 reffers to this function so get previous one

    if( !@count( $bt ))
    { $this->raise( ERR_DEBUGTRACE_INFO_INVALID ); }

    $sFuncName = $bt["function"];
    $sClassName = $bt["class"];
    $sParentClassName = get_parent_class( $sClassName );

    // check absurt situations to be sure
    if( empty( $sClassName ) or empty( $sParentClassName ) or $sParentClassName == $sClassName )
    { $this->raise( str_replace( array( “%s1″, “%s2″ ), array( $sFuncName, $sClassName ), ERR_CANNOT_CALL_INHERITED )); }

    // constructor or destructor called (or calls and old fashion way php 4 constructor or destructor)?
    $bIsConstruct = false;
    if( ( $bIsConstructor = ( $sFuncName == “__construct” ))
    or ( $bIsDestructor = ( $sFuncName == “__destruct” ))
    or ( $bIsConstruct = ( $sFuncName == $sClassName )) or ( $sFuncName == “_”.$sClassName ) )
    {
    // get parent constructor/destructor
    $sFuncName = (( !$bIsConstructor and !$bIsConstruct ) ? “_” : “” ).$sParentClassName;

    $bFunctionExist = method_exists( $sParentClassName, $sFuncName );
    $bConstructorExist = ( $bIsConstructor or $bIsConstruct ) ? method_exists( $sParentClassName, “__construct” ) : false;
    $bDestructorExist = ( !$bIsConstructor or !$bIsConstruct ) ? method_exists( $sParentClassName, “__destruct” ) : false;

    if( $bConstructorExist or $bDestructorExist )
    {
    $sFuncName = ( $bConstructorExist ) ? “__construct” : “__destruct”; // preffer to call this
    $bIsConstruct = !$bDestructorExist;
    $bIsConstructor = false;
    $bFunctionExist = true;
    }
    elseif( $bFunctionExist and !$bIsConstructor and !$bIsDestructor )
    { $bFunctionExist = ( $sFuncName != (( !$bIsConstruct ) ? “_” : “” ).$sClassName ); }

    if( !$bFunctionExist )
    { $this->raise( str_replace( array( “%s1″, “%s2″ ), array( $sFuncName, $sParentClassName ), ( $bIsConstruct ) ? ERR_CANNOT_CALL_INHERITED_CONSTRUCTOR : ERR_CANNOT_CALL_INHERITED_DESTRUCTOR )); }
    }
    else { $bFunctionExist = method_exists( $sParentClassName, $sFuncName ); }

    if( $bFunctionExist )
    {
    // If there are parameters specified, use these
    $args = func_get_args();
    if( ( $iCount = @count( $args )) 0 )
    {
    for( $i = 0; $i < $iCount; $i++ )
    { $sArgs.=”&$”.”args[$i]“.( $i raise( str_replace( array( “%s1″, “%s2″ ), array( $sFuncName, $sParentClassName ), ERR_CANNOT_CALL_INHERITED )); }
    }

    example (DEMO):
    class TTest extends TObject
    {
    public function __construct()
    {
    $this->inherited();
    }

    public function __destruct()
    {
    print( “destructror called TTest\n” );
    $this->inherited();
    }

    public function test(&$param1)
    {
    $param1 = $param1.” world”;
    return $param1.” “.$this->className;
    }
    }

    class TTest2 extends TTest
    {
    public function __construct()
    {
    $this->inherited();
    }

    public function __destruct()
    {
    print( “destructror called TTest2\n” );
    $this->inherited();
    }

    public function test(&$param1)
    {
    return $this->inherited();
    }
    }

    class TTest3 extends TTest2
    {
    public function __construct()
    {
    $this->inherited();
    }

    public function test(&$param1)
    {
    return $this->inherited();
    }

    public function __destruct()
    {
    print( “destructror called TTest3\n” );
    $this->inherited();
    }

    }

    $test = new TTest3();
    $s = “hello”;
    print( “\nOrginal reference string is : $s\n” );
    print( “function result is : “.$test->test( $s ).”\n” );
    print( “Modified reference string is: $s\n\n” );

    So what do u think?

  16. Pingback: PHP security « The Struggle

  17. Ali

    I think this this benchmark is not real results.
    when you call a page in browser, you will connect to a webserver.
    web server opens php (as cgi, fast-cgi, etc…) and then, PHP first open the script, compile it and then parse it.

    with timing method that you used, you not calculated these levels but parsing.
    when you use eval(), PHP will compile PHP code.

    unfortunately, PHP is an slow scripting language and because of this, there are dozen’s of compiler extensions for PHP like APC, ionCube, eAccelerator, Zend, …

    because of this problem, I have writen my own database, webserver and my blog and forums systems with C running on CentOS.
    It’s about 25x faster (in average) in my tests. (in some cases, more than 70x)

  18. Neorush

    I know this is an old post but, eval() really isn’t all that inefficent when used correctly, as a matter of fact it is just about as fast (maybe 0.001 secs slower in my tests) to use:
    as it is to use:

    I also wanted to point out that eval as actually more efficient than an include because of IO operations, so evaluating a function as a string instead of including it from a file is actually faster. Some additional tests to add to above:

    $start = microtime_float();
    function testNotInc($i) { $b = $i; }
    for($i = 0; $i < $rounds; $i++) {
    testNotInc($i);
    }
    $end = microtime_float();
    echo "Function NOT from include: $rounds times took ".($end-$start)."\n";

    $start = microtime_float();
    require('testinc.php'); // put function testinc($i) { $b = $i; } in this file
    for($i = 0; $i < $rounds; $i++) {
    testinc($i);
    }
    $end = microtime_float();
    echo "Function from include: $rounds times took ".($end-$start)."\n";

    Security is still a concern.

  19. Pingback: when is eval evil in php? - PHP Questions - Developers Q & A