php executa um processo em segundo plano


259

Preciso executar uma cópia do diretório em uma ação do usuário, mas os diretórios são muito grandes, portanto, eu gostaria de poder executar uma ação sem que o usuário esteja ciente do tempo que leva para a cópia ser concluída.

Qualquer sugestão será muito bem-vinda.


3
Este stackoverflow.com/questions/5367261/... explica como fazer isso janelas menores
Salatiel

Respostas:


365

Supondo que isso esteja sendo executado em uma máquina Linux, eu sempre lidei com isso assim:

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

Isso inicia o comando $cmd, redireciona a saída do comando $outputfilee grava a identificação do processo $pidfile.

Isso permite que você monitore facilmente o que o processo está fazendo e se ainda está em execução.

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

2
A instalação do sudo é executada sem solicitar uma senha? Qualquer comando que exija entrada do usuário não funcionará.
precisa

17
Este stackoverflow.com/questions/5367261/... explica como fazer as mesmas janelas menores
Salatiel

13
Eu usei isso, mas o atualizei um pouco para não precisar gravar o PID em um arquivo. Então, eu uso este formato: <code> exec (sprintf ("$ s> $ s2> & 1 & echo $ 1", $ cmd, $ outputfile), $ pidArr); </code> O processo PID resultante está em $ pidArr [0]
Kaiesh 28/03

3
@ Kaiesh: deve ser "eco $!"
Brunobg

8
Kaiesh cometeu erro de digitação do anoter, '$' em vez de '%'. Versão corrigida: exec (sprintf ("% s>% s 2> & 1 & echo $!", $ Cmd, $ outputfile), $ pidArr)
Yuri Gor

23

Escreva o processo como um script do lado do servidor em qualquer idioma (php / bash / perl / etc) que seja útil e chame-o a partir das funções de controle de processo no seu script php.

A função provavelmente detecta se o io padrão é usado como o fluxo de saída e, se for, isso definirá o valor de retorno.

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

Testei isso rapidamente na linha de comando usando "sleep 25s" como comando e funcionou como um encanto.

( Resposta encontrada aqui )


3
Esta é a única maneira que eu poderia fazê-lo funcionar em centos. Obrigado!
Deac Karns

Obrigado ! Há uma incompatibilidade de PHP e BASH. Se você executar um sistema mais novo, ele falhará ao iniciar em segundo plano usando system () ou exec (). Os descritores de arquivo parecem ficar presos, mesmo se você redirecionar. Seu método funcionou. Outra alternativa é usar um binário festa velho para PHP, mas isso é loucura
John

22

Você pode tentar anexar isso ao seu comando

>/dev/null 2>/dev/null &

por exemplo.

shell_exec('service named reload >/dev/null 2>/dev/null &');

Eu quis dizer> / dev / null 2> / dev / null &
wlf

É um trabalho para mim, e vou usar o makefile para fazer alguma coisa no site do servidor, obrigado! (ex. make, make restart)
Chu-Siang Lai 24/01

Isso é muito mais simples que a resposta aceita! (Contanto que você não precisa de nada mais complexo, como verificar se o processo ainda está em execução.)
Sean Bean

18

Eu gostaria de adicionar um exemplo muito simples para testar essa funcionalidade no Windows:

Crie os dois arquivos a seguir e salve-os em um diretório da web:

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

Conceda ao IUSR permissão para gravar no diretório em que você criou os arquivos acima

Permita IUSR para ler e executar C: \ Windows \ System32 \ cmd.exe

Pressione foreground.php em um navegador da web

O seguinte deve ser renderizado no navegador com os registros de data e hora atuais e o recurso local # na matriz de saída:

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

Você deve ver testoutput.php no mesmo diretório em que os arquivos acima foram salvos e ele deve estar vazio

Você deve ver testprocesses.php no mesmo diretório em que os arquivos acima foram salvos e deve conter o seguinte texto com os carimbos de data / hora atuais:

foreground start time = 1266003600
foreground end time = 1266003600
background start time = 1266003600
background end time = 1266003610

10

Se você precisar fazer algo em segundo plano sem que a página PHP aguarde a conclusão, você poderá usar outro script PHP (em segundo plano) que é "invocado" com o comando wget. Esse script PHP de segundo plano será executado com privilégios, é claro, como qualquer outro script PHP no seu sistema.

Aqui está um exemplo no Windows usando o wget dos pacotes gnuwin32.

O código de plano de fundo (arquivo test-proc-bg.php) como um exemplo ...

sleep(5);   // some delay
file_put_contents('test.txt', date('Y-m-d/H:i:s.u')); // writes time in a file

O script de primeiro plano, aquele que invoca ...

$proc_command = "wget.exe http://localhost/test-proc-bg.php -q -O - -b";
$proc = popen($proc_command, "r");
pclose($proc);

Você deve usar o popen / pclose para que isso funcione corretamente.

As opções do wget:

-q    keeps wget quiet.
-O -  outputs to stdout.
-b    works on background

7

Aqui está uma função para iniciar um processo em segundo plano no PHP. Finalmente, criei um que também funciona no Windows, depois de muita leitura e teste de diferentes abordagens e parâmetros.

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

Nota 1: No Windows, não use o /Bparâmetro conforme sugerido em outros lugares. Força o processo a executar a mesma janela do console que o startpróprio comando, resultando no processo sendo processado de forma síncrona. Para executar o processo em um thread separado (assincronamente), não use /B.

Nota 2: As aspas duplas vazias depois start ""serão necessárias se o comando for um caminho entre aspas. startO comando interpreta o primeiro parâmetro entre aspas como título da janela.


6

Bem, eu achei uma versão um pouco mais rápida e fácil de usar

