Monday, July 03, 2006

PHP Shell_exec problems

For a week, I struggled to execute a Perl script and grab it’s output, all to be done within a PHP program. Finally, after an exhaustive search, I found my savior – “Shell_exec” command in PHP. Although, the “shell_exec” command does exactly what I wanted, the road ahead was full of thorny issues. To help developers in the similar situation as of mine, here I present a structured document on the problems that one might encounter while trying to run “shell_exec” command.

Problem 1: Shell_exec() [function.shell-exec]: Cannot execute….. in Safe Mode:

Solution: In this case, the potential problem might be that your php is running in safe mode. Open php.ini file in a text editor and search for “safe_mode”. Make sure that it is turned off i.e

safe_mode = Off

Restart your webserver and try to run the php program again. "Shell_exec" command should be working now.

Problem 2: Able to execute simple commands such as “cmd”, “cd”, etc., but not perl script

Problem 3: Able to run perl script via command prompt but not using shell_exec

Solution: Problem 2 and 3 are related and, in most cases, are caused by the same problem. In both cases, probably, including the full path of perl.exe might solve the problem of running a perl script. For example

Shell_exec(“perl hello.pl”) // originally I was trying this

Shell_exec(“c:\usr\perl\bin\perl.exe hello.pl”); // by providing full path to perl.exe, my problem 2 and 3 got solved.

Another useful function that might was to consider is runExternal. It was contributed by one of the developers and is available on www.php.net. A copy of the runExternal function is below.

function runExternal($cmd,&$code) {
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a file to write to
);

$pipes= array();
$process = proc_open($cmd, $descriptorspec, $pipes);

$output= "";

if (!is_resource($process)) return false;

#close child's input imidiately
fclose($pipes[0]);

stream_set_blocking($pipes[1],false);
stream_set_blocking($pipes[2],false);

$todo= array($pipes[1],$pipes[2]);

while( true ) {
$read= array();
if( !feof($pipes[1]) ) $read[]= $pipes[1];
if( !feof($pipes[2]) ) $read[]= $pipes[2];

if (!$read) break;

$ready= stream_select($read, $write=NULL, $ex= NULL, 2);

if ($ready === false) {
break; #should never happen - something died
}

foreach ($read as $r) {
$s= fread($r,1024);
$output.= $s;
}
}

fclose($pipes[1]);
fclose($pipes[2]);

$code= proc_close($process);

return $output;
}
?>

here is how to use it:


$result= runExternal("ls -l some-file.txt",$code);

print "

";
print $result;
print "
\n";

print "code: $code\n";
?>

As you might already noticed, runExternal command replaces shell_exec. While shell_exec only returns output and no errors, runExternal returns output as well as errors. Thus runExternal is a useful during debugging process.