Como exibir porcentagem de carregamento e como fazê-lo sem javascript?

Eu quero fazer algo semelhante aos carregadores no PHP, então eu usei esse código:

<?php $x=1; while($x<=100) { echo "Loading: $x %
"; $x++; } ?>

Para exibir de “Carregando 1%” para “Carregando 100%”. Mas isso resultará em tudo aparecendo um a cada vez sem desaparecer após a nova linha aparecer. Então eu quero saber como fazer a nova linha aparecer e os antigos desaparecem e isso começa depois que a página é carregada, então o usuário poderá assistir a um carregador realmente carregando de 1% a 100%.

UPDATE: Eu sei que eu deveria usar JS e / ou Ajax para alcançá-lo, eu só queria saber se há uma maneira de fazê-lo também em PHP 🙂

Seguir tarefas de longa duração é comum, mas não é realmente fácil de implementar pela primeira vez. Aqui está um exemplo completo.

uma amostra da tarefa de longa duração (uma amostra da tarefa de longa duração no produto do Sellermania )

Contexto

A tarefa

Imagine que você atualmente tenha a seguinte tarefa e que deseja exibir uma barra de progresso para seus visitantes.

PHP task.php

   

A UI

Sua UI bonita parece assim:

um lindo ui

HTML ui.html

     My Task!   Run task 
Progression: XX%

O iniciador

Precisamos iniciar a tarefa em segundo plano, a fim de tornar relevante essa demonstração. Então, esse script php será chamado de forma assíncrona ao clicar em "executar" e executará a tarefa acima em segundo plano.

PHP task-launch.php

  /dev/null 2>&1 &"); 

Problemas

problemas

Existem três problemas aqui:

  1. Você pode executar a tarefa mais de uma vez clicando em várias vezes o botão de execução, como evitar várias tarefas em segundo plano ao mesmo tempo?

  2. A tarefa é executada no lado do servidor, então precisamos fazer algo para acessar o servidor e pedir informações de progressão.

  3. e quando estaremos conectados ao lado do servidor, a variável de $progress não está disponível para leitura, porque está armazenada no contexto da instância de execução de task.php .


Soluções

soluções

Armazene a informação de progressão em algo legível de fora

Geralmente, as informações de progressão são armazenadas em um database, ou em um arquivo, ou seja o que for que possa ser gravado por um programa (sua tarefa actaully) e legível por outro (seu ui onde a progressão deve ser exibida).

Desenvolvi uma class para o compartilhamento de dados em várias aplicações php ( disponível no github aqui ), funciona da mesma maneira que o stdClass mas sempre sincroniza seus conteúdos com segurança em um arquivo.

Basta baixar o src/Sync.php e alterar o task.php acima por:

PHP task.php

 progress = 0; while ($current_stuff < $total_stuffs) { $shared->progress = round($current_stuff * 100 / $total_stuffs, 2); // ... some stuff sleep(1); $current_stuff++; } // to tell the follower that the task ended $shared->progress = null; 

Nota importante : aqui, some_file.txt é onde estão armazenados os dados compartilhados da sua tarefa, por isso não hesite em usar "task_ [user_id] .txt", por exemplo, se cada usuário tiver sua própria tarefa. E veja o readme no github para otimizar o access aos arquivos.

Use a variável sincronizada para proteger o iniciador de tarefas

  • A progressão é definida como 0 no início da tarefa, então a primeira coisa a fazer é verificar, antes de executar a tarefa, que essa progressão não está definida como 0.

PHP task-launch.php

 progress)) { exec("/usr/bin/php task.php > /dev/null 2>&1 &"); } 
  • Se o botão de execução for clicado duas vezes muito rapidamente, ainda podemos ter 2 instâncias da tarefa. Para lidar com este caso, precisamos simular um mutex, em uma palavra, tornar a variável disponível apenas para o aplicativo atual para fazer algumas coisas - outras aplicações ficarão duras até que a variável compartilhada seja desbloqueada.

PHP task.php

 lock(); if (!is_null($shared->progress)) { $shared->unlock(); exit ; } $shared->progress = 0; $shared->unlock(); // ------------------ while ($current_stuff < $total_stuffs) { $shared->progress = round($current_stuff * 100 / $total_stuffs, 2); // ... some stuff sleep(1); $current_stuff++; } // the task ended, no more progression $shared->progress = null; 

Atenção : se sua tarefa falhar e nunca chegar ao final, você nunca mais poderá iniciá-la. Para evitar tais casos, você também pode armazenar os getmypid() e some time() da criança dentro de sua variável compartilhada e adicionar uma lógica de tempo limite em sua tarefa.

