如何显示加载百分比以及如何在没有javascript的情况下执行此操作?

Mar*_*ama 7 javascript php loops loading polling

我想在PHP中创建类似于加载器的东西,所以我使用了这段代码:

<?php 
$x=1;
while($x<=100) {
   echo "Loading: $x %<br>";
   $x++;
}   
?>
Run Code Online (Sandbox Code Playgroud)

这样它将显示从"加载1%"到"加载100%".但是,这将导致所有出现的一个在新线出现后不会消失.因此,我想知道如何使新行显示并且旧的消失,并且这在页面加载后开始,因此用户将能够观看实际加载的加载器从1%到100%.

更新:我知道我应该使用JS和/或Ajax来实现它,我只是想知道是否有办法在PHP中也这样做:)

Anr*_*nri 38

PHP是一种服务器端语言,它根据请求为您提供响应.

你无法在DOM上操纵(就像你想要的那样).

为此你应该使用javascript和AJAX来改变基于PHP结果的DOM元素

  • "PHP是一种服务器端语言......你无法在DOM上操纵(就像你想要的那样)." (7认同)
  • (我实际上是在回答你的回答,不仅因为它是正确的,而且还让你拥有罕见的反转徽章,显然是你的第一个金徽章!祝贺:-)) (7认同)
  • @MarwanOssama不会.一旦PHP向浏览器发送了一些东西,那就是它; PHP无法更改或以其他方式修改发送的内容. (2认同)

Ala*_*blo 28

遵循长时间运行的任务很常见,但第一次实施起来并不容易.这是一个完整的例子.

一个长期运行任务的样本 (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++;
  }
Run Code Online (Sandbox Code Playgroud)

用户界面

你漂亮的UI看起来像这样:

一个美丽的ui

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>
Run Code Online (Sandbox Code Playgroud)

发射器

我们需要在后台启动任务,以使此演示相关.因此,当单击"运行"时,将异步调用此PHP脚本,并将在后台执行上述任务.

PHP 任务-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 &");
Run Code Online (Sandbox Code Playgroud)

问题

问题

这里有3个问题:

  1. 你可以通过点击几次运行按钮多次运行任务,如何在后台同时避免多个任务?

  2. 任务在服务器端运行,因此我们需要做一些事情来访问服务器并询问进度信息.

  3. 当我们将连接到服务器端时,该$progress变量不可用于读取,因为它存储在task.php正在运行的实例的上下文中.


解决方案

解决方案

将进度信息存储在外部可读的内容中

通常,进度信息存储在数据库或文件中,或者程序可以写入的任何内容中(您的任务可行),并且可以被其他人(您应该显示进展的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;
Run Code Online (Sandbox Code Playgroud)

重要说明:此处some_file.txt是存储任务共享数据的位置,因此如果每个用户都有自己的任务,请不要犹豫使用"task_ [user_id] .txt".并查看github上的自述文件以优化文件访问.

使用synchronized变量来保护任务启动器

  • 在任务开始时将进度设置为0,因此首先要做的是在运行任务之前检查此进程未设置为0.

PHP 任务-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 &");
}
Run Code Online (Sandbox Code Playgroud)
  • 如果快速点击两次运行按钮,我们仍然可以有2个任务实例.为了处理这种情况,我们需要模拟一个互斥体,总之,使变量只对当前应用程序可用来做一些事情 - 其他应用程序将保持睡眠状态,直到共享变量被解锁.

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;
Run Code Online (Sandbox Code Playgroud)

警告:如果您的任务崩溃并且永远不会到达终点,您将永远无法再启动它.为了避免这种情况,您还可以将子项getmypid()和一些内容存储在time()共享变量中,并在任务中添加超时逻辑.

使用轮询来询问服务器进度信息

轮询代表每过一段时间(例如,1秒,5秒或其他)向服务器询问进展信息.总之,客户端每隔N秒向服务器询问进度信息.

  • 在服务器端,我们需要对处理程序进行编码以回答进度信息.

