| [ Team LiB ] |
|
28.9 Avoiding evalBefore we get into the gory details, the best way to remember this tip is to remember the catchy phrase eval() is evil. You should do your best to avoid it: eval suffers from slow performance because in order to execute the code, it must invoke the runtime compiler component in the Zend Engine, which is an expensive operation. In many situations, you can replace a call to eval with equivalent code that does not make use of eval. The most common case where eval can be replaced with faster code is when you use it for accessing variables or functions dynamically. Consider Listing 28.2. Listing 28.2 Unnecessary use of eval
<?php
function assign($varname, $value)
{
eval("global \$$varname; \$$varname = \$value;");
}
for($i=0; $i<100; $i++)
{
assign("foo", 5);
print($foo);
}
?>
In this example, assign can be used to assign values into variables when you have the variable name handy and not the variable itself. In our case, the eval string will expand to global $foo; $foo = $value; which assigns 5 to the global foo variable, and when we print it, we get 5, as expected. You can achieve the same functionality without using eval by using an indirect reference. See Listing 28.3. Listing 28.3 Removing unnecessary use of eval
<?php
function assign($varname, $value)
{
global $$varname;
$$varname = $value;
}
for($i=0; $i<100; $i++)
{
assign("foo", 5);
print($foo);
}
?>
Prefixing variable varname with an extra $ tells PHP to fetch the variable whose name is the value of var. This feature is called an indirect reference. In our case, the value of variable is foo, so PHP globalizes foo and assigns the value to it. Since it doesn't have to invoke the runtime compiler, this new version will yield approximately twice as many requests per second as the eval version! Another way to eliminate repeated calls to eval involves creating a function dynamically. Let's assume that we have a few lines of code in a variable named code, possibly fetched from a database, passed from a different part of the program or constructed locally. Listing 28.4 shows a naïve implementation. Listing 28.4 Call to dynamic code with eval
<?php
//create some example code
$code = "sqrt(pow(543, 12));";
for($i=0; $i<100; $i++)
{
eval($code);
}
?>
As mentioned before, this is exceptionally slow. PHP invokes the Zend Engine runtime compiler for each iteration. The technique in Listing 28.5 offers a better solution. Listing 28.5 Using a dynamic function to eliminate eval
<?php
//create some example code
$code = "sqrt(pow(543, 12));";
//create a function to wrap
//the loaded code
$func = create_function('', $code);
for($i=0; $i<100; $i++)
{
$func();
}
?>
The create_function function creates a new function from the code passed to it, as discussed in Chapter 11. While the results of Listing 28.4 and Listing 28.5 are identical, Listing 28.5 is several times faster. The reason is simple: Listing 28.4 invokes the runtime compiler 100 times, each time we eval the code. Using create_function, the script invokes the runtime compiler only once and declares an anonymous function, which it calls 100 times. This saves 99 invocations of the runtime compiler, which results in a huge performance boost. |
| [ Team LiB ] |
|