PHP脚本意外地没有继续

时间:2011-09-28 15:12:57

标签: php javascript html

我有一个PHP脚本(让我们称之为execute.php),它在开始时绘制整个页面(HTML标签和正文标签等),然后在后台执行一些命令(C ++程序) 。然后它等待这些程序终止(一些取决于其他程序的结果,因此它们可以按顺序执行)然后有一个JavaScript自动将表单提交给另一个PHP脚本(我们称之为results.php)因为results.php需要前一个脚本的POST信息。

execute.php

<?php
print"
<html>
<body>
   Some HTML code here
</body>
</html>
";

// Here come some C++-program calls
$pid_program1 = run_in_background($program1)
$pid_program2 = run_in_background($program2)
while (is_running($pid_program1) or is_running($pid_program2) )
{
  //echo(".");
  sleep(1);
}

// Here come some later C++-program calls that execute quickly
$pid_program3 = run_in_background($program3)
$pid_program4 = run_in_background($program4)
while (is_running($pid_program3) or is_running($pid_program4) )
{
  sleep(1);
}
...
// We are now finished 
print "
<form action=\"results.php\" id=\"go_to_results\" method=\"POST\">
<input type='hidden' name=\"session_id\" value=\"XYZ\">
</form>
<script type=\"text/javascript\">
AutoSubmitForm( 'go_to_results' );
</script>
";

如果C ++程序1和2快速执行,这很有效。但是,当他们花时间(总共大约25分钟)时,PHP脚本似乎无法继续。有趣的是,C ++程序3和4仍然执行并产生预期的输出等。

但是,当我在echo(".");之前的第一个while循环中放置sleep()时,它会一直有效,直到JavaScript自动提交为止。

所以在我看来,无论出于何种原因,当第一个while循环中没有输出时,剩余的PHP代码(包括自动提交)都不会发送。

我也尝试过使用set_time_limit(0)ignore_user_abort(true)以及其他不同的东西,比如写一个输出缓冲区(不想弄乱已经最终显示的网页)而不是回声,但这些都没有工作

当我在具有多个内核的计算机上运行相同的脚本时,可以并行执行program1和2,它也可以在没有echo(".")的情况下运行。

所以我目前非常困惑,无法在apache日志或PHP日志中找到任何错误消息,因此非常感谢您对此的想法。

修改 再次感谢您的建议到目前为止。 我现在采用了一个涉及(非常简单)AJAX的解决方案,这种方式肯定更好。 但是,如果C ++ - 程序执行需要“更长时间”,则不会自动提交到结果页面,这实际上是在这次创建的(之前没有这样做)。 基本上我所做的是:

    process.php:
    <?php
    $params = "someparam=1";
    ?>

    <html>
    <body>
    <script type="text/javascript">
function run_analyses(params){    
    // Use AJAX to execute the programs independenantly in the background
    // Allows for the user to close the process-page and come back at a later point to the results-link, w/o need to wait.
    if (window.XMLHttpRequest)
    {
        http_request = new XMLHttpRequest();
    }
    else
    {
        //Fallback for IE5 and IE6, as these don't support the above writing/code
        http_request = new ActiveXObject("Microsoft.XMLHTTP");
    }
    //Is http_request still false
    if (!http_request)
    {
        alert('Ende :( Kann keine XMLHTTP-Instanz erzeugen');
    }

    http_request.onreadystatechange=function(){
        if (http_request.readyState==4 && http_request.status==200){
            // Maybe used to display the progress of the execution
            //document.getElementById("output").innerHTML=http_request.responseText;

            // Call of programs is finished -> Go to the results-page
            document.getElementById( "go_to_results" ).submit();
        }
    };

    http_request.open("POST","execute.php",true);
    //Send the proper header information along with the request
    http_request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    http_request.setRequestHeader("Content-length", params.length);
    http_request.setRequestHeader("Connection", "close");
    http_request.send(params);
};
</script>

<?php
// Do some HTML-markup
...
// Start the programs!
print "
<script type=\"text/javascript\">
    run_analyses('".$params."');
</script>
<form action=\"results.html" id=\"go_to_results\" method=\"POST\">
<input type='hidden' name=\"session_id\" value=\"XYZ\">
</form>
?>
</html>
</body>

和execute.php包含C ++ - 程序调用,等待例程,最后通过“include(”results.php“)”创建结果页面。 同样,对于“不太长”的程序执行,自动提交按预期工作,但如果需要“更长”则不行。 “更长”的意思是大约25分钟。

我完全不知道是什么原因导致这种情况再次发生,没有找到错误消息。 我在那里错过了一个关键的配置选项(apache,php等)?

