2011-09-28 63 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