2024-07-28 05:00:06

PHP执行后台进程

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

任何建议都将不胜感激。


当前回答

我大量使用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模块运行时工作(这是正确的吗?!)

其他回答

假设这是在Linux机器上运行的,我总是这样处理它:

exec(sprintf("%s > %s 2>&1 & echo $! >> %s", $cmd, $outputfile, $pidfile));

这将启动命令$cmd,将命令输出重定向到$outputfile,并将进程id写入$pidfile。

这使您可以轻松地监视进程正在做什么以及它是否仍在运行。

function isRunning($pid){
    try{
        $result = shell_exec(sprintf("ps %d", $pid));
        if( count(preg_split("/\n/", $result)) > 2){
            return true;
        }
    }catch(Exception $e){}

    return false;
}

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

如果希望通过PHP执行后台进程,请将命令的输出输送到/dev/null,并在命令末尾添加&。

exec("bg_process > /dev/null &");

注意,不能使用exec()的$output参数,否则PHP将挂起(可能直到进程完成)。

一个适用于Windows和Linux的解决方案。在我的github页面上找到更多信息。

function run_process($cmd,$outputFile = '/dev/null', $append = false){
                    $pid=0;
                if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
                        $cmd = 'wmic process call create "'.$cmd.'" | find "ProcessId"';
                        $handle = popen("start /B ". $cmd, "r");
                        $read = fread($handle, 200); //Read the output 
                        $pid=substr($read,strpos($read,'=')+1);
                        $pid=substr($pid,0,strpos($pid,';') );
                        $pid = (int)$pid;
                        pclose($handle); //Close
                }else{
                    $pid = (int)shell_exec(sprintf('%s %s %s 2>&1 & echo $!', $cmd, ($append) ? '>>' : '>', $outputFile));
                }
                    return $pid;
            }
            function is_process_running($pid){
                if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
                        //tasklist /FI "PID eq 6480"
                    $result = shell_exec('tasklist /FI "PID eq '.$pid.'"' );
                    if (count(preg_split("/\n/", $result)) > 0 && !preg_match('/No tasks/', $result)) {
                        return true;
                    }
                }else{
                    $result = shell_exec(sprintf('ps %d 2>&1', $pid));
                    if (count(preg_split("/\n/", $result)) > 2 && !preg_match('/ERROR: Process ID out of range/', $result)) {
                        return true;
                    }
                }
                return false;
            }
            function stop_process($pid){
                    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
                            $result = shell_exec('taskkill /PID '.$pid );
                        if (count(preg_split("/\n/", $result)) > 0 && !preg_match('/No tasks/', $result)) {
                            return true;
                        }
                    }else{
                            $result = shell_exec(sprintf('kill %d 2>&1', $pid));
                        if (!preg_match('/No such process/', $result)) {
                            return true;
                        }
                    }
            }

如果使用PHP,使用pcntl_fork有一个更简单的方法:

http://www.php.net/manual/en/function.pcntl-fork.php