2

How can I convert callable (anonymous function) into a string for eval?

I'm trying to write unit tests in phpunit that use runkit to override methods. And in particular, runkit_method_redefine() requires a string parameter that will be eval()-called later.

I want to have syntax highlighting of my test code, so I don't want to write code inside of a string, so I want to do something like

deEval(function(){ 
   return 1;
});

that would output

"return 1;"

How can this be done easily (for example without doing fopen of the source file, finding a source line, parsing etc.)?

8
  • Hm I think you have to write your PHP code as string cause every statement you write as such one gets interpreted by PHP. So you hvae no chance to fetch it and use it as "plain code". Commented Oct 31, 2013 at 10:37
  • 3
    @User016 it's not deprecated It's evil Commented Oct 31, 2013 at 10:38
  • 1
    @AlmaDo using runkit is also considered a dark side of unit-testing, I know Commented Oct 31, 2013 at 10:39
  • 2
    Why don't use combinaison of eval and call_user_func() ? eval("call_user_func('yourfunction)") Commented Oct 31, 2013 at 10:52
  • 1
    or just append a pair of parentheses to the function name? Commented Oct 31, 2013 at 10:53

2 Answers 2

4

NOTE: I do not like this solution and I would not recommend it to anyone for anything, but it does solve the problem set out in the question.


class CallableStringifier
{
    private static $callables = array();

    public static function stringify($callable)
    {
        $id = count(self::$callables);
        self::$callables[$id] = $callable;
        return 'return ' . __CLASS__ . "::call($id, func_get_args());";
    }

    public static function call($id, $args)
    {
        return call_user_func_array(self::$callables[$id], $args);
    }
}

This will work for your specific use case (which is essentially create_function()). It will not work if you just eval() it, because it relies on being inside a function context.

Example:

$func = create_function('$arg1', CallableStringifier::stringify(function($arg1) {
    return $arg1;
}));

echo $func(1); // outputs 1

See it working

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

2 Comments

Thanks. Although people should keep in mind that this doesn't really generate a true method body text, because it depends on current php process and whatever is stored in CallableStringifier::$callables. So if someone wants to write generated code in DB to be executed later, its not a good idea (not to mention its not a good idea to do it at all). But for current automated testing purposes its more efficient than reading file source
Addendum: following on from the previous comment, if your are trying to "write generated code in DB to be executed later", you need to take a long, hard look at your application design ;-)
1

Wrote a not-so efficient function.. (doesn't take into account arguments)

/**
 * Converts method code to string by reading source code file
 * Function brackets {} syntax needs to start and end separately from function body
 *
 * @param Callable $callable method to deevaluate
 *
 * @return string
 */
public function callableToString($callable) {
    $refFunc = new ReflectionFunction($callable);
    $startLine = $refFunc->getStartLine();
    $endLine   = $refFunc->getEndLine();

    $f      = fopen($refFunc->getFileName(), 'r');
    $lineNo = 0;

    $methodBody = '';
    while($line = fgets($f)) {
        $lineNo++;
        if($lineNo > $startLine) {
            $methodBody .= $line;
        }
        if($lineNo == $endLine - 1) {
            break;
        }
    }
    fclose($f);

    return $methodBody;
}

Comments

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.