<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>潘锦的空间 &#187; 中断</title>
	<atom:link href="https://www.phppan.com/tag/%e4%b8%ad%e6%96%ad/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.phppan.com</link>
	<description>SaaS SaaS架构 团队管理 技术管理 技术架构 PHP 内核 扩展 项目管理</description>
	<lastBuildDate>Sun, 12 Apr 2026 03:47:23 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=3.9.40</generator>
	<item>
		<title>如何在用户中断时停止程序的运行</title>
		<link>https://www.phppan.com/2012/02/stop-program-by-user-abort/</link>
		<comments>https://www.phppan.com/2012/02/stop-program-by-user-abort/#comments</comments>
		<pubDate>Mon, 27 Feb 2012 00:59:34 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[connection_status]]></category>
		<category><![CDATA[ignore_user_abort]]></category>
		<category><![CDATA[PHP源码]]></category>
		<category><![CDATA[PHP源码阅读笔记]]></category>
		<category><![CDATA[中断]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1612</guid>
		<description><![CDATA[当我们以WEB的方式运行PHP脚本时，默认情况下，即使你关闭当前页面，程序也会继续执行，直接程序结束或超时。如 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>当我们以WEB的方式运行PHP脚本时，默认情况下，即使你关闭当前页面，程序也会继续执行，直接程序结束或超时。如果我们想在用户关闭页面或点击了停止页面运行时就中断程序，我们需要做些什么呢？上周和小毅同学讨论了这个问题，从而也引出了今天我们这篇文章。</p>
<p>我们知道HTTP协议是基于TCP/IP协议，对于一个PHP页面的请求就是一个HTTP请求（假设我们是Apache服务器），从而会创建TCP连接，当用户中断请求时，会给服务器一个abort状态。这个abort状态就是今天我们要讲的关键点。</p>
<style>
.entry p {margin:13px 5px 0 5px;}
</style>
<p>在PHP中有一个函数与abort状态有关：ignore_user_abort函数<br />
ignore_user_abort() 函数设置与客户机断开时是否会终止脚本的执行。它返回 user-abort 之前设置的布尔值。它的参数可选。如果设置为 true，则忽略与用户的断开，如果设置为 false，会导致脚本停止运行。</p>
<p>PHP 不会检测到用户是否已断开连接，直到尝试向客户机发送信息为止。因此如果我们只是使用echo语句，可能无法如实的看到abort的效果，因为PHP在输出时会有一个缓存，如果要刷新缓存，则可以使用flush() 函数。</p>
<p>如下代码t.php：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;"><span style="color: #990000;">ignore_user_abort</span><span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">TRUE</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #990000;">set_time_limit</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">50</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">echo</span> <span style="color: #000088;">$i</span><span style="color: #339933;">++,</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>    
    <span style="color: #990000;">flush</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000088;">$fp</span> <span style="color: #339933;">=</span> <span style="color: #990000;">fopen</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;data.txt&quot;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'a'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #990000;">fwrite</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$fp</span><span style="color: #339933;">,</span> <span style="color: #000088;">$i</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">&quot; <span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #990000;">fclose</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$fp</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #990000;">sleep</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>在浏览器中执行这段代码，过了大概两秒后，关闭请求的页面，50秒后，你会发现在data.txt文件中写入了至少50个数。这表示我们的中断操作是无效的。<br />
如果我们改一下，把第一句改为：ignore_user_abort(FALSE);，重复上面的操作，你会发现，只写入了极少的数字，这表示我们的中断操作有效了。<br />
现在通过ignore_user_abort函数，我们实现了用户中断就马上停止程序的操作。这里有一个问题，即我们需要不停的flush，通过flush函数来更新连接状态，当状态为abort时，程序根据ignore_user_abort的设置来判断是否中断程序。除此之外，我们也可以使用直接获取连接状态来check连接状态，并对特定的状态作出处理，如下代码：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;"><span style="color: #990000;">ignore_user_abort</span><span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">FALSE</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #990000;">set_time_limit</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">50</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #b1b100;">echo</span> <span style="color: #000088;">$i</span><span style="color: #339933;">++,</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
    <span style="color: #990000;">flush</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
     <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #990000;">connection_status</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> CONNECTION_NORMAL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">break</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #000088;">$fp</span> <span style="color: #339933;">=</span> <span style="color: #990000;">fopen</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;data.txt&quot;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'a'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #990000;">fwrite</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$fp</span><span style="color: #339933;">,</span> <span style="color: #000088;">$i</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">&quot;:&quot;</span> <span style="color: #339933;">.</span> <span style="color: #990000;">connection_status</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">&quot; <span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #990000;">fclose</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$fp</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #990000;">sleep</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>这里的connection_status函数的作用是获取连接的状态，当连接的状态非normal时，我们就中断循环，从而也达到了中断程序的操作。这个示例与前面的示例不同之处在于中断操作是由我们自己控制，而不是通过flush操作直接exit。如果在用户中断后还有一些其它的操作，这种方式会更合适一些。当然，这里的flush操作依旧不可少，我们还是需要通过这个函数做check操作。</p>
<p>ignore_user_abort函数和connection_status函数都实现了我们的目的，这两个函数的实现有没有关联？我们在ext/standard/basic_functions.c文件中找到这两个函数的实现如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/* {{{ proto int connection_aborted(void)
&nbsp;
Returns true if client disconnected */</span>
PHP_FUNCTION<span style="color: #009900;">&#40;</span>connection_aborted<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    RETURN_LONG<span style="color: #009900;">&#40;</span>PG<span style="color: #009900;">&#40;</span>connection_status<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span> PHP_CONNECTION_ABORTED<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #808080; font-style: italic;">/* }}} */</span>
&nbsp;
<span style="color: #808080; font-style: italic;">/* {{{ proto int connection_status(void)
Returns the connection status bitfield */</span>
PHP_FUNCTION<span style="color: #009900;">&#40;</span>connection_status<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    RETURN_LONG<span style="color: #009900;">&#40;</span>PG<span style="color: #009900;">&#40;</span>connection_status<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #808080; font-style: italic;">/* }}} */</span>
&nbsp;
<span style="color: #808080; font-style: italic;">/* {{{ proto int ignore_user_abort([string value])
Set whether we want to ignore a user abort event or not */</span>
PHP_FUNCTION<span style="color: #009900;">&#40;</span>ignore_user_abort<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>arg <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> arg_len <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> old_setting<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>zend_parse_parameters<span style="color: #009900;">&#40;</span>ZEND_NUM_ARGS<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> TSRMLS_CC<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;|s&quot;</span><span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>arg<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>arg_len<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> FAILURE<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">return</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    old_setting <span style="color: #339933;">=</span> PG<span style="color: #009900;">&#40;</span>ignore_user_abort<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>arg<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        zend_alter_ini_entry_ex<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;ignore_user_abort&quot;</span><span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;ignore_user_abort&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> arg<span style="color: #339933;">,</span> arg_len<span style="color: #339933;">,</span> PHP_INI_USER<span style="color: #339933;">,</span>     PHP_INI_STAGE_RUNTIME<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span> TSRMLS_CC<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    RETURN_LONG<span style="color: #009900;">&#40;</span>old_setting<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #808080; font-style: italic;">/* }}} */</span></pre></td></tr></table></div>

<p>connection_status函数直接返回PG(connection_status)的值，</p>
<p>ignore_user_abort函数重新设置PG(ignore_user_abort)的值，</p>
<p>不管是因为缓存满自动调用或通过flush函数调用的flush操作，其最终都会根据连接状态判断是否执行php_handle_aborted_connection函数，如果是abort状态，则执行。</p>
<p>其代码如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/* {{{ php_handle_aborted_connection
*/</span>
PHPAPI <span style="color: #993333;">void</span> php_handle_aborted_connection<span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    TSRMLS_FETCH<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    PG<span style="color: #009900;">&#40;</span>connection_status<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> PHP_CONNECTION_ABORTED<span style="color: #339933;">;</span>
    php_output_set_status<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">0</span> TSRMLS_CC<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>PG<span style="color: #009900;">&#40;</span>ignore_user_abort<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        zend_bailout<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #808080; font-style: italic;">/* }}} */</span></pre></td></tr></table></div>

<p>在PG(ignore_user_abort)为假时，即不忽略用户的中断行为时，如果调用了此函数，则使用zend_bailout函数跳出程序直接exit。</p>
<p>在默认情况下ignore_user_abort为0，即不忽略用户的中断行为。</p>
<p>如果你是ubuntu的默认apache环境下，可能上面的代码会无效。这是由于此环境下的apache开启了zip，在没有达到预定的大小时，服务器不会与客户端通信，从而也就无法获取客户端的状态，即使使用了flush函数也是一样。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2012/02/stop-program-by-user-abort/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
