views:

827

answers:

3

I am trying to find a way to get the source code for (user defined) PHP functions in a string.

For normal code this is easy, using reflection I can find the file and linenumbers where the function is defined, then I can open the file and read the function source code.

This will not work if a function is defined in eval'd code. I do not want to keep record of all eval'd code.

Is this possible? If yes, then how?

Example code:

<?php

function myfunction() {
 echo "Test";
}

eval('
function myevalfunction() {
 echo "Test";
}
');

$a = new ReflectionFunction('myfunction');
echo $a;

$b = new ReflectionFunction('myevalfunction');
echo $b;

?>

Output:

Function [ <user> <visibility error> function myfunction ] {
  @@ test.php 3 - 5
}
Function [ <user> <visibility error> function myevalfunction ] {
  @@ test.php(11) : eval()'d code 2 - 4
}
A: 

My initial response is to say there's virtually 0 good reason to create a function in eval.

You can create functions conditionally, ie:

if ( $cond ){ 
   function foo(){ 

   }
}

If you want closure like behaviour I guess eval is the only way to do it until PHP5.3, but its EPIC bad stuff and you should avoid it all cost.

Here is why:


01 <?php
02 
03 function foo ()
04 {
05   eval( '
06     function baz()
07     {
08        eval("throw new Exception();"); 
09     }
10   '); 
11   baz(); 
12 }
13 
14 
15 
16 try{ 
17   foo(); 
18 }catch( Exception $e ){
19   var_dump($e); 
20 }
21 try{ 
22   foo(); 
23 }
24 catch( Exception $e ){
25   var_dump($e);
26 }

Which Emits this:



object(Exception)#1 (6) {
  ["message:protected"]=>
  string(0) ""
  ["string:private"]=>
  string(0) ""
  ["code:protected"]=>
  int(0)
  ["file:protected"]=>
  string(50) "/tmp/xx.php(10) : eval()'d code(4) : eval()'d code"
  ["line:protected"]=>
  int(1)
  ["trace:private"]=>
  array(3) {
    [0]=>
    array(3) {
      ["file"]=>
      string(31) "/tmp/xx.php(10) : eval()'d code"
      ["line"]=>
      int(4)
      ["function"]=>
      string(4) "eval"
    }
    [1]=>
    array(4) {
      ["file"]=>
      string(11) "/tmp/xx.php"
      ["line"]=>
      int(11)
      ["function"]=>
      string(3) "baz"
      ["args"]=>
      array(0) {
      }
    }
    [2]=>
    array(4) {
      ["file"]=>
      string(11) "/tmp/xx.php"
      ["line"]=>
      int(17)
      ["function"]=>
      string(3) "foo"
      ["args"]=>
      array(0) {
      }
    }
  }
}

Fatal error: Cannot redeclare baz() (previously declared in /tmp/xx.php(10) : eval()'d code:2) in /tmp/xx.php(10) : eval()'d code on line 5

Call Stack:
    0.0002     115672   1. {main}() /tmp/xx.php:0
    0.0006     122304   2. foo() /tmp/xx.php:22

So much badness, so little effort.

Kent Fredric
I appreciate you taking the time to explain why eval'd functions are bad in general.Just because is should be avoided at all cost does not mean that nobody will do it.So if it should happen, how can I find the source? I guess it is not possible.
Tader
You can basically agressively search your codebase for it with grep, and then stage public shamings for situations where eval wasn't needed. Stocks + rotten tomatoes!
Kent Fredric
( For everything where eval *is* needed, externalise everything possible so the evaled code eliminates the cause of errors in it )
Kent Fredric
+1  A: 

You cant just search through the files yourself? grep or wingrep is perfect for this.

If not you can try the pecl function rename_function and log all the eval calls which create functions.

OIS
+1  A: 

How about you define your own eval-function, and do the tracking there?

function myeval($code) {
     my_eval_tracking($code, ...);  # use traceback to get more info if necessary
     # (...)
     return eval($code);
}

That said, I do share a lot of Kent Fredric's feelings on eval in this case.

rebra