Use a votação para solicitar informações sobre a progressão do servidor

Polling significa pedir informações de progressão para o servidor por lapso de tempo (como, por exemplo, 1 segundo, 5 segundos ou o que quer que seja). Em uma palavra, o cliente solicita informações de progressão para o servidor a cada N segundos.

  • No lado do servidor, precisamos codificar o manipulador para responder as informações de progressão.

PHP task-follow.php

 progress !== null) { echo $shared->progress; } else { echo "--"; // invalid value that will break polling } 
  • No lado do cliente, precisamos codificar as informações de "pedir informações de progressão para o servidor"

HTML ui-polling.html

     My Task!   Run task 
Progression: XX%

funciona!


Com um mínimo de JavaScript?

Eu também desenvolvi um plugin jquery, domajax , destinado a fazer "ajax sem javascript" (de fato, o plugin em si está no jQuery, mas o uso dele não requer código JavaScript) e, ao combinar as opções, você pode fazer votação.

Em nossa demonstração:

  • O seguidor torna-se:

PHP task-follow.php

 progress !== null) { echo $shared->progress; } 
  • A fonte de IU se torna:

HTML ui-domajax.html

     My Task!   Run task 
Progression: --%

Como você pode ver, não há nenhum javascript visível neste código. Limpo, não é?

Outros exemplos estão disponíveis no site do domajax , veja a aba "Gerenciar barras de progresso facilmente" no painel de demonstração. Todas as opções estão fortemente documentadas para detalhes.

O PHP é um idioma do lado do servidor e dá-lhe resposta com base no pedido.

você não pode manipular (como você deseja) no DOM .

Para isso, você deve usar javascript e AJAX para alterar os elementos DOM com base no resultado do PHP

Na verdade, você não precisa de Javascript para conseguir isso. Se a tarefa que você espera é persistente, você pode simplesmente criar um script PHP que:

  1. Localiza a tarefa (em um database, por exemplo)
  2. Verifica a porcentagem completa da tarefa
  3. Echo é a porcentagem para o usuário
  4. Se percentagem <100%
    • Atualizar a página
    • Caso contrário, redirecione para completar a página.

Você pode fazer uma atualização não-javascript usando uma atualização de meta HTML. Pode parecer algo assim (não realizou o PHP em 10 anos, então trate isso como pseudocódigo):

 percentComplete < 100; ?>          Loading: percentComplete ?>   

Outra abordagem, um hack realmente feio , seria usar o método flush () do PHP. Flush irá forçar o buffer de saída atual para o que está executando o PHP (CGI, por exemplo). Esta saída, às vezes, será adicionalmente armazenada em buffer pelo aplicativo host, mas em alguns servidores web ele enviará a saída diretamente para o usuário. Leia mais aqui: http://php.net/manual/en/function.flush.php

Você pode precisar combiná-lo com ob_flush, mas não sou positivo: http://php.net/manual/en/function.ob-flush.php

Com o CSS, você pode ocultar todo o texto de “carregamento” por padrão, mas replace o texto de carregamento do último filho e exibi-lo. Por exemplo:

  Loading: ', $x, '%
'; flush(); } ?>

Ambas as soluções são terríveis, eu só queria compartilhá-las para completar 🙂

Para elaborar um pouco sobre as respostas aqui;

O PHP é um idioma do lado do servidor, mas não é por isso que ele normalmente não funcionará.

Isso tem a ver com a maneira como nos comunicamos com o protocolo HTTP.
Quando um cliente envia uma solicitação HTTP ao servidor, a primeira coisa que acontece é estabelecer uma conexão. O servidor então processa a solicitação e produz uma resposta que então é enviada de volta para o cliente.
Uma vez que a resposta é recebida, a conexão morre.
Então, geralmente , você recebe uma resposta por solicitação.

Um equívoco comum é que, usando o header keep-alive agora com defeito, para conexões persistentes, você pode continuar empurrando o conteúdo enquanto o soquete permanece aberto. O HTTP realmente não funciona dessa maneira e exigiria um pouco mais de manipulação antes que você possa transmitir seus dados.
Mas mais sobre isso aqui e aqui .

O que você pode fazer?

