Я хочу сделать что-то похожее на загрузчиков в PHP, поэтому я использовал этот код:
<?php $x=1; while($x<=100) { echo "Loading: $x %<br>"; $x++; } ?>
Чтобы он отображался с «Загрузка 1%» на «Загрузка 100%». Но это приведет к появлению всех по одному, не исчезая после появления новой строки. Поэтому я хочу знать, как сделать новую строку, и старые исчезнут, и это начинается после загрузки страницы, поэтому пользователь сможет наблюдать загрузку загрузчика с 1% до 100%.
ОБНОВЛЕНИЕ: Я знаю, что я должен использовать JS и / или Ajax для его достижения, я просто хотел знать, есть ли способ сделать это и в PHP 🙂
Для выполнения долговременных задач распространено, но не очень просто реализовать в первый раз. Вот полный пример.
(образец долговременной задачи в продукте Sellermania )
Представьте, что у вас есть следующая задача, и вы хотите отобразить индикатор выполнения для ваших посетителей.
PHP task.php
<?php $total_stuffs = 200; $current_stuff = 0; while ($current_stuff < $total_stuffs) { $progress = round($current_stuff * 100 / $total_stuffs, 2); // ... some stuff sleep(1); $current_stuff++; }
Ваш красивый пользовательский интерфейс выглядит так:
HTML ui.html
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"/> <title>My Task!</title> </head> <body> <a id="run_task" href="#">Run task</a> <div id="task_progress">Progression: <span id="task_progress_pct">XX</span>% <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <script type="text/javascript"> $('#run_task').click(function(e) { e.preventDefault(); $.get('task-launch.php'); }); </script> </body> </html>
Нам нужно запустить задачу в фоновом режиме, чтобы сделать эту демонстрацию актуальной. Таким образом, этот php-скрипт будет вызываться асинхронно при нажатии «run» и будет выполнять задачу выше в фоновом режиме.
PHP task-launch.php
<?php // launches the task in background // see https://stackoverflow.com/a/12341511/731138 for details about the arguments exec("/usr/bin/php task.php > /dev/null 2>&1 &");
Здесь есть три проблемы:
вы можете запустить задачу несколько раз, нажав несколько раз кнопку запуска, как избежать нескольких задач в фоновом режиме в одно и то же время?
задача выполняется на стороне сервера, поэтому нам нужно что-то сделать для доступа к серверу и запросить информацию о прогрессировании.
и когда мы будем подключены к серверной стороне, переменная $progress
недоступна для чтения, потому что она сохраняется в контексте task.php
запуска task.php
.
Как правило, информация о прогрессировании хранится в базе данных или в файле или в любом месте, которое может быть записано программой (ваша задача actaully), и читается другой (ваш ui, где должна отображаться прогрессия).
Я разработал класс для обмена данными в нескольких php-приложениях ( доступно здесь в github ), он работает примерно так же, как и stdClass
но всегда безопасно синхронизирует его содержимое в файл.
Просто скачайте src/Sync.php
и измените task.php выше:
PHP task.php
<?php require("Sync.php"); $total_stuffs = 200; $current_stuff = 0; $shared = new Sync("some_file.txt"); $shared->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;
Важное примечание : здесь some_file.txt
– это где хранятся общие данные вашей задачи, поэтому не стесняйтесь использовать «task_ [user_id] .txt», например, если каждый пользователь имеет свою собственную задачу. И посмотрите на readme github для оптимизации доступа к файлам.
PHP task-launch.php
<?php require("Sync.php"); $shared = new Sync("some_file.txt"); if (is_null($shared->progress)) { exec("/usr/bin/php task.php > /dev/null 2>&1 &"); }
PHP task.php
<?php require("Sync.php"); $total_stuffs = 200; $current_stuff = 0; $shared = new Sync("some_file.txt"); // here is the magic: impossible to set the progression to 0 if an instance is running // ------------------ $shared->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;
Предупреждение : если ваша задача выйдет из строя и никогда не дойдет до конца, вы больше не сможете ее запускать. Чтобы избежать таких случаев, вы также можете сохранить getmypid()
и некоторое time()
в вашей общей переменной и добавить логику тайм-аута в свою задачу.
Опрос означает, что каждый промежуток времени запрашивает информацию о прогрессировании (например, 1 с, 5 секунд или что-то еще). Одним словом, клиент запрашивает информацию о прогрессии на сервере каждые N секунд.
PHP task-follow.php
<?php require("Sync.php"); $shared = new Sync("some_file.txt"); if ($shared->progress !== null) { echo $shared->progress; } else { echo "--"; // invalid value that will break polling }
HTML ui-polling.html
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"/> <title>My Task!</title> </head> <body> <a id="run_task" href="#">Run task</a> <div id="task_progress">Progression: <span id="task_progress_pct">XX</span>% <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <script type="text/javascript"> $('#run_task').click(function(e) { e.preventDefault(); <!-- not a good practice to run a long-running task this way but that's a sample --> $.get('task-launch.php'); <!-- launches the polling business --> setTimeout(function() { getProgressionInformation(); }, 1000); }); function getProgressionInformation() { $.get('task-follow.php', function(progress) { $('#task_progress_pct').html(progress); if (progress !== '--') { <!-- if the task has not finished, we restart the request after a 1000ms delay --> setTimeout(function() { getProgressionInformation(); }, 1000); } }); } /* the task might be already running when the page loads */ $(document).ready(function() { getProgressionInformation(); }); </script> </body> </html>
Я также разработал плагин jquery, domajax , предназначенный для выполнения «ajax без javascript» (фактически, сам плагин находится в jQuery, но при использовании его не требуется код JavaScript), и путем объединения параметров вы можете делать опросы.
В нашей демонстрации:
PHP task-follow.php
<?php require("Sync.php"); $shared = new Sync("some_file.txt"); if ($shared->progress !== null) { echo $shared->progress; }
HTML ui-domajax.html
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"/> <title>My Task!</title> </head> <body> <a href="#" id="run-task" class="domajax click" data-endpoint="task-launch.php" data-domajax-complete="#polling" >Run task</a> <div id="polling" data-endpoint="task-follow.php" data-delay="1000" data-output-not-empty="#task-progress-pct" data-domajax-not-empty="" ></div> <div id="task-progress">Progression: <span id="task-progress-pct">--</span>% <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <script src="//domajax.com/js/domajax/jquery.domajax.js"></script> </body> </html>
Как вы можете видеть, в этом коде нет видимого javascript. Чистота не так ли?
Другие примеры доступны на веб-сайте domajax , посмотрите вкладку «Управлять индикаторами прогресса легко» в демонстрационной панели. Все параметры документально задокументированы.
PHP – это серверный язык, и он дает вам ответ на основе запроса.
вы не можете манипулировать (как хотите) на DOM .
для этого вы должны использовать javascript и AJAX для изменения элементов DOM на основе результата PHP
Для этого вам не нужен Javascript. Если задача, которую вы ожидаете, является постоянной, вы можете просто создать скрипт PHP, который:
Вы можете обновить не-javascript, используя мета-обновление HTML. Это может выглядеть примерно так (не делайте PHP примерно через 10 лет, поэтому рассматривайте это как псевдокод):
<?php $taskId = $_GET("taskId"); $task = lookupTask($taskId); $refresh = $task->percentComplete < 100; ?> <html> <head> <? if($refresh): ?> <meta http-equiv="refresh" content="5"> <? else: ?> <meta http-equiv="refresh" content="0; url=taskComplete?taskId=<?=$taskId ?>" /> <? endif; ?> </head> <body> Loading: <?=$task->percentComplete ?> </body> </html>
Другим подходом, действительно уродливым взломом , было бы использование метода flush () PHP. Flush заставит текущий выходной буфер работать с PHP (например, CGI). Этот вывод иногда будет буферизироваться хост-приложением, но на некоторых веб-серверах он будет отправлять результат прямо пользователю. Подробнее здесь: http://php.net/manual/en/function.flush.php
Возможно, вам придется объединить его с ob_flush, но я не уверен: http://php.net/manual/en/function.ob-flush.php
С помощью CSS вы можете скрыть весь текст «погрузки» по умолчанию, но переопределите это значение для загружаемого текста последнего ребенка и покажите его. Например:
<style type="text/css"> .loadText { display: none; } .loadText:last-child { display: block; } </style> <?php for ($x = 0; $x <= 100; $x++) { echo '<div class="loadText">Loading: ', $x, '%</div>'; flush(); } ?>
Оба этих решения ужасны, я просто хотел поделиться ими для полноты 🙂
Выяснить здесь немного ответов;
PHP – это серверный язык, но это не значит, что он обычно не работает.
Это связано с тем, как мы общаемся по протоколу HTTP.
Когда клиент отправляет HTTP-запрос на сервер, первое, что происходит, это установление соединения. Затем сервер обрабатывает запрос и выдает ответ, который затем отправляется обратно клиенту.
После получения ответа соединение умирает.
Поэтому, как правило , вы получаете один ответ на запрос.
Распространенное заблуждение состоит в том, что с использованием ранее keep-alive
заголовка keep-alive
для постоянных подключений вы можете продолжать нажимать контент, поскольку сокет остается открытым. HTTP на самом деле не работает таким образом и потребует немного больше манипуляций, прежде чем вы сможете передавать свои данные.
Но больше об этом здесь и здесь .
Что ты можешь сделать?
Есть несколько вариантов, о которых я могу думать.
XMLHttpRequest
для запроса сервера на основе каждого полученного нами ответа, эффективно создающего поток мультимедиа. Наконец, вы можете прочитать этот популярный ответ, который проходит через некоторые из этих технологий.
Проще говоря:
<style> div:not(:last-child) { display:none } </style> <?php $x=1; while($x<=100) { echo "<div>Loading: $x %</div>"; $x++; } ?>
Таким образом, старые <div>
будут скрыты по мере появления новых.
Этот скрипт работает с PHP 5.4 и Nginx на стороне сервера и протестирован с Firefox на стороне клиента. Он не работает с Chrome.
Демо на YouTube
Советы
<?php header('X-Accel-Buffering: no'); // nginx related - turn the output buffer off header('Content-Encoding: chunked;'); header('Transfer-Encoding', 'chunked'); header('Content-Type', 'text/plain'); header('Connection', 'keep-alive'); function dump_chunk($chunk) { printf("%s\r\n", $chunk); flush(); ob_flush(); // if you comment this out, the chunk updates are slower } ob_start(); ?> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>A Chunked Response</title> <style> div:not(:last-child) { display:none; } </style> </head> <body> <?php // inital clear all ob_end_flush(); flush(); ob_flush(); // browser padding echo str_pad('', 4096) . "\n"; flush(); ob_flush(); for ($i = 0; $i < 1000; $i++) { usleep(15000); dump_chunk('<div>Sending data chunk ' . ($i + 1) . ' of 1000 <br /></div>'); } usleep(10000); // this is needed for the last update ?> </body> </html>
Я не знаю, действительно ли я понимаю ваш вопрос, но я делал что-то, что обычно люди не знают об этом. Чтобы создать впечатление, что строка перезаписывается, вы можете вставить CR (возврат каретки, «\ r») перед вашим выходом. Я записал это на примере:
<?php $x=1; while($x<=100) { echo "\rLoading: $x %"; usleep(10000); $x++; } ?>
Запустив это на терминале, я получаю прохладный статус загрузки: $ php code.php
Надеюсь, это то, что вы искали.
Нет, вы не можете сделать это без помощи javascript или jquery.
У вас может быть php-страница, если вам нужно, и это вернет прогресс, как показано ниже,
//yourprogresspage.php <?php $x="your progress"; echo "Loading: $x %<br>"; ?>
то вам нужно вызвать его со своей страницы, используя jquery
<script> var repeater; function updateprogress() { $.post( "yourprogresspage.php", function( data ) { $( ".progressdiv" ).html( data ); }); repeater = setTimeout(updateprogress, 1000); } updateprogress(); </script>
Я использовал немного CSS и HTML, вот что я придумал:
<html> <?php $x=1; while($x<=100) { ?> <div id="count"style="position:absolute;top:10px;left:10px;"> <style> #count:after { content:'<?php echo "Loading: $x % "; ?>'; } </style> </div> <?php $x++; sleep(1); } ?> </html>
в<html> <?php $x=1; while($x<=100) { ?> <div id="count"style="position:absolute;top:10px;left:10px;"> <style> #count:after { content:'<?php echo "Loading: $x % "; ?>'; } </style> </div> <?php $x++; sleep(1); } ?> </html>
того, как<html> <?php $x=1; while($x<=100) { ?> <div id="count"style="position:absolute;top:10px;left:10px;"> <style> #count:after { content:'<?php echo "Loading: $x % "; ?>'; } </style> </div> <?php $x++; sleep(1); } ?> </html>
Вам нужно обновить страницу с помощью javascript.
<?php echo '<div id="percent"></div>'; $x = 0; while($x<=100) { echo '<script type="text/javascript">document.getElementById("percent").innerHTML = '.$x.';'.'</script>'; $x++; sleep(1); }
Вы можете удалить функцию сна и выполнить свою работу.
На самом деле PHP не предоставляет такую функцию.
Чтобы показать индикатор выполнения, вы можете попробовать следующее:
Выполняя длительный процесс создания файлов, сохраните процентное значение в текущем сеансе пользователя
На странице, где показана форма загрузки, добавьте iFrame, который вызывает сценарий progress.php
Сценарий progress.php должен генерировать веб-страницу, которая должна перезагружаться автоматически-магически
Скрипт progress.php должен прочитать текущее значение прогресса из сеанса и нарисовать графику индикатора выполнения, которая визуально представляет прогресс.
Хотя это решение, основанное на PHP, вы можете подготовить еще один пример с помощью JS / AJAX.
Автозагрузка HTML должна выглядеть следующим образом:
После прочтения комментариев и ответов каждого, говорящих, что это невозможно с помощью PHP, я подумал, что я потрачу минутку, чтобы доказать их неправоту: P
Я не рекомендую фактически использовать это (потому что есть, конечно, лучшие способы достижения индикатора прогресса загрузки), но это будет «отменить эхо» предыдущей строки, если у вас нет сжатия zlib, включенного в PHP:
<?php $x = 0; echo "<html><body>"; echo "<div style=\"position: fixed; color: white; background-color: rgba(0,0,0,0.5); top: 0; left: 0;\" id=\"loader0\">Loading: 0%</div>"; 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("<div style=\"position: fixed; color: white; background-color: rgba(0,0,0,0.5); top: 0; left: 0;\" id=\"loader{$x}\">Loading: {$x}%<script>var prev = document.getElementById('loader".($x-1)."'); prev.parentNode.removeChild(prev)</script></div>", 4096, " "); sleep(1); //Just for demo } ?>
Я не вижу другого способа отправки нескольких запросов на один и тот же скрипт и сохранения процента прогресса в сеансе. Это может быть чисто PHP и HTML-решение.
<?php session_start(); if(isset($_SESSION['progress'])) $progress = $_SESSION['progress'] = $_SESSION['progress'] + 10; else $progress = $_SESSION['progress'] = 10; if($progress < 100){ echo '<head><meta http-equiv="refresh" content="1; url=http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'" /></head> <body>'.$progress.'%</body>'; } else { echo 'Done'; }
Как вы думаете ?
Вы можете использовать ob_flush();
и flush();
Это будет работать, но имейте в виду, что вы должны указать документ Charset
<meta charset="utf-8" />
а затем в начале php добавить ob_start();
в цикле скопируйте оба ob_flush();
flush();
но это будет печатать все от 1 до 100, вы должны установить php для печати
<script> document.getElementById('that div').innerHTML = "Loading [THE PERCENT]%"; </script>
Это мой собственный путь.
Так много для такого простого вопроса. На самом деле вы спрашиваете, как передать поток с сервера. Если я правильно задал вопрос, вызовите эту функцию.
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(); }
Затем в вашей петле сделайте что-нибудь подобное.
$sleep = 100; for( $i = 0; $i < $sleep; $i++){ echo str_repeat('.', $i+1)." ".$i."%\n"; flush(); sleep(1); }
Важная часть здесь – вызывать флеш, когда вы хотите генерировать выходные данные. Что касается изменения DOM, когда-либо слышавшего о IFRAME, просто запустите его в iframe и все это хорошо. Iframe являются червоточинами межсетевых экранов.
это отлично работает для меня
if(!session_id()) session_start(); if($_SESSION['loding']<=100){ echo "Loading:".$_SESSION['loding']++ ."%<br>"; ?> <div style="width:<?echo $_SESSION['loding'];?>px; height:10px; background-color:green;"></div> <? //the above div content displays a green loader type effect. header( "refresh:.5; url=loaderinphp.php" ); } else{ //redirect to another page or code here }//unset the session $_SESSION['loding'] while calling again for another page load
Скриншот:
вещь о php заключается в том, что она полностью загружается и отображается как содержимое html.
(то есть)
echo "1"; usleep(5000000); echo "2";
здесь одновременно отображаются оба эхо-заявления, но время загрузки страницы увеличивается, когда вы увеличиваете время в функции usleep.
поэтому мы должны делать запрос сервера каждый раз, когда мы увеличиваем значение в загружаемом контенте.
как вы уже знаете, что это можно сделать с помощью javascript, jquery, ajax, я пробовал это с помощью сеанса.
надеюсь, это пригодится …
Не возможно только с PHP, но это может быть достигнуто с помощью PHP + JS, попробуйте привести пример,
<?php ob_start(); ob_implicit_flush(); ob_end_flush(); ?> <html> <head> <title>Loader Test</title> <script type="text/javascript"> function updateLoader(val) { document.getElementById('loader').innerHTML = "Loading:" + val + "%"; } </script> </head> <body> <div id="loader"></div> <?php flush();ob_flush(); ?> <?php for( $i = 1 ; $i <= 100 ; $i++ ) { echo '<script type="text/javascript">updateLoader('.($i).')</script>'; flush(); ob_flush(); usleep(50000); } ?> <?php ob_end_flush(); ?> <p>Completed</p> </body> </html>
Примечание. Это не будет работать, если включено zlib.output_compression или какое-либо антивирусное программное обеспечение хранит буфер до тех пор, пока страница не будет загружена до отправки в браузер.
Допустим, у страницы есть большие данные для загрузки, поэтому вы хотите загрузчика.
Вы можете заставить пользователя подумать, что есть загрузчик.
<?php // Loader last id $loader_id = 0; // Initiate loader function initLoader(){ echo '<style>.php_loader{width:100px;height:50px;line-height:50px;margin-top:-25px;margin-left:-50px;position:fixed;top:50%;left:50%;}</style>'; // Load first loader echo '<div id="php_loader_'.$GLOBALS['loader_id'].'" class="php_loader">0%</div>'; } // Upadate loader function updateLoader($percent){ // Remove last loader clearLoader(); // Insert new loader $GLOBALS['loader_id']++; echo '<div id="php_loader_'.$GLOBALS['loader_id'].'" class="php_loader">'.$percent.'%</div>'; ob_flush(); flush(); } function clearLoader(){ // Remove last loader echo '<style>#php_loader_'.$GLOBALS['loader_id'].'{display:none;}</style>'; } ?> <html> <head> <title>Server Side Loader</title> </head> <body style="padding:30px; font-size:30px;font-family: Courier;"> <?php // On body start we init loader initLoader(); sleep(1); ?> 25% off the page just loaded</br> <?php updateLoader(25); sleep(1); ?> 30% off the page just loaded</br> <?php updateLoader(30); sleep(1); ?> 50% off the page just loaded</br> <?php updateLoader(50); sleep(1); ?> 80% off the page just loaded</br> <?php updateLoader(80); sleep(1); ?> 100% off the page just loaded</br> <?php updateLoader(100); sleep(1); // Clear loader before body clearLoader(); ?> </body> </html>
Хороший и гладкий.
Это действительно зависит от того, как вы выполняете загрузку, но что можете сделать, если вы действительно хотите использовать AJAX js для сохранения открытого соединения и каждого обновления из обработки на внутреннем сервере, чтобы обновить интерфейс с помощью JS, который обновил правильную отображать виджет / поле в обратном вызове AJAX .
Если вы просто хотите сделать это с помощью PHP, вы можете легко сделать следующее:
<?php $x=1; while($x<=100) { echo "Loading: $x %<br>"; $x++; <CALL THE FUNCTION THAT DOES THE ACTUAL PROCESSING HERE> ob_end_clean(); }
Чтобы добавить к этому вопросу, вы можете использовать выходную буферизацию, если вы действительно хотели этого достичь (это не рекомендуется). Единственное ограничение заключается в том, что вы не можете перезаписать предыдущий вывод. Это больше похоже на индикатор выполнения:
<?php while(@ob_get_clean()); echo "Loading: "; for($i=0; $i<10; $i++) { echo str_pad("+", 4096); usleep(200000); } echo "Done."; ?>
в<?php while(@ob_get_clean()); echo "Loading: "; for($i=0; $i<10; $i++) { echo str_pad("+", 4096); usleep(200000); } echo "Done."; ?>
См. Вывод вывода в частях на PHP .
Решение, не лучшее для разработчика, но оно будет работать именно так, как вы хотите. Вы сценарий в порядке. Но если вы видите весь результат вообще, и вы не видите прогресс, это потому, что есть буферный механизм. Возможно, php, возможно, веб-сервер (apache …) или даже, возможно, буфер браузера.
Взгляните на флеш-функцию flush (). Чтобы проверить, вам нужно использовать «браузер командной строки» (возможно, запрос telnet / GET вручную). Если в командной строке отображается строка, следующая за строкой, то проблема связана с вашим веб-браузером.
Но сначала вам нужно определить, где WHERE – текущий буферный механизм: php output? (см. функцию flush php), сжатие веб-сервера? (см. сжатие gzip), возможно, буферизация браузера? (хром ждет окончания файла, прежде чем показывать результаты)