PHP 任务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
}
Run Code Online (Sandbox Code Playgroud)
  • 在客户端,我们需要编写"向服务器询问进度信息"业务

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>
Run Code Online (Sandbox Code Playgroud)

有用!


用最少的JavaScript?

我还开发了一个jquery插件,domajax,打算做"没有javascript的ajax"(实际上,插件本身是在jQuery中,但使用它不需要JavaScript代码),并且通过组合选项,你可以进行轮询.

在我们的演示中:

  • 追随者变成:

PHP 任务follow.php

<?php

require("Sync.php");

$shared = new Sync("some_file.txt");

if ($shared->progress !== null) {
    echo $shared->progress;
}
Run Code Online (Sandbox Code Playgroud)
  • UI源变为:

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>
Run Code Online (Sandbox Code Playgroud)

如您所见,此代码中根本没有可见的javascript.干净不是吗?

其他示例可在domajax的网站找到,请查看演示窗格中的"轻松管理进度条"选项卡.所有选项都有详细记录以供详细说明

  • 真是个答案! (5认同)

Jim*_*ell 27

你实际上不需要 Javascript来完成这个.如果您正在等待的任务是持久的,您只需创建一个PHP脚本:

  1. 查找任务(例如在DB中)
  2. 检查任务的完成百分比
  3. Echo是用户的百分比
  4. 如果百分比<100%
    • 刷新页面
    • 否则,重定向到完成页面.

您可以使用HTML元刷新进行非JavaScript刷新.它可能看起来像这样(在10年内没有完成PHP,所以像伪代码一样对待它):

<?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>
Run Code Online (Sandbox Code Playgroud)

另一种方法,一个非常难看的黑客,将使用PHP的flush()方法.Flush会强制当前输出缓冲区运行PHP(例如CGI).此输出有时会被主机应用程序进一步缓冲,但在某些Web服务器上,它会将输出直接发送给用户.在这里阅读更多: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();
}
?>
Run Code Online (Sandbox Code Playgroud)

这两种解决方案都很糟糕,我只想分享它们的完整性:)


Lif*_*ery 11

详细说明这里的答案;

PHP是一种服务器端语言,但这不是它通常无法正常工作的原因.

这与我们通过HTTP协议进行通信的方式有关.
当客户端向服务器发送HTTP请求时,首先发生的是他们建立连接.然后,服务器处理请求并生成响应,然后将响应发送回客户端.
一旦收到响应,连接就会消失.
因此,通常,每个请求都会收到一个响应.

一个常见的误解是使用现在默认的keep-alive标头进行持久连接,您可以在套接字保持打开时继续推送内容.HTTP实际上不会以这种方式工作,并且在您可以流式传输数据之前需要更多操作.
但更多关于这里这里.

你能做什么?

我能想到几个选项.

  1. 使用客户端语言(通常是JavaScript),我们可以继续发送XMLHttpRequest对象,以根据我们收到的每个响应来查询服务器,从而有效地创建媒体流.
  2. Comet是一个相当广泛的术语,允许Web服务器通过HTTP请求将数据推送回客户端.APE就是其中的一个实现.
  3. 使用Ratchet查看Web套接字.
  4. 从理论上讲,如果您了解客户端的地址,您可以在双方编写可接受cURL和XHR的API.在这种情况下,双方将充当客户端和服务器,并可以在同一级别进行通信.这可以通过PHP实现,但不是浏览器的最佳解决方案.

最后,您可能想要阅读通过其中一些技术的流行答案.


jas*_*sel 7

更简单:

<style> div:not(:last-child) { display:none } </style>
<?php 
$x=1;
while($x<=100) {
   echo "<div>Loading: $x %</div>";
   $x++;
}   
?>
Run Code Online (Sandbox Code Playgroud)

这样,当新的<div>s出现时,旧s将被隐藏.