PHP - eval code from DB
there have been hundreds if not thousands of posts concerning the use of PHP's eval(); to run code from a database. Through all my searching I have not found an answer to my question (explained shortly).
Firstly I'll introduce you to my application.
I have three records of valid code stored in a database:
eg: ['code1']$num1 = 1;
$num2 = 3;
$num3 = $num1+$num2; //4
['code2']
$num4 = $num3; //4
$num5 = 5;
$num6 = $num4+$num5; //9
['code3']
$num7 = $num4; //4
$num8 = $num6; //9
$num9 = $num7+$num8; //13
echo $num9; //13
Next I have a function to call and run a record:
eg:function runCode($codeName) {
// assume db connection is established
$result = mysql_query("SELECT `code` FROM CodeStore WHERE `name` = '".$codeName."'");
if ($result) {
// Fetch one row
$row = mysql_fetch_开发者_如何学编程assoc($result);
if (!$row) {
die('No rows returned.');
} else {
return eval($row['code']);
}
} else {
die('Invalid query: '.mysql_error());
}
}
Now, what needs to happen is to call the three above snippets, one after each other, and have the variables inside ($numX) available for use between each other.
eg:runCode('code1');
runCode('code2');
runCode('code3');
The above call of the three snippets from the db should echo '13', it does not. And there is my question:
How can I make these variables available outside the eval'd code?
You're not fetching the results from the db. mysql_query()
does not return all the SELECTed rows.
function runCode($codeName) {
// assume db connection is established
$result = mysql_query("SELECT `code` FROM CodeStore WHERE `name` = '".$codeName."'");
if ($result) {
// Fetch one row
$row = mysql_fetch_assoc($result);
if (!$row)
die('No rows returned.');
$valid = eval($row['code']);
if($valid) {
return $valid;
} else {
die('Error executing: '.$codeName);
}
} else {
die('Invalid query: '.mysql_error());
}
}
See the manual for mysql_fetch_assoc
.
How can I make these variables available outside the eval'd code?
That's a bit difficult, there's no simple way to do this.
The eval'd pieces of code don't run in the same scope, so you'll have to somehow save the current symbol table and restore it afterwards.
If keeping the variables is enough for you, you might try get_defined_vars
(and probably a combo of compact
and extract
or a roll your own replacement for those functions). However this functions returns the name of all defined vars (include superglobals like $_GET
etc.).
If you build a clever diff algorithm, you could probably compare the variables which were defined before and after the eval'd code and find out which variables are new. Good luck with that :).
In addition to svens's answer, I think you're not checking the return value from eval()
correctly. From the PHP manual:
eval() returns NULL unless return is called in the evaluated code, in which case the value passed to return is returned. If there is a parse error in the evaluated code, eval() returns FALSE and execution of the following code continues normally.
You should only treat it as a failure if FALSE === eval($code)
. Even then, you could get in trouble if your code returns FALSE
.
Variables from DB are local in function. Make them global.
So you may use global keyword to change variables scope:
<?php
error_reporting (E_ALL);
$txt = array(
'$num1 = 1;
$num2 = 3;
global $num3; // make it global, will be used later
$num3 = $num1+$num2;
echo "num3=" . $num3 . "<br>"; //4',
'global $num3; // need to use value from this var
global $num4; // make it global, will be used later
$num4 = $num3; //4
$num5 = 5;
global $num6; // make it global, will be used later
$num6 = $num3+$num5;
echo "num6=" . $num6 . "<br>"; //9',
'global $num4; // need to use value from this var
global $num6; // need to use value from this var
$num7 = $num4; //4
$num8 = $num6; //9
global $num9; // make it global, will be used later (maybe)
$num9 = $num7+$num8; //13
echo "num9=" . $num9 . "<br>"; //13'
);
function runCode($codeName) { // just for example
eval($codeName);
}
runCode($txt[0]);
runCode($txt[1]);
runCode($txt[2]);
?>
This solution demands changing existing code in a DB. It may be difficult.
So, here is a different algorithm. At first, join all code chunks together. Then run eval function and pass joined code to it. For example:
function composeCode($codeName) {
// assume db connection is established
$result = mysql_query("SELECT `code` FROM CodeStore WHERE `name` = '".$codeName."'");
if ($result) {
// Fetch one row
$row = mysql_fetch_assoc($result);
if (!$row) {
die('No rows returned.');
} else {
return $row['code']; // CHANGED ORIGINAL CODE
}
} else {
die('Invalid query: '.mysql_error());
}
}
$code = '';
$code .= composeCode('code1');
$code .= composeCode('code2');
$code .= composeCode('code3');
eval($code);
I would definitely go with the "pass an array of code names to runCode()" way. The benefits you get:
- You can get all the snippets with a single
SELECT ... WHERE name IN ('code1', 'code2', ...)
query - You share the variables between the snippets while keeping them in the scope of the
runCode()
function call (so that they get destroyed when the function execution terminates)
Then, you can operate in two ways:
eval()
them one-by-one while fetching rows from the database resultimplode()
them with newline character to get a single code snippet toeval()
at once.
精彩评论