Existem algumas opções que posso pensar.

  1. Usando um idioma do lado do cliente (normalmente, JavaScript), podemos continuar enviando objects XMLHttpRequest para consultar o servidor com base em cada resposta que recebemos efetivamente criando um stream de mídia.
  2. O Comet é um termo bastante amplo para permitir que os servidores da Web enviem dados de volta ao cliente por meio de solicitações HTTP. O APE é uma implementação disso.
  3. Dê uma olhada em Web Sockets usando Ratchet .
  4. Você poderia, em teoria e se você soubesse o endereço do seu cliente, escreva APIs em ambos os lados, o que aceitaria cURLs e XHRs. Nesse caso, ambos os lados atuariam como clientes e servidores e poderiam se comunicar no mesmo nível. Isso é possível com o PHP, mas não a melhor solução para navegadores.

Finalmente, você pode querer ler esta resposta popular que passa por algumas dessas tecnologias.

Mais simplesmente:

  Loading: $x %
"; $x++; } ?>

Dessa forma, os velhos

s serão escondidos à medida que os novos aparecerem.

Este script funciona com PHP 5.4 e Nginx no lado do servidor e é testado com o Firefox no lado do cliente. Não funciona com o Chrome.

Demonstração no YouTube

Dicas

  • apenas um pedido
  • a conexão deve ser aberta
  • para enviar conteúdo em partes, a resposta deve ser fragmentada
  • você precisa definir um header sem buffer para o seu servidor web
  • O php deve ser configurado para permitir escoamentos imediatos
  • você precisa de um truque ou solução inteligente para escrever na mesma linha no navegador
    • aqui é usado um truque css

      A Chunked Response    Sending data chunk ' . ($i + 1) . ' of 1000 
'); } usleep(10000); // this is needed for the last update ?>

Eu não sei se eu realmente entendo sua pergunta, mas fiz algo geralmente que as pessoas não sabem sobre isso. Para dar a impressão de que a linha está sendo substituída, você pode inserir um CR (Carriage Return, “\ r”) na frente da sua saída. Eu escrevi sobre um exemplo:

  

Executando isso em um terminal, recebo um status de carregamento legal: $ php code.php

Espero que seja o que você procura.

Não, você não pode fazer isso sem a ajuda de javascript ou jquery.

Você pode ter uma página do php, se você precisar e isso irá retornar o progresso como abaixo,

 //yourprogresspage.php "; ?> 

então você precisa chamá-lo da sua página usando jquery

  

Eu usei um pouco de CSS e HTML. Isto é o que eu criei:

   

Você precisa atualizar a página usando javascript.

 
'; $x = 0; while($x<=100) { echo ''; $x++; sleep(1); }

Você pode remover a function de dormir e fazer seu trabalho lá.

Na verdade, o PHP não fornece esse recurso.

Para mostrar uma barra de progresso, você pode tentar isso:

Ao executar o processo de geração de arquivos de longa duração, salve um valor de porcentagem na session de usuário atual

Na página, que mostra o formulário de download, adicione um iFrame que chama um script progress.php

O script progress.php precisa gerar uma página da Web, que precisa ser recarregada automaticamente

O script progress.php precisa ler o valor de progresso atual da session e desenhar charts da barra de progresso, que visualmente representa o progresso.

Embora esta seja uma solução única para PHP, você pode preparar outra usando JS / AJAX.

O auto-recarregamento HTML deve gostar assim:

http://linuxfreak.com/progress.php “>

Depois de ler os comentários e respostas de todos dizendo que isso não é possível usando o PHP, pensei que demoraria um minuto para provar que eles estavam errados: P

Eu não recomendo realmente usar isso (porque certamente há maneiras melhores de obter um indicador de progresso de carregamento), mas isso “não-ecoará” a linha anterior, desde que você não tenha habilitada a compactação zlib no PHP:

 "; echo "
Loading: 0%
"; ob_implicit_flush(true); //Forces all output to immediately send while($x < 100) { $x++; //Padding the string overcomes browser caching and TCP packet delays due to insufficiently sized packets echo str_pad("
Loading: {$x}%
", 4096, " "); sleep(1); //Just for demo } ?>

Não consigo ver outra maneira, em seguida, enviar vários pedidos para o mesmo script e salvar a porcentagem de progresso na session. Esta poderia ser uma solução pura de PHP e HTML.

  '.$progress.'%'; } else { echo 'Done'; } 

O que você acha ?

Você pode usar ob_flush(); e flush();

Isso funcionará, mas esteja ciente de que você deve especificar o documento Charset

  

e então, no início do php, adicione ob_start(); no loop, copie tanto ob_flush(); flush();

mas isso imprimirá de 1 a 100, você deve configurar o php para imprimir

  

Este é meu próprio caminho.

