如何在php中等待proc_open()执行的进程?

时间:2014-01-25 17:18:11

标签: php

请在阅读完这个问题后,不要说它已被复制。我已经在网上搜索过,但没有一个解决方案适合我。

我在做什么: - >
我通过在本地主机上运行apache服务器来获取源文件(比如.c)。我将该文件保存在“/ code /”目录中,并使用php中的proc_open()编译并执行此源文件。我想要等待主php脚本,直到“proc_open()”创建的进程终止,所以我使用“pcntl_waitpid()”。但我认为使用“pcntl_waitpid()”有一个问题,因为“pcntl_waitpid()”之后的脚本没有执行。 由“pcntl_waitpid()”创建的进程从“/code/input.txt”文件获取输入并将输出提供给“/code/output.txt”文件,因此为了重定向目的,我使用proc_open(),这样我就可以轻松重定向流。

我的机器配置: -
OS - > Ubuntu 12.04 LTs
PHP 5.3.10
APACHE 2.2.22
在本地主机上运行

权限: -
/ code - > 777个
主php文件所在的文件夹 - > 777(我知道777由于安全原因不好,但因为我在本地服务器上运行脚本所以我对这些权限没有任何问题。)

我想要的是什么: - 任何人都可以告诉我任何方式,以便我可以停止主PHP脚本,直到proc-open()创建的进程终止或任何人可以告诉我其他方式来满足我的重定向要求?

以下是代码的一部分:

$descriptors = array(
            2 => array("pipe","w")
        );




        $process = proc_open($cmd,$descriptors,$pipes,$code);

        sleep(2);

        if(is_resource($process))
        {
            $status = proc_get_status($process);
            $pid = $status ["pid"];

            echo getmypid()."<br />";


            echo $pid;

            if(pcntl_waitpid($pid,$child_status) == -1)
            {
                fclose($pipes[2]);
                proc_close($process);
                exit("Sorry!Unable to compile the source_file.\n");


            }


            while(!file_exists("/code/test"))
            {

                proc_close($process);
                echo stream_get_contents($pipes[2]),"\n";
                fclose($pipes[2]);
                exit("Sorry!Unable to compile the source_file.\n");
            }

            echo "Compiled Successfully!\n";
            echo "Running test cases...\n";

            fclose($pipes[2]);

            proc_close($process);

        }
        else
        {
            exit("Sorry!Unable to compile the source_file.\n");
        }

谢谢!

2 个答案:

答案 0 :(得分:2)

最好使用管道来解决这个问题。 pcntl_wait()只是Linux,除非你处理fork(),否则它(根据我的经验)并不是很有用。使用stream_select进行等待。如果需要,我会进一步研究我的旧脚本,但它类似于:

<?php

$cwd = "/code/";

chmod("/code/test.c",0777);

switch($xtension)
{
    case "c":
        $cmd = "gcc -g test.c -o test -lm ";
    break;
    case "cpp":
        $cmd = "g++ -g test.cpp -o test";
    break;
    case "java":
        $cmd = "javac test.java";
    break;
}

$descriptors = array(   0 => array('pipe', 'r'), // stdin
                        1 => array('pipe', 'w'), // stdout
                        2 => array('pipe', 'a')  // stderr
);

$process = proc_open($cmd,$descriptors,$pipes,$code);

stream_set_blocking($pipes[2], 0);

if(($error = stream_get_contents($pipes[2]) !== false)
{
    // error starting command (error *in* command)
    die($error);

}else
{
    stream_set_blocking($pipes[2], 1);

    /* Prepare the read array */
    $read   = array($pipes[1], $pipes[2]);
    $write  = NULL;
    $except = NULL;
    $tv_sec = 0;      // secs
    $tv_usec = 1000;  // millionths of secs

    if(false === ($rv = stream_select($read, $write, $except, $tv_sec, $tv_usec)))
    {
        die('error in stream_select');

    }else if ($rv > 0)
    {
        foreach($read as $pipe_resource)
        {
            if($pipe_resource == $pipes[1])
            {
                // is stdout

            }else if($pipe_resource == $pipes[2])
            {
                // is stderr
            }
        }

    }else
    {
        // select timed out. plenty of stuff can be done here
    }
}

    // this will be executed after the process terminates

您可以通过多种方式解决问题。

首先,您可以将select中的超时设置为零,它将无限期地等待。如果您的可执行文件不使用stdout,则stream_select函数将阻塞,直到流程终止时管道关闭。

如果你的进程确实使用了stdout和stderr,你可以在循环中使用select,并在每次select返回时检查管道是否关闭。如果将来需要,这也将允许您有效地处理来自stdout和stderr的信息(json对此非常棒)。

答案 1 :(得分:1)

这是我的理解(读取stdout和stderr(希望如此)没有死锁):

class ExecResult {
    public $returnValue;
    public $stdoutBuffer;
    public $stderrBuffer;
}

class WtfPhpWhyIHaveToDoEverythingMyself {
    public static function exec($proc, $argv) {
        $cwd = getcwd();
        $env = [];

        $procAndArgv = count($argv) > 0 ?
            $proc . ' ' . implode(' ', self::escapeArgvs($argv)) :
            $proc;

        $pipes = null; // will get filled by proc_open()

        $result = new ExecResult();

        $processHandle = proc_open(
            $procAndArgv,
            [
                0 => ['pipe', 'r'], // read/write is from child process's perspective
                1 => ['pipe', 'w'],
                2 => ['pipe', 'w']
            ],
            $pipes,
            $cwd,
            $env);

        $stdin = $pipes[0];
        $stdout = $pipes[1];
        $stderr = $pipes[2];

        fclose($stdin);

        stream_set_blocking($stdout, false);
        stream_set_blocking($stderr, false);

        $outEof = false;
        $errEof = false;

        do {
            $read = [ $stdout, $stderr ]; // [1]
            $write = null; // [1]
            $except = null; // [1]

            // [1] need to be as variables because only vars can be passed by reference

            stream_select(
                $read,
                $write,
                $except,
                1, // seconds
                0); // microseconds

            $outEof = $outEof || feof($stdout);
            $errEof = $errEof || feof($stderr);

            if (!$outEof) {
                $result->stdoutBuffer .= fgets($stdout);
            }

            if (!$errEof) {
                $result->stderrBuffer .= fgets($stderr);
            }
        } while(!$outEof || !$errEof);

        fclose($stdout);
        fclose($stderr);

        $result->returnValue = proc_close($processHandle);

        return $result;
    }

    private static function escapeArgvs($argv) {
        return array_map(function ($item){
            return escapeshellarg($item);
        }, $argv);
    }
}

$result = WtfPhpWhyIHaveToDoEverythingMyself::exec('/var/www/html/hello.sh', [
    'arg 1',
    'arg 2',
]);

var_dump($result);

/*
object(ExecResult)#1 (3) {
  ["returnValue"]=> int(0)
  ["stdoutBuffer"]=>
  string(45) "var1 = arg 1
var2 = arg 2
"
  ["stderrBuffer"]=>
  string(0) ""
}
*/

我真的很失望,使用PHP,你必须自己编写所有的管道。例如,node.js已经用简单的child_process.spawn()

覆盖了你的背面