2024-07-28 05:00:06

PHP执行后台进程

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

任何建议都将不胜感激。


当前回答

来自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 &");  
    }
}
?>

其他回答

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

我大量使用fast_cgi_finish_request()

结合使用闭包和register_shutdown_function()

$message ='job executed';
$backgroundJob = function() use ($message) {
     //do some work here
    echo $message;
}

然后注册这个闭包,在关闭之前执行。

register_shutdown_function($backgroundJob);

最后,当响应被发送到客户端时,您可以关闭到客户端的连接,并继续使用PHP进程:

fast_cgi_finish_request();

闭包将在fast_cgi_finish_request之后执行。

$消息在任何时候都不可见。您可以注册任意数量的闭包,但要注意脚本执行时间。 这将只在PHP作为一个快速CGI模块运行时工作(这是正确的吗?!)

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

<?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");

我知道这是一个有100年历史的帖子,但不管怎样,我觉得它可能对某人有用。你可以在页面的某个地方放一个不可见的图像,指向需要在后台运行的url,就像这样:

<img src="run-in-background.php" border="0" alt="" width="1" height="1" />

下面是在PHP中启动后台进程的函数。在大量阅读和测试不同的方法和参数之后,终于创建了一个在Windows上也能工作的程序。

function LaunchBackgroundProcess($command){
  // Run command Asynchroniously (in a separate thread)
  if(PHP_OS=='WINNT' || PHP_OS=='WIN32' || PHP_OS=='Windows'){
    // Windows
    $command = 'start "" '. $command;
  } else {
    // Linux/UNIX
    $command = $command .' /dev/null &';
  }
  $handle = popen($command, 'r');
  if($handle!==false){
    pclose($handle);
    return true;
  } else {
    return false;
  }
}

注意1:在windows上,不要像其他地方建议的那样使用/B参数。它强制进程运行与start命令本身相同的控制台窗口,导致进程被同步处理。要在单独的线程中(异步地)运行进程,请不要使用/B。

注2:如果命令是带引号的路径,则必须在start ""后加上空双引号。Start命令将第一个带引号的参数解释为窗口标题。