我有一个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,因此进度页面不会混乱,可以用作解决方法。具体而言,我不一定会推荐它作为一般解决方案或良好实践。
答案 0 :(得分:3)
我觉得更好的方法是:
这样,您可以尽快将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来轮询它们的状态。