Muito por uma pergunta tão simples. Realmente o que você está perguntando é como transmitir a saída do servidor, se eu tiver uma pergunta correta, ligue para esta function.

 function streamOutput(){ // Turn off output buffering ini_set('output_buffering', 'off'); // Turn off PHP output compression ini_set('zlib.output_compression', false); //Flush (send) the output buffer and turn off output buffering //ob_end_flush(); while (@ob_end_flush()); // Implicitly flush the buffer(s) ini_set('implicit_flush', true); ob_implicit_flush(true); //prevent apache from buffering it for deflate/gzip. for($i = 0; $i < 1000; $i++){ echo ' '; } flush(); } 

Então, no seu loop, faça algo assim.

 $sleep = 100; for( $i = 0; $i < $sleep; $i++){ echo str_repeat('.', $i+1)." ".$i."%\n"; flush(); sleep(1); } 

A parte importante aqui é chamar flush sempre que quiser gerar saída. Quanto a mudar o DOM, alguma vez ouviu falar de um IFRAME, basta executá-lo em um iframe e está tudo bem. Iframes são os minhocas das interwebs.

Este trabalho é bom para mim

 if(!session_id()) session_start(); if($_SESSION['loding']<=100){ echo "Loading:".$_SESSION['loding']++ ."%
"; ?>

Captura de canvas:

insira a descrição da imagem aqui

A coisa sobre o php é que ele é totalmente carregado e exibido como conteúdo html.

(ie)

 echo "1"; usleep(5000000); echo "2"; 

aqui ambas as declarações de eco são exibidas ao mesmo tempo, mas o tempo de carregamento da página é estendido, pois aumenta o tempo na function usleep.

então temos que fazer um pedido de servidor sempre que incrementamos o valor no conteúdo do carregamento.

Como você já sabia que isso poderia ser feito com javascript, jquery, ajax eu tentei essa session de uso.

Espero que isso venha à mão ...

Não é possível apenas com o PHP, mas pode ser conseguido usando o PHP + JS, tente seguir o exemplo,

    Loader Test    
updateLoader('.($i).')'; flush(); ob_flush(); usleep(50000); } ?>

Completed

Nota: isto não funcionará se o zlib.output_compression estiver ativado ou algum software antivírus mantenha o buffer até que a página seja carregada antes de enviá-lo para o navegador.

Digamos que a página possui alguns dados importantes para carregar, então você quer um carregador.

Você pode fazer o usuário pensar que há um carregador.

 .php_loader{width:100px;height:50px;line-height:50px;margin-top:-25px;margin-left:-50px;position:fixed;top:50%;left:50%;}'; // Load first loader echo '
0%
'; } // Upadate loader function updateLoader($percent){ // Remove last loader clearLoader(); // Insert new loader $GLOBALS['loader_id']++; echo '
'.$percent.'%
'; ob_flush(); flush(); } function clearLoader(){ // Remove last loader echo ''; } ?> Server Side Loader 25% off the page just loaded 30% off the page just loaded 50% off the page just loaded 80% off the page just loaded 100% off the page just loaded

Agradável e suave.

Depende realmente de como você faz o carregamento, mas o que você poderia fazer se você realmente quiser usar os jj de AJAX para manter uma conexão aberta e em cada atualização do processamento no back-end para atualizar o front-end usando o JS que atualizou o correto exibir widget / campo no retorno de chamada AJAX .

Se você quiser apenas fazê-lo com PHP, você poderia facilmente fazer o seguinte:

 "; $x++;  ob_end_clean(); } 

Somente para adicionar a esta pergunta, você poderia usar o buffer de saída se você realmente quisesse alcançar isso (isso não é recomendado). A única limitação é que você não pode replace a saída anterior. Isto é mais como uma barra de progresso:

  

Veja Exibição de saída em partes em PHP .

Solução, não é o melhor para o desenvolvedor, mas funcionará para o que você deseja. Você é script está bem. Mas se você ver todo o resultado, e você não vê progressão, é porque existe um mecanismo de buffer. Talvez o PHP, talvez o servidor web (apache …) ou até mesmo o buffer do navegador.

Dê uma olhada na function flush () php. Para testar, você precisa usar um “navegador de linha de comando” (talvez solicitação manual de telnet / GET). Se a linha de comando mostrar a linha que vem após a linha, o problema é com o seu navegador.

Mas primeiro, você precisa identificar WHERE o mecanismo de buffer atual: php output? (veja a function flush php), a compression do servidor web? (veja a compression gzip) talvez o buffer do navegador? (o cromo aguarda o final do arquivo antes de mostrar os resultados)