修改 事实证明,让所请求的PHP脚本重复“回显”某些内容可以防止超时。所以它与没有AJAX的PHP解决方案基本相同,但这次,由于不一定需要AJAX请求的responseText,因此进度页面不会混乱,可以用作解决方法。具体而言,我不一定会推荐它作为一般解决方案或良好实践。

3 个答案:

答案 0 :(得分:3)

我觉得更好的方法是:

  1. 输出完整的HTML页面
  2. 向用户显示加载消息
  3. 发送AJAX请求以启动外部程序
  4. 等待回调(等待外部程序完成)
  5. 重复步骤3和4,直到所有程序都已执行
  6. 更新页面以告知用户正在进行的操作
  7. 提交表格
  8. 这样,您可以尽快将HTML提供给用户,然后以有序和受控的方式顺序执行程序,而不必担心达到max_execution_time阈值。这也使您能够通知用户 - 在每次AJAX回调之后,您可以告诉用户“程序ABC已完成,启动DEF ...”等等。

    修改

    根据请求,我将添加一个如何实现的概述。一个警告:如果你要为你的页面添加更多javascript派生的功能,你会考虑使用像jQuery或mootools这样的库(我个人最喜欢的)。这是你应该立即做出的决定 - 如果你不打算做很多javascript除了这个,那么一个库只会膨胀你的项目,但如果你要添加大量的javascript,你就不要我希望以后再回来并重新编写代码,因为你在项目中添加了3/4的库。

    我已经使用mootools来创建这个演示,但是如果这是你将要使用的唯一的东西,那么添加mootools是没有必要的,甚至是不可取的。我可以更容易地编写一个示例,而不必停下来思考:)

    首先是主页面。我们将此页面称为view.php。这应包含您的初始HTML以及将触发AJAX请求的JavaScript。基本上,整个jsFiddle都是view.php:http://jsfiddle.net/WPnEy/1/

    现在,execute.php看起来像这样:

    $program_name = isset($_POST['program_name']) ? $_POST['program_name'] : false;
    switch ($program_name) {
        case 'program1':
            $program_path = '/path/to/executable/';
            $friendly_name = 'Some program 1';
            break;
        case 'program2':
            $program_path = '/path/to/executable/';
            $friendly_name = 'Some program 2';
            break;
        case 'program3':
            $program_path = '/path/to/executable/';
            $friendly_name = 'Some program 3';
            break;
        case 'program4':
            $program_path = '/path/to/executable/';
            $friendly_name = 'Some program 4';
            break;
        default:
            die(json_encode(array(
                'program_name'=>'Invalid',
                'status'=>'FAILED',
                'error'->true,
                'error_msg'=>'Invalid program'
            )));
            break;
    }
    
    $pid = run_in_background($program_path)
    while (is_running(pid)) {
      sleep(1);
    }
    
    // check here for errors, get any error messages you might have
    $error = false;
    $error_msg = '';
    // use this for failures that are not necessarily errors...
    $status = 'OK';
    
    die(json_encode(array(
        'program_name'=>$friendly_name,
        'status'=>$status,
        'error'->$error,
        'error_msg'=>$error_msg
    )));
    
    然后为每个程序调用

    execute.php一次。 $friendly_program变量为您提供了一种回送内容供用户查看的方法。那里的switch语句确保不要求脚本执行你不期望的任何东西。程序被执行,完成后你会发送一些包含状态,友好名称,任何错误等的信息包。这会进入view.php的javascript,然后决定是否还有更多要运行的程序。如果是,则会再次致电execute.php ...如果没有,则会提交表单。

答案 1 :(得分:1)

这似乎相当复杂......而且风险很大。任何网络故障,用户的浏览器因任何原因关闭,甚至防火墙超时,这个脚本都会中止。

为什么不在后台运行整个事情?

<?php

session_start();
$_SESSION['background_run_is_done'] = false;
session_write_close(); // release session file lock

set_time_limit(0); 
ignore_user_abort(true); // allow job to keep running even if client disconnects.

.... your external stuff here ...

if ($successfully_completed) {
   session_start(); // re-open session file to update value
   $_SESSION['background_run_is_done'] = TRUE;
}

... use curl to submit job completion post here ...

?>

这会断开用户浏览器的状态与作业的处理。然后,您只需让客户端代码偶尔ping服务器以监控作业的进度。

答案 2 :(得分:1)

从Web服务器PHP流程启动和管理多个和长时间运行的流程充满了complications and complexity。它在不同的平台上也是非常不同的(你没有说你正在使用哪个)。

从PHP的执行中同步处理这些进程的调用并不是解决这个问题的方法。你真的需要在一个单独的会话组中运行程序 - 并使用(例如)Ajax或Comet来轮询它们的状态。