php execute a background process

前端 未结 19 1348
萌比男神i
萌比男神i 2020-11-21 07:04

I need to execute a directory copy upon a user action, but the directories are quite large, so I would like to be able to perform such an action without the user being aware

相关标签:
19条回答
  • 2020-11-21 07:39

    Assuming this is running on a Linux machine, I've always handled it like this:

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

    This launches the command $cmd, redirects the command output to $outputfile, and writes the process id to $pidfile.

    That lets you easily monitor what the process is doing and if it's still running.

    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;
    }
    
    0 讨论(0)
  • 2020-11-21 07:39

    I'd just like to add a very simple example for testing this functionality on Windows:

    Create the following two files and save them to a web directory:

    foreground.php:

    <?php
    
    ini_set("display_errors",1);
    error_reporting(E_ALL);
    
    echo "<pre>loading page</pre>";
    
    function run_background_process()
    {
        file_put_contents("testprocesses.php","foreground start time = " . time() . "\n");
        echo "<pre>  foreground start time = " . time() . "</pre>";
    
        // output from the command must be redirected to a file or another output stream 
        // http://ca.php.net/manual/en/function.exec.php
    
        exec("php background.php > testoutput.php 2>&1 & echo $!", $output);
    
        echo "<pre>  foreground end time = " . time() . "</pre>";
        file_put_contents("testprocesses.php","foreground end time = " . time() . "\n", FILE_APPEND);
        return $output;
    }
    
    echo "<pre>calling run_background_process</pre>";
    
    $output = run_background_process();
    
    echo "<pre>output = "; print_r($output); echo "</pre>";
    echo "<pre>end of page</pre>";
    ?>
    

    background.php:

    <?
    file_put_contents("testprocesses.php","background start time = " . time() . "\n", FILE_APPEND);
    sleep(10);
    file_put_contents("testprocesses.php","background end time = " . time() . "\n", FILE_APPEND);
    ?>
    

    Give IUSR permission to write to the directory in which you created the above files

    Give IUSR permission to READ and EXECUTE C:\Windows\System32\cmd.exe

    Hit foreground.php from a web browser

    The following should be rendered to the browser w/the current timestamps and local resource # in the output array:

    loading page
    calling run_background_process
      foreground start time = 1266003600
      foreground end time = 1266003600
    output = Array
    (
        [0] => 15010
    )
    end of page
    

    You should see testoutput.php in the same directory as the above files were saved, and it should be empty

    You should see testprocesses.php in the same directory as the above files were saved, and it should contain the following text w/the current timestamps:

    foreground start time = 1266003600
    foreground end time = 1266003600
    background start time = 1266003600
    background end time = 1266003610
    
    0 讨论(0)
  • 2020-11-21 07:39

    Instead of initiating a background process, what about creating a trigger file and having a scheduler like cron or autosys periodically execute a script that looks for and acts on the trigger files? The triggers could contain instructions or even raw commands (better yet, just make it a shell script).

    0 讨论(0)
  • 2020-11-21 07:40

    Here is a function to launch a background process in PHP. Finally created one that actually works on Windows too, after a lot of reading and testing different approaches and parameters.

    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;
      }
    }
    

    Note 1: On windows, do not use /B parameter as suggested elsewhere. It forces process to run the same console window as start command itself, resulting in the process being processed synchronously. To run the process in a separate thread (asynchronously), do not use /B.

    Note 2: The empty double quotes after start "" are required if the command is a quoted path. start command interprets the first quoted parameter as window title.

    0 讨论(0)
  • 2020-11-21 07:44

    Use this function to run your program in background. It cross-platform and fully customizable.

    <?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;
    }
    

    Note that after command started, by default this function closes the stdin and stdout of running process. You can redirect process output into some file via $redirectStdout and $redirectStderr arguments.

    Note for windows users:
    You cannot redirect stdout/stderr to nul in the following manner:

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

    However, you can do this:

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

    Notes for *nix users:

    1) Use exec shell command if you want get actual PID:

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

    2) Use $stdin argument if you want to pass some data to the input of your program:

    startBackgroundProcess('cat > input.txt', "Hello world!\n");
    
    0 讨论(0)
  • 2020-11-21 07:44

    A working solution for both Windows and Linux. Find more on My github page.

    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;
                            }
                        }
                }
    
    0 讨论(0)
提交回复
热议问题