gpt4 book ai didi

php - 在 PHP 中正确执行 shell

转载 作者:可可西里 更新时间:2023-11-01 12:17:35 27 4
gpt4 key购买 nike

问题

我正在使用一个利用 proc_open() 调用 shell 命令的函数。看来我执行 STDIO 的方式是错误的,有时会导致 PHP 或目标命令锁定。这是原始代码:

function execute($cmd, $stdin=null){
$proc=proc_open($cmd,array(0=>array('pipe','r'),1=>array('pipe','w'),2=>array('pipe','w')),$pipes);
fwrite($pipes[0],$stdin); fclose($pipes[0]);
$stdout=stream_get_contents($pipes[1]); fclose($pipes[1]);
$stderr=stream_get_contents($pipes[2]); fclose($pipes[2]);
return array( 'stdout'=>$stdout, 'stderr'=>$stderr, 'return'=>proc_close($proc) );
}

大部分时间都有效,但这还不够,我想让它始终有效。

问题在于如果 STDIO 缓冲区超过 4k 数据,stream_get_contents() 会锁定。

测试用例

function out($data){
file_put_contents('php://stdout',$data);
}
function err($data){
file_put_contents('php://stderr',$data);
}
if(isset($argc)){
// RUN CLI TESTCASE
out(str_repeat('o',1030);
err(str_repeat('e',1030);
out(str_repeat('O',1030);
err(str_repeat('E',1030);
die(128); // to test return error code
}else{
// RUN EXECUTION TEST CASE
$res=execute('php -f '.escapeshellarg(__FILE__));
}

我们两次向STDERR和STDOUT输出一个字符串,总长度为4120字节(超过4k)。这会导致 PHP 在两侧都被锁定。

解决方案

显然,stream_select() 是可行的方法。我有以下代码:

function execute($cmd,$stdin=null,$timeout=20000){
$proc=proc_open($cmd,array(0=>array('pipe','r'),1=>array('pipe','w'),2=>array('pipe','w')),$pipes);
$write = array($pipes[0]);
$read = array($pipes[1], $pipes[2]);
$except = null;
$stdout = '';
$stderr = '';
while($r = stream_select($read, $write, $except, null, $timeout)){
foreach($read as $stream){

// handle STDOUT
if($stream===$pipes[1])
/*...*/ $stdout.=stream_get_contents($stream);

// handle STDERR
if($stream===$pipes[2])
/*...*/ $stderr.=stream_get_contents($stream);
}

// Handle STDIN (???)
if(isset($write[0])) ;

// the following code is temporary
$n=isset($n) ? $n+1 : 0; if($n>10)break; // break while loop after 10 iterations

}
}

唯一剩下的难题是处理 STDIN(请参阅标记为 (???) 的行)。我发现无论调用我的函数 execute() 都必须提供 STDIN。但是,如果我根本不想使用 STDIN 怎么办?在我上面的测试用例中,我没有要求输入,但我应该对 STDIN 做一些事情。

也就是说,上述方法仍然卡住stream_get_contents()。我不太确定下一步该做什么/尝试什么。

致谢

该解决方案由 Jakob Truelsen 提出,并发现了原始问题。 4k 小费也是他的主意。在此之前,我对为什么该函数运行良好感到困惑(不知道这完全取决于缓冲区大小)。

最佳答案

好吧,好像一年过去​​了,忘了这件事还在悬而未决!

但是,我在一个很好的 PHP 类中解决了这个问题,您可以找到它 on Github .

剩下的主要问题是读取STDERR会导致PHP脚本阻塞,所以已经禁用。

好的一面是,多亏了事件和一些不错的编码(我希望如此!),人们实际上可以与正在执行的进程进行交互(因此类名 InterExec)。因此,您可以在 PHP 中拥有机器人风格的行为。

关于php - 在 PHP 中正确执行 shell,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6014761/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com