Is eval really evil?
In my eternal quest for the best possible performance, I have a dilema. Admittedly I have gone a bit too performance mad lately, benchmarking nearly ever decision I make. But after the experiences in my last article, I’ve come to realise the importance of a framework built for scalability. So now in my attempt to squeeze every milisecond of performance out of my framework, my dilema is this; If I can shave an extra few milliseconds off speed by using eval, should I? I’m talking about using it in query bindings. For those of you not familiar with them, they’re simply an easy way of escaping data in queries, eg:
$db->query(’SELECT * FROM my_table WHERE my_field = ?’, $_GET[’my_field’]);
Would then replace the ‘?’ with an escaped and quoted version of $_GET[’my_field’]. This makes sanitizing variables a breeze and makes SQL logic clearer.
Most query binding classes simply loop through the replacement arguments and replace any instances of the binding marker (in my example it’s a ‘?’) with a temporary maker, then replace all the query’s binding markers with their replacement argument, then replace back in the binding string that they replaced at the start. They do this so if the replacement text contains the binding marker (?), it won’t get replaced again by the system (otherwise it will think it’s another binding marker). Now this seems like a very long winded way of doing it to me. So it order to find a better way, I did some experimenting. Now I know that preg_replace will not replace part of a string that it’s already been through, so if I replace a ‘?’ with a ‘?’ it won’t continue to keep on replacing that with itself - thus avoiding our original problem. So after a few minutes playing, I came up with this:
$query = ‘To ? or ? ? ??, that is the ?’;
$args = array(’be’, ‘not’, ‘to’, ‘be’, ‘?’, ‘question’);$j = 0;
// Genius ey?
$query = preg_replace(”/\?/e”, “escape(@\$args[\$j++])”, $query);
As you can see, this replaces each ‘?’ with the current argument and increments the counter to the next argument. Note: this will give a notice if there’s more binding markers than replacements, so we have to put an ‘@’ in front to supress the notice (you could also check isset() but I’m trying to keep the code as simple as possible here). This works out to be much faster than the original solution, as you’re only looping through the bindings once, and only replacing them once. However, looking at the code, you can see that we’re not actually taking advantage of the PCRE patterns and PCRE functions can be quite resource heavy compared to simple string replacement functions. What we’re effectively doing is just loop through the binding markers and eval()ing the current replacement (that’s what the /e does). So why not use eval? Here’s what I came up with:
$query = str_replace(”?”, “‘. escape(\$args[\$j++]) .’”, addslashes($query));
eval(’$query = ”. $query .”;’);
Now we’re simply replacing and instances of the binding marker with the raw PHP, ready to be evaled. We’re effectively doing the same as before, except we’re saving the need to using PCRE. I know it doesn’t look as elegant as the previous solution, but benchmarks show it’s consistantly faster, even if it is just milliseconds. So, do I go with the elegant but not as fast and possibly unnecessary sollution? Or do I go with the evil and ugly, but faster and more efficient sollution?
A famous quote from the creator of PHP is “If eval() is the answer, you’re almost certainly asking the wrong question.”, well am I asking the right question?
