请帮我解决PHP代码[关闭]

In my code there are 3 sections. Top and bottom section must have to execute. Middle portion have to hide if input code is wrong.

This is my code:

 //correct code section. Must execute     
 echo "must show top";        
 //may contain wrong code. But this portion should not affect on lower
 portion code     
 ob_start();     
 // sample wrong code     
 function jk($i){};     
 function jk($i){ echo $i; }';     
 ob_end_clean();   
 //correct code section. Must execute     
 echo "must show bottom";

Current output:

Fatal error: Cannot redeclare jk() (previously ......

Expected output:

must show top must show bottom or must show top

Fatal error: Cannot redeclare jk() (previously ......

must show bottom

You can't do that in this way, because the PHP file is compiled (to bytecode, but whatever) before any part of it is executed.

You can handle minor errors by wrapping the middle part in try/catch, but fatal errors will still make the whole script not work.

So what you can do is to put the middle code somewhere else, and then access it from outside the whole process scope. This is possibly tricky if the two parts need to exchange information (e.g. some variables).

The simplest way to do that is to save the code into a temporary file, then access it from within PHP by using URL get_file_contents:

// Construct PHP tag -- hiding it from editor :-)
$prolog = '<'; $prolog .= '?'; $prolog .= "php
";

// prolog code contains PHP code, but here we treat is as text,
// so all $'s must be escaped. Same goes for epilog.

$prolog .= <<<PROLOG

// This is common code to make what follows work.
// We can pass any data to this script by creating a *second* file
// and passing the name of this second file via GET to avoid POST
// size limits.
// To avoid arbitrary inclusion (just in case), we could check that
// the file name thus passes conforms to a given syntax, e.g. 32
// random hex characters, no URLs or /etc/passwd or funny stuff.
if (isset(\$_GET['envfile'])) {
    \$name = basename(\$_GET['envfile']) . '.txt';
    \$data = unserialize(file_get_contents(\$name));
    unlink(\$name);
} else {
    \$data = null;
}

PROLOG;

$epilog = <<<EPILOG
    // The code is now hopefully loaded and defines a main()
    // function.
    if (!function_exists('main')) {
        die("-ERR: no main function");
    }
    try {
        die(json_encode(main(\$data)));
    } catch (\Exception \$err) {
        die("-ERR: main failed: {\$err->getMessage()}");
    }
EPILOG;

$unique   = uniqid();
// "special dir" is NOT available from THIS site, and is the webroot of another "sandbox" web site
$physical = "./special-dir/{$unique}.php";
// file must contain a full PHP script with tags and all
file_put_contents($physical, $prolog . $code . $epilog);

// Now ask the file to the web server with its "sandbox" personality
$response = file_get_contents("http://sandbox/{$unique}.php");

The above uses a virtual host which should be only accessible from localhost itself, and which (in addition to careful security: sensitive functions disabled, no access outside its own webroot, ...) should have full debugging information and error reporting turned on.

So if you get a PHP error page, you know that an error occurred (and you might even parse it). Otherwise, the script might return e.g. a JSON-encoded variable object which you could import:

if (0 === strpos($response, '-ERR')) {
    // $response is a simple error message
}
$package = json_decode($response, true);
if (false === $package) {
    // $response is probably a fatal error HTML page
}
// Do something with $package.

So to recap:

  • get the code from user
  • mix it with appropriate prolog/epilog stubs to e.g. prime some variables to make them available to the "child" script, and return data as JSON
  • save the resulting code into a temporary file within the webroot of
  • a secured "sandbox" website with full debug
  • invoke the sandbox URL to have a second PHP interpreter spawn and look at the code
  • interpret the results

NOTE: in PHP 7, the new code parse tree approach could perhaps allow you to validate the code directly, without the need of saving it and sending to a new PHP interpreter. Then if it's OK you could eval() it directly, but take care that doing so executes the code in the current script's scope, which has higher privileges. You are quite likely to have your sandbox pwn3d and/or abused in really short order.