shell_exec('screen -dmS $name_of_screen $command'); 

e funciona.


@ Alpha.Dev Eu testei no linux apenas não sei se vai funcionar em outro sistema.
Morfidon

4

Você pode organizar um processo separado e depois executar sua cópia em segundo plano? Já faz um tempo desde que eu fiz PHP, mas a função pcntl-fork parece promissora.


Isso não fornece uma resposta para a pergunta. Para criticar ou solicitar esclarecimentos a um autor, deixe um comentário abaixo da postagem.
precisa saber é o seguinte

13
Graças - Comentários não existia em '08, mas vou manter isso em mente :)
Matt Sheppard

4

Use esta função para executar seu programa em segundo plano. É multiplataforma e totalmente personalizável.

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

Observe que após o início do comando, por padrão, essa função fecha o stdin e o stdout do processo em execução. Você pode redirecionar a saída do processo para algum arquivo através dos argumentos $ redirectStdout e $ redirectStderr.

Nota para usuários do Windows:
Você não pode redirecionar stdout / stderr nulda seguinte maneira:

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

No entanto, você pode fazer isso:

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

Notas para usuários * nix:

1) Use o comando exec shell se desejar obter o PID real:

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

2) Use o argumento $ stdin se desejar passar alguns dados para a entrada do seu programa:

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

3

Você pode tentar um sistema de filas como o Resque . Você pode gerar um trabalho, que processa as informações e retorna rapidamente com a imagem "processando". Com essa abordagem, você não saberá quando terminar.

Esta solução é destinada a aplicações de maior escala, nas quais você não deseja que suas máquinas frontais façam o trabalho pesado, para que possam processar solicitações de usuários. Portanto, pode ou não funcionar com dados físicos, como arquivos e pastas, mas para processar lógicas mais complicadas ou outras tarefas assíncronas (ou seja, novos e-mails de registros), é bom ter e muito escalável.


2

Uma solução funcional para Windows e Linux. Encontre mais na página Meu 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;
                        }
                    }
            }


1

Em vez de iniciar um processo em segundo plano, que tal criar um arquivo acionador e fazer com que um agendador como cron ou autosys execute periodicamente um script que procure e atue nos arquivos acionadores? Os gatilhos podem conter instruções ou até comandos brutos (melhor ainda, basta transformá-lo em um shell script).



1

Estou usando muito fast_cgi_finish_request()

Em combinação com um encerramento e uma função register_shutdown_ ()

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

Em seguida, registre esse fechamento para ser executado antes do encerramento.

register_shutdown_function($backgroundJob);

Finalmente, quando a resposta foi enviada ao cliente, você pode fechar a conexão com o cliente e continuar trabalhando com o processo PHP:

fast_cgi_finish_request();

O fechamento será executado após fast_cgi_finish_request.

A mensagem $ não estará visível a qualquer momento. E você pode registrar quantos fechamentos quiser, mas tome cuidado com o tempo de execução do script. Isso só funcionará se o PHP estiver sendo executado como um módulo Fast CGI (isso estava certo ?!)


0

O script PHP não é como outra linguagem de desenvolvimento de aplicativos de desktop. Em linguagens de aplicativos de desktop, podemos definir threads do daemon para executar um processo em segundo plano, mas no PHP um processo está ocorrendo quando o usuário solicita uma página. No entanto, é possível definir um trabalho em segundo plano usando a funcionalidade cron job do servidor, executada pelo script php.


0

Para aqueles que usam o Windows , veja o seguinte:

Referência: http://php.net/manual/en/function.exec.php#43917

Eu também lutei para conseguir que um programa fosse executado em segundo plano no Windows enquanto o script continuava sendo executado. Esse método, diferente das outras soluções, permite iniciar qualquer programa minimizado, maximizado ou sem nenhuma janela. A solução do llbra @ phpbrasil funciona, mas às vezes produz uma janela indesejada na área de trabalho quando você realmente deseja que a tarefa seja oculta.

Inicie o Notepad.exe minimizado em segundo plano:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("notepad.exe", 7, false); 
?> 

inicie um comando shell invisível em segundo plano:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("cmd /C dir /S %windir%", 0, false); 
?> 

inicie o MSPaint maximizado e espere que você o feche antes de continuar o script:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("mspaint.exe", 3, true); 
?> 

Para mais informações sobre o método Run (), acesse: http://msdn.microsoft.com/library/en-us/script56/html/wsMthRun.asp

URL editado:

Vá para https://technet.microsoft.com/en-us/library/ee156605.aspx, pois o link acima não existe mais.


1
O que precisa ser feito para executar esse código? ReceboFatal error: Class 'COM' not found
Alph.Dev

O link do PS para o MSDN está quebrado
Alph.Dev

@ Alph.Dev: O documento msdn parece ter sido removido temporariamente. Continha mais exemplos e explicações de como usar o WScript.Shell.
Jimmy Ilenloa 17/02

@ Alph.Dev: Para a classe COM não encontrada, isso é outra coisa. Confira isso no google. Você também pode verificar este arquivo php.net/manual/en/com.installation.php #
Jimmy Ilenloa

@ Alph.Dev verifique technet.microsoft.com/en-us/library/ee156605.aspx para obter informações atualizadas sobre a função Run ()
Jimmy Ilenloa

-12

Eu sei que é um post de 100 anos, mas de qualquer maneira, pensei que poderia ser útil para alguém. Você pode colocar uma imagem invisível em algum lugar da página apontando para o URL que precisa ser executado em segundo plano, como este:

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


12
Solução muito ruim. Se o usuário sair da página, o processo será interrompido.
Sergey P. tcp azure

3
a "imagem invisível" e seu link são expostos no código-fonte da página.
gil Tony
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.