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