Pergunta

Eu estou tentando executar um processo em uma página web que irá retornar a sua saída em tempo real. Por exemplo, se eu executar o processo de 'ping' deve atualizar minha página cada vez que ele retorna uma nova linha (agora, quando eu uso exec (comando, output), sou forçado a usar a opção -c e esperar até conclusão do processo para ver o saída na minha página web). É possível fazer isso em php?

Eu também estou querendo saber o que é uma maneira correta para matar este tipo de processo quando alguém está a sair da página. Em caso de processo de 'ping' eu ainda sou capaz de ver o processo em execução no monitor do sistema (o que faz sentido).

Foi útil?

Solução

Isso funcionou para mim:

$cmd = "ping 127.0.0.1";

$descriptorspec = array(
   0 => array("pipe", "r"),   // stdin is a pipe that the child will read from
   1 => array("pipe", "w"),   // stdout is a pipe that the child will write to
   2 => array("pipe", "w")    // stderr is a pipe that the child will write to
);
flush();
$process = proc_open($cmd, $descriptorspec, $pipes, realpath('./'), array());
echo "<pre>";
if (is_resource($process)) {
    while ($s = fgets($pipes[1])) {
        print $s;
        flush();
    }
}
echo "</pre>";

Outras dicas

Esta é uma boa maneira de mostrar a saída em tempo real de seus comandos shell:

<?php
header("Content-type: text/plain");

// tell php to automatically flush after every output
// including lines of output produced by shell commands
disable_ob();

$command = 'rsync -avz /your/directory1 /your/directory2';
system($command);

Você vai precisar esta função para impedir que o buffer de saída:

function disable_ob() {
    // Turn off output buffering
    ini_set('output_buffering', 'off');
    // Turn off PHP output compression
    ini_set('zlib.output_compression', false);
    // Implicitly flush the buffer(s)
    ini_set('implicit_flush', true);
    ob_implicit_flush(true);
    // Clear, and turn off output buffering
    while (ob_get_level() > 0) {
        // Get the curent level
        $level = ob_get_level();
        // End the buffering
        ob_end_clean();
        // If the current level has not changed, abort
        if (ob_get_level() == $level) break;
    }
    // Disable apache output buffering/compression
    if (function_exists('apache_setenv')) {
        apache_setenv('no-gzip', '1');
        apache_setenv('dont-vary', '1');
    }
}

Ele não funciona em todos os servidores que eu tentei no entanto, eu gostaria de poder oferecer conselhos sobre o que procurar na sua configuração do PHP para determinar se você deve ou não puxar seu cabelo para fora tentando obter este tipo de comportamento ao trabalho no seu servidor! Mais alguém sabe?

Aqui está um exemplo fictício em PHP comum:

<?php
header("Content-type: text/plain");

disable_ob();

for($i=0;$i<10;$i++) 
{
    echo $i . "\n";
    usleep(300000);
}

Espero que isso ajude outras pessoas que pesquisei seu caminho aqui.

A melhor solução para este velho problema utilizando modernos Eventos HTML5 do lado do servidor é descrito aqui:

http://www.w3schools.com/html/html5_serversentevents.asp


Exemplo:

http://sink.agiletoolkit.org/realtime/console

Código: https: // github. com / atk4 / pia / blob / master / admin / page / realtime / console.php # L40

(implementado como um módulo no quadro Toolkit Agile)

Para o uso de linha de comando:

function execute($cmd) {
    $proc = proc_open($cmd, [['pipe','r'],['pipe','w'],['pipe','w']], $pipes);
    while(($line = fgets($pipes[1])) !== false) {
        fwrite(STDOUT,$line);
    }
    while(($line = fgets($pipes[2])) !== false) {
        fwrite(STDERR,$line);
    }
    fclose($pipes[0]);
    fclose($pipes[1]);
    fclose($pipes[2]);
    return proc_close($proc);
}

Se você está tentando executar um arquivo, você pode precisar de dar permissões de execução em primeiro lugar:

chmod('/path/to/script',0755);

verificado todas as respostas, nada funciona ...

Aqui

Ele funciona em windows (eu acho que essa resposta é útil para usuários que pesquisam por lá)

<?php
$a = popen('ping www.google.com', 'r'); 

while($b = fgets($a, 2048)) { 
echo $b."<br>\n"; 
ob_flush();flush(); 
} 
pclose($a); 

?>

