WEB モードで PHP スクリプトを実行すると、デフォルトでは、現在のページを閉じてもプログラムは実行を継続し、プログラムは直接終了するかタイムアウトになります。ユーザーがページを閉じたとき、またはクリックしてページの実行を停止したときにプログラムを中断したい場合は、どうすればよいでしょうか?先週、この問題についてクラスメートのシャオ・イーと話し合いました。それが今日の記事につながりました。
HTTP プロトコルは TCP/IP プロトコルに基づいていることがわかっています。PHP ページのリクエストは HTTP リクエストであるため、ユーザーがリクエストを中断すると、TCP 接続が作成されます。中止ステータスがサーバーに与えられます。この中断状態が、今日説明する重要なポイントです。
PHP には中止ステータスに関連する関数があります:ignore_user_abort 関数
ignore_user_abort() 関数は、クライアントから切断されたときにスクリプトの実行を終了するかどうかを設定します。以前に user-abort によって設定されたブール値を返します。そのパラメータはオプションです。 true に設定すると、ユーザーからの切断は無視されます。false に設定すると、スクリプトの実行が停止します。
PHP は、クライアントに情報を送信しようとするまで、ユーザーが切断したかどうかを検出しません。したがって、echo ステートメントだけを使用すると、PHP は出力時にキャッシュを保持するため、abort の効果を忠実に確認できない可能性があります。キャッシュを更新したい場合は、flush() 関数を使用できます。
次のコード t.php:
ignore_user_abort(TRUE);set_time_limit(50); while(1){echo$i++,"\r\n";flush(); $fp=fopen("data.txt",'a');fwrite($fp,$i." \r\n");fclose($fp); sleep(1);}
ブラウザでこのコードを実行し、約 2 秒後に要求されたページを閉じます。50 秒後に、data.txt ファイルに少なくとも 50 個の数字が書き込まれていることがわかります。これは、割り込み操作が無効であることを意味します。
これを変更して最初の文をignore_user_abort(FALSE); に変更し、上記の操作を繰り返すと、非常に小さな数値のみが書き込まれていることがわかります。これは、割り込み操作が有効であることを意味します。
これで、ignore_user_abort 関数を使用して、ユーザーが中断したときにプログラムの動作を即座に停止できるようになりました。ここで問題があります。つまり、継続的にフラッシュし、フラッシュ関数を通じて接続ステータスを更新する必要があります。ステータスが中止の場合、プログラムはignore_user_abortの設定に基づいてプログラムを中断するかどうかを決定します。さらに、次のコードに示すように、接続ステータスを直接取得して接続ステータスを確認し、特定のステータスを処理することもできます。
ignore_user_abort(FALSE);set_time_limit(50); while(1){ echo$i++,"\r\n";flush(); if(connection_status()!= CONNECTION_NORMAL){break;} $fp=fopen("data.txt",'a');fwrite($fp,$i.":".connection_status()." \r\n");fclose($fp); sleep(1);}
ここでの connection_status 関数の機能は、接続のステータスを取得することであり、接続のステータスが正常でない場合にループを中断し、それによってプログラムの動作も中断します。この例と前の例の違いは、フラッシュ操作によって直接終了するのではなく、割り込み操作が自分たちで制御されることです。このアプローチは、ユーザーが中断した後に他の操作がある場合により適しています。もちろん、ここでのフラッシュ操作は依然として不可欠であり、この機能によるチェック操作を実行する必要があります。
ignore_user_abort 関数と connection_status 関数は両方とも目的を達成します。これら 2 つの関数の実装には関連性がありますか?これら 2 つの関数の実装は、次のように ext/standard/basic_functions.c ファイルにあります。
/* {{{ proto int connection_aborted(void) Returns true if client disconnected */ PHP_FUNCTION(connection_aborted){ RETURN_LONG(PG(connection_status)& PHP_CONNECTION_ABORTED);}/* }}} */ /* {{{ proto int connection_status(void)Returns the connection status bitfield */ PHP_FUNCTION(connection_status){ RETURN_LONG(PG(connection_status));}/* }}} */ /* {{{ proto int ignore_user_abort([string value])Set whether we want to ignore a user abort event or not */ PHP_FUNCTION(ignore_user_abort){char*arg = NULL;int arg_len =0;int old_setting; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"|s",&arg,&arg_len)== FAILURE){return;} old_setting = PG(ignore_user_abort); if(arg){ zend_alter_ini_entry_ex("ignore_user_abort",sizeof("ignore_user_abort"), arg, arg_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME,0 TSRMLS_CC);} RETURN_LONG(old_setting);}/* }}} */
connection_status 関数は、PG (connection_status) の値を直接返します。
ignore_user_abort 関数は PG (ignore_user_abort) の値をリセットします。
キャッシュがいっぱいであるために自動的に呼び出されるのか、フラッシュ関数を介してフラッシュ操作が呼び出されるのかは、接続ステータスに基づいて php_handle_aborted_connection 関数を実行するかどうかを最終的に決定します。処刑される。
コードは次のとおりです:
/* {{{ php_handle_aborted_connection*/ PHPAPI void php_handle_aborted_connection(void){ TSRMLS_FETCH(); PG(connection_status)= PHP_CONNECTION_ABORTED; php_output_set_status(0 TSRMLS_CC); if(!PG(ignore_user_abort)){ zend_bailout();}}/* }}} */
PG (ignore_user_abort) が false の場合、つまりユーザーの割り込み動作が無視されない場合、この関数が呼び出された場合は、zend_bailout 関数を使用してプログラムを飛び出して直接終了します。
デフォルトでは、ignore_user_abort は 0 です。つまり、ユーザーの割り込み動作は無視されません。
ubuntu のデフォルトの Apache 環境を使用している場合、上記のコードは有効ではない可能性があります。これは、この環境の apache では zip が有効になっているため、所定のサイズに達しない場合、サーバーはクライアントと通信しないため、flush 関数を使用してもクライアントの状態を取得できません。