When we run the PHP script as a Web, by default, even if you close the current page, the program will continue to execute, direct program end or timeout. What do we need to do if we want to break the program when the user closes the page or clicks the Stop page to run? This issue was discussed with Xiao Yi students last week, which leads us to this article today.
We know that the HTTP protocol is based on the TCP/IP protocol, the request for a PHP page is an HTTP request (assuming we are an Apache server), which creates a TCP connection and gives the server an abort state when the user interrupts the request. This abort state is the key point we're going to talk about today.
There is a function in PHP that is related to the Abort state: Ignore_user_abort function
The Ignore_user_abort () function sets whether execution of the script terminates when the client disconnects. It returns the Boolean value that was set before User-abort. Its parameters are optional. If set to true, disconnecting from the user is ignored and setting to false causes the script to stop running.
PHP does not detect whether a user has been disconnected until an attempt is made to send a message to the client. So if we just use the echo statement, we may not be able to see the effect of abort as it is, because PHP has a cache at output and you can use the flush () function if you want to flush the cache.
The following code 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);}
Execute this code in the browser, after about two seconds, close the requested page, 50 seconds later, you will find that at least 50 numbers are written in the Data.txt file. This means that our interrupt operation is invalid.
If we change it, replace the first sentence with the following: Ignore_user_abort (FALSE); Repeat the above, and you will find that only a very small number is written, which means that our interrupt operation is valid.
Now, with the Ignore_user_abort function, we have implemented a user interrupt to stop the program immediately. Here's the problem that we need to keep flush, update the connection state with the flush function, and when the state is abort, the program determines whether or not to interrupt the program according to the Ignore_user_abort settings. In addition, we can also use direct access to the connection state to check the connection status, and the specific status of processing, the following code:
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);}
The function of the connection_status here is to get the state of the connection, when the state of the connection is not normal, we interrupt the loop, which also achieves the operation of the interrupt program. This example differs from the previous example in that the interrupt operation is controlled by ourselves, rather than by the flush operation of the direct exit. This is a more appropriate approach if there are other actions after the user interrupts. Of course, the flush operation here is still not very small, we still need to do check operation through this function.
Both the Ignore_user_abort function and the Connection_status function implement our purpose, are there any correlations between the implementations of the two functions? The implementations of these two functions are found in the EXT/STANDARD/BASIC_FUNCTIONS.C file:
/* {{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);} /* }}} */
The Connection_status function returns the value of PG (CONNECTION_STATUS) directly,
The Ignore_user_abort function re-sets the value of PG (IGNORE_USER_ABORT),
Either because the cache is full of automatic calls or flush operations that are called through the flush function, it will ultimately determine whether the Php_handle_aborted_connection function is executed based on the connection state, and if it is the abort state, execute.
The code is as follows:
/* {{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 ();}} /* }}} */
When the PG (IGNORE_USER_ABORT) is false, that is, if the user's interrupt behavior is not ignored, if this function is called, the Zend_bailout function is used to exit the program directly.
By default, Ignore_user_abort is 0, which means that the user's interrupt behavior is not ignored.
If you are Ubuntu's default Apache environment, the above code may be invalid. This is because Apache in this environment has opened the ZIP, the server does not communicate with the client when the intended size is reached, and thus cannot get the state of the client, even if the flush function is used.