Tente este (testado no servidor wamp máquina Windows +)

        header('Content-Encoding: none;');

        set_time_limit(0);

        $handle = popen("<<< Your Shell Command >>>", "r");

        if (ob_get_level() == 0) 
            ob_start();

        while(!feof($handle)) {

            $buffer = fgets($handle);
            $buffer = trim(htmlspecialchars($buffer));

            echo $buffer . "<br />";
            echo str_pad('', 4096);    

            ob_flush();
            flush();
            sleep(1);
        }

        pclose($handle);
        ob_end_flush();

Eu tentei vários comandos de execução PHP no Windows e descobriu que eles diferem bastante.

  • não funcionam para streaming: shell_exec, exec, passthru
  • o tipo de obras:. proc_open, popen - "espécie de" porque você não pode passar argumentos para o seu comando (ou seja,' não vai funcionar com my.exe --something, irá trabalhar com _my_something.bat)

A abordagem melhor (mais fácil) é:

  1. Você deve se certificar que seu exe está esvaziando comandos (veja printf flushing problema ). Sem isso, você provavelmente irá receber lotes de cerca de 4096 bytes de texto o que você faz.
  2. Se você puder, use header('Content-Type: text/event-stream'); (em vez de header('Content-Type: text/plain; charset=...');). Isso não vai funcionar em todos os navegadores / clientes embora! Streaming vai trabalhar sem isso, mas pelo menos primeiras linhas que serão colocados pelo navegador.
  3. Você também pode querer desativar header('Cache-Control: no-cache'); cache.
  4. Desligue o buffer de saída (no php.ini ou com ini_set('output_buffering', 'off');). Isso também pode ter que ser feito em Apache / Nginx / whatever servidor que você usa na frente.
  5. Ligue de compressão (ou no php.ini ou com ini_set('zlib.output_compression', false);). Isso também pode ter que ser feito em Apache / Nginx / whatever servidor que você usa na frente.

Assim programa no seu C ++ você fazer algo assim (mais uma vez, por outras soluções ver printf problema flushing ):

Logger::log(...) {
  printf (text);
  fflush(stdout);
}

Em PHP você fazer algo como:

function setupStreaming() {
    // Turn off output buffering
    ini_set('output_buffering', 'off');
    // Turn off PHP output compression
    ini_set('zlib.output_compression', false);
    // Disable Apache output buffering/compression
    if (function_exists('apache_setenv')) {
        apache_setenv('no-gzip', '1');
        apache_setenv('dont-vary', '1');
    }
}
function runStreamingCommand($cmd){
    echo "\nrunning $cmd\n";
    system($cmd);
}
...

setupStreaming();
runStreamingCommand($cmd);

Primeiro, verifique se flush () funciona para você. Se isso acontecer, bom, se isso não acontecer isso provavelmente significa que o servidor web está tamponamento por algum motivo, por exemplo mod_gzip está habilitado.

Para algo como Ping, a técnica mais fácil é fazer um loop dentro de PHP, correndo "ping -c 1" várias vezes, e chamar flush () após cada saída. Assumindo que o PHP é configurado para abortar quando a conexão HTTP é fechada pelo usuário (que normalmente é o padrão, ou você pode chamar ignore_user_abort (false) para certificar-se), então você não precisa se preocupar com processos de ping run-away quer .

Se for realmente necessário que você só executar o processo de criança uma vez e exibir sua saída de forma contínua, que pode ser mais difícil - você provavelmente tem que executá-lo em segundo plano, saída de redirecionamento para um riacho, e depois ter PHP echo que o fluxo de volta para o usuário, intercaladas com nivelado normal () chamadas.

Se você estiver olhando para executar comandos do sistema via PHP olhar para, a documentação exec .

Eu não recomendo fazer isso em um site de alto tráfego, porém, bifurcação um processo para cada pedido é um processo bastante robusto. Alguns programas oferecem a opção de escrever o seu ID do processo para um arquivo de tal forma que você pode verificar, e encerrar o processo à vontade, mas para comandos como de ping, não estou certo de que é possível, verifique as páginas do manual.

Você pode ser melhor servido por simplesmente abrindo um socket na porta que você espera estar escutando (IE: a porta 80 para HTTP) no host remoto, de que maneira você sabe que tudo está indo bem em userland, bem como sobre a rede.

Se você está tentando dados olhar binário saída em do php função header , e garantir que você definir a adequada tipo de conteúdo , e content-disposition . Revisão documentação, para obter mais informações sobre o uso / desativar o buffer de saída .

Tente alterar o conjunto de arquivos php.ini "output_buffering = Off". Você deve ser capaz de obter a saída em tempo real na página comando uso do sistema em vez de .. comando exec sistema irá liberar a saída

porque não basta canalizar a saída em um arquivo de log e, em seguida, usar esse arquivo para retornar conteúdo para o cliente. Não bastante tempo real, mas talvez bom o suficiente?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top