web-dev-qa-db-ja.com

致命的なエラーをキャッチする方法:PHPで最大実行時間が30秒を超えました

私は開発中のシステムをいじくり回しており、これを引き起こすことができました:

致命的なエラー:最大実行時間が30秒を超えています

私が非現実的なことをしているときに起こりましたが、それでもユーザーに起こる可能性がありました。

この例外をキャッチする方法があるかどうか誰もが知っていますか?読んでみましたが、誰もが許可されている時間を増やすことを提案しているようです。

54
ingh.am

唯一のオプションは、スクリプトの許可された実行時間を延長する(0に設定すると無限になりますが、これは推奨されません)か、新しいスレッドを生成して最善を期待します。

これがキャッチできない理由は、実際にはスローされないからです。実際にエラーを引き起こしたコードは1行もありません。むしろ、PHPは、「申し訳ありませんが、これは長すぎます。今すぐシャットダウンする時間です。」と言っています。 30秒の最大実行時間でそのエラーをキャッチし、さらに30秒かかります...不十分に設計されたプログラムでは、悪用されるやや厄介な機会が開かれます。少なくとも、 [〜 #〜] dos [〜#〜] 攻撃。

26
cwallenpoole

PHPドキュメンテーション(そう...少なくともその読者の1人)が次のように言ってみてください。

<?php 
function shutdown() 
 { 
     $a=error_get_last(); 
     if($a==null)   
         echo "No errors"; 
     else 
          print_r($a); 

 } 
register_shutdown_function('shutdown'); 
ini_set('max_execution_time',1 ); 
sleep(3); 
?>

次のリンクをご覧ください。

  1. http://www.php.net/manual/en/function.set-error-handler.php#106061
  2. http://www.php.net/manual/en/function.register-shutdown-function.php
72
TheJanOnline

これは例外ではなく、エラーです。例外とエラーには重要な違いがあります。何よりもまず、エラーはtry/catchセマンティクスではキャッチできません。

PHPスクリプトは短い実行時間のパラダイムに基づいて構築されているため、デフォルトでPHPは、スクリプトが30秒以上実行されている場合、無限ループに陥る必要があると想定して構成されます。終了しました。これは、誤ったPHPスクリプトが、偶然または悪意によってサービス拒否を引き起こすのを防ぐためです。

ただし、スクリプトは、デフォルトで割り当てられているよりも長い実行時間を必要とする場合があります。

set_time_limit()を使用するか、_max_execution_time_の値を変更することにより、最大実行時間の変更を試みることができます。 _php.ini_ファイルで制限を引き上げます。実行時間を0に設定して制限を完全に削除することもできますが、これは推奨されません。

set_time_limit()_disable_functions_ などのメカニズムによって無効にされる可能性があるため、利用できない可能性があり、同様にアクセスできない可能性があります_php.ini_に。これらの両方が該当する場合は、ホストに連絡して支援を求めてください。

1つの例外は、コマンドラインから実行されるPHPスクリプトです。これらの実行条件下では、PHPスクリプトは対話型である可能性があり、データの処理または入力の待機に長い時間を費やす必要があります。このため、デフォルトでコマンドラインから実行されるスクリプトには_max_execution_time_の制限はありません。


編集して追加:PHP 7のエラー処理は大幅に見直されました。エラーと例外は、両方ともThrowableのサブクラスになったと思います。これにより、上記がPHP7 +に関連しなくなる可能性がありますが、確実にエラー処理がどのように機能するかの詳細をさらに詳しく調べる必要があります。

18
GordonM

あなたがそれについてできることは何もありません。しかし、register_shutdown_functionを使用して正常にシャットダウンすることができます

<?php 

ini_set('display_errors', '0');         
ini_set("max_execution_time",15 ); //you can use this if you know your script should not take longer than 15 seconds to finish

register_shutdown_function('shutdown');


function shutdown() 
{ 
       $error = error_get_last();

       if ($error['type'] === E_ERROR) {

      //do your shutdown stuff here
      //be care full do not call any other function from within shutdown function
      //as php may not wait until that function finishes
      //its a strange behavior. During testing I realized that if function is called 
      //from here that function may or may not finish and code below that function
      //call may or may not get executed. every time I had a different result. 

      // e.g.

      other_function();

      //code below this function may not get executed

       } 

} 

while(true)
{

}

function other_function()
{
  //code in this function may not get executed if this function
  //called from shutdown function
} 

?>
8
pinkal vansia

ええ、TheJanOnlineでソリューションをテストしました。 sleep()はphpの実行時間にカウントされないので、ここに無限ループのWORKINGバージョンがあります:

<?php 
function shutdown() 
 { 
     $a=error_get_last(); 
     if($a==null)   
         echo "No errors"; 
     else 
          print_r($a); 

 } 
register_shutdown_function('shutdown'); 
ini_set('max_execution_time',1 ); 
while(1) {/*nothing*/}
// will die after 1 sec and print error
?>
5
Michal

特定の場合に例外として「致命的なエラー:最大30秒の実行時間を超過」を処理するための少し注意深い方法があります。

function time_sublimit($k = 0.8) {
    $limit = ini_get('max_execution_time'); // changes even when you set_time_limit()
    $sub_limit = round($limit * $k);
    if($sub_limit === 0) {
        $sub_limit = INF;
    }
    return $sub_limit;
}

コードでは、実行時間を測定し、タイムアウトの致命的なエラーがトリガーされる前に例外をスローする必要があります。 $ k = 0.8は許容実行時間の80%であるため、例外を処理する時間の20%があります。

try{
    $t1 = time(); // start to mesure time.
    while (true) { // put your long-time loop condition here
        time_spent = time() - $t1;
        if(time_spent >= time_sublimit()) {
            throw new Exception('Time sublimit reached');
        }
        // do work here
    }
} catch(Exception $e) {
    // catch exception here
}
4
user3901681

@ pinkal-vansiaの答えに基づいて、これを思いつきました。だから、私は元の答えを主張しているのではなく、実用的なアプリケーションでの答えを主張しています。タイムアウトが発生した場合にページを更新する方法が必要でした。コードが機能していることを知るのに十分なcURLスクリプトのタイムアウトを観察しているが、何らかの理由でリモートサーバーへの接続に失敗したり、提供されたhtmlを完全に読んだりすると、リフレッシュすると問題がなくなる最大実行タイムアウトエラーを「修正」するために、スクリプト自体を更新しても問題ありません。

<?php //script name: scrape_script.php

ini_set('max_execution_time', 300);

register_shutdown_function('shutdown');

function shutdown() 
{ 
    ?><meta http-equiv="refresh" content="0; url=scrape_script.php"><?php
    // just do a meta refresh. Haven't tested with header location, but
    // this works fine.
}

参考までに、実行しているスクレイピングスクリプトの300秒は長すぎません。これは、スクレイピングしている種類のページからデータを抽出するのにかかる時間よりもわずかに短い時間です。接続の不規則性が原因で、数秒しか経過しないこともあります。スクリプトの処理ではなく接続時間が失敗することがあることを知っているため、タイムアウトを増やすのではなく、ページを自動的に更新してから再試行することをお勧めします。

4
TARKUS