2

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?

4 Answers 4

2

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 :).

Sign up to request clarification or add additional context in comments.

1 Comment

thanks svens, I come across that a while ago. Ill look into it further
1

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.

1 Comment

I actually dont have to check if the code is valid, at this point in time, but I have added your suggestion to my list. thanks
0

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);

4 Comments

Either that or pass in an array of code names and eval() them all in the same function call.
so like: global $numX; //for every variable? ??
Yes. Give me a few minutes, I'll edit my post to show code sample.
Perfect! and quite funnily while talking with a friend today I came up with a solution that uses the idea of appending all the code to a string, then evaluating it all at the end. Thanks very much for your contribution.
0

I would definitely go with the "pass an array of code names to runCode()" way. The benefits you get:

  1. You can get all the snippets with a single SELECT ... WHERE name IN ('code1', 'code2', ...) query
  2. 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:

  1. eval() them one-by-one while fetching rows from the database result
  2. implode() them with newline character to get a single code snippet to eval() at once.

2 Comments

redShadow, this would be my preferred way to operate, although in some cases I may want nested runCode() functions so in turn doing it this way would render itself, unnecessary. Thanks for your suggestion.
What about using some 'tags' (something like {{codeXX}}), to replace with the appropriate code snippet from DB using preg_replace_callback() before actually eval()-ing the code? (This would also help you while debugging code that could become quite complex due to nesting..)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.