2011-09-28 92 views
2

我有一个PHP脚本(我们称之为execute.php),它在开始时绘制整个页面(HTML标签和body标签等),并且在一开始就执行一些命令(C++程序)背景。然后它会等待这些程序终止(有些依赖于其他人的结果,因此可能会按顺序执行),然后有一个JavaScript自动将表单提交给另一个PHP脚本(我们称之为results.php),因为results.php需要来自前面脚本的POST信息。PHP脚本意外不会继续

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的自动提交。

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

我也尝试过使用set_time_limit(0)ignore_user_abort(true)以及其他不同的东西,比如写入输出缓冲区(不想混乱已经显示的网页)而不是echo,但这些都不起作用。

当我在具有多个内核的机器上运行相同的脚本,以便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++ - 程序调用,等待例程,最后,通过“包括(” results.php“)”创建的结果页。 同样,对于“不太长”的程序执行,自动提交按预期工作,但如果需要“更长”,则不会。 “更长”我的意思是大约25分钟。

我完全不知道什么可能导致这一点再次没有错误信息被发现。 我是否缺少一个关键的配置选项(Apache,PHP等)?

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

+0

你是如何调用'execute.php'?通过'http'请求或命令行? –

+0

通过http请求。 – Shadow

回答

3

它发生,我认为更好的办法是:

  1. 输出的完整的HTML页面
  2. 显示加载信息给用户
  3. 发送AJAX请求开始外部程序
  4. 等待回拨(等待外部程序完成)
  5. 重复步骤3和4,直到所有程序都已执行
  6. 更新的页面,告诉什么是对
  7. 去用户提交表单

这样,你得到的HTML给用户尽可能快地,那么你在有序和可控执行程序顺序时尚而不用担心打到门槛。这也可以让你保持你的用户通知 - 在每次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 ......如果没有,它会提交表格。

+0

这将是一个更好的方法。 –

+0

谢谢。这绝对是更协调和更好。由于我对网络编程相当陌生,并且已经设法通过HTML,PHP和几行JavaScript实现了所有的工作,所以我对AJAX并不熟悉。因此,我可以用一个例子来建立。可能有很多例子,所以有一个与我现在解释的我接近的例子会很棒。 – Shadow

+0

我啄了一个快速而脏的例子,看看编辑。 –

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服务器以监控作业的进度。

+0

谢谢。我知道你提到的一些问题,并且建议采取不同的方式。然而,我不知道如何真正地“客户端代码ping服务器”,所以更多的信息将受到欢迎。尽管AJAX可能是一条可行的路线,但我始终对可能不涉及其他“图层”的解决方案感兴趣,所以坚持到目前为止所取得的成果。 – Shadow

1

从网络服务器PHP进程启动和管理多个和长时间运行的进程充满了complications and complexity。它在不同的平台上也有很大的不同(你没有说你使用的是哪一种)。

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

+0

我在Linux机器上使用Apache和PHP-5模块。 – Shadow