2024-07-28 05:00:06

PHP执行后台进程

我需要在用户操作时执行目录复制,但目录相当大,因此我希望能够在用户不知道完成复制所需时间的情况下执行这样的操作。

任何建议都将不胜感激。


当前回答

使用此函数在后台运行程序。它是跨平台的,完全可定制的。

<?php
function startBackgroundProcess(
    $command,
    $stdin = null,
    $redirectStdout = null,
    $redirectStderr = null,
    $cwd = null,
    $env = null,
    $other_options = null
) {
    $descriptorspec = array(
        1 => is_string($redirectStdout) ? array('file', $redirectStdout, 'w') : array('pipe', 'w'),
        2 => is_string($redirectStderr) ? array('file', $redirectStderr, 'w') : array('pipe', 'w'),
    );
    if (is_string($stdin)) {
        $descriptorspec[0] = array('pipe', 'r');
    }
    $proc = proc_open($command, $descriptorspec, $pipes, $cwd, $env, $other_options);
    if (!is_resource($proc)) {
        throw new \Exception("Failed to start background process by command: $command");
    }
    if (is_string($stdin)) {
        fwrite($pipes[0], $stdin);
        fclose($pipes[0]);
    }
    if (!is_string($redirectStdout)) {
        fclose($pipes[1]);
    }
    if (!is_string($redirectStderr)) {
        fclose($pipes[2]);
    }
    return $proc;
}

注意,在命令启动后,默认情况下,该函数关闭正在运行进程的stdin和stdout。你可以通过$redirectStdout和$redirectStderr参数将进程输出重定向到某个文件中。

windows用户注意: 不能以以下方式将stdout/stderr重定向为null:

startBackgroundProcess('ping yandex.com', null, 'nul', 'nul');

然而,你可以这样做:

startBackgroundProcess('ping yandex.com >nul 2>&1');

*nix用户注意事项:

1)如果你想得到实际的PID,使用exec shell命令:

$proc = startBackgroundProcess('exec ping yandex.com -c 15', null, '/dev/null', '/dev/null');
print_r(proc_get_status($proc));

2)使用$stdin参数,如果你想传递一些数据到你的程序的输入:

startBackgroundProcess('cat > input.txt', "Hello world!\n");

其他回答

您是否可以安排派生一个单独的进程,然后在后台运行您的副本?我已经有一段时间没有使用PHP了,但是函数pcntl-fork看起来很有前途。

使用此函数在后台运行程序。它是跨平台的,完全可定制的。

<?php
function startBackgroundProcess(
    $command,
    $stdin = null,
    $redirectStdout = null,
    $redirectStderr = null,
    $cwd = null,
    $env = null,
    $other_options = null
) {
    $descriptorspec = array(
        1 => is_string($redirectStdout) ? array('file', $redirectStdout, 'w') : array('pipe', 'w'),
        2 => is_string($redirectStderr) ? array('file', $redirectStderr, 'w') : array('pipe', 'w'),
    );
    if (is_string($stdin)) {
        $descriptorspec[0] = array('pipe', 'r');
    }
    $proc = proc_open($command, $descriptorspec, $pipes, $cwd, $env, $other_options);
    if (!is_resource($proc)) {
        throw new \Exception("Failed to start background process by command: $command");
    }
    if (is_string($stdin)) {
        fwrite($pipes[0], $stdin);
        fclose($pipes[0]);
    }
    if (!is_string($redirectStdout)) {
        fclose($pipes[1]);
    }
    if (!is_string($redirectStderr)) {
        fclose($pipes[2]);
    }
    return $proc;
}

注意,在命令启动后,默认情况下,该函数关闭正在运行进程的stdin和stdout。你可以通过$redirectStdout和$redirectStderr参数将进程输出重定向到某个文件中。

windows用户注意: 不能以以下方式将stdout/stderr重定向为null:

startBackgroundProcess('ping yandex.com', null, 'nul', 'nul');

然而,你可以这样做:

startBackgroundProcess('ping yandex.com >nul 2>&1');

*nix用户注意事项:

1)如果你想得到实际的PID,使用exec shell命令:

$proc = startBackgroundProcess('exec ping yandex.com -c 15', null, '/dev/null', '/dev/null');
print_r(proc_get_status($proc));

2)使用$stdin参数,如果你想传递一些数据到你的程序的输入:

startBackgroundProcess('cat > input.txt', "Hello world!\n");

来自PHP官方文档(php.net)

<?php
function execInBackground($cmd) {
    if (substr(php_uname(), 0, 7) == "Windows"){
        pclose(popen("start /B ". $cmd, "r")); 
    }
    else {
        exec($cmd . " > /dev/null &");  
    }
}
?>

老问题的新答案。使用这个库,下面的代码将生成一个异步/并行PHPThread来执行后台工作。

必须有pcntl, posix和套接字扩展名 在CLI模式下设计/测试。

EZ代码示例:

 function threadproc($thread, $param) {
 
    echo "\tI'm a PHPThread.  In this example, I was given only one parameter: \"". print_r($param, true) ."\" to work with, but I can accept as many as you'd like!\n";
 
    for ($i = 0; $i < 10; $i++) {
        usleep(1000000);
        echo "\tPHPThread working, very busy...\n";
    }
 
    return "I'm a return value!";
}
 

$thread_id = phpthread_create($thread, array(), "threadproc", null, array("123456"));
 
echo "I'm the main thread doing very important work!\n";
 
for ($n = 0; $n < 5; $n++) {
    usleep(1000000);
    echo "Main thread...working!\n";
}
 
echo "\nMain thread done working.  Waiting on our PHPThread...\n";
 
phpthread_join($thread_id, $retval);
 
echo "\n\nOur PHPThread returned: " . print_r($retval, true) . "!\n";

用任何方便的语言(php/bash/perl/etc)将进程作为服务器端脚本编写,然后从php脚本中的进程控制函数调用它。

该函数可能会检测是否使用标准io作为输出流,如果是,则设置返回值,如果不是,则结束

proc_close( proc_open( "./command --foo=1 &", array(), $foo ) );

我在命令行中使用“sleep 25s”作为命令快速测试了这个功能,效果非常好。

(答案在这里)