Skip to content

Instantly share code, notes, and snippets.

@fordnox
Last active September 16, 2020 07:48
Show Gist options
  • Save fordnox/f79c90c100ccea07880c to your computer and use it in GitHub Desktop.
Save fordnox/f79c90c100ccea07880c to your computer and use it in GitHub Desktop.
Execute shell command with timeout and converting stderr to Exception if any
<?php
/**
* @param int $timeout - max process execution time in seconds until it is terminated
**/
function execute($cmd, $stdin = null, $timeout = 600)
{
$this->log->debug("executing: " . $cmd . " ". $stdin);
$cmd = str_replace("\n", "", $cmd);
$cmd = str_replace("\r", "", $cmd);
$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 pipe that the child will write to
);
$pipes = array();
$timeout += time();
$process = proc_open($cmd, $descriptorspec, $pipes);
if (!is_resource($process)) {
throw new Exception('Invalid resource', 1011);
}
$output = '';
$error = '';
if($stdin) {
// 0 => writeable handle connected to child stdin
fwrite($pipes[0], $stdin);
}
fclose($pipes[0]);
do {
$write = null;
$exceptions = null;
$timeleft = $timeout - time();
if ($timeleft <= 0) {
proc_terminate($process);
throw new Exception("command timeout", 1012);
}
$read = array($pipes[1],$pipes[2]);
stream_select($read, $write, $exceptions, $timeleft);
if (!empty($read)) {
$output .= fread($pipes[1], 1024);
$error .= fread($pipes[2], 1024);
}
$output_exists = (!feof($pipes[1]) || !feof($pipes[2]));
} while ($output_exists && $timeleft > 0);
if ($timeleft <= 0) {
proc_terminate($process);
throw new Exception("command timeout", 1013);
}
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
if($error) {
throw new Exception($error, 1012);
}
return $output;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment