<?php
// Execute command with PHP and read/write STDOUT, STDERR and STDIN
$cmd = array(
'cwd' => '/home/user/Desktop',
'cmd' => '/home/user/Desktop/test.sh arg1 arg2 arg3',
// 実行するコマンドのための環境変数の配列。 現在の PHP プロセスと同じ環境変数を使用する場合は NULL を指定します。
'env' => NULL,
'stdout' => '',
'stderr' => '',
'stdin' => array(
'[\r\n]ABC[\r\n]' => "Yes\n",
'[\r\n]DEF[\r\n]' => "No\n",
),
);
try {
$result = exec_cmd($cmd);
print_r($result);
} catch (Exception $e) {
echo $e->getMessage();
}
/**
* See: http://php.net/manual/ja/function.proc-open.php
* https://stackoverflow.com/questions/16351302/reading-from-stdin-pipe-when-using-proc-open
* http://ishijima.blog97.fc2.com/blog-entry-68.html
*/
function exec_cmd($cmd, $timeout_sec = 3) {
$timeout = time() + $timeout_sec; // sec
$descriptorspec = array(
0 => array("pipe", "r"), // stdin read by child process
1 => array('pipe', 'w'), // stdout write by child process
2 => array('pipe', 'w') // stderr write by child process
);
$pipes = NULL;
$proc = proc_open($cmd['cmd'], $descriptorspec, $pipes, $cmd['cwd'], $cmd['env']);
if (!is_resource($proc)) {
return false;
}
// print_r($pipes);
// non blocking, need to timeout.
stream_set_blocking($pipes[0] ,0);
stream_set_blocking($pipes[1] ,0);
stream_set_blocking($pipes[2] ,0);
$stdin_key = key($cmd['stdin']);
$stdin_val = array_shift($cmd['stdin']);
$match_string = '';
$close = function($proc, $pipes) {
foreach ($pipes as &$pipe) {
if ($pipe) {
fclose($pipe);
}
}
unset($pipes);
return proc_close($proc);
};
// now, poll for child termination
while (true) {
// echo 'while ';
// detect if the child has terminated - the php way
$status = proc_get_status($proc);
// check return value
if ($status === FALSE) {
$close($proc, $pipes);
throw new Exception ("Failed to obtain status information for ".$status['pid']);
}
if ($timeout - time() <= 0) {
$close($proc, $pipes);
throw new Exception ("Timeout ".$status['pid']);
}
if ($status['running'] === FALSE) {
$cmd['exitcode'] = $status['exitcode'];
$cmd['proc_close'] = $close($proc, $pipes);
// print_r($cmd);
return $cmd;
}
// read from child stdout and stderr
// avoid *forever* blocking through using a time out (50000usec)
foreach (array(1, 2) as $desc) {
// echo 'foreach ';
// check stdout for data
$read = array($pipes[$desc]);
$write = NULL;
$except = NULL;
$tv = 0;
$utv = 50000; // microsecond
$n = stream_select($read, $write, $except, $tv, $utv);
if ($n == 0) {
continue;
}
do {
// echo 'do ';
$data = fread($pipes[$desc], 8092);
// fwrite(STDOUT, $data);
if ($desc == 1) {
$cmd['stdout'] .= $data;
$match_string .= $data;
} else {
$cmd['stderr'] .= $data;
}
if ($stdin_key) {
if (preg_match('/('.$stdin_key.')(.*)$/', $match_string, $matches)) {
// echo '-- matches --';
// print_r($matches);
$match_string = $matches[count($matches) - 1]; // Behind the matched part
fwrite($pipes[0], $stdin_val);
$stdin_key = key($cmd['stdin']);
$stdin_val = array_shift($cmd['stdin']);
}
}
} while (strlen($data) > 0);
} // foreach stdout and stderr
}
}